Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc24a33600 | ||
|
|
9bb5af9823 | ||
|
|
82eab9c8d0 | ||
|
|
3cca3801ca | ||
|
|
03408f404a | ||
|
|
92e7a3ca08 | ||
|
|
b27c7552d2 | ||
|
|
426257d4ae | ||
|
|
7d34d599bb | ||
|
|
067fe2d10f | ||
|
|
4759c2f92c | ||
|
|
ca26d8ce27 | ||
|
|
6c52cc3e26 | ||
|
|
e42d3a3abf | ||
|
|
884844bc23 | ||
|
|
f556db8c89 | ||
|
|
96d15b28c6 | ||
|
|
37f7afb426 | ||
|
|
7dd4e76c1d | ||
|
|
daa0deb1bf | ||
|
|
43bd733f0a | ||
|
|
70367e3e7c | ||
|
|
45f8343659 |
@@ -1,4 +1,16 @@
|
|||||||
# Changelog
|
# 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
|
## 0.10.4
|
||||||
+ With major thanks to @Adubbz for his work, the NCM system module has now been re-implemented.
|
+ 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.
|
+ This was a major stepping stone towards the goal of having implementations everything in the Switch's package1/package2 firmware.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
[subrepo]
|
[subrepo]
|
||||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||||
branch = master
|
branch = master
|
||||||
commit = a4ce117292cc86e951d82666f4e13bc1dc40443f
|
commit = 30f3e4c33d2767126c36a51b9259051c6c42d6b5
|
||||||
parent = 95d5375158f6df5376ce876e6ed8c22150ad88ff
|
parent = 82eab9c8d0093e4d8907a668e9335981f6f4949b
|
||||||
method = merge
|
method = merge
|
||||||
cmdver = 0.4.1
|
cmdver = 0.4.1
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ include $(DEVKITPRO)/libnx/switch_rules
|
|||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# options for code generation
|
# 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 SETTINGS = $(ATMOSPHERE_SETTINGS) -O2
|
||||||
export CFLAGS = $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
|
export CFLAGS = $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
|
||||||
export CXXFLAGS = $(CFLAGS) $(ATMOSPHERE_CXXFLAGS)
|
export CXXFLAGS = $(CFLAGS) $(ATMOSPHERE_CXXFLAGS)
|
||||||
|
|||||||
@@ -84,6 +84,10 @@ namespace ams::kern::arch::arm64 {
|
|||||||
return this->page_table.UnmapPages(addr, num_pages, state);
|
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 {
|
bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress address) const {
|
||||||
return this->page_table.GetPhysicalAddress(out, address);
|
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 GetAliasRegionStart() const { return this->page_table.GetAliasRegionStart(); }
|
||||||
KProcessAddress GetStackRegionStart() const { return this->page_table.GetStackRegionStart(); }
|
KProcessAddress GetStackRegionStart() const { return this->page_table.GetStackRegionStart(); }
|
||||||
KProcessAddress GetKernelMapRegionStart() const { return this->page_table.GetKernelMapRegionStart(); }
|
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 GetAddressSpaceSize() const { return this->page_table.GetAddressSpaceSize(); }
|
||||||
size_t GetHeapRegionSize() const { return this->page_table.GetHeapRegionSize(); }
|
size_t GetHeapRegionSize() const { return this->page_table.GetHeapRegionSize(); }
|
||||||
size_t GetAliasRegionSize() const { return this->page_table.GetAliasRegionSize(); }
|
size_t GetAliasRegionSize() const { return this->page_table.GetAliasRegionSize(); }
|
||||||
size_t GetStackRegionSize() const { return this->page_table.GetStackRegionSize(); }
|
size_t GetStackRegionSize() const { return this->page_table.GetStackRegionSize(); }
|
||||||
size_t GetKernelMapRegionSize() const { return this->page_table.GetKernelMapRegionSize(); }
|
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();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,9 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
/* Power management. */
|
/* Power management. */
|
||||||
static void SleepSystem();
|
static void SleepSystem();
|
||||||
static NORETURN void StopSystem();
|
static NORETURN void StopSystem();
|
||||||
|
|
||||||
|
/* User access. */
|
||||||
|
static void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -222,6 +222,8 @@ namespace ams::kern {
|
|||||||
KScopedAutoObject(o).Swap(*this);
|
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 IsNull() const { return this->obj == nullptr; }
|
||||||
constexpr ALWAYS_INLINE bool IsNotNull() const { return this->obj != nullptr; }
|
constexpr ALWAYS_INLINE bool IsNotNull() const { return this->obj != nullptr; }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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. */
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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. */
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -130,11 +130,11 @@ namespace ams::kern {
|
|||||||
MESOSPHERE_ASSERT_THIS();
|
MESOSPHERE_ASSERT_THIS();
|
||||||
|
|
||||||
/* Handle pseudo-handles. */
|
/* 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) {
|
if (handle == ams::svc::PseudoHandle::CurrentProcess) {
|
||||||
return GetCurrentProcessPointer();
|
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) {
|
if (handle == ams::svc::PseudoHandle::CurrentThread) {
|
||||||
return GetCurrentThreadPointer();
|
return GetCurrentThreadPointer();
|
||||||
}
|
}
|
||||||
@@ -156,11 +156,11 @@ namespace ams::kern {
|
|||||||
static_assert(!std::is_base_of<KInterruptEvent, T>::value);
|
static_assert(!std::is_base_of<KInterruptEvent, T>::value);
|
||||||
|
|
||||||
/* Handle pseudo-handles. */
|
/* 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) {
|
if (handle == ams::svc::PseudoHandle::CurrentProcess) {
|
||||||
return GetCurrentProcessPointer();
|
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) {
|
if (handle == ams::svc::PseudoHandle::CurrentThread) {
|
||||||
return GetCurrentThreadPointer();
|
return GetCurrentThreadPointer();
|
||||||
}
|
}
|
||||||
@@ -201,7 +201,7 @@ namespace ams::kern {
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
ALWAYS_INLINE void Register(ams::svc::Handle handle, T *obj) {
|
ALWAYS_INLINE void Register(ams::svc::Handle handle, T *obj) {
|
||||||
static_assert(std::is_base_of<KAutoObject, T>::value);
|
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:
|
private:
|
||||||
NOINLINE Result Add(ams::svc::Handle *out_handle, KAutoObject *obj, u16 type);
|
NOINLINE Result Add(ams::svc::Handle *out_handle, KAutoObject *obj, u16 type);
|
||||||
@@ -278,7 +278,7 @@ namespace ams::kern {
|
|||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr NOINLINE KAutoObject *GetObjectImpl(ams::svc::Handle handle) const {
|
constexpr ALWAYS_INLINE KAutoObject *GetObjectImpl(ams::svc::Handle handle) const {
|
||||||
MESOSPHERE_ASSERT_THIS();
|
MESOSPHERE_ASSERT_THIS();
|
||||||
|
|
||||||
/* Handles must not have reserved bits set. */
|
/* 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();
|
MESOSPHERE_ASSERT_THIS();
|
||||||
|
|
||||||
/* Index must be in bounds. */
|
/* Index must be in bounds. */
|
||||||
@@ -310,6 +310,49 @@ namespace ams::kern {
|
|||||||
*out_handle = EncodeHandle(index, entry->GetLinearId());
|
*out_handle = EncodeHandle(index, entry->GetLinearId());
|
||||||
return entry->GetObject();
|
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;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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. */
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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. */
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -16,14 +16,52 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <mesosphere/kern_common.hpp>
|
#include <mesosphere/kern_common.hpp>
|
||||||
#include <mesosphere/kern_k_synchronization_object.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>
|
#include <mesosphere/kern_slab_helpers.hpp>
|
||||||
|
|
||||||
namespace ams::kern {
|
namespace ams::kern {
|
||||||
|
|
||||||
|
class KClientPort;
|
||||||
|
class KProcess;
|
||||||
|
|
||||||
class KLightSession final : public KAutoObjectWithSlabHeapAndContainer<KLightSession, KAutoObjectWithList> {
|
class KLightSession final : public KAutoObjectWithSlabHeapAndContainer<KLightSession, KAutoObjectWithList> {
|
||||||
MESOSPHERE_AUTOOBJECT_TRAITS(KLightSession, KAutoObject);
|
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:
|
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. */
|
/* 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; }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,39 @@ namespace ams::kern {
|
|||||||
|
|
||||||
class KObjectName : public KSlabAllocated<KObjectName>, public util::IntrusiveListBaseNode<KObjectName> {
|
class KObjectName : public KSlabAllocated<KObjectName>, public util::IntrusiveListBaseNode<KObjectName> {
|
||||||
public:
|
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; }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,8 +191,6 @@ namespace ams::kern {
|
|||||||
KPageTableImpl &GetImpl() { return this->impl; }
|
KPageTableImpl &GetImpl() { return this->impl; }
|
||||||
const KPageTableImpl &GetImpl() const { 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 IsLockedByCurrentThread() const { return this->general_lock.IsLockedByCurrentThread(); }
|
||||||
|
|
||||||
bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr) {
|
bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr) {
|
||||||
@@ -245,6 +243,8 @@ namespace ams::kern {
|
|||||||
return this->GetImpl().GetPhysicalAddress(out, virt_addr);
|
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 SetMemoryPermission(KProcessAddress addr, size_t size, ams::svc::MemoryPermission perm);
|
||||||
Result SetProcessMemoryPermission(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);
|
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 *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 MapPageGroup(KProcessAddress address, const KPageGroup &pg, KMemoryState state, KMemoryPermission perm);
|
||||||
Result UnmapPageGroup(KProcessAddress address, const KPageGroup &pg, KMemoryState state);
|
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:
|
public:
|
||||||
KProcessAddress GetAddressSpaceStart() const { return this->address_space_start; }
|
KProcessAddress GetAddressSpaceStart() const { return this->address_space_start; }
|
||||||
KProcessAddress GetHeapRegionStart() const { return this->heap_region_start; }
|
KProcessAddress GetHeapRegionStart() const { return this->heap_region_start; }
|
||||||
KProcessAddress GetAliasRegionStart() const { return this->alias_region_start; }
|
KProcessAddress GetAliasRegionStart() const { return this->alias_region_start; }
|
||||||
KProcessAddress GetStackRegionStart() const { return this->stack_region_start; }
|
KProcessAddress GetStackRegionStart() const { return this->stack_region_start; }
|
||||||
KProcessAddress GetKernelMapRegionStart() const { return this->kernel_map_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 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 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 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 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 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:
|
public:
|
||||||
static ALWAYS_INLINE KVirtualAddress GetLinearVirtualAddress(KPhysicalAddress addr) {
|
static ALWAYS_INLINE KVirtualAddress GetLinearVirtualAddress(KPhysicalAddress addr) {
|
||||||
return KMemoryLayout::GetLinearVirtualAddress(addr);
|
return KMemoryLayout::GetLinearVirtualAddress(addr);
|
||||||
|
|||||||
@@ -16,14 +16,46 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <mesosphere/kern_common.hpp>
|
#include <mesosphere/kern_common.hpp>
|
||||||
#include <mesosphere/kern_k_synchronization_object.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>
|
#include <mesosphere/kern_slab_helpers.hpp>
|
||||||
|
|
||||||
namespace ams::kern {
|
namespace ams::kern {
|
||||||
|
|
||||||
class KPort final : public KAutoObjectWithSlabHeapAndContainer<KPort, KAutoObjectWithList> {
|
class KPort final : public KAutoObjectWithSlabHeapAndContainer<KPort, KAutoObjectWithList> {
|
||||||
MESOSPHERE_AUTOOBJECT_TRAITS(KPort, KAutoObject);
|
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:
|
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; }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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. */
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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. */
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -16,14 +16,52 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <mesosphere/kern_common.hpp>
|
#include <mesosphere/kern_common.hpp>
|
||||||
#include <mesosphere/kern_k_synchronization_object.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>
|
#include <mesosphere/kern_slab_helpers.hpp>
|
||||||
|
|
||||||
namespace ams::kern {
|
namespace ams::kern {
|
||||||
|
|
||||||
|
class KClientPort;
|
||||||
|
class KProcess;
|
||||||
|
|
||||||
class KSession final : public KAutoObjectWithSlabHeapAndContainer<KSession, KAutoObjectWithList> {
|
class KSession final : public KAutoObjectWithSlabHeapAndContainer<KSession, KAutoObjectWithList> {
|
||||||
MESOSPHERE_AUTOOBJECT_TRAITS(KSession, KAutoObject);
|
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:
|
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. */
|
/* 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; }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ namespace ams::kern {
|
|||||||
#define MESOSPHERE_UNUSED(...) ::ams::kern::UnusedImpl(__VA_ARGS__)
|
#define MESOSPHERE_UNUSED(...) ::ams::kern::UnusedImpl(__VA_ARGS__)
|
||||||
|
|
||||||
#ifdef MESOSPHERE_ENABLE_DEBUG_PRINT
|
#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
|
#else
|
||||||
#define MESOSPHERE_PANIC(...) do { MESOSPHERE_UNUSED(__VA_ARGS__); ::ams::kern::Panic(); } while(0)
|
#define MESOSPHERE_PANIC(...) do { MESOSPHERE_UNUSED(__VA_ARGS__); ::ams::kern::Panic(); } while(0)
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -150,6 +150,15 @@ namespace ams::kern::arch::arm64 {
|
|||||||
HandleUserException(context, esr, far, afsr0, afsr1, data);
|
HandleUserException(context, esr, far, afsr0, afsr1, data);
|
||||||
}
|
}
|
||||||
} else {
|
} 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");
|
MESOSPHERE_PANIC("Unhandled Exception in Supervisor Mode\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -327,4 +327,53 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
while (true) { /* ... */ }
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -71,6 +71,42 @@ namespace ams::kern::board::nintendo::nx::smc {
|
|||||||
args.x[7] = x7;
|
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) {
|
void CallPrivilegedSecureMonitorFunctionForInit(SecureMonitorArguments &args) {
|
||||||
/* Load arguments into registers. */
|
/* Load arguments into registers. */
|
||||||
register u64 x0 asm("x0") = args.x[0];
|
register u64 x0 asm("x0") = args.x[0];
|
||||||
@@ -188,4 +224,8 @@ namespace ams::kern::board::nintendo::nx::smc {
|
|||||||
while (true) { /* ... */ }
|
while (true) { /* ... */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args) {
|
||||||
|
CallUserSecureMonitorFunction(args);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -91,6 +91,8 @@ namespace ams::kern::board::nintendo::nx::smc {
|
|||||||
|
|
||||||
void NORETURN Panic(u32 color);
|
void NORETURN Panic(u32 color);
|
||||||
|
|
||||||
|
void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args);
|
||||||
|
|
||||||
namespace init {
|
namespace init {
|
||||||
|
|
||||||
void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
|
void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
|
||||||
|
|||||||
46
libraries/libmesosphere/source/kern_k_client_port.cpp
Normal file
46
libraries/libmesosphere/source/kern_k_client_port.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
107
libraries/libmesosphere/source/kern_k_object_name.cpp
Normal file
107
libraries/libmesosphere/source/kern_k_object_name.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1020,4 +1020,27 @@ namespace ams::kern {
|
|||||||
return ResultSuccess();
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
44
libraries/libmesosphere/source/kern_k_port.cpp
Normal file
44
libraries/libmesosphere/source/kern_k_port.cpp
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
96
libraries/libmesosphere/source/kern_k_server_port.cpp
Normal file
96
libraries/libmesosphere/source/kern_k_server_port.cpp
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
31
libraries/libmesosphere/source/libc/arch/arm64/asmdefs.h
Normal file
31
libraries/libmesosphere/source/libc/arch/arm64/asmdefs.h
Normal 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
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -61,6 +61,7 @@ QUICKREF
|
|||||||
/*SUPPRESS 20*/
|
/*SUPPRESS 20*/
|
||||||
void *
|
void *
|
||||||
//__inhibit_loop_to_libcall
|
//__inhibit_loop_to_libcall
|
||||||
|
__attribute__((weak))
|
||||||
memmove (void *dst_void,
|
memmove (void *dst_void,
|
||||||
const void *src_void,
|
const void *src_void,
|
||||||
size_t length)
|
size_t length)
|
||||||
@@ -169,6 +170,7 @@ QUICKREF
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
void *
|
void *
|
||||||
|
__attribute__((weak))
|
||||||
memcpy (void * dst0,
|
memcpy (void * dst0,
|
||||||
const void * __restrict src0,
|
const void * __restrict src0,
|
||||||
size_t len0)
|
size_t len0)
|
||||||
@@ -259,6 +261,7 @@ QUICKREF
|
|||||||
#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE)
|
#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE)
|
||||||
|
|
||||||
void *
|
void *
|
||||||
|
__attribute__((weak))
|
||||||
memset (void *m,
|
memset (void *m,
|
||||||
int c,
|
int c,
|
||||||
size_t n)
|
size_t n)
|
||||||
@@ -357,6 +360,7 @@ QUICKREF
|
|||||||
#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE)
|
#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE)
|
||||||
|
|
||||||
int
|
int
|
||||||
|
__attribute__((weak))
|
||||||
memcmp (const void *m1,
|
memcmp (const void *m1,
|
||||||
const void *m2,
|
const void *m2,
|
||||||
size_t n)
|
size_t n)
|
||||||
@@ -417,6 +421,228 @@ memcmp (const void *m1,
|
|||||||
#endif /* not PREFER_SIZE_OVER_SPEED */
|
#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
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
#endif
|
#endif
|
||||||
@@ -21,28 +21,112 @@ namespace ams::kern::svc {
|
|||||||
|
|
||||||
namespace {
|
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 ============================= */
|
/* ============================= 64 ABI ============================= */
|
||||||
|
|
||||||
Result GetInfo64(uint64_t *out, ams::svc::InfoType info_type, ams::svc::Handle handle, uint64_t info_subtype) {
|
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) {
|
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 ============================= */
|
/* ============================= 64From32 ABI ============================= */
|
||||||
|
|
||||||
Result GetInfo64From32(uint64_t *out, ams::svc::InfoType info_type, ams::svc::Handle handle, uint64_t info_subtype) {
|
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) {
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,61 @@ namespace ams::kern::svc {
|
|||||||
|
|
||||||
namespace {
|
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) {
|
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) {
|
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) {
|
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) {
|
Result ConnectToPort64From32(ams::svc::Handle *out_handle, ams::svc::Handle port) {
|
||||||
|
|||||||
@@ -21,6 +21,32 @@ namespace ams::kern::svc {
|
|||||||
|
|
||||||
namespace {
|
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) {
|
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) {
|
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) {
|
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) {
|
Result GetProcessList64From32(int32_t *out_num_processes, KUserPointer<uint64_t *> out_process_ids, int32_t max_out_count) {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ namespace ams::kern::svc {
|
|||||||
/* ============================= 64 ABI ============================= */
|
/* ============================= 64 ABI ============================= */
|
||||||
|
|
||||||
void CallSecureMonitor64(ams::svc::lp64::SecureMonitorArguments *args) {
|
void CallSecureMonitor64(ams::svc::lp64::SecureMonitorArguments *args) {
|
||||||
MESOSPHERE_PANIC("Stubbed SvcCallSecureMonitor64 was called.");
|
KSystemControl::CallSecureMonitorFromUser(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================= 64From32 ABI ============================= */
|
/* ============================= 64From32 ABI ============================= */
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ include $(DEVKITPRO)/libnx/switch_rules
|
|||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# options for code generation
|
# options for code generation
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE
|
DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE -D_GNU_SOURCE
|
||||||
SETTINGS := $(ATMOSPHERE_SETTINGS) -O2
|
SETTINGS := $(ATMOSPHERE_SETTINGS) -O2
|
||||||
CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
|
CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
|
||||||
CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -flto
|
CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -flto
|
||||||
|
|||||||
@@ -22,11 +22,15 @@
|
|||||||
/* Libstratosphere-only utility. */
|
/* Libstratosphere-only utility. */
|
||||||
#include "stratosphere/util.hpp"
|
#include "stratosphere/util.hpp"
|
||||||
|
|
||||||
|
/* Sadly required shims. */
|
||||||
|
#include "stratosphere/svc/svc_stratosphere_shims.hpp"
|
||||||
|
|
||||||
/* Critical modules with no dependencies. */
|
/* Critical modules with no dependencies. */
|
||||||
#include "stratosphere/ams.hpp"
|
#include "stratosphere/ams.hpp"
|
||||||
#include "stratosphere/os.hpp"
|
#include "stratosphere/os.hpp"
|
||||||
#include "stratosphere/dd.hpp"
|
#include "stratosphere/dd.hpp"
|
||||||
#include "stratosphere/lmem.hpp"
|
#include "stratosphere/lmem.hpp"
|
||||||
|
#include "stratosphere/mem.hpp"
|
||||||
|
|
||||||
/* Pull in all ID definitions from NCM. */
|
/* Pull in all ID definitions from NCM. */
|
||||||
#include "stratosphere/ncm/ncm_ids.hpp"
|
#include "stratosphere/ncm/ncm_ids.hpp"
|
||||||
@@ -54,7 +58,8 @@
|
|||||||
#include "stratosphere/spl.hpp"
|
#include "stratosphere/spl.hpp"
|
||||||
#include "stratosphere/updater.hpp"
|
#include "stratosphere/updater.hpp"
|
||||||
|
|
||||||
|
|
||||||
/* Include FS last. */
|
/* Include FS last. */
|
||||||
#include "stratosphere/fs.hpp"
|
#include "stratosphere/fs.hpp"
|
||||||
#include "stratosphere/fssrv.hpp"
|
#include "stratosphere/fssrv.hpp"
|
||||||
#include "stratosphere/fssystem.hpp"
|
#include "stratosphere/fssystem.hpp"
|
||||||
@@ -45,7 +45,8 @@
|
|||||||
#include <stratosphere/fs/fs_save_data_types.hpp>
|
#include <stratosphere/fs/fs_save_data_types.hpp>
|
||||||
#include <stratosphere/fs/fs_save_data_management.hpp>
|
#include <stratosphere/fs/fs_save_data_management.hpp>
|
||||||
#include <stratosphere/fs/fs_save_data_transaction.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_sd_card.hpp>
|
||||||
#include <stratosphere/fs/fs_signed_system_partition.hpp>
|
#include <stratosphere/fs/fs_signed_system_partition.hpp>
|
||||||
#include <stratosphere/fs/fs_system_data.hpp>
|
#include <stratosphere/fs/fs_system_data.hpp>
|
||||||
#include <stratosphere/fs/fs_system_save_data.hpp>
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <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);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -33,7 +33,7 @@ namespace ams::fs {
|
|||||||
public:
|
public:
|
||||||
SubStorage() : shared_base_storage(), base_storage(nullptr), offset(0), size(0), resizable(false) { /* ... */ }
|
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) {
|
SubStorage &operator=(const SubStorage &rhs) {
|
||||||
if (this != std::addressof(rhs)) {
|
if (this != std::addressof(rhs)) {
|
||||||
this->base_storage = rhs.base_storage;
|
this->base_storage = rhs.base_storage;
|
||||||
@@ -44,19 +44,19 @@ namespace ams::fs {
|
|||||||
return *this;
|
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->IsValid());
|
||||||
AMS_ABORT_UNLESS(this->offset >= 0);
|
AMS_ABORT_UNLESS(this->offset >= 0);
|
||||||
AMS_ABORT_UNLESS(this->size >= 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->IsValid());
|
||||||
AMS_ABORT_UNLESS(this->offset >= 0);
|
AMS_ABORT_UNLESS(this->offset >= 0);
|
||||||
AMS_ABORT_UNLESS(this->size >= 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->IsValid());
|
||||||
AMS_ABORT_UNLESS(this->offset >= 0);
|
AMS_ABORT_UNLESS(this->offset >= 0);
|
||||||
AMS_ABORT_UNLESS(this->size >= 0);
|
AMS_ABORT_UNLESS(this->size >= 0);
|
||||||
|
|||||||
20
libraries/libstratosphere/include/stratosphere/mem.hpp
Normal file
20
libraries/libstratosphere/include/stratosphere/mem.hpp
Normal 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>
|
||||||
@@ -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; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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 *));
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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 *)>;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
@@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -16,20 +16,25 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "os/os_common_types.hpp"
|
#include <stratosphere/os/os_common_types.hpp>
|
||||||
#include "os/os_memory_common.hpp"
|
#include <stratosphere/os/os_tick.hpp>
|
||||||
#include "os/os_managed_handle.hpp"
|
#include <stratosphere/os/os_memory_common.hpp>
|
||||||
#include "os/os_process_handle.hpp"
|
#include <stratosphere/os/os_memory_permission.hpp>
|
||||||
#include "os/os_random.hpp"
|
#include <stratosphere/os/os_memory_heap_api.hpp>
|
||||||
#include "os/os_mutex.hpp"
|
#include <stratosphere/os/os_memory_virtual_address_api.hpp>
|
||||||
#include "os/os_condvar.hpp"
|
#include <stratosphere/os/os_managed_handle.hpp>
|
||||||
#include "os/os_rw_lock.hpp"
|
#include <stratosphere/os/os_process_handle.hpp>
|
||||||
#include "os/os_semaphore.hpp"
|
#include <stratosphere/os/os_random.hpp>
|
||||||
#include "os/os_timeout_helper.hpp"
|
#include <stratosphere/os/os_mutex.hpp>
|
||||||
#include "os/os_event.hpp"
|
#include <stratosphere/os/os_condvar.hpp>
|
||||||
#include "os/os_system_event.hpp"
|
#include <stratosphere/os/os_rw_lock.hpp>
|
||||||
#include "os/os_interrupt_event.hpp"
|
#include <stratosphere/os/os_semaphore.hpp>
|
||||||
#include "os/os_thread.hpp"
|
#include <stratosphere/os/os_timeout_helper.hpp>
|
||||||
#include "os/os_message_queue.hpp"
|
#include <stratosphere/os/os_event.hpp>
|
||||||
#include "os/os_waitable_holder.hpp"
|
#include <stratosphere/os/os_system_event.hpp>
|
||||||
#include "os/os_waitable_manager.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>
|
||||||
|
|||||||
@@ -22,4 +22,12 @@ namespace ams::os {
|
|||||||
|
|
||||||
constexpr inline size_t MemoryBlockUnitSize = 0x200000;
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -101,10 +101,19 @@ namespace ams::os {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
NX_INLINE u32 GetCurrentThreadPriority() {
|
ALWAYS_INLINE s32 GetCurrentThreadPriority() {
|
||||||
u32 prio;
|
s32 prio;
|
||||||
R_ABORT_UNLESS(svcGetThreadPriority(&prio, CUR_THREAD_HANDLE));
|
R_ABORT_UNLESS(svcGetThreadPriority(&prio, CUR_THREAD_HANDLE));
|
||||||
return prio;
|
return prio;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: ThreadManager? */
|
||||||
|
ALWAYS_INLINE s32 GetCurrentProcessorNumber() {
|
||||||
|
return svcGetCurrentProcessorNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE s32 GetCurrentCoreNumber() {
|
||||||
|
return GetCurrentProcessorNumber();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -38,9 +38,9 @@ Service *amsBpcGetServiceSession(void) {
|
|||||||
|
|
||||||
Result amsBpcRebootToFatalError(void *ctx) {
|
Result amsBpcRebootToFatalError(void *ctx) {
|
||||||
/* Note: this takes in an sts::ams::FatalErrorContext. */
|
/* 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,
|
return serviceDispatch(&g_amsBpcSrv, 65000,
|
||||||
.buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcMapAlias | SfBufferAttr_FixedSize },
|
.buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcMapAlias | SfBufferAttr_FixedSize },
|
||||||
.buffers = { { ctx, 0x350 } },
|
.buffers = { { ctx, 0x450 } },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ namespace ams::diag {
|
|||||||
__builtin_unreachable();
|
__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
|
#ifdef AMS_ENABLE_DEBUG_PRINT
|
||||||
os::Mutex g_debug_log_lock;
|
os::Mutex g_debug_log_lock;
|
||||||
@@ -55,7 +55,7 @@ namespace ams::diag {
|
|||||||
svc::OutputDebugString(g_debug_buffer, strlen(g_debug_buffer));
|
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;
|
::std::va_list vl;
|
||||||
va_start(vl, format);
|
va_start(vl, format);
|
||||||
DebugLogImpl(format, vl);
|
DebugLogImpl(format, vl);
|
||||||
|
|||||||
@@ -241,6 +241,7 @@ namespace ams::fs {
|
|||||||
R_TRY(OpenSdCardCodeOrCodeFileSystemImpl(std::addressof(fsa), path, program_id));
|
R_TRY(OpenSdCardCodeOrCodeFileSystemImpl(std::addressof(fsa), path, program_id));
|
||||||
this->code_fs.emplace(std::move(fsa), program_id, is_specific);
|
this->code_fs.emplace(std::move(fsa), program_id, is_specific);
|
||||||
|
|
||||||
|
this->program_id = program_id;
|
||||||
this->initialized = true;
|
this->initialized = true;
|
||||||
|
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
|
|||||||
51
libraries/libstratosphere/source/fs/fs_device_save_data.cpp
Normal file
51
libraries/libstratosphere/source/fs/fs_device_save_data.cpp
Normal 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -222,7 +222,7 @@ namespace ams::kvdb {
|
|||||||
R_UNLESS(file_size <= static_cast<s64>(max_out_size), ResultBufferInsufficient());
|
R_UNLESS(file_size <= static_cast<s64>(max_out_size), ResultBufferInsufficient());
|
||||||
|
|
||||||
/* Read the value. */
|
/* 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));
|
R_TRY(fs::ReadFile(file, 0, out_value, value_size));
|
||||||
*out_size = value_size;
|
*out_size = value_size;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
344
libraries/libstratosphere/source/mem/mem_standard_allocator.cpp
Normal file
344
libraries/libstratosphere/source/mem/mem_standard_allocator.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -118,7 +118,7 @@ namespace ams::os::impl {
|
|||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
/* Continuously wait, until success. */
|
/* Continuously wait, until success. */
|
||||||
R_TRY_CATCH(svcWaitSynchronizationSingle(handle, U64_MAX)) {
|
R_TRY_CATCH(svcWaitSynchronizationSingle(handle, std::numeric_limits<u64>::max())) {
|
||||||
R_CATCH(svc::ResultCancelled) { continue; }
|
R_CATCH(svc::ResultCancelled) { continue; }
|
||||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::os::impl {
|
||||||
|
|
||||||
|
void SetMemoryPermissionImpl(uintptr_t address, size_t size, MemoryPermission perm);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
|
||||||
|
namespace ams::os::impl {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void SetMemoryPermissionBySvc(uintptr_t start, size_t size, svc::MemoryPermission perm) {
|
||||||
|
uintptr_t cur_address = start;
|
||||||
|
size_t remaining_size = size;
|
||||||
|
while (remaining_size > 0) {
|
||||||
|
svc::MemoryInfo mem_info;
|
||||||
|
svc::PageInfo page_info;
|
||||||
|
R_ABORT_UNLESS(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), cur_address));
|
||||||
|
|
||||||
|
size_t cur_size = std::min(mem_info.addr + mem_info.size - cur_address, remaining_size);
|
||||||
|
|
||||||
|
if (mem_info.perm != perm) {
|
||||||
|
R_ABORT_UNLESS(svc::SetMemoryPermission(cur_address, cur_size, perm));
|
||||||
|
}
|
||||||
|
|
||||||
|
cur_address += cur_size;
|
||||||
|
remaining_size -= cur_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetMemoryPermissionImpl(uintptr_t address, size_t size, MemoryPermission perm) {
|
||||||
|
switch (perm) {
|
||||||
|
case MemoryPermission_None:
|
||||||
|
return SetMemoryPermissionBySvc(address, size, svc::MemoryPermission_None);
|
||||||
|
case MemoryPermission_ReadOnly:
|
||||||
|
return SetMemoryPermissionBySvc(address, size, svc::MemoryPermission_Read);
|
||||||
|
case MemoryPermission_ReadWrite:
|
||||||
|
return SetMemoryPermissionBySvc(address, size, svc::MemoryPermission_ReadWrite);
|
||||||
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "os_resource_manager.hpp"
|
||||||
|
|
||||||
|
namespace ams::os::impl {
|
||||||
|
|
||||||
|
/* TODO: C++20 constinit */
|
||||||
|
OsResourceManager ResourceManagerHolder::s_resource_manager = {};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "os_rng_manager_impl.hpp"
|
||||||
|
#include "os_tick_manager_impl.hpp"
|
||||||
|
|
||||||
|
namespace ams::os::impl {
|
||||||
|
|
||||||
|
class OsResourceManager {
|
||||||
|
private:
|
||||||
|
RngManager rng_manager{};
|
||||||
|
/* TODO */
|
||||||
|
TickManager tick_manager{};
|
||||||
|
/* TODO */
|
||||||
|
public:
|
||||||
|
constexpr OsResourceManager() = default;
|
||||||
|
|
||||||
|
constexpr ALWAYS_INLINE RngManager &GetRngManager() { return this->rng_manager; }
|
||||||
|
constexpr ALWAYS_INLINE TickManager &GetTickManager() { return this->tick_manager; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class ResourceManagerHolder {
|
||||||
|
private:
|
||||||
|
static /* TODO: C++20 constinit */ OsResourceManager s_resource_manager;
|
||||||
|
private:
|
||||||
|
constexpr ResourceManagerHolder() { /* ... */ }
|
||||||
|
public:
|
||||||
|
static ALWAYS_INLINE OsResourceManager &GetResourceManagerInstance() {
|
||||||
|
return s_resource_manager;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ALWAYS_INLINE OsResourceManager &GetResourceManager() {
|
||||||
|
return ResourceManagerHolder::GetResourceManagerInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
25
libraries/libstratosphere/source/os/impl/os_rng_manager.hpp
Normal file
25
libraries/libstratosphere/source/os/impl/os_rng_manager.hpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::os::impl {
|
||||||
|
|
||||||
|
ALWAYS_INLINE RngManager &GetRngManager() {
|
||||||
|
return GetResourceManager().GetRngManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "os_rng_manager_impl.hpp"
|
||||||
|
|
||||||
|
namespace ams::os::impl {
|
||||||
|
|
||||||
|
u64 RngManager::GenerateRandomU64() {
|
||||||
|
std::scoped_lock lk(this->lock);
|
||||||
|
|
||||||
|
if (AMS_UNLIKELY(!this->initialized)) {
|
||||||
|
this->Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->mt.GenerateRandomU64();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::os::impl {
|
||||||
|
|
||||||
|
class RngManager {
|
||||||
|
private:
|
||||||
|
util::TinyMT mt;
|
||||||
|
os::Mutex lock;
|
||||||
|
bool initialized;
|
||||||
|
private:
|
||||||
|
void Initialize();
|
||||||
|
public:
|
||||||
|
constexpr RngManager() : mt(), lock(), initialized() { /* ... */ }
|
||||||
|
public:
|
||||||
|
u64 GenerateRandomU64();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "os_rng_manager_impl.hpp"
|
||||||
|
|
||||||
|
namespace ams::os::impl {
|
||||||
|
|
||||||
|
void RngManager::Initialize() {
|
||||||
|
/* Retrieve entropy from kernel. */
|
||||||
|
u32 seed[4];
|
||||||
|
static_assert(util::size(seed) == util::TinyMT::NumStateWords);
|
||||||
|
|
||||||
|
/* Nintendo does not check the result of these invocations, but we will for safety. */
|
||||||
|
/* Nintendo uses entropy values 0, 1 to seed the public TinyMT random, and values */
|
||||||
|
/* 2, 3 to seed os::detail::RngManager's private TinyMT random. */
|
||||||
|
R_ABORT_UNLESS(svcGetInfo(reinterpret_cast<u64 *>(&seed[0]), InfoType_RandomEntropy, INVALID_HANDLE, 2));
|
||||||
|
R_ABORT_UNLESS(svcGetInfo(reinterpret_cast<u64 *>(&seed[2]), InfoType_RandomEntropy, INVALID_HANDLE, 3));
|
||||||
|
|
||||||
|
this->mt.Initialize(seed, util::size(seed));
|
||||||
|
|
||||||
|
/* Note that we've initialized. */
|
||||||
|
this->initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
30
libraries/libstratosphere/source/os/impl/os_tick_manager.hpp
Normal file
30
libraries/libstratosphere/source/os/impl/os_tick_manager.hpp
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "os_resource_manager.hpp"
|
||||||
|
|
||||||
|
namespace ams::os::impl {
|
||||||
|
|
||||||
|
ALWAYS_INLINE TickManager &GetTickManager() {
|
||||||
|
return GetResourceManager().GetTickManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE Tick GetCurrentTick() {
|
||||||
|
return GetTickManager().GetTick();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "os_tick_manager.hpp"
|
||||||
|
|
||||||
|
namespace ams::os::impl {
|
||||||
|
|
||||||
|
TimeSpan TickManager::ConvertToTimeSpan(Tick tick) const {
|
||||||
|
/* Get the tick value. */
|
||||||
|
const s64 tick_val = tick.GetInt64Value();
|
||||||
|
|
||||||
|
/* Get the tick frequency. */
|
||||||
|
const s64 tick_freq = GetTickFrequency();
|
||||||
|
AMS_AUDIT(tick_freq < MaxTickFrequency);
|
||||||
|
|
||||||
|
/* Clamp tick to range. */
|
||||||
|
if (tick_val > GetMaxTick()) {
|
||||||
|
return TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::max());
|
||||||
|
} else if (tick_val < -GetMaxTick()) {
|
||||||
|
return TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::min());
|
||||||
|
} else {
|
||||||
|
/* Convert to timespan. */
|
||||||
|
constexpr s64 NanoSecondsPerSecond = TimeSpan::FromSeconds(1).GetNanoSeconds();
|
||||||
|
const s64 seconds = tick_val / tick_freq;
|
||||||
|
const s64 frac = tick_val % tick_freq;
|
||||||
|
const TimeSpan ts = TimeSpan::FromSeconds(seconds) + TimeSpan::FromNanoSeconds(frac * NanoSecondsPerSecond / tick_freq);
|
||||||
|
|
||||||
|
constexpr TimeSpan ZeroTS = TimeSpan::FromNanoSeconds(0);
|
||||||
|
AMS_ASSERT(!((tick_val > 0 && ts < ZeroTS) || (tick_val < 0 && ts > ZeroTS)));
|
||||||
|
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Tick TickManager::ConvertToTick(TimeSpan ts) const {
|
||||||
|
/* Get the TimeSpan in nanoseconds. */
|
||||||
|
const s64 ns = ts.GetNanoSeconds();
|
||||||
|
|
||||||
|
/* Clamp ns to range. */
|
||||||
|
if (ns > GetMaxTimeSpanNs()) {
|
||||||
|
return Tick(std::numeric_limits<s64>::max());
|
||||||
|
} else if (ns < -GetMaxTimeSpanNs()) {
|
||||||
|
return Tick(std::numeric_limits<s64>::min());
|
||||||
|
} else {
|
||||||
|
/* Get the tick frequency. */
|
||||||
|
const s64 tick_freq = GetTickFrequency();
|
||||||
|
AMS_AUDIT(tick_freq < MaxTickFrequency);
|
||||||
|
|
||||||
|
/* Convert to tick. */
|
||||||
|
constexpr s64 NanoSecondsPerSecond = TimeSpan::FromSeconds(1).GetNanoSeconds();
|
||||||
|
const bool negative = ns < 0;
|
||||||
|
s64 seconds = ns / NanoSecondsPerSecond;
|
||||||
|
s64 frac = ns % NanoSecondsPerSecond;
|
||||||
|
|
||||||
|
/* If negative, negate seconds/frac. */
|
||||||
|
if (negative) {
|
||||||
|
seconds = -seconds;
|
||||||
|
frac = -frac;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate the tick, and invert back to negative if needed. */
|
||||||
|
s64 tick = (seconds * tick_freq) + ((frac * tick_freq + NanoSecondsPerSecond - 1) / NanoSecondsPerSecond);
|
||||||
|
if (negative) {
|
||||||
|
tick = -tick;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Tick(tick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
#ifdef ATMOSPHERE_OS_HORIZON
|
||||||
|
#include "os_tick_manager_impl.os.horizon.hpp"
|
||||||
|
#else
|
||||||
|
#error "Unknown OS for TickManagerImpl"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ams::os::impl {
|
||||||
|
|
||||||
|
/* Tick frequency must be less than INT64_MAX / 1 second. */
|
||||||
|
static constexpr s64 MaxTickFrequency = (std::numeric_limits<s64>::max() / TimeSpan::FromSeconds(1).GetNanoSeconds()) - 1;
|
||||||
|
|
||||||
|
class TickManager {
|
||||||
|
private:
|
||||||
|
TickManagerImpl impl;
|
||||||
|
public:
|
||||||
|
constexpr TickManager() : impl() { /* ... */ }
|
||||||
|
|
||||||
|
ALWAYS_INLINE Tick GetTick() const {
|
||||||
|
return this->impl.GetTick();
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE s64 GetTickFrequency() const {
|
||||||
|
return this->impl.GetTickFrequency();
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE s64 GetMaxTick() const {
|
||||||
|
return this->impl.GetMaxTick();
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE s64 GetMaxTimeSpanNs() const {
|
||||||
|
return this->impl.GetMaxTimeSpanNs();
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeSpan ConvertToTimeSpan(Tick tick) const;
|
||||||
|
Tick ConvertToTick(TimeSpan ts) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::os::impl {
|
||||||
|
|
||||||
|
class TickManagerImpl {
|
||||||
|
public:
|
||||||
|
constexpr TickManagerImpl() { /* ... */ }
|
||||||
|
|
||||||
|
ALWAYS_INLINE Tick GetTick() const {
|
||||||
|
s64 tick;
|
||||||
|
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||||
|
__asm__ __volatile__("mrs %[tick], cntpct_el0" : [tick]"=&r"(tick) :: "memory");
|
||||||
|
#else
|
||||||
|
#error "Unknown Architecture for TickManagerImpl::GetTick"
|
||||||
|
#endif
|
||||||
|
return Tick(tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr ALWAYS_INLINE s64 GetTickFrequency() {
|
||||||
|
return static_cast<s64>(::ams::svc::TicksPerSecond);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr ALWAYS_INLINE s64 GetMaxTick() {
|
||||||
|
static_assert(GetTickFrequency() <= TimeSpan::FromSeconds(1).GetNanoSeconds());
|
||||||
|
return (std::numeric_limits<s64>::max() / TimeSpan::FromSeconds(1).GetNanoSeconds()) * GetTickFrequency();
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr ALWAYS_INLINE s64 GetMaxTimeSpanNs() {
|
||||||
|
static_assert(GetTickFrequency() <= TimeSpan::FromSeconds(1).GetNanoSeconds());
|
||||||
|
return TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::max()).GetNanoSeconds();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -38,7 +38,7 @@ namespace ams::os::impl {
|
|||||||
virtual Handle GetHandle() const = 0;
|
virtual Handle GetHandle() const = 0;
|
||||||
/* Gets the amount of time remaining until this wakes up. */
|
/* Gets the amount of time remaining until this wakes up. */
|
||||||
virtual u64 GetWakeupTime() const {
|
virtual u64 GetWakeupTime() const {
|
||||||
return U64_MAX;
|
return std::numeric_limits<u64>::max();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Interface with manager. */
|
/* Interface with manager. */
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ namespace ams::os::impl{
|
|||||||
WaitableHolderBase *objects[MaximumHandleCount];
|
WaitableHolderBase *objects[MaximumHandleCount];
|
||||||
|
|
||||||
const size_t count = this->BuildHandleArray(object_handles, objects);
|
const size_t count = this->BuildHandleArray(object_handles, objects);
|
||||||
const u64 end_time = infinite ? U64_MAX : armTicksToNs(armGetSystemTick());
|
const u64 end_time = infinite ? std::numeric_limits<u64>::max() : armTicksToNs(armGetSystemTick());
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
this->current_time = armTicksToNs(armGetSystemTick());
|
this->current_time = armTicksToNs(armGetSystemTick());
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ namespace ams::os::impl {
|
|||||||
public:
|
public:
|
||||||
/* Wait. */
|
/* Wait. */
|
||||||
WaitableHolderBase *WaitAny() {
|
WaitableHolderBase *WaitAny() {
|
||||||
return this->WaitAnyImpl(true, U64_MAX);
|
return this->WaitAnyImpl(true, std::numeric_limits<u64>::max());
|
||||||
}
|
}
|
||||||
|
|
||||||
WaitableHolderBase *TryWaitAny() {
|
WaitableHolderBase *TryWaitAny() {
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ namespace ams::os {
|
|||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
/* Continuously wait, until success. */
|
/* Continuously wait, until success. */
|
||||||
R_TRY_CATCH(svcWaitSynchronizationSingle(this->handle.Get(), U64_MAX)) {
|
R_TRY_CATCH(svcWaitSynchronizationSingle(this->handle.Get(), std::numeric_limits<u64>::max())) {
|
||||||
R_CATCH(svc::ResultCancelled) { continue; }
|
R_CATCH(svc::ResultCancelled) { continue; }
|
||||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||||
|
|
||||||
|
|||||||
28
libraries/libstratosphere/source/os/os_memory_heap.cpp
Normal file
28
libraries/libstratosphere/source/os/os_memory_heap.cpp
Normal 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/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::os {
|
||||||
|
|
||||||
|
Result AllocateMemoryBlock(uintptr_t *out_address, size_t size) {
|
||||||
|
AMS_ABORT("Not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeMemoryBlock(uintptr_t address, size_t size) {
|
||||||
|
AMS_ABORT("Not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
25
libraries/libstratosphere/source/os/os_memory_permission.cpp
Normal file
25
libraries/libstratosphere/source/os/os_memory_permission.cpp
Normal 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/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "impl/os_memory_permission_impl.hpp"
|
||||||
|
|
||||||
|
namespace ams::os {
|
||||||
|
|
||||||
|
void SetMemoryPermission(uintptr_t address, size_t size, MemoryPermission perm) {
|
||||||
|
return impl::SetMemoryPermissionImpl(address, size, perm);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -42,7 +42,7 @@ namespace ams::os {
|
|||||||
void GenerateRandomBytes(void *dst, size_t size) {
|
void GenerateRandomBytes(void *dst, size_t size) {
|
||||||
std::scoped_lock lk(g_random_mutex);
|
std::scoped_lock lk(g_random_mutex);
|
||||||
|
|
||||||
if (!g_initialized_random) {
|
if (AMS_UNLIKELY(!g_initialized_random)) {
|
||||||
impl::InitializeRandomImpl(&g_random);
|
impl::InitializeRandomImpl(&g_random);
|
||||||
g_initialized_random = true;
|
g_initialized_random = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::os {
|
||||||
|
|
||||||
|
/* TODO: How will this work without libnx? */
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using LibnxTlsDestructor = void (*)(void *);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Result AllocateTlsSlot(TlsSlot *out, TlsDestructor destructor) {
|
||||||
|
s32 slot = ::threadTlsAlloc(reinterpret_cast<LibnxTlsDestructor>(destructor));
|
||||||
|
R_UNLESS(slot >= 0, os::ResultOutOfResource());
|
||||||
|
|
||||||
|
*out = { static_cast<u32>(slot) };
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeTlsSlot(TlsSlot slot) {
|
||||||
|
::threadTlsFree(static_cast<s32>(slot._value));
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t GetTlsValue(TlsSlot slot) {
|
||||||
|
return reinterpret_cast<uintptr_t>(::threadTlsGet(static_cast<s32>(slot._value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTlsValue(TlsSlot slot, uintptr_t value) {
|
||||||
|
::threadTlsSet(static_cast<s32>(slot._value), reinterpret_cast<void *>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
37
libraries/libstratosphere/source/os/os_tick.cpp
Normal file
37
libraries/libstratosphere/source/os/os_tick.cpp
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "impl/os_tick_manager.hpp"
|
||||||
|
|
||||||
|
namespace ams::os {
|
||||||
|
|
||||||
|
Tick GetSystemTick() {
|
||||||
|
return impl::GetTickManager().GetTick();
|
||||||
|
}
|
||||||
|
|
||||||
|
s64 GetSystemTickFrequency() {
|
||||||
|
return impl::GetTickManager().GetTickFrequency();
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeSpan ConvertToTimeSpan(Tick tick) {
|
||||||
|
return impl::GetTickManager().ConvertToTimeSpan(tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
Tick ConvertToTick(TimeSpan ts) {
|
||||||
|
return impl::GetTickManager().ConvertToTick(ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::os {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/* TODO: Remove, add VammManager */
|
||||||
|
size_t GetSystemResourceSize() {
|
||||||
|
u64 v;
|
||||||
|
if (R_SUCCEEDED(svcGetInfo(std::addressof(v), InfoType_SystemResourceSizeTotal, CUR_PROCESS_HANDLE, 0))) {
|
||||||
|
return v;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsVirtualAddressMemoryEnabled() {
|
||||||
|
return GetSystemResourceSize() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -237,7 +237,10 @@ namespace ams::patcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Print the path for this directory. */
|
/* Print the path for this directory. */
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wformat-truncation"
|
||||||
std::snprintf(path + patches_dir_path_len, sizeof(path) - patches_dir_path_len, "/%s", entry.name);
|
std::snprintf(path + patches_dir_path_len, sizeof(path) - patches_dir_path_len, "/%s", entry.name);
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
const size_t patch_dir_path_len = patches_dir_path_len + 1 + std::strlen(entry.name);
|
const size_t patch_dir_path_len = patches_dir_path_len + 1 + std::strlen(entry.name);
|
||||||
|
|
||||||
/* Open the patch directory. */
|
/* Open the patch directory. */
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ namespace ams::sf::hipc {
|
|||||||
s32 unused_index;
|
s32 unused_index;
|
||||||
if (message_buf == armGetTls()) {
|
if (message_buf == armGetTls()) {
|
||||||
/* Consider: AMS_ABORT_UNLESS(message_buf_size == TlsMessageBufferSize); */
|
/* Consider: AMS_ABORT_UNLESS(message_buf_size == TlsMessageBufferSize); */
|
||||||
return svcReplyAndReceive(&unused_index, &session_handle, 1, INVALID_HANDLE, U64_MAX);
|
return svcReplyAndReceive(&unused_index, &session_handle, 1, INVALID_HANDLE, std::numeric_limits<u64>::max());
|
||||||
} else {
|
} else {
|
||||||
return svcReplyAndReceiveWithUserBuffer(&unused_index, message_buf, message_buf_size, &session_handle, 1, INVALID_HANDLE, U64_MAX);
|
return svcReplyAndReceiveWithUserBuffer(&unused_index, message_buf, message_buf_size, &session_handle, 1, INVALID_HANDLE, std::numeric_limits<u64>::max());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user