Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
070d2786c6 | ||
|
|
efa4a346af | ||
|
|
d75f9bbedf | ||
|
|
ea7f51a279 | ||
|
|
a65b6df8d2 | ||
|
|
4e112de223 | ||
|
|
20d200471d | ||
|
|
5f2d713fe4 | ||
|
|
114d2598da | ||
|
|
36bdb83cfc | ||
|
|
a975689c59 | ||
|
|
a809e23320 | ||
|
|
4db485083b | ||
|
|
e96972c939 | ||
|
|
3545c0aac2 | ||
|
|
d85875b910 | ||
|
|
b1367942a2 | ||
|
|
c2c0a2e169 | ||
|
|
f5052b4bca | ||
|
|
70d67bb115 | ||
|
|
9056e0b05f | ||
|
|
895b6d0470 | ||
|
|
dfba595cdc | ||
|
|
175a34da43 | ||
|
|
02b126c2be | ||
|
|
b45671fd35 | ||
|
|
106599895d | ||
|
|
80154b0a54 |
@@ -1,4 +1,13 @@
|
||||
# Changelog
|
||||
## 1.3.2
|
||||
+ Support was improved for 14.0.0+.
|
||||
+ `loader` was updated to reflect the latest official behaviors.
|
||||
+ `ro` was updated to reflect the latest official behaviors.
|
||||
+ A number of minor issues were fixed and improvements were made, including:
|
||||
+ A memory leak was fixed in filesystem path management; this could cause a crash when launching games ~100 times, or when deleting/re-downloading games.
|
||||
+ A bug was fixed that could cause threads to not see a newly signaled semaphore.
|
||||
+ A number of minor inaccuracies were fixed in the updated FileSystem APIs.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
## 1.3.1
|
||||
+ Support was added for 14.1.0.
|
||||
+ A number of minor under the hood improvements to accuracy were made to better reflect latest official system module behavior, particularly around FS apis.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||
branch = master
|
||||
commit = 590cdaf022a9c6a50b7bd712d35add3ddf81dc9c
|
||||
parent = 72baa4ff188fdc132fcafe7be7f461ff3b00dcd2
|
||||
commit = b91294d3b9960eafef6d5d80b08870d427324bc9
|
||||
parent = 3545c0aac2cdfc1f6f897e8c669a8e33358b3ece
|
||||
method = merge
|
||||
cmdver = 0.4.1
|
||||
|
||||
@@ -67,7 +67,7 @@ endif
|
||||
|
||||
ifeq ($(ATMOSPHERE_BOARD),nx-hac-001)
|
||||
export LDFLAGS = -specs=$(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/stratosphere.specs -specs=$(DEVKITPRO)/libnx/switch.specs $(CXXFLAGS) $(CXXWRAPS) $(CXXREQUIRED) -Wl,-Map,$(notdir $*.map)
|
||||
else ifeq ($(ATMOSPHERE_COMPILER_NAME),clang)
|
||||
else ifeq ($(ATMOSPHERE_OS_NAME),macos)
|
||||
export LDFLAGS = $(CXXFLAGS) $(CXXWRAPS) $(CXXREQUIRED) -Wl,-map,$(notdir $@.map)
|
||||
else
|
||||
export LDFLAGS = $(CXXFLAGS) $(CXXWRAPS) $(CXXREQUIRED) -Wl,-Map,$(notdir $@.map)
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace ams::kern {
|
||||
s32 new_value;
|
||||
if (count <= 0) {
|
||||
if ((it != m_tree.end()) && (it->GetAddressArbiterKey() == addr)) {
|
||||
new_value = value - 2;
|
||||
new_value = value - 1;
|
||||
} else {
|
||||
new_value = value + 1;
|
||||
}
|
||||
@@ -132,7 +132,7 @@ namespace ams::kern {
|
||||
auto tmp_it = it;
|
||||
s32 tmp_num_waiters = 0;
|
||||
while ((++tmp_it != m_tree.end()) && (tmp_it->GetAddressArbiterKey() == addr)) {
|
||||
if ((tmp_num_waiters++) >= count) {
|
||||
if ((++tmp_num_waiters) >= count) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,10 @@ namespace ams::fs {
|
||||
}
|
||||
|
||||
constexpr WriteBuffer &operator=(WriteBuffer &&rhs) {
|
||||
if (m_buffer != nullptr) {
|
||||
::ams::fs::impl::Deallocate(m_buffer, this->GetLength());
|
||||
}
|
||||
|
||||
m_buffer = rhs.m_buffer;
|
||||
m_length_and_is_normalized = rhs.m_length_and_is_normalized;
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ namespace ams::fssrv::impl {
|
||||
Result CleanDirectoryRecursively(const fssrv::sf::Path &path);
|
||||
Result GetFileTimeStampRaw(ams::sf::Out<fs::FileTimeStampRaw> out, const fssrv::sf::Path &path);
|
||||
|
||||
Result QueryEntry(const ams::sf::OutBuffer &out_buf, const ams::sf::InBuffer &in_buf, s32 query_id, const fssrv::sf::Path &path);
|
||||
Result QueryEntry(const ams::sf::OutNonSecureBuffer &out_buf, const ams::sf::InNonSecureBuffer &in_buf, s32 query_id, const fssrv::sf::Path &path);
|
||||
};
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
@@ -252,7 +252,7 @@ namespace ams::fssrv::impl {
|
||||
R_RETURN(fsFsGetFileTimeStampRaw(std::addressof(m_base_fs), path.str, reinterpret_cast<::FsTimeStampRaw *>(out.GetPointer())));
|
||||
}
|
||||
|
||||
Result QueryEntry(const ams::sf::OutBuffer &out_buf, const ams::sf::InBuffer &in_buf, s32 query_id, const fssrv::sf::Path &path) {
|
||||
Result QueryEntry(const ams::sf::OutNonSecureBuffer &out_buf, const ams::sf::InNonSecureBuffer &in_buf, s32 query_id, const fssrv::sf::Path &path) {
|
||||
R_RETURN(fsFsQueryEntry(std::addressof(m_base_fs), out_buf.GetPointer(), out_buf.GetSize(), in_buf.GetPointer(), in_buf.GetSize(), path.str, static_cast<FsFileSystemQueryId>(query_id)));
|
||||
}
|
||||
|
||||
|
||||
@@ -24,21 +24,21 @@
|
||||
|
||||
/* ACCURATE_TO_VERSION: 13.4.0.0 */
|
||||
#define AMS_FSSRV_I_FILESYSTEM_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, CreateFile, (const ams::fssrv::sf::Path &path, s64 size, s32 option), (path, size, option)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, DeleteFile, (const ams::fssrv::sf::Path &path), (path)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, CreateDirectory, (const ams::fssrv::sf::Path &path), (path)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 3, Result, DeleteDirectory, (const ams::fssrv::sf::Path &path), (path)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 4, Result, DeleteDirectoryRecursively, (const ams::fssrv::sf::Path &path), (path)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 5, Result, RenameFile, (const ams::fssrv::sf::Path &old_path, const ams::fssrv::sf::Path &new_path), (old_path, new_path)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 6, Result, RenameDirectory, (const ams::fssrv::sf::Path &old_path, const ams::fssrv::sf::Path &new_path), (old_path, new_path)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 7, Result, GetEntryType, (ams::sf::Out<u32> out, const ams::fssrv::sf::Path &path), (out, path)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 8, Result, OpenFile, (ams::sf::Out<ams::sf::SharedPointer<ams::fssrv::sf::IFile>> out, const ams::fssrv::sf::Path &path, u32 mode), (out, path, mode)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 9, Result, OpenDirectory, (ams::sf::Out<ams::sf::SharedPointer<ams::fssrv::sf::IDirectory>> out, const ams::fssrv::sf::Path &path, u32 mode), (out, path, mode)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 10, Result, Commit, (), ()) \
|
||||
AMS_SF_METHOD_INFO(C, H, 11, Result, GetFreeSpaceSize, (ams::sf::Out<s64> out, const ams::fssrv::sf::Path &path), (out, path)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 12, Result, GetTotalSpaceSize, (ams::sf::Out<s64> out, const ams::fssrv::sf::Path &path), (out, path)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 13, Result, CleanDirectoryRecursively, (const ams::fssrv::sf::Path &path), (path), hos::Version_3_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 14, Result, GetFileTimeStampRaw, (ams::sf::Out<ams::fs::FileTimeStampRaw> out, const ams::fssrv::sf::Path &path), (out, path), hos::Version_3_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 15, Result, QueryEntry, (const ams::sf::OutBuffer &out_buf, const ams::sf::InBuffer &in_buf, s32 query_id, const ams::fssrv::sf::Path &path), (out_buf, in_buf, query_id, path), hos::Version_4_0_0)
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, CreateFile, (const ams::fssrv::sf::Path &path, s64 size, s32 option), (path, size, option)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, DeleteFile, (const ams::fssrv::sf::Path &path), (path)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, CreateDirectory, (const ams::fssrv::sf::Path &path), (path)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 3, Result, DeleteDirectory, (const ams::fssrv::sf::Path &path), (path)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 4, Result, DeleteDirectoryRecursively, (const ams::fssrv::sf::Path &path), (path)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 5, Result, RenameFile, (const ams::fssrv::sf::Path &old_path, const ams::fssrv::sf::Path &new_path), (old_path, new_path)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 6, Result, RenameDirectory, (const ams::fssrv::sf::Path &old_path, const ams::fssrv::sf::Path &new_path), (old_path, new_path)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 7, Result, GetEntryType, (ams::sf::Out<u32> out, const ams::fssrv::sf::Path &path), (out, path)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 8, Result, OpenFile, (ams::sf::Out<ams::sf::SharedPointer<ams::fssrv::sf::IFile>> out, const ams::fssrv::sf::Path &path, u32 mode), (out, path, mode)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 9, Result, OpenDirectory, (ams::sf::Out<ams::sf::SharedPointer<ams::fssrv::sf::IDirectory>> out, const ams::fssrv::sf::Path &path, u32 mode), (out, path, mode)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 10, Result, Commit, (), ()) \
|
||||
AMS_SF_METHOD_INFO(C, H, 11, Result, GetFreeSpaceSize, (ams::sf::Out<s64> out, const ams::fssrv::sf::Path &path), (out, path)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 12, Result, GetTotalSpaceSize, (ams::sf::Out<s64> out, const ams::fssrv::sf::Path &path), (out, path)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 13, Result, CleanDirectoryRecursively, (const ams::fssrv::sf::Path &path), (path), hos::Version_3_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 14, Result, GetFileTimeStampRaw, (ams::sf::Out<ams::fs::FileTimeStampRaw> out, const ams::fssrv::sf::Path &path), (out, path), hos::Version_3_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 15, Result, QueryEntry, (const ams::sf::OutNonSecureBuffer &out_buf, const ams::sf::InNonSecureBuffer &in_buf, s32 query_id, const ams::fssrv::sf::Path &path), (out_buf, in_buf, query_id, path), hos::Version_4_0_0)
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::fssrv::sf, IFileSystem, AMS_FSSRV_I_FILESYSTEM_INTERFACE_INFO, 0xD4EA59E7)
|
||||
|
||||
@@ -42,8 +42,14 @@ namespace ams::fssystem {
|
||||
};
|
||||
|
||||
struct Entry {
|
||||
enum class Encryption : u8 {
|
||||
Encrypted = 0,
|
||||
NotEncrypted = 1,
|
||||
};
|
||||
|
||||
u8 offset[sizeof(s64)];
|
||||
s32 reserved;
|
||||
Encryption encryption_value;
|
||||
u8 reserved[3];
|
||||
s32 generation;
|
||||
|
||||
void SetOffset(s64 value) {
|
||||
@@ -117,6 +123,8 @@ namespace ams::fssystem {
|
||||
AMS_UNUSED(size);
|
||||
R_THROW(fs::ResultUnsupportedSetSizeForAesCtrCounterExtendedStorage());
|
||||
}
|
||||
|
||||
Result GetEntryList(Entry *out_entries, s32 *out_entry_count, s32 entry_count, s64 offset, s64 size);
|
||||
private:
|
||||
Result Initialize(IAllocator *allocator, const void *key, size_t key_size, u32 secure_value, fs::SubStorage data_storage, fs::SubStorage table_storage);
|
||||
};
|
||||
|
||||
@@ -60,8 +60,8 @@ namespace ams::fssystem {
|
||||
constexpr IHash256GeneratorFactory() = default;
|
||||
virtual constexpr ~IHash256GeneratorFactory() { /* ... */ }
|
||||
|
||||
std::unique_ptr<IHash256Generator> Create() {
|
||||
return this->DoCreate();
|
||||
Result Create(std::unique_ptr<IHash256Generator> *out) {
|
||||
return this->DoCreate(out);
|
||||
}
|
||||
|
||||
void GenerateHash(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||
@@ -73,11 +73,11 @@ namespace ams::fssystem {
|
||||
return this->DoGenerateHash(dst, dst_size, src, src_size);
|
||||
}
|
||||
protected:
|
||||
virtual std::unique_ptr<IHash256Generator> DoCreate() = 0;
|
||||
virtual Result DoCreate(std::unique_ptr<IHash256Generator> *out) = 0;
|
||||
virtual void DoGenerateHash(void *dst, size_t dst_size, const void *src, size_t src_size) = 0;
|
||||
};
|
||||
|
||||
/* ACCURATE_TO_VERSION: 13.4.0.0 */
|
||||
/* ACCURATE_TO_VERSION: 14.3.0.0 */
|
||||
class IHash256GeneratorFactorySelector {
|
||||
public:
|
||||
constexpr IHash256GeneratorFactorySelector() = default;
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace ams::fssystem {
|
||||
IntegrityVerificationStorage() : m_verification_block_size(0), m_verification_block_order(0), m_upper_layer_verification_block_size(0), m_upper_layer_verification_block_order(0), m_buffer_manager(nullptr), m_salt(util::nullopt) { /* ... */ }
|
||||
virtual ~IntegrityVerificationStorage() override { this->Finalize(); }
|
||||
|
||||
Result Initialize(fs::SubStorage hs, fs::SubStorage ds, s64 verif_block_size, s64 upper_layer_verif_block_size, fs::IBufferManager *bm, fssystem::IHash256GeneratorFactory *hgf, const util::optional<fs::HashSalt> &salt, bool is_real_data, bool is_writable, bool allow_cleared_blocks);
|
||||
void Initialize(fs::SubStorage hs, fs::SubStorage ds, s64 verif_block_size, s64 upper_layer_verif_block_size, fs::IBufferManager *bm, fssystem::IHash256GeneratorFactory *hgf, const util::optional<fs::HashSalt> &salt, bool is_real_data, bool is_writable, bool allow_cleared_blocks);
|
||||
void Finalize();
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override;
|
||||
@@ -66,9 +66,8 @@ namespace ams::fssystem {
|
||||
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override;
|
||||
using IStorage::OperateRange;
|
||||
|
||||
void CalcBlockHash(BlockHash *out, const void *buffer, size_t block_size) const {
|
||||
auto generator = m_hash_generator_factory->Create();
|
||||
return this->CalcBlockHash(out, buffer, block_size, generator);
|
||||
void CalcBlockHash(BlockHash *out, const void *buffer, std::unique_ptr<fssystem::IHash256Generator> &generator) const {
|
||||
return this->CalcBlockHash(out, buffer, static_cast<size_t>(m_verification_block_size), generator);
|
||||
}
|
||||
|
||||
s64 GetBlockSize() const {
|
||||
@@ -81,10 +80,6 @@ namespace ams::fssystem {
|
||||
|
||||
void CalcBlockHash(BlockHash *out, const void *buffer, size_t block_size, std::unique_ptr<fssystem::IHash256Generator> &generator) const;
|
||||
|
||||
void CalcBlockHash(BlockHash *out, const void *buffer, std::unique_ptr<fssystem::IHash256Generator> &generator) const {
|
||||
return this->CalcBlockHash(out, buffer, static_cast<size_t>(m_verification_block_size), generator);
|
||||
}
|
||||
|
||||
Result IsCleared(bool *is_cleared, const BlockHash &hash);
|
||||
private:
|
||||
static void SetValidationBit(BlockHash *hash) {
|
||||
|
||||
@@ -29,7 +29,9 @@ namespace ams::fssystem {
|
||||
NON_COPYABLE(ShaHashGenerator);
|
||||
NON_MOVEABLE(ShaHashGenerator);
|
||||
private:
|
||||
Traits::Generator m_generator;
|
||||
using Generator = typename Traits::Generator;
|
||||
private:
|
||||
Generator m_generator;
|
||||
public:
|
||||
ShaHashGenerator() = default;
|
||||
protected:
|
||||
@@ -54,8 +56,12 @@ namespace ams::fssystem {
|
||||
public:
|
||||
constexpr ShaHashGeneratorFactory() = default;
|
||||
protected:
|
||||
virtual std::unique_ptr<IHash256Generator> DoCreate() override {
|
||||
return std::unique_ptr<IHash256Generator>(new ShaHashGenerator<Traits>());
|
||||
virtual Result DoCreate(std::unique_ptr<IHash256Generator> *out) override {
|
||||
auto generator = std::unique_ptr<IHash256Generator>(new ShaHashGenerator<Traits>());
|
||||
R_UNLESS(generator != nullptr, fs::ResultAllocationMemoryFailedNew());
|
||||
|
||||
*out = std::move(generator);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual void DoGenerateHash(void *dst, size_t dst_size, const void *src, size_t src_size) override {
|
||||
|
||||
@@ -71,6 +71,7 @@ namespace ams::hos {
|
||||
Version_13_2_1 = ::ams::TargetFirmware_13_2_1,
|
||||
Version_14_0_0 = ::ams::TargetFirmware_14_0_0,
|
||||
Version_14_1_0 = ::ams::TargetFirmware_14_1_0,
|
||||
Version_14_1_1 = ::ams::TargetFirmware_14_1_1,
|
||||
|
||||
Version_Current = ::ams::TargetFirmware_Current,
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace ams::ldr {
|
||||
AcidFlag_DeprecatedUseSecureMemory = (1 << 2),
|
||||
|
||||
AcidFlag_PoolPartitionShift = 2,
|
||||
AcidFlag_PoolPartitionMask = (3 << AcidFlag_PoolPartitionShift),
|
||||
AcidFlag_PoolPartitionMask = (0xF << AcidFlag_PoolPartitionShift),
|
||||
};
|
||||
|
||||
enum PoolPartition {
|
||||
@@ -198,7 +198,8 @@ namespace ams::ldr {
|
||||
u32 magic;
|
||||
u32 size;
|
||||
u8 version;
|
||||
u8 reserved_209[3];
|
||||
u8 unknown_209;
|
||||
u8 reserved_20A[2];
|
||||
u32 flags;
|
||||
ncm::ProgramId program_id_min;
|
||||
ncm::ProgramId program_id_max;
|
||||
|
||||
@@ -843,7 +843,7 @@ namespace ams::ncm {
|
||||
R_SUCCEED_IF(m_header.has_value());
|
||||
|
||||
/* Get our header. */
|
||||
PatchMetaExtendedDataHeader header;
|
||||
PatchMetaExtendedDataHeader header{};
|
||||
R_TRY(this->GetHeader(std::addressof(header)));
|
||||
|
||||
/* Set our header. */
|
||||
|
||||
@@ -21,10 +21,13 @@
|
||||
#include <stratosphere/os/os_memory_common.hpp>
|
||||
#include <stratosphere/os/os_memory_fence.hpp>
|
||||
#include <stratosphere/os/os_memory_permission.hpp>
|
||||
#include <stratosphere/os/os_memory_heap_api.hpp>
|
||||
#include <stratosphere/os/os_memory_attribute.hpp>
|
||||
#include <stratosphere/os/os_memory_heap.hpp>
|
||||
#include <stratosphere/os/os_virtual_address_memory.hpp>
|
||||
#include <stratosphere/os/os_native_handle.hpp>
|
||||
#include <stratosphere/os/os_process_handle_api.hpp>
|
||||
#include <stratosphere/os/os_process_memory_api.hpp>
|
||||
#include <stratosphere/os/os_process_code_memory_api.hpp>
|
||||
#include <stratosphere/os/os_random.hpp>
|
||||
#include <stratosphere/os/os_mutex.hpp>
|
||||
#include <stratosphere/os/os_condition_variable.hpp>
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 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 {
|
||||
|
||||
enum MemoryAttribute {
|
||||
MemoryAttribute_Normal,
|
||||
MemoryAttribute_Uncached,
|
||||
};
|
||||
|
||||
void SetMemoryAttribute(uintptr_t address, size_t size, MemoryAttribute attr);
|
||||
|
||||
}
|
||||
@@ -18,16 +18,14 @@
|
||||
|
||||
namespace ams::os {
|
||||
|
||||
constexpr inline size_t MemoryPageSize = 0x1000;
|
||||
|
||||
constexpr inline size_t MemoryBlockUnitSize = 0x200000;
|
||||
|
||||
enum MemoryPermission {
|
||||
MemoryPermission_None = (0 << 0),
|
||||
MemoryPermission_ReadOnly = (1 << 0),
|
||||
MemoryPermission_WriteOnly = (1 << 1),
|
||||
MemoryPermission_None = (0 << 0),
|
||||
MemoryPermission_ReadOnly = (1 << 0),
|
||||
MemoryPermission_WriteOnly = (1 << 1),
|
||||
MemoryPermission_ExecuteOnly = (1 << 2),
|
||||
|
||||
MemoryPermission_ReadWrite = MemoryPermission_ReadOnly | MemoryPermission_WriteOnly,
|
||||
MemoryPermission_ReadWrite = MemoryPermission_ReadOnly | MemoryPermission_WriteOnly,
|
||||
MemoryPermission_ReadExecute = MemoryPermission_ReadOnly | MemoryPermission_ExecuteOnly,
|
||||
};
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 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_memory_heap_common.hpp>
|
||||
#include <stratosphere/os/os_memory_heap_api.hpp>
|
||||
@@ -16,10 +16,15 @@
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/os/os_common_types.hpp>
|
||||
#include <stratosphere/os/os_memory_common.hpp>
|
||||
#include <stratosphere/os/os_memory_heap_common.hpp>
|
||||
|
||||
namespace ams::os {
|
||||
|
||||
Result SetMemoryHeapSize(size_t size);
|
||||
|
||||
uintptr_t GetMemoryHeapAddress();
|
||||
size_t GetMemoryHeapSize();
|
||||
|
||||
Result AllocateMemoryBlock(uintptr_t *out_address, size_t size);
|
||||
void FreeMemoryBlock(uintptr_t address, size_t size);
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 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 {
|
||||
|
||||
constexpr inline size_t MemoryHeapUnitSize = 2_MB;
|
||||
constexpr inline size_t MemoryBlockUnitSize = 2_MB;
|
||||
|
||||
constexpr inline size_t MemoryPageSize = 4_KB;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 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_native_handle.hpp>
|
||||
#include <stratosphere/os/os_memory_common.hpp>
|
||||
|
||||
namespace ams::os {
|
||||
|
||||
struct ProcessMemoryRegion {
|
||||
u64 address;
|
||||
u64 size;
|
||||
};
|
||||
|
||||
Result MapProcessCodeMemory(u64 *out, NativeHandle handle, const ProcessMemoryRegion *regions, size_t num_regions);
|
||||
Result UnmapProcessCodeMemory(NativeHandle handle, u64 process_code_address, const ProcessMemoryRegion *regions, size_t num_regions);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 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_native_handle.hpp>
|
||||
#include <stratosphere/os/os_memory_common.hpp>
|
||||
|
||||
namespace ams::os {
|
||||
|
||||
Result MapProcessMemory(void **out, NativeHandle handle, u64 process_address, size_t process_size);
|
||||
void UnmapProcessMemory(void *mapped_memory, NativeHandle handle, u64 process_address, size_t process_size);
|
||||
|
||||
Result SetProcessMemoryPermission(NativeHandle handle, u64 process_address, u64 process_size, MemoryPermission perm);
|
||||
|
||||
}
|
||||
@@ -38,6 +38,7 @@ namespace ams::pwm::driver {
|
||||
virtual Result SetPeriod(IPwmDevice *device, TimeSpan period) = 0;
|
||||
virtual Result GetPeriod(TimeSpan *out, IPwmDevice *device) = 0;
|
||||
|
||||
/* TODO: Nintendo removed these in 14.0.0. Should we? */
|
||||
virtual Result SetDuty(IPwmDevice *device, int duty) = 0;
|
||||
virtual Result GetDuty(int *out, IPwmDevice *device) = 0;
|
||||
|
||||
|
||||
@@ -18,14 +18,14 @@
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/pwm/pwm_types.hpp>
|
||||
|
||||
#define AMS_PWM_I_CHANNEL_SESSION_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, SetPeriod, (TimeSpanType period), (period) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, GetPeriod, (ams::sf::Out<TimeSpanType> out), (out) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, SetDuty, (int duty), (duty) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 3, Result, GetDuty, (ams::sf::Out<int> out), (out) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 4, Result, SetEnabled, (bool enabled), (enabled) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 5, Result, GetEnabled, (ams::sf::Out<bool> out), (out) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 6, Result, SetScale, (double scale), (scale), hos::Version_6_0_0) \
|
||||
#define AMS_PWM_I_CHANNEL_SESSION_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, SetPeriod, (TimeSpanType period), (period)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, GetPeriod, (ams::sf::Out<TimeSpanType> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, SetDuty, (int duty), (duty), hos::Version_Min, hos::Version_13_2_1) \
|
||||
AMS_SF_METHOD_INFO(C, H, 3, Result, GetDuty, (ams::sf::Out<int> out), (out), hos::Version_Min, hos::Version_13_2_1) \
|
||||
AMS_SF_METHOD_INFO(C, H, 4, Result, SetEnabled, (bool enabled), (enabled)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 5, Result, GetEnabled, (ams::sf::Out<bool> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 6, Result, SetScale, (double scale), (scale), hos::Version_6_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 7, Result, GetScale, (ams::sf::Out<double> out), (out), hos::Version_6_0_0)
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::pwm::sf, IChannelSession, AMS_PWM_I_CHANNEL_SESSION_INTERFACE_INFO, 0xAC0A18F9)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
|
||||
namespace ams::svc {
|
||||
|
||||
@@ -22,485 +22,10 @@
|
||||
|
||||
namespace aarch64::lp64 {
|
||||
|
||||
ALWAYS_INLINE Result SetHeapSize(::ams::svc::Address *out_address, ::ams::svc::Size size) {
|
||||
R_RETURN(::svcSetHeapSize(reinterpret_cast<void **>(out_address), size));
|
||||
}
|
||||
|
||||
/* Convenience accessor. */
|
||||
ALWAYS_INLINE Result SetHeapSize(uintptr_t *out_address, ::ams::svc::Size size) {
|
||||
static_assert(sizeof(::ams::svc::Address) == sizeof(uintptr_t));
|
||||
R_RETURN(::svcSetHeapSize(reinterpret_cast<void **>(out_address), size));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result SetMemoryPermission(::ams::svc::Address address, ::ams::svc::Size size, ::ams::svc::MemoryPermission perm) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_RETURN(::svcGetThreadPriority(out_priority, thread_handle));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result SetThreadPriority(::ams::svc::Handle thread_handle, int32_t priority) {
|
||||
R_RETURN(::svcSetThreadPriority(thread_handle, priority));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result GetThreadCoreMask(int32_t *out_core_id, uint64_t *out_affinity_mask, ::ams::svc::Handle thread_handle) {
|
||||
R_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) {
|
||||
R_RETURN(::svcSetThreadCoreMask(thread_handle, core_id, affinity_mask));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE int32_t GetCurrentProcessorNumber() {
|
||||
return ::svcGetCurrentProcessorNumber();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result SignalEvent(::ams::svc::Handle event_handle) {
|
||||
R_RETURN(::svcSignalEvent(event_handle));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result ClearEvent(::ams::svc::Handle event_handle) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_RETURN(::svcCloseHandle(handle));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result ResetSignal(::ams::svc::Handle handle) {
|
||||
R_RETURN(::svcResetSignal(handle));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result WaitSynchronization(int32_t *out_index, ::ams::svc::UserPointer<const ::ams::svc::Handle *> handles, int32_t num_handles, int64_t timeout_ns) {
|
||||
R_RETURN(::svcWaitSynchronization(out_index, handles.GetPointerUnsafe(), num_handles, timeout_ns));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result CancelSynchronization(::ams::svc::Handle handle) {
|
||||
R_RETURN(::svcCancelSynchronization(handle));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result ArbitrateLock(::ams::svc::Handle thread_handle, ::ams::svc::Address address, uint32_t tag) {
|
||||
R_RETURN(::svcArbitrateLock(thread_handle, reinterpret_cast<u32 *>(static_cast<uintptr_t>(address)), tag));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result ArbitrateUnlock(::ams::svc::Address address) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_RETURN(::svcConnectToNamedPort(out_handle, name.GetPointerUnsafe()));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result SendSyncRequestLight(::ams::svc::Handle session_handle) {
|
||||
R_RETURN(::svcSendSyncRequestLight(session_handle));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result SendSyncRequest(::ams::svc::Handle session_handle) {
|
||||
R_RETURN(::svcSendSyncRequest(session_handle));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result SendSyncRequestWithUserBuffer(::ams::svc::Address message_buffer, ::ams::svc::Size message_buffer_size, ::ams::svc::Handle session_handle) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_RETURN(::svcGetProcessId(out_process_id, process_handle));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result GetThreadId(uint64_t *out_thread_id, ::ams::svc::Handle thread_handle) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_RETURN(::svcFlushDataCache(reinterpret_cast<void *>(static_cast<uintptr_t>(address)), size));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result MapPhysicalMemory(::ams::svc::Address address, ::ams::svc::Size size) {
|
||||
R_RETURN(::svcMapPhysicalMemory(reinterpret_cast<void *>(static_cast<uintptr_t>(address)), size));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result UnmapPhysicalMemory(::ams::svc::Address address, ::ams::svc::Size size) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_RETURN(::svcSignalToAddress(reinterpret_cast<void *>(static_cast<uintptr_t>(address)), signal_type, value, count));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void SynchronizePreemptionState() {
|
||||
return ::svcSynchronizePreemptionState();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result GetResourceLimitPeakValue(int64_t *out_peak_value, ::ams::svc::Handle resource_limit_handle, ::ams::svc::LimitableResource which) {
|
||||
R_RETURN(::svcGetResourceLimitPeakValue(out_peak_value, resource_limit_handle, static_cast<::LimitableResource>(which)));
|
||||
}
|
||||
|
||||
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) {
|
||||
R_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) {
|
||||
R_RETURN(::svcAcceptSession(out_handle, port));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result ReplyAndReceiveLight(::ams::svc::Handle handle) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_RETURN(::svcCreateEvent(out_write_handle, out_read_handle));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result MapPhysicalMemoryUnsafe(::ams::svc::Address address, ::ams::svc::Size size) {
|
||||
R_RETURN(::svcMapPhysicalMemoryUnsafe(reinterpret_cast<void *>(static_cast<uintptr_t>(address)), size));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result UnmapPhysicalMemoryUnsafe(::ams::svc::Address address, ::ams::svc::Size size) {
|
||||
R_RETURN(::svcUnmapPhysicalMemoryUnsafe(reinterpret_cast<void *>(static_cast<uintptr_t>(address)), size));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result SetUnsafeLimit(::ams::svc::Size limit) {
|
||||
R_RETURN(::svcSetUnsafeLimit(limit));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result CreateCodeMemory(::ams::svc::Handle *out_handle, ::ams::svc::Address address, ::ams::svc::Size size) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_RETURN(::svcReadWriteRegister(out_value, address, mask, value));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result SetProcessActivity(::ams::svc::Handle process_handle, ::ams::svc::ProcessActivity process_activity) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_RETURN(::svcQueryPhysicalAddress(reinterpret_cast<::PhysicalMemoryInfo *>(out_info), address));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result QueryIoMapping(::ams::svc::Address *out_address, ::ams::svc::Size *out_size, ::ams::svc::PhysicalAddress physical_address, ::ams::svc::Size size) {
|
||||
R_RETURN(::svcQueryIoMapping(reinterpret_cast<u64 *>(out_address), reinterpret_cast<u64 *>(out_size), physical_address, size));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result LegacyQueryIoMapping(::ams::svc::Address *out_address, ::ams::svc::PhysicalAddress physical_address, ::ams::svc::Size size) {
|
||||
R_RETURN(::svcLegacyQueryIoMapping(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) {
|
||||
R_RETURN(::svcCreateDeviceAddressSpace(out_handle, das_address, das_size));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result AttachDeviceAddressSpace(::ams::svc::DeviceName device_name, ::ams::svc::Handle das_handle) {
|
||||
R_RETURN(::svcAttachDeviceAddressSpace(static_cast<u64>(device_name), das_handle));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result DetachDeviceAddressSpace(::ams::svc::DeviceName device_name, ::ams::svc::Handle das_handle) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_RETURN(::svcMapDeviceAddressSpaceAligned(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) {
|
||||
R_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) {
|
||||
R_RETURN(::svcInvalidateProcessDataCache(process_handle, address, size));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result StoreProcessDataCache(::ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
|
||||
R_RETURN(::svcStoreProcessDataCache(process_handle, address, size));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result FlushProcessDataCache(::ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
|
||||
R_RETURN(::svcFlushProcessDataCache(process_handle, address, size));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result DebugActiveProcess(::ams::svc::Handle *out_handle, uint64_t process_id) {
|
||||
R_RETURN(::svcDebugActiveProcess(out_handle, process_id));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result BreakDebugProcess(::ams::svc::Handle debug_handle) {
|
||||
R_RETURN(::svcBreakDebugProcess(debug_handle));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result TerminateDebugProcess(::ams::svc::Handle debug_handle) {
|
||||
R_RETURN(::svcTerminateDebugProcess(debug_handle));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result GetDebugEvent(::ams::svc::UserPointer< ::ams::svc::lp64::DebugEventInfo *> out_info, ::ams::svc::Handle debug_handle) {
|
||||
R_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) {
|
||||
R_RETURN(::svcContinueDebugEvent(debug_handle, flags, const_cast<u64 *>(thread_ids.GetPointerUnsafe()), num_thread_ids));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result LegacyContinueDebugEvent(::ams::svc::Handle debug_handle, uint32_t flags, uint64_t thread_id) {
|
||||
R_RETURN(::svcLegacyContinueDebugEvent(debug_handle, flags, thread_id));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result GetProcessList(int32_t *out_num_processes, ::ams::svc::UserPointer<uint64_t *> out_process_ids, int32_t max_out_count) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_RETURN(::svcManageNamedPort(out_server_handle, name.GetPointerUnsafe(), max_sessions));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result ConnectToPort(::ams::svc::Handle *out_handle, ::ams::svc::Handle port) {
|
||||
R_RETURN(::svcConnectToPort(out_handle, port));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result SetProcessMemoryPermission(::ams::svc::Handle process_handle, uint64_t address, uint64_t size, ::ams::svc::MemoryPermission perm) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_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) {
|
||||
R_RETURN(::svcStartProcess(process_handle, priority, core_id, main_thread_stack_size));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result TerminateProcess(::ams::svc::Handle process_handle) {
|
||||
R_RETURN(::svcTerminateProcess(process_handle));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result GetProcessInfo(int64_t *out_info, ::ams::svc::Handle process_handle, ::ams::svc::ProcessInfoType info_type) {
|
||||
R_RETURN(::svcGetProcessInfo(out_info, process_handle, static_cast<::ProcessInfoType>(info_type)));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result CreateResourceLimit(::ams::svc::Handle *out_handle) {
|
||||
R_RETURN(::svcCreateResourceLimit(out_handle));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result SetResourceLimitLimitValue(::ams::svc::Handle resource_limit_handle, ::ams::svc::LimitableResource which, int64_t limit_value) {
|
||||
R_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));
|
||||
return ::ams::svc::aarch64::lp64::SetHeapSize(reinterpret_cast<::ams::svc::Address *>(out_address), size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -148,6 +148,8 @@ init_libnx_shim.os.horizon.o: CXXFLAGS += -fno-lto
|
||||
|
||||
result_get_name.o: CXXFLAGS += -fno-lto
|
||||
|
||||
crypto_sha256_generator.o: CXXFLAGS += -fno-lto
|
||||
|
||||
spl_secure_monitor_api.os.generic.o: CXXFLAGS += -I$(ATMOSPHERE_LIBRARIES_DIR)/libexosphere/include
|
||||
fs_id_string_impl.os.generic.o: CXXFLAGS += -I$(ATMOSPHERE_LIBRARIES_DIR)/libexosphere/include
|
||||
|
||||
|
||||
@@ -256,7 +256,7 @@ namespace ams::fs::impl {
|
||||
fssrv::sf::Path fsp_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
|
||||
|
||||
R_RETURN(m_x->QueryEntry(sf::OutBuffer(dst, dst_size), sf::InBuffer(src, src_size), static_cast<s32>(query), fsp_path));
|
||||
R_RETURN(m_x->QueryEntry(sf::OutNonSecureBuffer(dst, dst_size), sf::InNonSecureBuffer(src, src_size), static_cast<s32>(query), fsp_path));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -330,7 +330,7 @@ namespace ams::fssrv::impl {
|
||||
R_RETURN(m_base_fs->GetFileTimeStampRaw(out.GetPointer(), fs_path));
|
||||
}
|
||||
|
||||
Result FileSystemInterfaceAdapter::QueryEntry(const ams::sf::OutBuffer &out_buf, const ams::sf::InBuffer &in_buf, s32 query_id, const fssrv::sf::Path &path) {
|
||||
Result FileSystemInterfaceAdapter::QueryEntry(const ams::sf::OutNonSecureBuffer &out_buf, const ams::sf::InNonSecureBuffer &in_buf, s32 query_id, const fssrv::sf::Path &path) {
|
||||
/* Check that we have permission to perform the operation. */
|
||||
switch (static_cast<fs::fsa::QueryId>(query_id)) {
|
||||
using enum fs::fsa::QueryId;
|
||||
|
||||
@@ -87,7 +87,11 @@ namespace ams::fssystem {
|
||||
AMS_ASSERT(decryptor != nullptr);
|
||||
|
||||
/* Initialize the bucket tree table. */
|
||||
R_TRY(m_table.Initialize(allocator, node_storage, entry_storage, NodeSize, sizeof(Entry), entry_count));
|
||||
if (entry_count > 0) {
|
||||
R_TRY(m_table.Initialize(allocator, node_storage, entry_storage, NodeSize, sizeof(Entry), entry_count));
|
||||
} else {
|
||||
m_table.Initialize(NodeSize, 0);
|
||||
}
|
||||
|
||||
/* Set members. */
|
||||
m_data_storage = data_storage;
|
||||
@@ -106,6 +110,66 @@ namespace ams::fssystem {
|
||||
}
|
||||
}
|
||||
|
||||
Result AesCtrCounterExtendedStorage::GetEntryList(Entry *out_entries, s32 *out_entry_count, s32 entry_count, s64 offset, s64 size) {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(offset >= 0);
|
||||
AMS_ASSERT(size >= 0);
|
||||
AMS_ASSERT(this->IsInitialized());
|
||||
|
||||
/* Clear the out count. */
|
||||
R_UNLESS(out_entry_count != nullptr, fs::ResultNullptrArgument());
|
||||
*out_entry_count = 0;
|
||||
|
||||
/* Succeed if there's no range. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* If we have an output array, we need it to be non-null. */
|
||||
R_UNLESS(out_entries != nullptr || entry_count == 0, fs::ResultNullptrArgument());
|
||||
|
||||
/* Check that our range is valid. */
|
||||
BucketTree::Offsets table_offsets;
|
||||
R_TRY(m_table.GetOffsets(std::addressof(table_offsets)));
|
||||
|
||||
R_UNLESS(table_offsets.IsInclude(offset, size), fs::ResultOutOfRange());
|
||||
|
||||
/* Find the offset in our tree. */
|
||||
BucketTree::Visitor visitor;
|
||||
R_TRY(m_table.Find(std::addressof(visitor), offset));
|
||||
{
|
||||
const auto entry_offset = visitor.Get<Entry>()->GetOffset();
|
||||
R_UNLESS(0 <= entry_offset && table_offsets.IsInclude(entry_offset), fs::ResultInvalidAesCtrCounterExtendedEntryOffset());
|
||||
}
|
||||
|
||||
/* Prepare to loop over entries. */
|
||||
const auto end_offset = offset + static_cast<s64>(size);
|
||||
s32 count = 0;
|
||||
|
||||
auto cur_entry = *visitor.Get<Entry>();
|
||||
while (cur_entry.GetOffset() < end_offset) {
|
||||
/* Try to write the entry to the out list */
|
||||
if (entry_count != 0) {
|
||||
if (count >= entry_count) {
|
||||
break;
|
||||
}
|
||||
std::memcpy(out_entries + count, std::addressof(cur_entry), sizeof(Entry));
|
||||
}
|
||||
|
||||
count++;
|
||||
|
||||
/* Advance. */
|
||||
if (visitor.CanMoveNext()) {
|
||||
R_TRY(visitor.MoveNext());
|
||||
cur_entry = *visitor.Get<Entry>();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the output count. */
|
||||
*out_entry_count = count;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result AesCtrCounterExtendedStorage::Read(s64 offset, void *buffer, size_t size) {
|
||||
/* Validate preconditions. */
|
||||
AMS_ASSERT(offset >= 0);
|
||||
@@ -122,7 +186,7 @@ namespace ams::fssystem {
|
||||
BucketTree::Offsets table_offsets;
|
||||
R_TRY(m_table.GetOffsets(std::addressof(table_offsets)));
|
||||
|
||||
R_UNLESS(table_offsets.IsInclude(offset, size), fs::ResultOutOfRange());
|
||||
R_UNLESS(table_offsets.IsInclude(offset, size), fs::ResultOutOfRange());
|
||||
|
||||
/* Read the data. */
|
||||
R_TRY(m_data_storage.Read(offset, buffer, size));
|
||||
@@ -174,15 +238,18 @@ namespace ams::fssystem {
|
||||
const auto cur_size = static_cast<size_t>(std::min(remaining_size, data_size));
|
||||
AMS_ASSERT(cur_size <= size);
|
||||
|
||||
/* Make the CTR for the data we're decrypting. */
|
||||
const auto counter_offset = m_counter_offset + cur_entry_offset + data_offset;
|
||||
NcaAesCtrUpperIv upper_iv = { .part = { .generation = static_cast<u32>(cur_entry.generation), .secure_value = m_secure_value } };
|
||||
/* If necessary, perform decryption. */
|
||||
if (cur_entry.encryption_value == Entry::Encryption::Encrypted) {
|
||||
/* Make the CTR for the data we're decrypting. */
|
||||
const auto counter_offset = m_counter_offset + cur_entry_offset + data_offset;
|
||||
NcaAesCtrUpperIv upper_iv = { .part = { .generation = static_cast<u32>(cur_entry.generation), .secure_value = m_secure_value } };
|
||||
|
||||
u8 iv[IvSize];
|
||||
AesCtrStorageByPointer::MakeIv(iv, IvSize, upper_iv.value, counter_offset);
|
||||
u8 iv[IvSize];
|
||||
AesCtrStorageByPointer::MakeIv(iv, IvSize, upper_iv.value, counter_offset);
|
||||
|
||||
/* Decrypt. */
|
||||
m_decryptor->Decrypt(cur_data, cur_size, m_key, KeySize, iv, IvSize);
|
||||
/* Decrypt. */
|
||||
m_decryptor->Decrypt(cur_data, cur_size, m_key, KeySize, iv, IvSize);
|
||||
}
|
||||
|
||||
/* Advance. */
|
||||
cur_data += cur_size;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
void AesCtrStorage<BasePointer>::MakeIv(void *dst, size_t dst_size, u64 upper, s64 offset) {
|
||||
AMS_ASSERT(dst != nullptr);
|
||||
AMS_ASSERT(dst_size == IvSize);
|
||||
@@ -30,7 +30,7 @@ namespace ams::fssystem {
|
||||
util::StoreBigEndian(reinterpret_cast<s64 *>(out_addr + sizeof(u64)), static_cast<s64>(offset / BlockSize));
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
AesCtrStorage<BasePointer>::AesCtrStorage(BasePointer base, const void *key, size_t key_size, const void *iv, size_t iv_size) : m_base_storage(std::move(base)) {
|
||||
AMS_ASSERT(m_base_storage != nullptr);
|
||||
AMS_ASSERT(key != nullptr);
|
||||
@@ -43,7 +43,7 @@ namespace ams::fssystem {
|
||||
std::memcpy(m_iv, iv, IvSize);
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
Result AesCtrStorage<BasePointer>::Read(s64 offset, void *buffer, size_t size) {
|
||||
/* Allow zero-size reads. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
@@ -73,7 +73,7 @@ namespace ams::fssystem {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
Result AesCtrStorage<BasePointer>::Write(s64 offset, const void *buffer, size_t size) {
|
||||
/* Allow zero-size writes. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
@@ -127,23 +127,23 @@ namespace ams::fssystem {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
Result AesCtrStorage<BasePointer>::Flush() {
|
||||
R_RETURN(m_base_storage->Flush());
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
Result AesCtrStorage<BasePointer>::SetSize(s64 size) {
|
||||
AMS_UNUSED(size);
|
||||
R_THROW(fs::ResultUnsupportedSetSizeForAesCtrStorage());
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
Result AesCtrStorage<BasePointer>::GetSize(s64 *out) {
|
||||
R_RETURN(m_base_storage->GetSize(out));
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
Result AesCtrStorage<BasePointer>::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
|
||||
/* If operation isn't invalidate, special case. */
|
||||
if (op_id != fs::OperationId::Invalidate) {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
void AesXtsStorage<BasePointer>::MakeAesXtsIv(void *dst, size_t dst_size, s64 offset, size_t block_size) {
|
||||
AMS_ASSERT(dst != nullptr);
|
||||
AMS_ASSERT(dst_size == IvSize);
|
||||
@@ -29,7 +29,7 @@ namespace ams::fssystem {
|
||||
util::StoreBigEndian<s64>(reinterpret_cast<s64 *>(out_addr + sizeof(s64)), offset / block_size);
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
AesXtsStorage<BasePointer>::AesXtsStorage(BasePointer base, const void *key1, const void *key2, size_t key_size, const void *iv, size_t iv_size, size_t block_size) : m_base_storage(std::move(base)), m_block_size(block_size), m_mutex() {
|
||||
AMS_ASSERT(m_base_storage != nullptr);
|
||||
AMS_ASSERT(key1 != nullptr);
|
||||
@@ -45,7 +45,7 @@ namespace ams::fssystem {
|
||||
std::memcpy(m_iv, iv, IvSize);
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
Result AesXtsStorage<BasePointer>::Read(s64 offset, void *buffer, size_t size) {
|
||||
/* Allow zero-size reads. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
@@ -111,7 +111,7 @@ namespace ams::fssystem {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
Result AesXtsStorage<BasePointer>::Write(s64 offset, const void *buffer, size_t size) {
|
||||
/* Allow zero-size writes. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
@@ -209,24 +209,24 @@ namespace ams::fssystem {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
Result AesXtsStorage<BasePointer>::Flush() {
|
||||
R_RETURN(m_base_storage->Flush());
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
Result AesXtsStorage<BasePointer>::SetSize(s64 size) {
|
||||
R_UNLESS(util::IsAligned(size, AesBlockSize), fs::ResultUnexpectedInAesXtsStorageA());
|
||||
|
||||
R_RETURN(m_base_storage->SetSize(size));
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
Result AesXtsStorage<BasePointer>::GetSize(s64 *out) {
|
||||
R_RETURN(m_base_storage->GetSize(out));
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
Result AesXtsStorage<BasePointer>::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
|
||||
/* Unless invalidating cache, check the arguments. */
|
||||
if (op_id != fs::OperationId::Invalidate) {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
AesXtsStorageExternal<BasePointer>::AesXtsStorageExternal(BasePointer bs, const void *key1, const void *key2, size_t key_size, const void *iv, size_t iv_size, size_t block_size, CryptAesXtsFunction ef, CryptAesXtsFunction df) : m_base_storage(std::move(bs)), m_block_size(block_size), m_encrypt_function(ef), m_decrypt_function(df) {
|
||||
AMS_ASSERT(key_size == KeySize);
|
||||
AMS_ASSERT(iv_size == IvSize);
|
||||
@@ -34,7 +34,7 @@ namespace ams::fssystem {
|
||||
std::memcpy(m_iv, iv, IvSize);
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
Result AesXtsStorageExternal<BasePointer>::Read(s64 offset, void *buffer, size_t size) {
|
||||
/* Allow zero size. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
@@ -102,7 +102,7 @@ namespace ams::fssystem {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
Result AesXtsStorageExternal<BasePointer>::Write(s64 offset, const void *buffer, size_t size) {
|
||||
/* Allow zero-size writes. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
@@ -111,7 +111,7 @@ namespace ams::fssystem {
|
||||
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
/* Ensure we can encrypt. */
|
||||
R_UNLESS(m_decrypt_function != nullptr, fs::ResultNullptrArgument());
|
||||
R_UNLESS(m_encrypt_function != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
/* We can only write at block aligned offsets. */
|
||||
R_UNLESS(util::IsAligned(offset, AesBlockSize), fs::ResultInvalidArgument());
|
||||
@@ -195,7 +195,7 @@ namespace ams::fssystem {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
Result AesXtsStorageExternal<BasePointer>::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
|
||||
/* Unless invalidating cache, check the arguments. */
|
||||
if (op_id != fs::OperationId::Invalidate) {
|
||||
@@ -210,17 +210,17 @@ namespace ams::fssystem {
|
||||
R_RETURN(m_base_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
Result AesXtsStorageExternal<BasePointer>::GetSize(s64 *out) {
|
||||
R_RETURN(m_base_storage->GetSize(out));
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
Result AesXtsStorageExternal<BasePointer>::Flush() {
|
||||
R_RETURN(m_base_storage->Flush());
|
||||
}
|
||||
|
||||
template<typename BasePointer>
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
Result AesXtsStorageExternal<BasePointer>::SetSize(s64 size) {
|
||||
R_UNLESS(util::IsAligned(size, AesBlockSize), fs::ResultUnexpectedInAesXtsStorageA());
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
Result IntegrityVerificationStorage::Initialize(fs::SubStorage hs, fs::SubStorage ds, s64 verif_block_size, s64 upper_layer_verif_block_size, fs::IBufferManager *bm, fssystem::IHash256GeneratorFactory *hgf, const util::optional<fs::HashSalt> &salt, bool is_real_data, bool is_writable, bool allow_cleared_blocks) {
|
||||
void IntegrityVerificationStorage::Initialize(fs::SubStorage hs, fs::SubStorage ds, s64 verif_block_size, s64 upper_layer_verif_block_size, fs::IBufferManager *bm, fssystem::IHash256GeneratorFactory *hgf, const util::optional<fs::HashSalt> &salt, bool is_real_data, bool is_writable, bool allow_cleared_blocks) {
|
||||
/* Validate preconditions. */
|
||||
AMS_ASSERT(verif_block_size >= HashSize);
|
||||
AMS_ASSERT(bm != nullptr);
|
||||
@@ -61,7 +61,6 @@ namespace ams::fssystem {
|
||||
m_is_real_data = is_real_data;
|
||||
m_is_writable = is_writable;
|
||||
m_allow_cleared_blocks = allow_cleared_blocks;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void IntegrityVerificationStorage::Finalize() {
|
||||
@@ -120,7 +119,8 @@ namespace ams::fssystem {
|
||||
Result verify_hash_result = ResultSuccess();
|
||||
|
||||
/* Create hash generator. */
|
||||
auto generator = m_hash_generator_factory->Create();
|
||||
std::unique_ptr<IHash256Generator> generator = nullptr;
|
||||
R_TRY(m_hash_generator_factory->Create(std::addressof(generator)));
|
||||
|
||||
/* Prepare to validate the signatures. */
|
||||
const auto signature_count = size >> m_verification_block_order;
|
||||
@@ -221,7 +221,9 @@ namespace ams::fssystem {
|
||||
PooledBuffer signature_buffer(signature_count * sizeof(BlockHash), sizeof(BlockHash));
|
||||
const auto buffer_count = std::min(signature_count, signature_buffer.GetSize() / sizeof(BlockHash));
|
||||
|
||||
auto generator = m_hash_generator_factory->Create();
|
||||
/* Create hash generator. */
|
||||
std::unique_ptr<IHash256Generator> generator = nullptr;
|
||||
R_TRY(m_hash_generator_factory->Create(std::addressof(generator)));
|
||||
|
||||
while (updated_count < signature_count) {
|
||||
const auto cur_count = std::min(buffer_count, signature_count - updated_count);
|
||||
@@ -326,7 +328,7 @@ namespace ams::fssystem {
|
||||
R_TRY(m_hash_storage.Read(sign_offset, buf.get(), sign_size));
|
||||
|
||||
/* Clear the signature. */
|
||||
/* This sets all bytes to FF, with the verification bit cleared. */
|
||||
/* This flips all bits other than the verification bit. */
|
||||
for (auto i = 0; i < sign_size; ++i) {
|
||||
buf[i] ^= ((i + 1) % HashSize == 0 ? 0x7F : 0xFF);
|
||||
}
|
||||
|
||||
@@ -25,8 +25,19 @@ namespace ams::lr {
|
||||
private:
|
||||
/* Storage for RegisteredData entries by data id. */
|
||||
RegisteredStorages<ncm::DataId, 0x800> m_registered_storages;
|
||||
private:
|
||||
static ALWAYS_INLINE size_t GetStorageCapacity() {
|
||||
const auto version = hos::GetVersion();
|
||||
if (version >= hos::Version_12_0_0) {
|
||||
return 0x8;
|
||||
} else if (version >= hos::Version_9_0_0) {
|
||||
return 0x2;
|
||||
} else {
|
||||
return 0x800;
|
||||
}
|
||||
}
|
||||
public:
|
||||
AddOnContentLocationResolverImpl() : m_registered_storages(hos::GetVersion() < hos::Version_9_0_0 ? 0x800 : 0x2) { /* ... */ }
|
||||
AddOnContentLocationResolverImpl() : m_registered_storages(GetStorageCapacity()) { /* ... */ }
|
||||
|
||||
/* Actual commands. */
|
||||
Result ResolveAddOnContentPath(sf::Out<Path> out, ncm::DataId id);
|
||||
|
||||
@@ -25,10 +25,13 @@ namespace ams::os::impl {
|
||||
AddressAllocationResult_OutOfSpace,
|
||||
};
|
||||
|
||||
template<std::unsigned_integral AddressType, std::unsigned_integral SizeType>
|
||||
template<std::unsigned_integral AddressType_, std::unsigned_integral SizeType_>
|
||||
class AddressSpaceAllocatorBase {
|
||||
NON_COPYABLE(AddressSpaceAllocatorBase);
|
||||
NON_MOVEABLE(AddressSpaceAllocatorBase);
|
||||
public:
|
||||
using AddressType = AddressType_;
|
||||
using SizeType = SizeType_;
|
||||
private:
|
||||
static constexpr size_t MaxForbiddenRegions = 2;
|
||||
private:
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace ams::os::impl {
|
||||
public:
|
||||
using Base::Base;
|
||||
public:
|
||||
virtual bool CheckFreeSpace(uintptr_t address, size_t size) override {
|
||||
virtual bool CheckFreeSpace(AddressType address, SizeType size) override {
|
||||
/* Query the memory. */
|
||||
svc::MemoryInfo memory_info;
|
||||
svc::PageInfo page_info;
|
||||
|
||||
@@ -32,9 +32,9 @@ namespace ams::os::impl {
|
||||
|
||||
static constexpr size_t ForbiddenRegionCount = 2;
|
||||
private:
|
||||
static u64 GetAslrInfo(svc::InfoType type) {
|
||||
static u64 GetAslrInfo(os::NativeHandle process_handle, svc::InfoType type) {
|
||||
u64 value;
|
||||
R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), type, svc::PseudoHandle::CurrentProcess, 0));
|
||||
R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), type, process_handle, 0));
|
||||
|
||||
static_assert(std::same_as<size_t, uintptr_t>);
|
||||
AMS_ASSERT(value <= std::numeric_limits<size_t>::max());
|
||||
@@ -45,8 +45,11 @@ namespace ams::os::impl {
|
||||
AddressSpaceAllocatorForbiddenRegion m_forbidden_regions[ForbiddenRegionCount];
|
||||
public:
|
||||
AslrSpaceManagerHorizonImpl() {
|
||||
m_forbidden_regions[0] = { .address = GetHeapSpaceBeginAddress(), .size = GetHeapSpaceSize() };
|
||||
m_forbidden_regions[1] = { .address = GetAliasSpaceBeginAddress(), .size = GetAliasSpaceSize() };
|
||||
this->InitializeForbiddenRegions(svc::PseudoHandle::CurrentProcess);
|
||||
}
|
||||
|
||||
AslrSpaceManagerHorizonImpl(os::NativeHandle process_handle) {
|
||||
this->InitializeForbiddenRegions(process_handle);
|
||||
}
|
||||
|
||||
const AddressSpaceAllocatorForbiddenRegion *GetForbiddenRegions() const {
|
||||
@@ -57,28 +60,33 @@ namespace ams::os::impl {
|
||||
return ForbiddenRegionCount;
|
||||
}
|
||||
|
||||
static u64 GetHeapSpaceBeginAddress() {
|
||||
return GetAslrInfo(svc::InfoType_HeapRegionAddress);
|
||||
static u64 GetHeapSpaceBeginAddress(os::NativeHandle process_handle = svc::PseudoHandle::CurrentProcess) {
|
||||
return GetAslrInfo(process_handle, svc::InfoType_HeapRegionAddress);
|
||||
}
|
||||
|
||||
static u64 GetHeapSpaceSize() {
|
||||
return GetAslrInfo(svc::InfoType_HeapRegionSize);
|
||||
static u64 GetHeapSpaceSize(os::NativeHandle process_handle = svc::PseudoHandle::CurrentProcess) {
|
||||
return GetAslrInfo(process_handle, svc::InfoType_HeapRegionSize);
|
||||
}
|
||||
|
||||
static u64 GetAliasSpaceBeginAddress() {
|
||||
return GetAslrInfo(svc::InfoType_AliasRegionAddress);
|
||||
static u64 GetAliasSpaceBeginAddress(os::NativeHandle process_handle = svc::PseudoHandle::CurrentProcess) {
|
||||
return GetAslrInfo(process_handle, svc::InfoType_AliasRegionAddress);
|
||||
}
|
||||
|
||||
static u64 GetAliasSpaceSize() {
|
||||
return GetAslrInfo(svc::InfoType_AliasRegionSize);
|
||||
static u64 GetAliasSpaceSize(os::NativeHandle process_handle = svc::PseudoHandle::CurrentProcess) {
|
||||
return GetAslrInfo(process_handle, svc::InfoType_AliasRegionSize);
|
||||
}
|
||||
|
||||
static u64 GetAslrSpaceBeginAddress() {
|
||||
return GetAslrInfo(svc::InfoType_AslrRegionAddress);
|
||||
static u64 GetAslrSpaceBeginAddress(os::NativeHandle process_handle = svc::PseudoHandle::CurrentProcess) {
|
||||
return GetAslrInfo(process_handle, svc::InfoType_AslrRegionAddress);
|
||||
}
|
||||
|
||||
static u64 GetAslrSpaceEndAddress() {
|
||||
return GetAslrInfo(svc::InfoType_AslrRegionAddress) + GetAslrInfo(svc::InfoType_AslrRegionSize);
|
||||
static u64 GetAslrSpaceEndAddress(os::NativeHandle process_handle = svc::PseudoHandle::CurrentProcess) {
|
||||
return GetAslrInfo(process_handle, svc::InfoType_AslrRegionAddress) + GetAslrInfo(process_handle, svc::InfoType_AslrRegionSize);
|
||||
}
|
||||
private:
|
||||
void InitializeForbiddenRegions(os::NativeHandle process_handle) {
|
||||
m_forbidden_regions[0] = { .address = GetHeapSpaceBeginAddress(process_handle), .size = GetHeapSpaceSize(process_handle) };
|
||||
m_forbidden_regions[1] = { .address = GetAliasSpaceBeginAddress(process_handle), .size = GetAliasSpaceSize(process_handle) };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -38,6 +38,9 @@ namespace ams::os::impl {
|
||||
class AslrSpaceManagerTemplate {
|
||||
NON_COPYABLE(AslrSpaceManagerTemplate);
|
||||
NON_MOVEABLE(AslrSpaceManagerTemplate);
|
||||
private:
|
||||
using AddressType = typename Allocator::AddressType;
|
||||
using SizeType = typename Allocator::SizeType;
|
||||
private:
|
||||
Impl m_impl;
|
||||
Allocator m_allocator;
|
||||
@@ -46,10 +49,16 @@ namespace ams::os::impl {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
uintptr_t AllocateSpace(size_t size) {
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
AslrSpaceManagerTemplate(os::NativeHandle process_handle) : m_impl(process_handle), m_allocator(m_impl.GetAslrSpaceBeginAddress(process_handle), m_impl.GetAslrSpaceEndAddress(process_handle), AslrSpaceGuardSize, m_impl.GetForbiddenRegions(), m_impl.GetForbiddenRegionCount(), process_handle) {
|
||||
/* ... */
|
||||
}
|
||||
#endif
|
||||
|
||||
AddressType AllocateSpace(SizeType size, SizeType align_offset) {
|
||||
/* Try to allocate a large-aligned space, if we can. */
|
||||
if (size >= AslrSpaceLargeAlign) {
|
||||
if (auto large_align = m_allocator.AllocateSpace(size, AslrSpaceLargeAlign, 0); large_align != 0) {
|
||||
if (align_offset || (size / AslrSpaceLargeAlign) != 0) {
|
||||
if (auto large_align = m_allocator.AllocateSpace(size, AslrSpaceLargeAlign, align_offset & (AslrSpaceLargeAlign - 1)); large_align != 0) {
|
||||
return large_align;
|
||||
}
|
||||
}
|
||||
@@ -58,16 +67,16 @@ namespace ams::os::impl {
|
||||
return m_allocator.AllocateSpace(size, MemoryPageSize, 0);
|
||||
}
|
||||
|
||||
bool CheckGuardSpace(uintptr_t address, size_t size) {
|
||||
bool CheckGuardSpace(AddressType address, SizeType size) {
|
||||
return m_allocator.CheckGuardSpace(address, size, AslrSpaceGuardSize);
|
||||
}
|
||||
|
||||
template<typename MapFunction, typename UnmapFunction>
|
||||
Result MapAtRandomAddress(uintptr_t *out, size_t size, MapFunction map_function, UnmapFunction unmap_function) {
|
||||
Result MapAtRandomAddress(AddressType *out, MapFunction map_function, UnmapFunction unmap_function, SizeType size, SizeType align_offset) {
|
||||
/* Try to map up to 64 times. */
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
for (auto i = 0; i < 64; ++i) {
|
||||
/* Reserve space to map the memory. */
|
||||
const uintptr_t map_address = this->AllocateSpace(size);
|
||||
const uintptr_t map_address = this->AllocateSpace(size, align_offset);
|
||||
if (map_address == 0) {
|
||||
break;
|
||||
}
|
||||
@@ -82,6 +91,9 @@ namespace ams::os::impl {
|
||||
if (!this->CheckGuardSpace(map_address, size)) {
|
||||
/* We don't have guard space, so unmap. */
|
||||
unmap_function(map_address, size);
|
||||
|
||||
/* NOTE: Nintendo is missing this continue; this is almost certainly a bug. */
|
||||
/* This will cause them to incorrectly return success after unmapping if guard space is not present. */
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,10 @@ namespace ams::os::impl {
|
||||
AMS_ASSERT(out_size != nullptr);
|
||||
|
||||
/* Get the current stack by NT_TIB */
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Warray-bounds"
|
||||
auto *tib = reinterpret_cast<NT_TIB *>(::NtCurrentTeb());
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
*out_stack = reinterpret_cast<uintptr_t>(tib->StackLimit);
|
||||
*out_size = reinterpret_cast<uintptr_t>(tib->StackBase) - reinterpret_cast<uintptr_t>(tib->StackLimit);
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace ams::os::impl {
|
||||
|
||||
/* Map at a random address. */
|
||||
uintptr_t mapped_address;
|
||||
R_TRY(impl::GetAslrSpaceManager().MapAtRandomAddress(std::addressof(mapped_address), size,
|
||||
R_TRY(impl::GetAslrSpaceManager().MapAtRandomAddress(std::addressof(mapped_address),
|
||||
[handle, svc_perm](uintptr_t map_address, size_t map_size) -> Result {
|
||||
R_TRY_CATCH(svc::MapIoRegion(handle, map_address, map_size, svc_perm)) {
|
||||
/* TODO: What's the correct result for these? */
|
||||
@@ -73,7 +73,9 @@ namespace ams::os::impl {
|
||||
},
|
||||
[handle](uintptr_t map_address, size_t map_size) -> void {
|
||||
return IoRegionImpl::UnmapIoRegion(handle, reinterpret_cast<void *>(map_address), map_size);
|
||||
}
|
||||
},
|
||||
size,
|
||||
0
|
||||
));
|
||||
|
||||
/* Return the address we mapped at. */
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::ro::impl {
|
||||
namespace ams::os::impl {
|
||||
|
||||
void SetMemoryAttributeImpl(uintptr_t address, size_t size, MemoryAttribute attr);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 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 {
|
||||
|
||||
void SetMemoryAttributeImpl(uintptr_t address, size_t size, MemoryAttribute attr) {
|
||||
/* Determine svc arguments. */
|
||||
u32 svc_mask = svc::MemoryAttribute_Uncached;
|
||||
u32 svc_attr = 0;
|
||||
|
||||
switch (attr) {
|
||||
case os::MemoryAttribute_Normal: svc_attr = 0; break;
|
||||
case os::MemoryAttribute_Uncached: svc_attr = svc::MemoryAttribute_Uncached; break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
/* Loop, setting attribute. */
|
||||
auto cur_address = address;
|
||||
auto remaining = size;
|
||||
while (remaining > 0) {
|
||||
/* Query the memory. */
|
||||
svc::MemoryInfo mem_info;
|
||||
svc::PageInfo page_info;
|
||||
R_ABORT_UNLESS(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), cur_address));
|
||||
|
||||
/* Determine the current size. */
|
||||
const size_t cur_size = std::min<size_t>(mem_info.base_address + mem_info.size - cur_address, remaining);
|
||||
|
||||
/* Set the attribute, if necessary. */
|
||||
if (mem_info.attribute != svc_attr) {
|
||||
if (const auto res = svc::SetMemoryAttribute(address, size, svc_mask, svc_attr); R_FAILED(res)) {
|
||||
/* NOTE: Nintendo logs here. */
|
||||
R_ABORT_UNLESS(res);
|
||||
}
|
||||
}
|
||||
|
||||
/* Advance. */
|
||||
cur_address += cur_size;
|
||||
remaining -= cur_size;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 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 {
|
||||
|
||||
void SetMemoryAttributeImpl(uintptr_t address, size_t size, MemoryAttribute attr) {
|
||||
/* TODO: Should this do anything? */
|
||||
AMS_UNUSED(address, size, attr);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 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 {
|
||||
|
||||
void SetMemoryAttributeImpl(uintptr_t address, size_t size, MemoryAttribute attr) {
|
||||
/* TODO: Should this do anything? */
|
||||
AMS_UNUSED(address, size, attr);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 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 {
|
||||
|
||||
void SetMemoryAttributeImpl(uintptr_t address, size_t size, MemoryAttribute attr) {
|
||||
/* TODO: Should this do anything? */
|
||||
AMS_UNUSED(address, size, attr);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright (c) 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_memory_heap_manager.hpp"
|
||||
#include "os_thread_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
Result MemoryHeapManager::SetHeapSize(size_t size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(util::IsAligned(size, MemoryHeapUnitSize));
|
||||
|
||||
/* Acquire locks. */
|
||||
std::scoped_lock lk1(util::GetReference(::ams::os::impl::GetCurrentThread()->cs_thread));
|
||||
std::scoped_lock lk2(m_cs);
|
||||
|
||||
/* If we need to, expand the heap. */
|
||||
if (size > m_heap_size) {
|
||||
/* Set the new heap size. */
|
||||
uintptr_t address = 0;
|
||||
R_TRY(m_impl.SetHeapSize(std::addressof(address), size));
|
||||
R_UNLESS(address != 0, os::ResultOutOfMemory());
|
||||
|
||||
/* Check that the new heap address is consistent. */
|
||||
if (m_heap_size == 0) {
|
||||
AMS_ASSERT(util::IsAligned(address, MemoryHeapUnitSize));
|
||||
} else {
|
||||
AMS_ASSERT(address == m_heap_address);
|
||||
}
|
||||
|
||||
/* Set up the new heap address. */
|
||||
this->AddToFreeSpaceUnsafe(address + m_heap_size, size - m_heap_size);
|
||||
|
||||
/* Set our heap address. */
|
||||
m_heap_address = address;
|
||||
m_heap_size = size;
|
||||
} else if (size < m_heap_size) {
|
||||
/* We're shrinking the heap, so we need to remove memory blocks. */
|
||||
const uintptr_t end_address = m_heap_address + size;
|
||||
const size_t remove_size = m_heap_size - size;
|
||||
|
||||
/* Get the end of the heap. */
|
||||
auto it = m_free_memory_list.end();
|
||||
--it;
|
||||
R_UNLESS(it != m_free_memory_list.end(), os::ResultBusy());
|
||||
|
||||
/* Check that the block can be decommitted. */
|
||||
R_UNLESS(it->GetAddress() <= end_address, os::ResultBusy());
|
||||
R_UNLESS(it->GetSize() >= remove_size, os::ResultBusy());
|
||||
|
||||
/* Adjust the last node. */
|
||||
if (const size_t node_size = it->GetSize() - remove_size; node_size == 0) {
|
||||
m_free_memory_list.erase(it);
|
||||
it->Clean();
|
||||
} else {
|
||||
it->SetSize(node_size);
|
||||
}
|
||||
|
||||
/* Set the reduced heap size. */
|
||||
uintptr_t address = 0;
|
||||
R_ABORT_UNLESS(m_impl.SetHeapSize(std::addressof(address), size));
|
||||
|
||||
/* Set our heap address. */
|
||||
m_heap_size = size;
|
||||
if (size == 0) {
|
||||
m_heap_address = 0;
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MemoryHeapManager::AllocateFromHeap(uintptr_t *out_address, size_t size) {
|
||||
/* Acquire locks. */
|
||||
std::scoped_lock lk1(util::GetReference(::ams::os::impl::GetCurrentThread()->cs_thread));
|
||||
std::scoped_lock lk2(m_cs);
|
||||
|
||||
/* Find free space. */
|
||||
auto it = this->FindFreeSpaceUnsafe(size);
|
||||
R_UNLESS(it != m_free_memory_list.end(), os::ResultOutOfMemory());
|
||||
|
||||
/* If necessary, split the memory block. */
|
||||
if (it->GetSize() > size) {
|
||||
this->SplitFreeMemoryNodeUnsafe(it, size);
|
||||
}
|
||||
|
||||
/* Remove the block. */
|
||||
m_free_memory_list.erase(it);
|
||||
it->Clean();
|
||||
|
||||
/* Increment the used heap size. */
|
||||
m_used_heap_size += size;
|
||||
|
||||
/* Set the output address. */
|
||||
*out_address = it->GetAddress();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void MemoryHeapManager::ReleaseToHeap(uintptr_t address, size_t size) {
|
||||
/* Acquire locks. */
|
||||
std::scoped_lock lk1(util::GetReference(::ams::os::impl::GetCurrentThread()->cs_thread));
|
||||
std::scoped_lock lk2(m_cs);
|
||||
|
||||
/* Check pre-condition. */
|
||||
AMS_ABORT_UNLESS(this->IsRegionAllocatedMemoryUnsafe(address, size));
|
||||
|
||||
/* Restore the permissions on the memory. */
|
||||
os::SetMemoryPermission(address, size, MemoryPermission_ReadWrite);
|
||||
os::SetMemoryAttribute(address, size, MemoryAttribute_Normal);
|
||||
|
||||
/* Add the memory back to our free list. */
|
||||
this->AddToFreeSpaceUnsafe(address, size);
|
||||
|
||||
/* Decrement the used heap size. */
|
||||
m_used_heap_size -= size;
|
||||
}
|
||||
|
||||
MemoryHeapManager::FreeMemoryList::iterator MemoryHeapManager::FindFreeSpaceUnsafe(size_t size) {
|
||||
/* Find the best fit candidate. */
|
||||
auto best = m_free_memory_list.end();
|
||||
|
||||
for (auto it = m_free_memory_list.begin(); it != m_free_memory_list.end(); ++it) {
|
||||
if (const size_t node_size = it->GetSize(); node_size >= size) {
|
||||
if (best == m_free_memory_list.end() || node_size < best->GetSize()) {
|
||||
best = it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
MemoryHeapManager::FreeMemoryList::iterator MemoryHeapManager::ConcatenatePreviousFreeMemoryNodeUnsafe(FreeMemoryList::iterator node) {
|
||||
/* Get the previous node. */
|
||||
auto prev = node;
|
||||
--prev;
|
||||
|
||||
/* If there's no previous, we're done. */
|
||||
if (prev == m_free_memory_list.end() || node == m_free_memory_list.end()) {
|
||||
return node;
|
||||
}
|
||||
|
||||
/* Otherwise, if the previous isn't contiguous, we can't merge. */
|
||||
if (prev->GetAddress() + prev->GetSize() != node->GetAddress()) {
|
||||
return node;
|
||||
}
|
||||
|
||||
/* Otherwise, increase the size of the previous node, and remove the current node. */
|
||||
prev->SetSize(prev->GetSize() + node->GetSize());
|
||||
m_free_memory_list.erase(node);
|
||||
node->Clean();
|
||||
|
||||
return prev;
|
||||
}
|
||||
|
||||
void MemoryHeapManager::SplitFreeMemoryNodeUnsafe(FreeMemoryList::iterator it, size_t size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(it->GetSize() > size);
|
||||
AMS_ASSERT(util::IsAligned(size, MemoryBlockUnitSize));
|
||||
|
||||
/* Create new node. */
|
||||
auto *new_node = std::construct_at(reinterpret_cast<FreeMemoryNode *>(it->GetAddress() + size));
|
||||
new_node->SetSize(it->GetSize() - size);
|
||||
|
||||
/* Set the old node's size. */
|
||||
it->SetSize(size);
|
||||
|
||||
/* Insert the new node. */
|
||||
m_free_memory_list.insert(++it, *new_node);
|
||||
}
|
||||
|
||||
void MemoryHeapManager::AddToFreeSpaceUnsafe(uintptr_t address, size_t size) {
|
||||
/* Create new node. */
|
||||
auto *new_node = std::construct_at(reinterpret_cast<FreeMemoryNode *>(address));
|
||||
new_node->SetSize(size);
|
||||
|
||||
/* Find the appropriate place to insert the node. */
|
||||
auto it = m_free_memory_list.begin();
|
||||
for (/* ... */; it != m_free_memory_list.end(); ++it) {
|
||||
if (address < it->GetAddress()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert the new node. */
|
||||
it = m_free_memory_list.insert(it, *new_node);
|
||||
|
||||
/* Perform coalescing as relevant. */
|
||||
it = this->ConcatenatePreviousFreeMemoryNodeUnsafe(it);
|
||||
this->ConcatenatePreviousFreeMemoryNodeUnsafe(++it);
|
||||
}
|
||||
|
||||
bool MemoryHeapManager::IsRegionAllocatedMemoryUnsafe(uintptr_t address, size_t size) {
|
||||
/* Look for a node containing the region. */
|
||||
for (auto it = m_free_memory_list.begin(); it != m_free_memory_list.end(); ++it) {
|
||||
const uintptr_t node_address = it->GetAddress();
|
||||
const size_t node_size = it->GetSize();
|
||||
|
||||
if (node_address < address + size && address < node_address + node_size) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 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_memory_heap_manager_types.hpp"
|
||||
#include "os_resource_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
ALWAYS_INLINE MemoryHeapManager &GetMemoryHeapManager() {
|
||||
return GetResourceManager().GetMemoryHeapManager();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 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 MemoryHeapManagerHorizonImpl {
|
||||
public:
|
||||
Result SetHeapSize(uintptr_t *out, size_t size) {
|
||||
R_TRY_CATCH(svc::SetHeapSize(out, size)) {
|
||||
R_CONVERT(svc::ResultOutOfMemory, os::ResultOutOfMemory())
|
||||
R_CONVERT(svc::ResultLimitReached, os::ResultOutOfMemory())
|
||||
R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfMemory())
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
using MemoryHeapManagerImpl = MemoryHeapManagerHorizonImpl;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 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 <sys/mman.h>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MemoryHeapManagerLinuxImpl {
|
||||
NON_COPYABLE(MemoryHeapManagerLinuxImpl);
|
||||
NON_MOVEABLE(MemoryHeapManagerLinuxImpl);
|
||||
private:
|
||||
uintptr_t m_real_reserved_address;
|
||||
size_t m_real_reserved_size;
|
||||
uintptr_t m_aligned_reserved_heap_address;
|
||||
size_t m_aligned_reserved_heap_size;
|
||||
size_t m_committed_size;
|
||||
public:
|
||||
MemoryHeapManagerLinuxImpl() : m_real_reserved_address(0), m_real_reserved_size(0), m_aligned_reserved_heap_address(0), m_aligned_reserved_heap_size(0), m_committed_size(0) {
|
||||
/* Reserve a 32 GB region of virtual address space. */
|
||||
constexpr size_t TargetReservedSize = 32_GB;
|
||||
const auto reserved = ::mmap(nullptr, TargetReservedSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
AMS_ABORT_UNLESS(reserved != MAP_FAILED);
|
||||
|
||||
m_real_reserved_address = reinterpret_cast<uintptr_t>(reserved);
|
||||
m_real_reserved_size = TargetReservedSize;
|
||||
|
||||
m_aligned_reserved_heap_address = util::AlignUp(m_real_reserved_address, MemoryHeapUnitSize);
|
||||
m_aligned_reserved_heap_size = m_real_reserved_size - MemoryHeapUnitSize;
|
||||
}
|
||||
|
||||
Result SetHeapSize(uintptr_t *out, size_t size) {
|
||||
/* Check that we have a reserved address. */
|
||||
R_UNLESS(m_real_reserved_address != 0, os::ResultOutOfMemory());
|
||||
|
||||
/* If necessary, commit the new memory. */
|
||||
if (size > m_committed_size) {
|
||||
R_UNLESS(this->CommitMemory(size), os::ResultOutOfMemory());
|
||||
} else if (size < m_committed_size) {
|
||||
/* Otherwise, decommit. */
|
||||
this->DecommitMemory(m_aligned_reserved_heap_address + size, m_committed_size - size);
|
||||
}
|
||||
|
||||
/* Set the committed size. */
|
||||
m_committed_size = size;
|
||||
|
||||
/* Set the out address. */
|
||||
*out = m_aligned_reserved_heap_address;
|
||||
R_SUCCEED();
|
||||
}
|
||||
private:
|
||||
bool CommitMemory(size_t size) {
|
||||
const auto res = ::mprotect(reinterpret_cast<void *>(m_aligned_reserved_heap_address), size, PROT_READ | PROT_WRITE);
|
||||
return res == 0;
|
||||
}
|
||||
|
||||
void DecommitMemory(uintptr_t address, size_t size) {
|
||||
const auto reserved = ::mmap(reinterpret_cast<void *>(address), size, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
AMS_ABORT_UNLESS(reserved != MAP_FAILED);
|
||||
}
|
||||
};
|
||||
|
||||
using MemoryHeapManagerImpl = MemoryHeapManagerLinuxImpl;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 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 <sys/mman.h>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MemoryHeapManagerMacosImpl {
|
||||
NON_COPYABLE(MemoryHeapManagerMacosImpl);
|
||||
NON_MOVEABLE(MemoryHeapManagerMacosImpl);
|
||||
private:
|
||||
uintptr_t m_real_reserved_address;
|
||||
size_t m_real_reserved_size;
|
||||
uintptr_t m_aligned_reserved_heap_address;
|
||||
size_t m_aligned_reserved_heap_size;
|
||||
size_t m_committed_size;
|
||||
public:
|
||||
MemoryHeapManagerMacosImpl() : m_real_reserved_address(0), m_real_reserved_size(0), m_aligned_reserved_heap_address(0), m_aligned_reserved_heap_size(0), m_committed_size(0) {
|
||||
/* Reserve a 32 GB region of virtual address space. */
|
||||
constexpr size_t TargetReservedSize = 32_GB;
|
||||
const auto reserved = ::mmap(nullptr, TargetReservedSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
AMS_ABORT_UNLESS(reserved != MAP_FAILED);
|
||||
|
||||
m_real_reserved_address = reinterpret_cast<uintptr_t>(reserved);
|
||||
m_real_reserved_size = TargetReservedSize;
|
||||
|
||||
m_aligned_reserved_heap_address = util::AlignUp(m_real_reserved_address, MemoryHeapUnitSize);
|
||||
m_aligned_reserved_heap_size = m_real_reserved_size - MemoryHeapUnitSize;
|
||||
}
|
||||
|
||||
Result SetHeapSize(uintptr_t *out, size_t size) {
|
||||
/* Check that we have a reserved address. */
|
||||
R_UNLESS(m_real_reserved_address != 0, os::ResultOutOfMemory());
|
||||
|
||||
/* If necessary, commit the new memory. */
|
||||
if (size > m_committed_size) {
|
||||
R_UNLESS(this->CommitMemory(size), os::ResultOutOfMemory());
|
||||
} else if (size < m_committed_size) {
|
||||
/* Otherwise, decommit. */
|
||||
this->DecommitMemory(m_aligned_reserved_heap_address + size, m_committed_size - size);
|
||||
}
|
||||
|
||||
/* Set the committed size. */
|
||||
m_committed_size = size;
|
||||
|
||||
/* Set the out address. */
|
||||
*out = m_aligned_reserved_heap_address;
|
||||
R_SUCCEED();
|
||||
}
|
||||
private:
|
||||
bool CommitMemory(size_t size) {
|
||||
const auto res = ::mprotect(reinterpret_cast<void *>(m_aligned_reserved_heap_address), size, PROT_READ | PROT_WRITE);
|
||||
return res == 0;
|
||||
}
|
||||
|
||||
void DecommitMemory(uintptr_t address, size_t size) {
|
||||
const auto reserved = ::mmap(reinterpret_cast<void *>(address), size, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
AMS_ABORT_UNLESS(reserved != MAP_FAILED);
|
||||
}
|
||||
};
|
||||
|
||||
using MemoryHeapManagerImpl = MemoryHeapManagerMacosImpl;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) 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 <stratosphere/windows.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MemoryHeapManagerWindowsImpl {
|
||||
NON_COPYABLE(MemoryHeapManagerWindowsImpl);
|
||||
NON_MOVEABLE(MemoryHeapManagerWindowsImpl);
|
||||
private:
|
||||
LPVOID m_real_reserved_address;
|
||||
size_t m_real_reserved_size;
|
||||
LPVOID m_aligned_reserved_heap_address;
|
||||
size_t m_aligned_reserved_heap_size;
|
||||
size_t m_committed_size;
|
||||
public:
|
||||
MemoryHeapManagerWindowsImpl() : m_real_reserved_address(nullptr), m_real_reserved_size(0), m_aligned_reserved_heap_address(nullptr), m_aligned_reserved_heap_size(0), m_committed_size(0) {
|
||||
/* Define target size. */
|
||||
constexpr size_t TargetReservedSize = 32_GB;
|
||||
|
||||
/* Allocate appropriate amount of virtual space. */
|
||||
size_t reserved_size = 0;
|
||||
size_t reserved_addend = TargetReservedSize;
|
||||
while (reserved_addend >= MemoryHeapUnitSize) {
|
||||
if (this->ReserveVirtualSpace(0, reserved_size + reserved_addend)) {
|
||||
this->ReleaseVirtualSpace();
|
||||
|
||||
reserved_size += reserved_addend;
|
||||
if (reserved_size >= TargetReservedSize) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
reserved_addend /= 2;
|
||||
}
|
||||
|
||||
/* Reserve virtual space. */
|
||||
AMS_ABORT_UNLESS(this->ReserveVirtualSpace(0, reserved_size));
|
||||
}
|
||||
|
||||
Result SetHeapSize(uintptr_t *out, size_t size) {
|
||||
/* Check that we have a reserved address. */
|
||||
R_UNLESS(m_real_reserved_address != nullptr, os::ResultOutOfMemory());
|
||||
|
||||
/* If necessary, commit the new memory. */
|
||||
if (size > m_committed_size) {
|
||||
R_UNLESS(this->CommitMemory(size), os::ResultOutOfMemory());
|
||||
} else if (size < m_committed_size) {
|
||||
/* Otherwise, decommit. */
|
||||
this->DecommitMemory(reinterpret_cast<uintptr_t>(m_aligned_reserved_heap_address) + size, m_committed_size - size);
|
||||
}
|
||||
|
||||
/* Set the committed size. */
|
||||
m_committed_size = size;
|
||||
|
||||
/* Set the out address. */
|
||||
*out = reinterpret_cast<uintptr_t>(m_aligned_reserved_heap_address);
|
||||
R_SUCCEED();
|
||||
}
|
||||
private:
|
||||
bool ReserveVirtualSpace(uintptr_t address, size_t size) {
|
||||
AMS_ABORT_UNLESS(m_real_reserved_address == nullptr);
|
||||
AMS_ABORT_UNLESS(m_real_reserved_size == 0);
|
||||
|
||||
size_t reserve_size = util::AlignUp(size, MemoryHeapUnitSize);
|
||||
if constexpr (constexpr size_t VirtualAllocUnitSize = 64_KB; MemoryHeapUnitSize > VirtualAllocUnitSize) {
|
||||
reserve_size += MemoryHeapUnitSize - VirtualAllocUnitSize;
|
||||
}
|
||||
|
||||
LPVOID res = ::VirtualAlloc(reinterpret_cast<LPVOID>(address), reserve_size, MEM_RESERVE, PAGE_READWRITE);
|
||||
if (res == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_real_reserved_address = res;
|
||||
m_real_reserved_size = reserve_size;
|
||||
|
||||
m_aligned_reserved_heap_address = reinterpret_cast<LPVOID>(util::AlignUp(reinterpret_cast<uintptr_t>(m_real_reserved_address), MemoryHeapUnitSize));
|
||||
m_aligned_reserved_heap_size = size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReleaseVirtualSpace() {
|
||||
if (m_real_reserved_address != nullptr) {
|
||||
auto res = ::VirtualFree(m_real_reserved_address, 0, MEM_RELEASE);
|
||||
AMS_ASSERT(res);
|
||||
AMS_UNUSED(res);
|
||||
|
||||
m_real_reserved_address = nullptr;
|
||||
m_real_reserved_size = 0;
|
||||
|
||||
m_aligned_reserved_heap_address = nullptr;
|
||||
m_aligned_reserved_heap_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool CommitMemory(size_t size) {
|
||||
LPVOID address = ::VirtualAlloc(m_aligned_reserved_heap_address, static_cast<SIZE_T>(size), MEM_COMMIT, PAGE_READWRITE);
|
||||
if (address == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AMS_ABORT_UNLESS(address == m_aligned_reserved_heap_address);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DecommitMemory(uintptr_t address, size_t size) {
|
||||
auto res = ::VirtualFree(reinterpret_cast<LPVOID>(address), static_cast<SIZE_T>(size), MEM_DECOMMIT);
|
||||
AMS_ASSERT(res);
|
||||
AMS_UNUSED(res);
|
||||
}
|
||||
};
|
||||
|
||||
using MemoryHeapManagerImpl = MemoryHeapManagerWindowsImpl;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 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>
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
#include "os_memory_heap_manager_impl.os.horizon.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_WINDOWS)
|
||||
#include "os_memory_heap_manager_impl.os.windows.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_LINUX)
|
||||
#include "os_memory_heap_manager_impl.os.linux.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_MACOS)
|
||||
#include "os_memory_heap_manager_impl.os.macos.hpp"
|
||||
#else
|
||||
#error "Unknown OS for MemoryHeapManagerImpl"
|
||||
#endif
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MemoryHeapManager;
|
||||
|
||||
class FreeMemoryNode {
|
||||
private:
|
||||
friend class MemoryHeapManager;
|
||||
private:
|
||||
util::IntrusiveListNode m_node;
|
||||
size_t m_size;
|
||||
public:
|
||||
ALWAYS_INLINE uintptr_t GetAddress() const { return reinterpret_cast<uintptr_t>(this); }
|
||||
ALWAYS_INLINE size_t GetSize() const { return m_size; }
|
||||
ALWAYS_INLINE void SetSize(size_t size) { m_size = size; }
|
||||
ALWAYS_INLINE void Clean() { std::memset(reinterpret_cast<void *>(this), 0, sizeof(FreeMemoryNode)); }
|
||||
};
|
||||
static_assert(sizeof(FreeMemoryNode) == sizeof(util::IntrusiveListNode) + sizeof(size_t));
|
||||
|
||||
class MemoryHeapManager {
|
||||
NON_COPYABLE(MemoryHeapManager);
|
||||
NON_MOVEABLE(MemoryHeapManager);
|
||||
private:
|
||||
using FreeMemoryList = typename util::IntrusiveListMemberTraits<&FreeMemoryNode::m_node>::ListType;
|
||||
private:
|
||||
uintptr_t m_heap_address;
|
||||
size_t m_heap_size;
|
||||
size_t m_used_heap_size;
|
||||
FreeMemoryList m_free_memory_list;
|
||||
InternalCriticalSection m_cs;
|
||||
MemoryHeapManagerImpl m_impl;
|
||||
public:
|
||||
MemoryHeapManager() : m_heap_address(0), m_heap_size(0), m_used_heap_size(0) { /* ... */ }
|
||||
|
||||
Result SetHeapSize(size_t size);
|
||||
|
||||
Result AllocateFromHeap(uintptr_t *out_address, size_t size);
|
||||
void ReleaseToHeap(uintptr_t address, size_t size);
|
||||
|
||||
bool IsRegionInMemoryHeap(uintptr_t address, size_t size) const {
|
||||
return m_heap_address <= address && (address + size) <= (m_heap_address + m_heap_size);
|
||||
}
|
||||
|
||||
uintptr_t GetHeapAddress() const { return m_heap_address; }
|
||||
size_t GetHeapSize() const { return m_heap_size; }
|
||||
size_t GetUsedHeapSize() const { return m_used_heap_size; }
|
||||
private:
|
||||
FreeMemoryList::iterator FindFreeSpaceUnsafe(size_t size);
|
||||
FreeMemoryList::iterator ConcatenatePreviousFreeMemoryNodeUnsafe(FreeMemoryList::iterator node);
|
||||
void SplitFreeMemoryNodeUnsafe(FreeMemoryList::iterator it, size_t size);
|
||||
void AddToFreeSpaceUnsafe(uintptr_t address, size_t size);
|
||||
bool IsRegionAllocatedMemoryUnsafe(uintptr_t address, size_t size);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 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 <sys/mman.h>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
void SetMemoryPermissionImpl(uintptr_t address, size_t size, MemoryPermission perm) {
|
||||
switch (perm) {
|
||||
case MemoryPermission_None:
|
||||
{
|
||||
auto res = ::mprotect(reinterpret_cast<void *>(address), size, PROT_NONE);
|
||||
AMS_ABORT_UNLESS(res);
|
||||
AMS_UNUSED(res);
|
||||
}
|
||||
break;
|
||||
case MemoryPermission_ReadOnly:
|
||||
{
|
||||
auto res = ::mprotect(reinterpret_cast<void *>(address), size, PROT_READ);
|
||||
AMS_ABORT_UNLESS(res);
|
||||
AMS_UNUSED(res);
|
||||
}
|
||||
break;
|
||||
case MemoryPermission_ReadWrite:
|
||||
{
|
||||
auto res = ::mprotect(reinterpret_cast<void *>(address), size, PROT_READ | PROT_WRITE);
|
||||
AMS_ABORT_UNLESS(res);
|
||||
AMS_UNUSED(res);
|
||||
}
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 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 <sys/mman.h>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
void SetMemoryPermissionImpl(uintptr_t address, size_t size, MemoryPermission perm) {
|
||||
switch (perm) {
|
||||
case MemoryPermission_None:
|
||||
{
|
||||
auto res = ::mprotect(reinterpret_cast<void *>(address), size, PROT_NONE);
|
||||
AMS_ABORT_UNLESS(res);
|
||||
AMS_UNUSED(res);
|
||||
}
|
||||
break;
|
||||
case MemoryPermission_ReadOnly:
|
||||
{
|
||||
auto res = ::mprotect(reinterpret_cast<void *>(address), size, PROT_READ);
|
||||
AMS_ABORT_UNLESS(res);
|
||||
AMS_UNUSED(res);
|
||||
}
|
||||
break;
|
||||
case MemoryPermission_ReadWrite:
|
||||
{
|
||||
auto res = ::mprotect(reinterpret_cast<void *>(address), size, PROT_READ | PROT_WRITE);
|
||||
AMS_ABORT_UNLESS(res);
|
||||
AMS_UNUSED(res);
|
||||
}
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 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 <stratosphere/windows.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
void SetMemoryPermissionImpl(uintptr_t address, size_t size, MemoryPermission perm) {
|
||||
DWORD old;
|
||||
|
||||
uintptr_t cur_address = address;
|
||||
size_t remaining = size;
|
||||
while (remaining > 0) {
|
||||
const size_t cur_size = std::min<size_t>(remaining, 2_GB);
|
||||
switch (perm) {
|
||||
case MemoryPermission_None:
|
||||
{
|
||||
auto res = ::VirtualProtect(reinterpret_cast<LPVOID>(cur_address), static_cast<DWORD>(cur_size), PAGE_NOACCESS, std::addressof(old));
|
||||
AMS_ABORT_UNLESS(res);
|
||||
AMS_UNUSED(res);
|
||||
}
|
||||
break;
|
||||
case MemoryPermission_ReadOnly:
|
||||
{
|
||||
auto res = ::VirtualProtect(reinterpret_cast<LPVOID>(cur_address), static_cast<DWORD>(cur_size), PAGE_READONLY, std::addressof(old));
|
||||
AMS_ABORT_UNLESS(res);
|
||||
AMS_UNUSED(res);
|
||||
}
|
||||
break;
|
||||
case MemoryPermission_ReadWrite:
|
||||
{
|
||||
auto res = ::VirtualProtect(reinterpret_cast<LPVOID>(cur_address), static_cast<DWORD>(cur_size), PAGE_READWRITE, std::addressof(old));
|
||||
AMS_ABORT_UNLESS(res);
|
||||
AMS_UNUSED(res);
|
||||
}
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
cur_address += cur_size;
|
||||
remaining -= cur_size;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -30,46 +30,50 @@ namespace ams::os::impl {
|
||||
public:
|
||||
/* Gets whether the held object is currently signaled. */
|
||||
virtual TriBool IsSignaled() const = 0;
|
||||
|
||||
/* Adds to multi wait's object list, returns is signaled. */
|
||||
virtual TriBool LinkToObjectList() = 0;
|
||||
virtual TriBool AddToObjectList() = 0;
|
||||
|
||||
/* Removes from the multi wait's object list. */
|
||||
virtual void UnlinkFromObjectList() = 0;
|
||||
/* Gets handle to output, returns os::InvalidNativeHandle on failure. */
|
||||
virtual NativeHandle GetHandle() const = 0;
|
||||
virtual void RemoveFromObjectList() = 0;
|
||||
|
||||
/* Gets whether waitable has a native handle, writes to output if it does. */
|
||||
virtual bool GetNativeHandle(os::NativeHandle *) const = 0;
|
||||
|
||||
/* Gets the amount of time remaining until this wakes up. */
|
||||
virtual TimeSpan GetAbsoluteWakeupTime() const {
|
||||
virtual TimeSpan GetAbsoluteTimeToWakeup() const {
|
||||
return TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::max());
|
||||
}
|
||||
|
||||
/* Interface with multi wait. */
|
||||
void SetMultiWait(MultiWaitImpl *m) {
|
||||
ALWAYS_INLINE void SetMultiWait(MultiWaitImpl *m) {
|
||||
m_multi_wait = m;
|
||||
}
|
||||
|
||||
MultiWaitImpl *GetMultiWait() const {
|
||||
ALWAYS_INLINE MultiWaitImpl *GetMultiWait() const {
|
||||
return m_multi_wait;
|
||||
}
|
||||
|
||||
bool IsLinked() const {
|
||||
return m_multi_wait != nullptr;
|
||||
}
|
||||
ALWAYS_INLINE bool IsLinked() const { return m_multi_wait != nullptr; }
|
||||
ALWAYS_INLINE bool IsNotLinked() const { return m_multi_wait == nullptr; }
|
||||
};
|
||||
|
||||
class MultiWaitHolderOfUserObject : public MultiWaitHolderBase {
|
||||
class MultiWaitHolderOfUserWaitObject : public MultiWaitHolderBase {
|
||||
public:
|
||||
/* All user objects have no handle to wait on. */
|
||||
virtual NativeHandle GetHandle() const override final {
|
||||
return os::InvalidNativeHandle;
|
||||
virtual bool GetNativeHandle(os::NativeHandle *) const override final {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class MultiWaitHolderOfKernelObject : public MultiWaitHolderBase {
|
||||
class MultiWaitHolderOfNativeWaitObject : public MultiWaitHolderBase {
|
||||
public:
|
||||
/* All kernel objects have native handles, and thus don't have object list semantics. */
|
||||
virtual TriBool LinkToObjectList() override final {
|
||||
/* All native objects have native handles, and thus don't have object list semantics. */
|
||||
virtual TriBool AddToObjectList() override final {
|
||||
return TriBool::Undefined;
|
||||
}
|
||||
virtual void UnlinkFromObjectList() override final {
|
||||
|
||||
virtual void RemoveFromObjectList() override final {
|
||||
/* ... */
|
||||
}
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "os_multiple_wait_holder_of_handle.hpp"
|
||||
#include "os_multiple_wait_holder_of_native_handle.hpp"
|
||||
#include "os_multiple_wait_holder_of_event.hpp"
|
||||
#include "os_multiple_wait_holder_of_inter_process_event.hpp"
|
||||
#include "os_multiple_wait_holder_of_interrupt_event.hpp"
|
||||
@@ -27,30 +27,30 @@ namespace ams::os::impl {
|
||||
|
||||
struct MultiWaitHolderImpl {
|
||||
union {
|
||||
util::TypedStorage<MultiWaitHolderOfHandle> holder_of_handle_storage;
|
||||
util::TypedStorage<MultiWaitHolderOfNativeHandle> holder_of_native_handle_storage;
|
||||
util::TypedStorage<MultiWaitHolderOfEvent> holder_of_event_storage;
|
||||
util::TypedStorage<MultiWaitHolderOfInterProcessEvent> holder_of_inter_process_event_storage;
|
||||
util::TypedStorage<MultiWaitHolderOfInterruptEvent> holder_of_interrupt_event_storage;
|
||||
util::TypedStorage<MultiWaitHolderOfTimerEvent> holder_of_timer_event_storage;
|
||||
util::TypedStorage<MultiWaitHolderOfThread> holder_of_thread_storage;
|
||||
util::TypedStorage<MultiWaitHolderOfSemaphore> holder_of_semaphore_storage;
|
||||
util::TypedStorage<MultiWaitHolderOfMessageQueueForNotFull> holder_of_mq_for_not_full_storage;
|
||||
util::TypedStorage<MultiWaitHolderOfMessageQueueForNotEmpty> holder_of_mq_for_not_empty_storage;
|
||||
util::TypedStorage<MultiWaitHolderOfMessageQueueNotFull> holder_of_mq_not_full_storage;
|
||||
util::TypedStorage<MultiWaitHolderOfMessageQueueNotEmpty> holder_of_mq_not_empty_storage;
|
||||
};
|
||||
};
|
||||
|
||||
#define CHECK_HOLDER(T) \
|
||||
static_assert(std::is_base_of<::ams::os::impl::MultiWaitHolderBase, T>::value && std::is_trivially_destructible<T>::value, #T)
|
||||
|
||||
CHECK_HOLDER(MultiWaitHolderOfHandle);
|
||||
CHECK_HOLDER(MultiWaitHolderOfNativeHandle);
|
||||
CHECK_HOLDER(MultiWaitHolderOfEvent);
|
||||
CHECK_HOLDER(MultiWaitHolderOfInterProcessEvent);
|
||||
CHECK_HOLDER(MultiWaitHolderOfInterruptEvent);
|
||||
CHECK_HOLDER(MultiWaitHolderOfTimerEvent);
|
||||
CHECK_HOLDER(MultiWaitHolderOfThread);
|
||||
CHECK_HOLDER(MultiWaitHolderOfSemaphore);
|
||||
CHECK_HOLDER(MultiWaitHolderOfMessageQueueForNotFull);
|
||||
CHECK_HOLDER(MultiWaitHolderOfMessageQueueForNotEmpty);
|
||||
CHECK_HOLDER(MultiWaitHolderOfMessageQueueNotFull);
|
||||
CHECK_HOLDER(MultiWaitHolderOfMessageQueueNotEmpty);
|
||||
|
||||
#undef CHECK_HOLDER
|
||||
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MultiWaitHolderOfEvent : public MultiWaitHolderOfUserObject {
|
||||
class MultiWaitHolderOfEvent : public MultiWaitHolderOfUserWaitObject {
|
||||
private:
|
||||
EventType *m_event;
|
||||
private:
|
||||
TriBool IsSignaledImpl() const {
|
||||
ALWAYS_INLINE TriBool IsSignaledUnsafe() const {
|
||||
return m_event->signaled ? TriBool::True : TriBool::False;
|
||||
}
|
||||
public:
|
||||
@@ -32,20 +32,20 @@ namespace ams::os::impl {
|
||||
/* IsSignaled, Link, Unlink implemented. */
|
||||
virtual TriBool IsSignaled() const override {
|
||||
std::scoped_lock lk(GetReference(m_event->cs_event));
|
||||
return this->IsSignaledImpl();
|
||||
return this->IsSignaledUnsafe();
|
||||
}
|
||||
|
||||
virtual TriBool LinkToObjectList() override {
|
||||
virtual TriBool AddToObjectList() override {
|
||||
std::scoped_lock lk(GetReference(m_event->cs_event));
|
||||
|
||||
GetReference(m_event->multi_wait_object_list_storage).LinkMultiWaitHolder(*this);
|
||||
return this->IsSignaledImpl();
|
||||
GetReference(m_event->multi_wait_object_list_storage).PushBackToList(*this);
|
||||
return this->IsSignaledUnsafe();
|
||||
}
|
||||
|
||||
virtual void UnlinkFromObjectList() override {
|
||||
virtual void RemoveFromObjectList() override {
|
||||
std::scoped_lock lk(GetReference(m_event->cs_event));
|
||||
|
||||
GetReference(m_event->multi_wait_object_list_storage).UnlinkMultiWaitHolder(*this);
|
||||
GetReference(m_event->multi_wait_object_list_storage).EraseFromList(*this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MultiWaitHolderOfInterProcessEvent : public MultiWaitHolderOfKernelObject {
|
||||
class MultiWaitHolderOfInterProcessEvent : public MultiWaitHolderOfNativeWaitObject {
|
||||
private:
|
||||
InterProcessEventType *m_event;
|
||||
public:
|
||||
@@ -30,8 +30,9 @@ namespace ams::os::impl {
|
||||
return TriBool::Undefined;
|
||||
}
|
||||
|
||||
virtual NativeHandle GetHandle() const override {
|
||||
return m_event->readable_handle;
|
||||
virtual bool GetNativeHandle(os::NativeHandle *out) const override {
|
||||
*out = m_event->readable_handle;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -19,8 +19,9 @@
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
NativeHandle MultiWaitHolderOfInterruptEvent::GetHandle() const {
|
||||
return GetReference(m_event->impl).GetHandle();
|
||||
bool MultiWaitHolderOfInterruptEvent::GetNativeHandle(os::NativeHandle *out) const {
|
||||
*out = GetReference(m_event->impl).GetHandle();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MultiWaitHolderOfInterruptEvent : public MultiWaitHolderOfKernelObject {
|
||||
class MultiWaitHolderOfInterruptEvent : public MultiWaitHolderOfNativeWaitObject {
|
||||
private:
|
||||
InterruptEventType *m_event;
|
||||
public:
|
||||
@@ -29,7 +29,7 @@ namespace ams::os::impl {
|
||||
return TriBool::Undefined;
|
||||
}
|
||||
|
||||
virtual NativeHandle GetHandle() const override;
|
||||
virtual bool GetNativeHandle(os::NativeHandle *out) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -19,57 +19,64 @@
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
template<MessageQueueWaitType WaitType>
|
||||
class MultiWaitHolderOfMessageQueue : public MultiWaitHolderOfUserObject {
|
||||
static_assert(WaitType == MessageQueueWaitType::ForNotEmpty || WaitType == MessageQueueWaitType::ForNotFull);
|
||||
class MultiWaitHolderOfMessageQueueNotEmpty : public MultiWaitHolderOfUserWaitObject {
|
||||
private:
|
||||
MessageQueueType *m_mq;
|
||||
private:
|
||||
constexpr inline TriBool IsSignaledImpl() const {
|
||||
if constexpr (WaitType == MessageQueueWaitType::ForNotEmpty) {
|
||||
/* ForNotEmpty. */
|
||||
return m_mq->count > 0 ? TriBool::True : TriBool::False;
|
||||
} else if constexpr (WaitType == MessageQueueWaitType::ForNotFull) {
|
||||
/* ForNotFull */
|
||||
return m_mq->count < m_mq->capacity ? TriBool::True : TriBool::False;
|
||||
} else {
|
||||
static_assert(WaitType != WaitType);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr inline MultiWaitObjectList &GetObjectList() const {
|
||||
if constexpr (WaitType == MessageQueueWaitType::ForNotEmpty) {
|
||||
return GetReference(m_mq->waitlist_not_empty);
|
||||
} else if constexpr (WaitType == MessageQueueWaitType::ForNotFull) {
|
||||
return GetReference(m_mq->waitlist_not_full);
|
||||
} else {
|
||||
static_assert(WaitType != WaitType);
|
||||
}
|
||||
ALWAYS_INLINE TriBool IsSignaledUnsafe() const {
|
||||
return m_mq->count > 0 ? TriBool::True : TriBool::False;
|
||||
}
|
||||
public:
|
||||
explicit MultiWaitHolderOfMessageQueue(MessageQueueType *mq) : m_mq(mq) { /* ... */ }
|
||||
explicit MultiWaitHolderOfMessageQueueNotEmpty(MessageQueueType *mq) : m_mq(mq) { /* ... */ }
|
||||
|
||||
/* IsSignaled, Link, Unlink implemented. */
|
||||
virtual TriBool IsSignaled() const override {
|
||||
std::scoped_lock lk(GetReference(m_mq->cs_queue));
|
||||
return this->IsSignaledImpl();
|
||||
return this->IsSignaledUnsafe();
|
||||
}
|
||||
|
||||
virtual TriBool LinkToObjectList() override {
|
||||
virtual TriBool AddToObjectList() override {
|
||||
std::scoped_lock lk(GetReference(m_mq->cs_queue));
|
||||
|
||||
this->GetObjectList().LinkMultiWaitHolder(*this);
|
||||
return this->IsSignaledImpl();
|
||||
GetReference(m_mq->waitlist_not_empty).PushBackToList(*this);
|
||||
return this->IsSignaledUnsafe();
|
||||
}
|
||||
|
||||
virtual void UnlinkFromObjectList() override {
|
||||
virtual void RemoveFromObjectList() override {
|
||||
std::scoped_lock lk(GetReference(m_mq->cs_queue));
|
||||
|
||||
this->GetObjectList().UnlinkMultiWaitHolder(*this);
|
||||
GetReference(m_mq->waitlist_not_empty).EraseFromList(*this);
|
||||
}
|
||||
};
|
||||
|
||||
using MultiWaitHolderOfMessageQueueForNotEmpty = MultiWaitHolderOfMessageQueue<MessageQueueWaitType::ForNotEmpty>;
|
||||
using MultiWaitHolderOfMessageQueueForNotFull = MultiWaitHolderOfMessageQueue<MessageQueueWaitType::ForNotFull>;
|
||||
class MultiWaitHolderOfMessageQueueNotFull : public MultiWaitHolderOfUserWaitObject {
|
||||
private:
|
||||
MessageQueueType *m_mq;
|
||||
private:
|
||||
ALWAYS_INLINE TriBool IsSignaledUnsafe() const {
|
||||
return m_mq->count < m_mq->capacity ? TriBool::True : TriBool::False;
|
||||
}
|
||||
public:
|
||||
explicit MultiWaitHolderOfMessageQueueNotFull(MessageQueueType *mq) : m_mq(mq) { /* ... */ }
|
||||
|
||||
/* IsSignaled, Link, Unlink implemented. */
|
||||
virtual TriBool IsSignaled() const override {
|
||||
std::scoped_lock lk(GetReference(m_mq->cs_queue));
|
||||
return this->IsSignaledUnsafe();
|
||||
}
|
||||
|
||||
virtual TriBool AddToObjectList() override {
|
||||
std::scoped_lock lk(GetReference(m_mq->cs_queue));
|
||||
|
||||
GetReference(m_mq->waitlist_not_full).PushBackToList(*this);
|
||||
return this->IsSignaledUnsafe();
|
||||
}
|
||||
|
||||
virtual void RemoveFromObjectList() override {
|
||||
std::scoped_lock lk(GetReference(m_mq->cs_queue));
|
||||
|
||||
GetReference(m_mq->waitlist_not_full).EraseFromList(*this);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -18,19 +18,20 @@
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MultiWaitHolderOfHandle : public MultiWaitHolderOfKernelObject {
|
||||
class MultiWaitHolderOfNativeHandle : public MultiWaitHolderOfNativeWaitObject {
|
||||
private:
|
||||
NativeHandle m_handle;
|
||||
public:
|
||||
explicit MultiWaitHolderOfHandle(NativeHandle h) : m_handle(h) { /* ... */ }
|
||||
explicit MultiWaitHolderOfNativeHandle(NativeHandle h) : m_handle(h) { /* ... */ }
|
||||
|
||||
/* IsSignaled, GetHandle both implemented. */
|
||||
virtual TriBool IsSignaled() const override {
|
||||
return TriBool::Undefined;
|
||||
}
|
||||
|
||||
virtual NativeHandle GetHandle() const override {
|
||||
return m_handle;
|
||||
virtual bool GetNativeHandle(os::NativeHandle *out) const override {
|
||||
*out = m_handle;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MultiWaitHolderOfSemaphore : public MultiWaitHolderOfUserObject {
|
||||
class MultiWaitHolderOfSemaphore : public MultiWaitHolderOfUserWaitObject {
|
||||
private:
|
||||
SemaphoreType *m_semaphore;
|
||||
private:
|
||||
@@ -35,17 +35,17 @@ namespace ams::os::impl {
|
||||
return this->IsSignaledImpl();
|
||||
}
|
||||
|
||||
virtual TriBool LinkToObjectList() override {
|
||||
virtual TriBool AddToObjectList() override {
|
||||
std::scoped_lock lk(GetReference(m_semaphore->cs_sema));
|
||||
|
||||
GetReference(m_semaphore->waitlist).LinkMultiWaitHolder(*this);
|
||||
GetReference(m_semaphore->waitlist).PushBackToList(*this);
|
||||
return this->IsSignaledImpl();
|
||||
}
|
||||
|
||||
virtual void UnlinkFromObjectList() override {
|
||||
virtual void RemoveFromObjectList() override {
|
||||
std::scoped_lock lk(GetReference(m_semaphore->cs_sema));
|
||||
|
||||
GetReference(m_semaphore->waitlist).UnlinkMultiWaitHolder(*this);
|
||||
GetReference(m_semaphore->waitlist).EraseFromList(*this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MultiWaitHolderOfThread : public MultiWaitHolderOfUserObject {
|
||||
class MultiWaitHolderOfThread : public MultiWaitHolderOfUserWaitObject {
|
||||
private:
|
||||
ThreadType *m_thread;
|
||||
private:
|
||||
@@ -34,17 +34,17 @@ namespace ams::os::impl {
|
||||
return this->IsSignaledImpl();
|
||||
}
|
||||
|
||||
virtual TriBool LinkToObjectList() override {
|
||||
virtual TriBool AddToObjectList() override {
|
||||
std::scoped_lock lk(GetReference(m_thread->cs_thread));
|
||||
|
||||
GetReference(m_thread->waitlist).LinkMultiWaitHolder(*this);
|
||||
GetReference(m_thread->waitlist).PushBackToList(*this);
|
||||
return this->IsSignaledImpl();
|
||||
}
|
||||
|
||||
virtual void UnlinkFromObjectList() override {
|
||||
virtual void RemoveFromObjectList() override {
|
||||
std::scoped_lock lk(GetReference(m_thread->cs_thread));
|
||||
|
||||
GetReference(m_thread->waitlist).UnlinkMultiWaitHolder(*this);
|
||||
GetReference(m_thread->waitlist).EraseFromList(*this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -21,13 +21,15 @@
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class MultiWaitHolderOfTimerEvent : public MultiWaitHolderOfUserObject {
|
||||
class MultiWaitHolderOfTimerEvent : public MultiWaitHolderOfUserWaitObject {
|
||||
private:
|
||||
TimerEventType *m_event;
|
||||
private:
|
||||
TriBool IsSignaledImpl() const {
|
||||
TimeSpan cur_time = this->GetMultiWait()->GetCurrentTime();
|
||||
UpdateSignalStateAndRecalculateNextTimeToWakeupUnsafe(m_event, cur_time);
|
||||
TriBool IsSignaledUnsafe() const {
|
||||
TimeSpan cur_time = this->GetMultiWait()->GetCurrTime();
|
||||
|
||||
os::impl::UpdateSignalStateAndRecalcNextTimeToWakeupUnsafe(m_event, cur_time);
|
||||
|
||||
return m_event->signaled ? TriBool::True : TriBool::False;
|
||||
}
|
||||
public:
|
||||
@@ -36,31 +38,30 @@ namespace ams::os::impl {
|
||||
/* IsSignaled, Link, Unlink implemented. */
|
||||
virtual TriBool IsSignaled() const override {
|
||||
std::scoped_lock lk(GetReference(m_event->cs_timer_event));
|
||||
return this->IsSignaledImpl();
|
||||
|
||||
return this->IsSignaledUnsafe();
|
||||
}
|
||||
|
||||
virtual TriBool LinkToObjectList() override {
|
||||
virtual TriBool AddToObjectList() override {
|
||||
std::scoped_lock lk(GetReference(m_event->cs_timer_event));
|
||||
|
||||
GetReference(m_event->multi_wait_object_list_storage).LinkMultiWaitHolder(*this);
|
||||
return this->IsSignaledImpl();
|
||||
GetReference(m_event->multi_wait_object_list_storage).PushBackToList(*this);
|
||||
|
||||
return this->IsSignaledUnsafe();
|
||||
}
|
||||
|
||||
virtual void UnlinkFromObjectList() override {
|
||||
virtual void RemoveFromObjectList() override {
|
||||
std::scoped_lock lk(GetReference(m_event->cs_timer_event));
|
||||
|
||||
GetReference(m_event->multi_wait_object_list_storage).UnlinkMultiWaitHolder(*this);
|
||||
GetReference(m_event->multi_wait_object_list_storage).EraseFromList(*this);
|
||||
}
|
||||
|
||||
/* Gets the amount of time remaining until this wakes up. */
|
||||
virtual TimeSpan GetAbsoluteWakeupTime() const override {
|
||||
virtual TimeSpan GetAbsoluteTimeToWakeup() const override {
|
||||
std::scoped_lock lk(GetReference(m_event->cs_timer_event));
|
||||
|
||||
if (m_event->timer_state == TimerEventType::TimerState_Stop) {
|
||||
return TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::max());
|
||||
}
|
||||
|
||||
return GetReference(m_event->next_time_to_wakeup);
|
||||
return m_event->timer_state != TimerEventType::TimerState_Stop ? GetReference(m_event->next_time_to_wakeup)
|
||||
: TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::max());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -20,149 +20,200 @@
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
Result MultiWaitImpl::WaitAnyImpl(MultiWaitHolderBase **out, bool infinite, TimeSpan timeout, bool reply, NativeHandle reply_target) {
|
||||
template<bool AllowReply>
|
||||
Result MultiWaitImpl::WaitAnyImpl(MultiWaitHolderBase **out, bool infinite, TimeSpan timeout, NativeHandle reply_target) {
|
||||
/* Prepare for processing. */
|
||||
m_signaled_holder = nullptr;
|
||||
m_target_impl.SetCurrentThreadHandleForCancelWait();
|
||||
MultiWaitHolderBase *holder = this->LinkHoldersToObjectList();
|
||||
|
||||
/* Add each holder to the object list, and try to find one that's signaled. */
|
||||
MultiWaitHolderBase *signaled_holder = this->AddToEachObjectListAndCheckObjectState();
|
||||
|
||||
/* When we're done, cleanup and set output. */
|
||||
ON_SCOPE_EXIT {
|
||||
/* Unlink holders from the current object list. */
|
||||
this->UnlinkHoldersFromObjectList();
|
||||
/* Remove each holder from the current object list. */
|
||||
this->RemoveFromEachObjectList();
|
||||
|
||||
/* Clear cancel wait. */
|
||||
m_target_impl.ClearCurrentThreadHandleForCancelWait();
|
||||
|
||||
/* Set output holder. */
|
||||
*out = holder;
|
||||
*out = signaled_holder;
|
||||
};
|
||||
|
||||
/* Check if we've been signaled. */
|
||||
{
|
||||
std::scoped_lock lk(m_cs_wait);
|
||||
if (m_signaled_holder != nullptr) {
|
||||
holder = m_signaled_holder;
|
||||
signaled_holder = m_signaled_holder;
|
||||
}
|
||||
}
|
||||
|
||||
/* Process object array. */
|
||||
if (holder != nullptr) {
|
||||
R_SUCCEED_IF(!(reply && reply_target != os::InvalidNativeHandle));
|
||||
if (signaled_holder != nullptr) {
|
||||
/* If we have a signaled holder and we're allowed to reply, try to do so. */
|
||||
if constexpr (AllowReply) {
|
||||
/* Try to reply to the reply target. */
|
||||
if (reply_target != os::InvalidNativeHandle) {
|
||||
ON_RESULT_FAILURE { signaled_holder = nullptr; };
|
||||
|
||||
ON_RESULT_FAILURE { holder = nullptr; };
|
||||
|
||||
s32 index;
|
||||
R_RETURN(m_target_impl.TimedReplyAndReceive(std::addressof(index), nullptr, 0, 0, reply_target, TimeSpan::FromNanoSeconds(0)))
|
||||
s32 index;
|
||||
R_TRY(m_target_impl.TimedReplyAndReceive(std::addressof(index), nullptr, 0, 0, reply_target, TimeSpan::FromNanoSeconds(0)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
R_RETURN(this->WaitAnyHandleImpl(std::addressof(holder), infinite, timeout, reply, reply_target));
|
||||
/* If there's no signaled holder, wait for one to be signaled. */
|
||||
R_TRY(this->InternalWaitAnyImpl<AllowReply>(std::addressof(signaled_holder), infinite, timeout, reply_target));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MultiWaitImpl::WaitAnyHandleImpl(MultiWaitHolderBase **out, bool infinite, TimeSpan timeout, bool reply, NativeHandle reply_target) {
|
||||
template<bool AllowReply>
|
||||
Result MultiWaitImpl::InternalWaitAnyImpl(MultiWaitHolderBase **out, bool infinite, TimeSpan timeout, NativeHandle reply_target) {
|
||||
/* Build the objects array. */
|
||||
NativeHandle object_handles[MaximumHandleCount];
|
||||
MultiWaitHolderBase *objects[MaximumHandleCount];
|
||||
|
||||
const s32 count = this->BuildHandleArray(object_handles, objects, MaximumHandleCount);
|
||||
const TimeSpan end_time = infinite ? TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::max()) : GetCurrentTick().ToTimeSpan() + timeout;
|
||||
const s32 count = this->ConstructObjectsArray(object_handles, objects, MaximumHandleCount);
|
||||
|
||||
/* Determine the appropriate end time for our wait. */
|
||||
const TimeSpan end_time = infinite ? TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::max()) : os::impl::GetCurrentTick().ToTimeSpan() + timeout;
|
||||
|
||||
/* Loop, waiting until we're done. */
|
||||
while (true) {
|
||||
m_current_time = GetCurrentTick().ToTimeSpan();
|
||||
/* Update the current time for our loop. */
|
||||
m_current_time = os::impl::GetCurrentTick().ToTimeSpan();
|
||||
|
||||
/* Determine which object has the minimum wakeup time. */
|
||||
TimeSpan min_timeout = 0;
|
||||
MultiWaitHolderBase *min_timeout_object = this->RecalculateNextTimeout(std::addressof(min_timeout), end_time);
|
||||
MultiWaitHolderBase *min_timeout_object = this->RecalcMultiWaitTimeout(std::addressof(min_timeout), end_time);
|
||||
|
||||
s32 index = WaitInvalid;
|
||||
/* Perform the wait using native apis. */
|
||||
s32 index = WaitInvalid;
|
||||
Result wait_result = ResultSuccess();
|
||||
if (reply) {
|
||||
if (infinite && min_timeout_object == nullptr) {
|
||||
if (infinite && min_timeout_object == nullptr) {
|
||||
/* If we're performing an infinite wait, just do the appropriate wait or reply/receive. */
|
||||
if constexpr (AllowReply) {
|
||||
wait_result = m_target_impl.ReplyAndReceive(std::addressof(index), object_handles, MaximumHandleCount, count, reply_target);
|
||||
} else {
|
||||
wait_result = m_target_impl.TimedReplyAndReceive(std::addressof(index), object_handles, MaximumHandleCount, count, reply_target, min_timeout);
|
||||
wait_result = m_target_impl.WaitAny(std::addressof(index), object_handles, MaximumHandleCount, count);
|
||||
}
|
||||
} else if (infinite && min_timeout_object == nullptr) {
|
||||
wait_result = m_target_impl.WaitAny(std::addressof(index), object_handles, MaximumHandleCount, count);
|
||||
} else {
|
||||
if (count == 0 && min_timeout == 0) {
|
||||
index = WaitTimedOut;
|
||||
/* We need to do our wait with a timeout. */
|
||||
if constexpr (AllowReply) {
|
||||
wait_result = m_target_impl.TimedReplyAndReceive(std::addressof(index), object_handles, MaximumHandleCount, count, reply_target, min_timeout);
|
||||
} else {
|
||||
wait_result = m_target_impl.TimedWaitAny(std::addressof(index), object_handles, MaximumHandleCount, count, min_timeout);
|
||||
AMS_ABORT_UNLESS(index != WaitInvalid);
|
||||
if (count != 0 || min_timeout != 0) {
|
||||
wait_result = m_target_impl.TimedWaitAny(std::addressof(index), object_handles, MaximumHandleCount, count, min_timeout);
|
||||
} else {
|
||||
index = WaitTimedOut;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (index == WaitInvalid) {
|
||||
*out = nullptr;
|
||||
R_RETURN(wait_result);
|
||||
}
|
||||
|
||||
/* Process the result of our wait. */
|
||||
switch (index) {
|
||||
case WaitTimedOut:
|
||||
if (min_timeout_object) {
|
||||
m_current_time = GetCurrentTick().ToTimeSpan();
|
||||
if (min_timeout_object->IsSignaled() == TriBool::True) {
|
||||
std::scoped_lock lk(m_cs_wait);
|
||||
m_signaled_holder = min_timeout_object;
|
||||
*out = min_timeout_object;
|
||||
R_RETURN(wait_result);
|
||||
}
|
||||
} else {
|
||||
case WaitInvalid:
|
||||
/* If an invalid wait was performed, just return no signaled holder. */
|
||||
{
|
||||
*out = nullptr;
|
||||
R_RETURN(wait_result);
|
||||
}
|
||||
break;
|
||||
case WaitCancelled:
|
||||
/* If the wait was canceled, it might be because a non-native waitable was signaled. Check and return it, if this is the case. */
|
||||
{
|
||||
std::scoped_lock lk(m_cs_wait);
|
||||
|
||||
if (m_signaled_holder) {
|
||||
*out = m_signaled_holder;
|
||||
R_RETURN(wait_result);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WaitTimedOut:
|
||||
/* If we timed out, this might have been because a timer is now signaled. */
|
||||
if (min_timeout_object != nullptr) {
|
||||
/* Update our current time. */
|
||||
m_current_time = GetCurrentTick().ToTimeSpan();
|
||||
|
||||
/* Check if the minimum timeout object is now signaled. */
|
||||
if (min_timeout_object->IsSignaled() == TriBool::True) {
|
||||
std::scoped_lock lk(m_cs_wait);
|
||||
|
||||
/* Set our signaled holder (and the output) as the newly signaled minimum timeout object. */
|
||||
m_signaled_holder = min_timeout_object;
|
||||
*out = min_timeout_object;
|
||||
R_RETURN(wait_result);
|
||||
}
|
||||
} else {
|
||||
/* If we have no minimum timeout object but we timed out, just return no signaled holder. */
|
||||
*out = nullptr;
|
||||
R_RETURN(wait_result);
|
||||
}
|
||||
break;
|
||||
default: /* 0 - 0x3F, valid. */
|
||||
{
|
||||
if constexpr (MaximumHandleCount > 0) {
|
||||
AMS_ASSERT(0 <= index && index < static_cast<s32>(MaximumHandleCount));
|
||||
/* Sanity check that the returned index is within the range of our objects array. */
|
||||
AMS_ASSERT(0 <= index && index < count);
|
||||
|
||||
std::scoped_lock lk(m_cs_wait);
|
||||
m_signaled_holder = objects[index];
|
||||
*out = objects[index];
|
||||
R_RETURN(wait_result);
|
||||
} else {
|
||||
AMS_ABORT_UNLESS(MaximumHandleCount > 0);
|
||||
}
|
||||
std::scoped_lock lk(m_cs_wait);
|
||||
|
||||
/* Set our signaled holder (and the output) as the newly signaled object. */
|
||||
m_signaled_holder = objects[index];
|
||||
*out = objects[index];
|
||||
R_RETURN(wait_result);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
reply_target = os::InvalidNativeHandle;
|
||||
/* We're going to be looping again; prevent ourselves from replying to the same object twice. */
|
||||
if constexpr (AllowReply) {
|
||||
reply_target = os::InvalidNativeHandle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s32 MultiWaitImpl::BuildHandleArray(NativeHandle out_handles[], MultiWaitHolderBase *out_objects[], s32 num) {
|
||||
MultiWaitHolderBase *MultiWaitImpl::WaitAnyImpl(bool infinite, TimeSpan timeout) {
|
||||
MultiWaitHolderBase *holder = nullptr;
|
||||
|
||||
const Result wait_result = this->WaitAnyImpl<false>(std::addressof(holder), infinite, timeout, os::InvalidNativeHandle);
|
||||
R_ASSERT(wait_result);
|
||||
AMS_UNUSED(wait_result);
|
||||
|
||||
return holder;
|
||||
}
|
||||
|
||||
Result MultiWaitImpl::ReplyAndReceive(MultiWaitHolderBase **out, NativeHandle reply_target) {
|
||||
R_RETURN(this->WaitAnyImpl<true>(out, true, TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::max()), reply_target));
|
||||
}
|
||||
|
||||
s32 MultiWaitImpl::ConstructObjectsArray(NativeHandle out_handles[], MultiWaitHolderBase *out_objects[], s32 num) {
|
||||
/* Add all objects with a native handle to the output array. */
|
||||
s32 count = 0;
|
||||
|
||||
for (MultiWaitHolderBase &holder_base : m_multi_wait_list) {
|
||||
if (auto handle = holder_base.GetHandle(); handle != os::InvalidNativeHandle) {
|
||||
os::NativeHandle handle = os::InvalidNativeHandle;
|
||||
if (holder_base.GetNativeHandle(std::addressof(handle))) {
|
||||
AMS_ABORT_UNLESS(count < num);
|
||||
|
||||
out_handles[count] = handle;
|
||||
out_objects[count] = std::addressof(holder_base);
|
||||
count++;
|
||||
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
MultiWaitHolderBase *MultiWaitImpl::LinkHoldersToObjectList() {
|
||||
MultiWaitHolderBase *MultiWaitImpl::AddToEachObjectListAndCheckObjectState() {
|
||||
/* Add each holder to the current object list, checking for the first signaled object. */
|
||||
MultiWaitHolderBase *signaled_holder = nullptr;
|
||||
|
||||
for (MultiWaitHolderBase &holder_base : m_multi_wait_list) {
|
||||
TriBool is_signaled = holder_base.LinkToObjectList();
|
||||
|
||||
if (signaled_holder == nullptr && is_signaled == TriBool::True) {
|
||||
if (const TriBool is_signaled = holder_base.AddToObjectList(); signaled_holder == nullptr && is_signaled == TriBool::True) {
|
||||
signaled_holder = std::addressof(holder_base);
|
||||
}
|
||||
}
|
||||
@@ -170,36 +221,43 @@ namespace ams::os::impl {
|
||||
return signaled_holder;
|
||||
}
|
||||
|
||||
void MultiWaitImpl::UnlinkHoldersFromObjectList() {
|
||||
void MultiWaitImpl::RemoveFromEachObjectList() {
|
||||
/* Remove each holder from the current object list. */
|
||||
for (MultiWaitHolderBase &holder_base : m_multi_wait_list) {
|
||||
holder_base.UnlinkFromObjectList();
|
||||
holder_base.RemoveFromObjectList();
|
||||
}
|
||||
}
|
||||
|
||||
MultiWaitHolderBase *MultiWaitImpl::RecalculateNextTimeout(TimeSpan *out_min_timeout, TimeSpan end_time) {
|
||||
MultiWaitHolderBase *MultiWaitImpl::RecalcMultiWaitTimeout(TimeSpan *out_min_timeout, TimeSpan end_time) {
|
||||
/* Find the holder with the minimum end time. */
|
||||
MultiWaitHolderBase *min_timeout_holder = nullptr;
|
||||
TimeSpan min_time = end_time;
|
||||
|
||||
for (MultiWaitHolderBase &holder_base : m_multi_wait_list) {
|
||||
if (const TimeSpan cur_time = holder_base.GetAbsoluteWakeupTime(); cur_time < min_time) {
|
||||
if (const TimeSpan cur_time = holder_base.GetAbsoluteTimeToWakeup(); cur_time < min_time) {
|
||||
min_timeout_holder = std::addressof(holder_base);
|
||||
min_time = cur_time;
|
||||
min_time = cur_time;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the minimum time is under the current time, we can't wait; otherwise, get the time to the minimum end time. */
|
||||
if (min_time < m_current_time) {
|
||||
*out_min_timeout = 0;
|
||||
} else {
|
||||
*out_min_timeout = min_time - m_current_time;
|
||||
}
|
||||
|
||||
return min_timeout_holder;
|
||||
}
|
||||
|
||||
void MultiWaitImpl::SignalAndWakeupThread(MultiWaitHolderBase *holder_base) {
|
||||
void MultiWaitImpl::NotifyAndWakeupThread(MultiWaitHolderBase *holder_base) {
|
||||
std::scoped_lock lk(m_cs_wait);
|
||||
|
||||
/* If we don't have a signaled holder, set our signaled holder. */
|
||||
if (m_signaled_holder == nullptr) {
|
||||
m_signaled_holder = holder_base;
|
||||
|
||||
/* Cancel any ongoing waits. */
|
||||
m_target_impl.CancelWait();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,24 +44,20 @@ namespace ams::os::impl {
|
||||
InternalCriticalSection m_cs_wait;
|
||||
MultiWaitTargetImpl m_target_impl;
|
||||
private:
|
||||
Result WaitAnyImpl(MultiWaitHolderBase **out, bool infinite, TimeSpan timeout, bool reply, NativeHandle reply_target);
|
||||
Result WaitAnyHandleImpl(MultiWaitHolderBase **out, bool infinite, TimeSpan timeout, bool reply, NativeHandle reply_target);
|
||||
s32 BuildHandleArray(NativeHandle out_handles[], MultiWaitHolderBase *out_objects[], s32 num);
|
||||
template<bool AllowReply>
|
||||
Result WaitAnyImpl(MultiWaitHolderBase **out, bool infinite, TimeSpan timeout, NativeHandle reply_target);
|
||||
|
||||
MultiWaitHolderBase *LinkHoldersToObjectList();
|
||||
void UnlinkHoldersFromObjectList();
|
||||
template<bool AllowReply>
|
||||
Result InternalWaitAnyImpl(MultiWaitHolderBase **out, bool infinite, TimeSpan timeout, NativeHandle reply_target);
|
||||
|
||||
MultiWaitHolderBase *RecalculateNextTimeout(TimeSpan *out_min_timeout, TimeSpan end_time);
|
||||
s32 ConstructObjectsArray(NativeHandle out_handles[], MultiWaitHolderBase *out_objects[], s32 num);
|
||||
|
||||
MultiWaitHolderBase *WaitAnyImpl(bool infinite, TimeSpan timeout) {
|
||||
MultiWaitHolderBase *holder = nullptr;
|
||||
MultiWaitHolderBase *AddToEachObjectListAndCheckObjectState();
|
||||
void RemoveFromEachObjectList();
|
||||
|
||||
const Result wait_result = this->WaitAnyImpl(std::addressof(holder), infinite, timeout, false, os::InvalidNativeHandle);
|
||||
R_ASSERT(wait_result);
|
||||
AMS_UNUSED(wait_result);
|
||||
MultiWaitHolderBase *RecalcMultiWaitTimeout(TimeSpan *out_min_timeout, TimeSpan end_time);
|
||||
|
||||
return holder;
|
||||
}
|
||||
MultiWaitHolderBase *WaitAnyImpl(bool infinite, TimeSpan timeout);
|
||||
public:
|
||||
/* Wait. */
|
||||
MultiWaitHolderBase *WaitAny() {
|
||||
@@ -76,44 +72,47 @@ namespace ams::os::impl {
|
||||
return this->WaitAnyImpl(false, ts);
|
||||
}
|
||||
|
||||
Result ReplyAndReceive(MultiWaitHolderBase **out, NativeHandle reply_target) {
|
||||
R_RETURN(this->WaitAnyImpl(out, true, TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::max()), true, reply_target));
|
||||
}
|
||||
Result ReplyAndReceive(MultiWaitHolderBase **out, NativeHandle reply_target);
|
||||
|
||||
/* List management. */
|
||||
bool IsEmpty() const {
|
||||
bool IsListEmpty() const {
|
||||
return m_multi_wait_list.empty();
|
||||
}
|
||||
|
||||
void LinkMultiWaitHolder(MultiWaitHolderBase &holder_base) {
|
||||
bool IsListNotEmpty() const {
|
||||
return !m_multi_wait_list.empty();
|
||||
}
|
||||
|
||||
void PushBackToList(MultiWaitHolderBase &holder_base) {
|
||||
m_multi_wait_list.push_back(holder_base);
|
||||
}
|
||||
|
||||
void UnlinkMultiWaitHolder(MultiWaitHolderBase &holder_base) {
|
||||
void EraseFromList(MultiWaitHolderBase &holder_base) {
|
||||
m_multi_wait_list.erase(m_multi_wait_list.iterator_to(holder_base));
|
||||
}
|
||||
|
||||
void UnlinkAll() {
|
||||
while (!this->IsEmpty()) {
|
||||
void EraseAllFromList() {
|
||||
while (!m_multi_wait_list.empty()) {
|
||||
m_multi_wait_list.front().SetMultiWait(nullptr);
|
||||
m_multi_wait_list.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
void MoveAllFrom(MultiWaitImpl &other) {
|
||||
void MoveAllFromOther(MultiWaitImpl &other) {
|
||||
/* Set ourselves as multi wait for all of the other's holders. */
|
||||
for (auto &w : other.m_multi_wait_list) {
|
||||
w.SetMultiWait(this);
|
||||
}
|
||||
|
||||
m_multi_wait_list.splice(m_multi_wait_list.end(), other.m_multi_wait_list);
|
||||
}
|
||||
|
||||
/* Other. */
|
||||
TimeSpan GetCurrentTime() const {
|
||||
TimeSpan GetCurrTime() const {
|
||||
return m_current_time;
|
||||
}
|
||||
|
||||
void SignalAndWakeupThread(MultiWaitHolderBase *holder_base);
|
||||
void NotifyAndWakeupThread(MultiWaitHolderBase *holder_base);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -25,15 +25,15 @@ namespace ams::os::impl {
|
||||
private:
|
||||
ListType m_object_list;
|
||||
public:
|
||||
void SignalAllThreads() {
|
||||
void WakeupAllMultiWaitThreadsUnsafe() {
|
||||
for (MultiWaitHolderBase &holder_base : m_object_list) {
|
||||
holder_base.GetMultiWait()->SignalAndWakeupThread(std::addressof(holder_base));
|
||||
holder_base.GetMultiWait()->NotifyAndWakeupThread(std::addressof(holder_base));
|
||||
}
|
||||
}
|
||||
|
||||
void BroadcastAllThreads() {
|
||||
void BroadcastToUpdateObjectStateUnsafe() {
|
||||
for (MultiWaitHolderBase &holder_base : m_object_list) {
|
||||
holder_base.GetMultiWait()->SignalAndWakeupThread(nullptr);
|
||||
holder_base.GetMultiWait()->NotifyAndWakeupThread(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,11 +41,11 @@ namespace ams::os::impl {
|
||||
return m_object_list.empty();
|
||||
}
|
||||
|
||||
void LinkMultiWaitHolder(MultiWaitHolderBase &holder_base) {
|
||||
void PushBackToList(MultiWaitHolderBase &holder_base) {
|
||||
m_object_list.push_back(holder_base);
|
||||
}
|
||||
|
||||
void UnlinkMultiWaitHolder(MultiWaitHolderBase &holder_base) {
|
||||
void EraseFromList(MultiWaitHolderBase &holder_base) {
|
||||
m_object_list.erase(m_object_list.iterator_to(holder_base));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 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 ProcessCodeMemoryImpl {
|
||||
public:
|
||||
static Result Map(u64 *out, NativeHandle handle, const ProcessMemoryRegion *regions, size_t num_regions);
|
||||
static Result Unmap(NativeHandle handle, u64 process_code_address, const ProcessMemoryRegion *regions, size_t num_regions);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (c) 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_process_code_memory_impl.hpp"
|
||||
#include "os_aslr_space_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
class ProcessAddressSpaceAllocator final : public AddressSpaceAllocatorBase<u64, u64> {
|
||||
private:
|
||||
using Base = AddressSpaceAllocatorBase<u64, u64>;
|
||||
private:
|
||||
NativeHandle m_handle;
|
||||
public:
|
||||
ProcessAddressSpaceAllocator(u64 start_address, u64 end_address, SizeType guard_size, const AddressSpaceAllocatorForbiddenRegion *forbidden_regions, size_t num_forbidden_regions, NativeHandle handle) : Base(start_address, end_address, guard_size, forbidden_regions, num_forbidden_regions), m_handle(handle) {
|
||||
/* ... */
|
||||
}
|
||||
public:
|
||||
virtual bool CheckFreeSpace(AddressType address, SizeType size) override {
|
||||
/* Query the memory. */
|
||||
svc::MemoryInfo memory_info;
|
||||
svc::PageInfo page_info;
|
||||
R_ABORT_UNLESS(svc::QueryProcessMemory(std::addressof(memory_info), std::addressof(page_info), m_handle, address));
|
||||
|
||||
return memory_info.state == svc::MemoryState_Free && address + size <= memory_info.base_address + memory_info.size;
|
||||
}
|
||||
};
|
||||
|
||||
using ProcessAslrSpaceManager = AslrSpaceManagerTemplate<ProcessAddressSpaceAllocator, AslrSpaceManagerImpl>;
|
||||
|
||||
size_t GetTotalProcessMemoryRegionSize(const ProcessMemoryRegion *regions, size_t num_regions) {
|
||||
size_t total = 0;
|
||||
|
||||
for (size_t i = 0; i < num_regions; ++i) {
|
||||
total += regions[i].size;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result ProcessCodeMemoryImpl::Map(u64 *out, NativeHandle handle, const ProcessMemoryRegion *regions, size_t num_regions) {
|
||||
/* Get the total process memory region size. */
|
||||
const size_t total_size = GetTotalProcessMemoryRegionSize(regions, num_regions);
|
||||
|
||||
/* Create an aslr space manager for the process. */
|
||||
auto process_aslr_space_manager = ProcessAslrSpaceManager(handle);
|
||||
|
||||
/* Map at a random address. */
|
||||
u64 mapped_address;
|
||||
R_TRY(process_aslr_space_manager.MapAtRandomAddress(std::addressof(mapped_address),
|
||||
[handle, regions, num_regions](u64 map_address, u64 map_size) -> Result {
|
||||
AMS_UNUSED(map_size);
|
||||
|
||||
/* Map the regions in order. */
|
||||
u64 mapped_size = 0;
|
||||
for (size_t i = 0; i < num_regions; ++i) {
|
||||
/* If we fail, unmap up to where we've mapped. */
|
||||
ON_RESULT_FAILURE { R_ABORT_UNLESS(ProcessCodeMemoryImpl::Unmap(handle, map_address, regions, i)); };
|
||||
|
||||
/* Map the current region. */
|
||||
R_TRY_CATCH(svc::MapProcessCodeMemory(handle, map_address + mapped_size, regions[i].address, regions[i].size)) {
|
||||
R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource())
|
||||
R_CATCH(svc::ResultInvalidCurrentMemory) {
|
||||
/* Check if the process memory is invalid. */
|
||||
const u64 last_address = regions[i].address + regions[i].size - 1;
|
||||
u64 cur_address = regions[i].address;
|
||||
while (cur_address <= last_address) {
|
||||
svc::MemoryInfo memory_info;
|
||||
svc::PageInfo page_info;
|
||||
R_ABORT_UNLESS(svc::QueryProcessMemory(std::addressof(memory_info), std::addressof(page_info), handle, cur_address));
|
||||
|
||||
R_UNLESS(memory_info.state == svc::MemoryState_Normal, os::ResultInvalidProcessMemory());
|
||||
R_UNLESS(memory_info.permission == svc::MemoryPermission_ReadWrite, os::ResultInvalidProcessMemory());
|
||||
R_UNLESS(memory_info.attribute == static_cast<svc::MemoryAttribute>(0), os::ResultInvalidProcessMemory());
|
||||
|
||||
cur_address = memory_info.base_address + memory_info.size;
|
||||
}
|
||||
|
||||
R_THROW(os::ResultInvalidCurrentMemoryState());
|
||||
}
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
|
||||
mapped_size += regions[i].size;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
},
|
||||
[handle, regions, num_regions](u64 map_address, u64 map_size) -> void {
|
||||
AMS_UNUSED(map_size);
|
||||
R_ABORT_UNLESS(ProcessCodeMemoryImpl::Unmap(handle, map_address, regions, num_regions));
|
||||
},
|
||||
total_size,
|
||||
regions[0].address /* NOTE: This seems like a Nintendo bug, if the caller passed no regions. */
|
||||
));
|
||||
|
||||
/* Set the output address. */
|
||||
*out = mapped_address;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ProcessCodeMemoryImpl::Unmap(NativeHandle handle, u64 process_code_address, const ProcessMemoryRegion *regions, size_t num_regions) {
|
||||
/* Get the total process memory region size. */
|
||||
const size_t total_size = GetTotalProcessMemoryRegionSize(regions, num_regions);
|
||||
|
||||
/* Unmap each region in order. */
|
||||
size_t cur_offset = total_size;
|
||||
for (size_t i = 0; i < num_regions; ++i) {
|
||||
/* We want to unmap in reverse order. */
|
||||
const auto &cur_region = regions[num_regions - 1 - i];
|
||||
|
||||
/* Subtract to update the current offset. */
|
||||
cur_offset -= cur_region.size;
|
||||
|
||||
/* Unmap. */
|
||||
R_TRY_CATCH(svc::UnmapProcessCodeMemory(handle, process_code_address + cur_offset, cur_region.address, cur_region.size)) {
|
||||
R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource())
|
||||
R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState())
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 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 ProcessMemoryImpl {
|
||||
public:
|
||||
static Result Map(void **out, NativeHandle handle, u64 process_address, size_t size);
|
||||
static void Unmap(void *mapped_memory, NativeHandle handle, u64 process_address, size_t size);
|
||||
|
||||
static Result SetMemoryPermission(NativeHandle handle, u64 process_address, u64 size, MemoryPermission perm);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 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_process_memory_impl.hpp"
|
||||
#include "os_aslr_space_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
svc::MemoryPermission ConvertToSvcMemoryPermission(os::MemoryPermission perm) {
|
||||
switch (perm) {
|
||||
case os::MemoryPermission_None: return svc::MemoryPermission_None;
|
||||
case os::MemoryPermission_ReadOnly: return svc::MemoryPermission_Read;
|
||||
case os::MemoryPermission_ReadWrite: return svc::MemoryPermission_ReadWrite;
|
||||
case os::MemoryPermission_ReadExecute: return svc::MemoryPermission_ReadExecute;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result ProcessMemoryImpl::Map(void **out, NativeHandle handle, u64 process_address, size_t size) {
|
||||
/* Map at a random address. */
|
||||
uintptr_t mapped_address;
|
||||
R_TRY(impl::GetAslrSpaceManager().MapAtRandomAddress(std::addressof(mapped_address),
|
||||
[handle, process_address](uintptr_t map_address, size_t map_size) -> Result {
|
||||
R_TRY_CATCH(svc::MapProcessMemory(map_address, handle, process_address, map_size)) {
|
||||
R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource())
|
||||
R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState())
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
|
||||
R_SUCCEED();
|
||||
},
|
||||
[handle, process_address](uintptr_t map_address, size_t map_size) -> void {
|
||||
return ProcessMemoryImpl::Unmap(reinterpret_cast<void *>(map_address), handle, process_address, map_size);
|
||||
},
|
||||
size,
|
||||
process_address
|
||||
));
|
||||
|
||||
/* Return the address we mapped at. */
|
||||
*out = reinterpret_cast<void *>(mapped_address);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void ProcessMemoryImpl::Unmap(void *mapped_memory, NativeHandle handle, u64 process_address, size_t size) {
|
||||
R_ABORT_UNLESS(svc::UnmapProcessMemory(reinterpret_cast<uintptr_t>(mapped_memory), handle, process_address, size));
|
||||
}
|
||||
|
||||
Result ProcessMemoryImpl::SetMemoryPermission(NativeHandle handle, u64 process_address, u64 size, MemoryPermission perm) {
|
||||
/* Set the process memory permission. */
|
||||
R_TRY_CATCH(svc::SetProcessMemoryPermission(handle, process_address, size, ConvertToSvcMemoryPermission(perm))) {
|
||||
R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource())
|
||||
R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState())
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "os_aslr_space_manager_types.hpp"
|
||||
#include "os_tls_manager_types.hpp"
|
||||
#include "os_giant_lock_types.hpp"
|
||||
#include "os_memory_heap_manager_types.hpp"
|
||||
#include "os_vamm_manager_types.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
@@ -34,7 +35,7 @@ namespace ams::os::impl {
|
||||
ThreadManager m_thread_manager{};
|
||||
//TlsManager m_tls_manager{};
|
||||
TickManager m_tick_manager{};
|
||||
/* TODO */
|
||||
MemoryHeapManager m_memory_heap_manager;
|
||||
VammManager m_vamm_manager;
|
||||
GiantLock m_giant_lock{};
|
||||
public:
|
||||
@@ -46,6 +47,7 @@ namespace ams::os::impl {
|
||||
constexpr ALWAYS_INLINE StackGuardManager &GetStackGuardManager() { return m_stack_guard_manager; }
|
||||
//constexpr ALWAYS_INLINE TlsManager &GetTlsManager() { return m_tls_manager; }
|
||||
constexpr ALWAYS_INLINE TickManager &GetTickManager() { return m_tick_manager; }
|
||||
constexpr ALWAYS_INLINE MemoryHeapManager &GetMemoryHeapManager() { return m_memory_heap_manager; }
|
||||
constexpr ALWAYS_INLINE VammManager &GetVammManager() { return m_vamm_manager; }
|
||||
constexpr ALWAYS_INLINE GiantLock &GetGiantLock() { return m_giant_lock; }
|
||||
};
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace ams::os::impl {
|
||||
|
||||
/* Map at a random address. */
|
||||
uintptr_t mapped_address;
|
||||
R_TRY(impl::GetAslrSpaceManager().MapAtRandomAddress(std::addressof(mapped_address), size,
|
||||
R_TRY(impl::GetAslrSpaceManager().MapAtRandomAddress(std::addressof(mapped_address),
|
||||
[handle, svc_perm](uintptr_t map_address, size_t map_size) -> Result {
|
||||
R_TRY_CATCH(svc::MapSharedMemory(handle, map_address, map_size, svc_perm)) {
|
||||
R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState())
|
||||
@@ -69,7 +69,9 @@ namespace ams::os::impl {
|
||||
},
|
||||
[handle](uintptr_t map_address, size_t map_size) -> void {
|
||||
return SharedMemoryImpl::Unmap(handle, reinterpret_cast<void *>(map_address), map_size);
|
||||
}
|
||||
},
|
||||
size,
|
||||
0
|
||||
));
|
||||
|
||||
/* Return the address we mapped at. */
|
||||
|
||||
@@ -111,7 +111,7 @@ namespace ams::os::impl {
|
||||
thread->state = ThreadType::State_Terminated;
|
||||
|
||||
GetReference(thread->cv_thread).Broadcast();
|
||||
GetReference(thread->waitlist).SignalAllThreads();
|
||||
GetReference(thread->waitlist).WakeupAllMultiWaitThreadsUnsafe();
|
||||
}
|
||||
|
||||
void ThreadManager::CleanupThread() {
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace ams::os::impl {
|
||||
event->timer_state = TimerEventType::TimerState_Stop;
|
||||
}
|
||||
|
||||
bool UpdateSignalStateAndRecalculateNextTimeToWakeupUnsafe(TimerEventType *event, TimeSpan cur_time) {
|
||||
bool UpdateSignalStateAndRecalcNextTimeToWakeupUnsafe(TimerEventType *event, TimeSpan cur_time) {
|
||||
TimeSpan next_time = GetReference(event->next_time_to_wakeup);
|
||||
|
||||
switch (event->timer_state) {
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace ams::os {
|
||||
|
||||
TimeSpan SaturatedAdd(TimeSpan t1, TimeSpan t2);
|
||||
void StopTimerUnsafe(TimerEventType *event);
|
||||
bool UpdateSignalStateAndRecalculateNextTimeToWakeupUnsafe(TimerEventType *event, TimeSpan cur_time);
|
||||
bool UpdateSignalStateAndRecalcNextTimeToWakeupUnsafe(TimerEventType *event, TimeSpan cur_time);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace ams::os::impl {
|
||||
|
||||
/* Map at a random address. */
|
||||
uintptr_t mapped_address;
|
||||
R_TRY(impl::GetAslrSpaceManager().MapAtRandomAddress(std::addressof(mapped_address), size,
|
||||
R_TRY(impl::GetAslrSpaceManager().MapAtRandomAddress(std::addressof(mapped_address),
|
||||
[handle, svc_owner_perm](uintptr_t map_address, size_t map_size) -> Result {
|
||||
R_TRY_CATCH(svc::MapTransferMemory(handle, map_address, map_size, svc_owner_perm)) {
|
||||
R_CONVERT(svc::ResultInvalidHandle, os::ResultInvalidHandle())
|
||||
@@ -71,7 +71,9 @@ namespace ams::os::impl {
|
||||
},
|
||||
[handle](uintptr_t map_address, size_t map_size) -> void {
|
||||
return TransferMemoryImpl::Unmap(handle, reinterpret_cast<void *>(map_address), map_size);
|
||||
}
|
||||
},
|
||||
size,
|
||||
0
|
||||
));
|
||||
|
||||
/* Return the address we mapped at. */
|
||||
|
||||
@@ -37,7 +37,8 @@ namespace ams::os::impl {
|
||||
}
|
||||
|
||||
static Result FreePhysicalMemoryImpl(uintptr_t address, size_t size) {
|
||||
R_UNLESS(::mprotect(reinterpret_cast<void *>(address), size, PROT_NONE) == 0, os::ResultBusy());
|
||||
const auto reserved = ::mmap(reinterpret_cast<void *>(address), size, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
R_UNLESS(reserved != MAP_FAILED, os::ResultBusy());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,8 @@ namespace ams::os::impl {
|
||||
}
|
||||
|
||||
static Result FreePhysicalMemoryImpl(uintptr_t address, size_t size) {
|
||||
R_UNLESS(::mprotect(reinterpret_cast<void *>(address), size, PROT_NONE) == 0, os::ResultBusy());
|
||||
const auto reserved = ::mmap(reinterpret_cast<void *>(address), size, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
R_UNLESS(reserved != MAP_FAILED, os::ResultBusy());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace ams::os {
|
||||
}
|
||||
|
||||
/* Wake up whatever manager, if any. */
|
||||
GetReference(event->multi_wait_object_list_storage).SignalAllThreads();
|
||||
GetReference(event->multi_wait_object_list_storage).WakeupAllMultiWaitThreadsUnsafe();
|
||||
}
|
||||
|
||||
void WaitEvent(EventType *event) {
|
||||
|
||||
29
libraries/libstratosphere/source/os/os_memory_attribute.cpp
Normal file
29
libraries/libstratosphere/source/os/os_memory_attribute.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 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_attribute_impl.hpp"
|
||||
|
||||
namespace ams::os {
|
||||
|
||||
void SetMemoryAttribute(uintptr_t address, size_t size, MemoryAttribute attr) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(util::IsAligned(address, MemoryPageSize));
|
||||
AMS_ASSERT(util::IsAligned(size, MemoryPageSize));
|
||||
|
||||
return impl::SetMemoryAttributeImpl(address, size, attr);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,17 +14,47 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "impl/os_memory_heap_manager.hpp"
|
||||
|
||||
namespace ams::os {
|
||||
|
||||
Result SetMemoryHeapSize(size_t size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(util::IsAligned(size, MemoryHeapUnitSize));
|
||||
|
||||
/* Set the heap size. */
|
||||
R_RETURN(impl::GetMemoryHeapManager().SetHeapSize(size));
|
||||
}
|
||||
|
||||
uintptr_t GetMemoryHeapAddress() {
|
||||
return impl::GetMemoryHeapManager().GetHeapAddress();
|
||||
}
|
||||
|
||||
size_t GetMemoryHeapSize() {
|
||||
return impl::GetMemoryHeapManager().GetHeapSize();
|
||||
}
|
||||
|
||||
Result AllocateMemoryBlock(uintptr_t *out_address, size_t size) {
|
||||
AMS_UNUSED(out_address, size);
|
||||
AMS_ABORT("Not implemented yet");
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(size > 0);
|
||||
AMS_ASSERT(util::IsAligned(size, MemoryBlockUnitSize));
|
||||
|
||||
/* Allocate from heap. */
|
||||
R_RETURN(impl::GetMemoryHeapManager().AllocateFromHeap(out_address, size));
|
||||
}
|
||||
|
||||
void FreeMemoryBlock(uintptr_t address, size_t size) {
|
||||
AMS_UNUSED(address, size);
|
||||
AMS_ABORT("Not implemented yet");
|
||||
/* Get memory heap manager. */
|
||||
auto &manager = impl::GetMemoryHeapManager();
|
||||
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(util::IsAligned(address, MemoryBlockUnitSize));
|
||||
AMS_ASSERT(size > 0);
|
||||
AMS_ASSERT(util::IsAligned(size, MemoryBlockUnitSize));
|
||||
AMS_ABORT_UNLESS(manager.IsRegionInMemoryHeap(address, size));
|
||||
|
||||
/* Release the memory block. */
|
||||
manager.ReleaseToHeap(address, size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace ams::os {
|
||||
/* Send, signal. */
|
||||
MessageQueueHelper::EnqueueUnsafe(mq, data);
|
||||
GetReference(mq->cv_not_empty).Broadcast();
|
||||
GetReference(mq->waitlist_not_empty).SignalAllThreads();
|
||||
GetReference(mq->waitlist_not_empty).WakeupAllMultiWaitThreadsUnsafe();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace ams::os {
|
||||
/* Send, signal. */
|
||||
MessageQueueHelper::EnqueueUnsafe(mq, data);
|
||||
GetReference(mq->cv_not_empty).Broadcast();
|
||||
GetReference(mq->waitlist_not_empty).SignalAllThreads();
|
||||
GetReference(mq->waitlist_not_empty).WakeupAllMultiWaitThreadsUnsafe();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -127,7 +127,7 @@ namespace ams::os {
|
||||
/* Send, signal. */
|
||||
MessageQueueHelper::EnqueueUnsafe(mq, data);
|
||||
GetReference(mq->cv_not_empty).Broadcast();
|
||||
GetReference(mq->waitlist_not_empty).SignalAllThreads();
|
||||
GetReference(mq->waitlist_not_empty).WakeupAllMultiWaitThreadsUnsafe();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -148,7 +148,7 @@ namespace ams::os {
|
||||
/* Send, signal. */
|
||||
MessageQueueHelper::JamUnsafe(mq, data);
|
||||
GetReference(mq->cv_not_empty).Broadcast();
|
||||
GetReference(mq->waitlist_not_empty).SignalAllThreads();
|
||||
GetReference(mq->waitlist_not_empty).WakeupAllMultiWaitThreadsUnsafe();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ namespace ams::os {
|
||||
/* Send, signal. */
|
||||
MessageQueueHelper::JamUnsafe(mq, data);
|
||||
GetReference(mq->cv_not_empty).Broadcast();
|
||||
GetReference(mq->waitlist_not_empty).SignalAllThreads();
|
||||
GetReference(mq->waitlist_not_empty).WakeupAllMultiWaitThreadsUnsafe();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -191,7 +191,7 @@ namespace ams::os {
|
||||
/* Send, signal. */
|
||||
MessageQueueHelper::JamUnsafe(mq, data);
|
||||
GetReference(mq->cv_not_empty).Broadcast();
|
||||
GetReference(mq->waitlist_not_empty).SignalAllThreads();
|
||||
GetReference(mq->waitlist_not_empty).WakeupAllMultiWaitThreadsUnsafe();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -212,7 +212,7 @@ namespace ams::os {
|
||||
/* Receive, signal. */
|
||||
*out = MessageQueueHelper::DequeueUnsafe(mq);
|
||||
GetReference(mq->cv_not_full).Broadcast();
|
||||
GetReference(mq->waitlist_not_full).SignalAllThreads();
|
||||
GetReference(mq->waitlist_not_full).WakeupAllMultiWaitThreadsUnsafe();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,7 +230,7 @@ namespace ams::os {
|
||||
/* Receive, signal. */
|
||||
*out = MessageQueueHelper::DequeueUnsafe(mq);
|
||||
GetReference(mq->cv_not_full).Broadcast();
|
||||
GetReference(mq->waitlist_not_full).SignalAllThreads();
|
||||
GetReference(mq->waitlist_not_full).WakeupAllMultiWaitThreadsUnsafe();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -255,7 +255,7 @@ namespace ams::os {
|
||||
/* Receive, signal. */
|
||||
*out = MessageQueueHelper::DequeueUnsafe(mq);
|
||||
GetReference(mq->cv_not_full).Broadcast();
|
||||
GetReference(mq->waitlist_not_full).SignalAllThreads();
|
||||
GetReference(mq->waitlist_not_full).WakeupAllMultiWaitThreadsUnsafe();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -324,10 +324,10 @@ namespace ams::os {
|
||||
|
||||
switch (type) {
|
||||
case MessageQueueWaitType::ForNotFull:
|
||||
util::ConstructAt(GetReference(multi_wait_holder->impl_storage).holder_of_mq_for_not_full_storage, mq);
|
||||
util::ConstructAt(GetReference(multi_wait_holder->impl_storage).holder_of_mq_not_full_storage, mq);
|
||||
break;
|
||||
case MessageQueueWaitType::ForNotEmpty:
|
||||
util::ConstructAt(GetReference(multi_wait_holder->impl_storage).holder_of_mq_for_not_empty_storage, mq);
|
||||
util::ConstructAt(GetReference(multi_wait_holder->impl_storage).holder_of_mq_not_empty_storage, mq);
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace ams::os {
|
||||
auto &impl = GetMultiWaitImpl(multi_wait);
|
||||
|
||||
AMS_ASSERT(multi_wait->state == MultiWaitType::State_Initialized);
|
||||
AMS_ASSERT(impl.IsEmpty());
|
||||
AMS_ASSERT(impl.IsListEmpty());
|
||||
AMS_UNUSED(impl);
|
||||
|
||||
/* Mark not initialized. */
|
||||
@@ -58,7 +58,7 @@ namespace ams::os {
|
||||
auto &impl = GetMultiWaitImpl(multi_wait);
|
||||
|
||||
AMS_ASSERT(multi_wait->state == MultiWaitType::State_Initialized);
|
||||
AMS_ASSERT(!impl.IsEmpty());
|
||||
AMS_ASSERT(impl.IsListNotEmpty());
|
||||
|
||||
auto *holder = CastToMultiWaitHolder(impl.WaitAny());
|
||||
AMS_ASSERT(holder != nullptr);
|
||||
@@ -69,7 +69,7 @@ namespace ams::os {
|
||||
auto &impl = GetMultiWaitImpl(multi_wait);
|
||||
|
||||
AMS_ASSERT(multi_wait->state == MultiWaitType::State_Initialized);
|
||||
AMS_ASSERT(!impl.IsEmpty());
|
||||
AMS_ASSERT(impl.IsListNotEmpty());
|
||||
|
||||
auto *holder = CastToMultiWaitHolder(impl.TryWaitAny());
|
||||
return holder;
|
||||
@@ -79,7 +79,7 @@ namespace ams::os {
|
||||
auto &impl = GetMultiWaitImpl(multi_wait);
|
||||
|
||||
AMS_ASSERT(multi_wait->state == MultiWaitType::State_Initialized);
|
||||
AMS_ASSERT(!impl.IsEmpty());
|
||||
AMS_ASSERT(impl.IsListNotEmpty());
|
||||
AMS_ASSERT(timeout.GetNanoSeconds() >= 0);
|
||||
|
||||
auto *holder = CastToMultiWaitHolder(impl.TimedWaitAny(timeout));
|
||||
@@ -89,7 +89,7 @@ namespace ams::os {
|
||||
void FinalizeMultiWaitHolder(MultiWaitHolderType *holder) {
|
||||
auto *holder_base = reinterpret_cast<impl::MultiWaitHolderBase *>(GetPointer(holder->impl_storage));
|
||||
|
||||
AMS_ASSERT(!holder_base->IsLinked());
|
||||
AMS_ASSERT(holder_base->IsNotLinked());
|
||||
|
||||
/* Destroy. */
|
||||
static_assert(std::is_trivially_destructible<impl::MultiWaitHolderBase>::value);
|
||||
@@ -102,9 +102,9 @@ namespace ams::os {
|
||||
auto *holder_base = reinterpret_cast<impl::MultiWaitHolderBase *>(GetPointer(holder->impl_storage));
|
||||
|
||||
AMS_ASSERT(multi_wait->state == MultiWaitType::State_Initialized);
|
||||
AMS_ASSERT(!holder_base->IsLinked());
|
||||
AMS_ASSERT(holder_base->IsNotLinked());
|
||||
|
||||
impl.LinkMultiWaitHolder(*holder_base);
|
||||
impl.PushBackToList(*holder_base);
|
||||
holder_base->SetMultiWait(std::addressof(impl));
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ namespace ams::os {
|
||||
/* Don't allow unlinking of an unlinked holder. */
|
||||
AMS_ABORT_UNLESS(holder_base->IsLinked());
|
||||
|
||||
holder_base->GetMultiWait()->UnlinkMultiWaitHolder(*holder_base);
|
||||
holder_base->GetMultiWait()->EraseFromList(*holder_base);
|
||||
holder_base->SetMultiWait(nullptr);
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace ams::os {
|
||||
|
||||
AMS_ASSERT(multi_wait->state == MultiWaitType::State_Initialized);
|
||||
|
||||
return impl.UnlinkAll();
|
||||
return impl.EraseAllFromList();
|
||||
}
|
||||
|
||||
void MoveAllMultiWaitHolder(MultiWaitType *_dst, MultiWaitType *_src) {
|
||||
@@ -133,7 +133,7 @@ namespace ams::os {
|
||||
AMS_ASSERT(_dst->state == MultiWaitType::State_Initialized);
|
||||
AMS_ASSERT(_src->state == MultiWaitType::State_Initialized);
|
||||
|
||||
return dst.MoveAllFrom(src);
|
||||
return dst.MoveAllFromOther(src);
|
||||
}
|
||||
|
||||
void SetMultiWaitHolderUserData(MultiWaitHolderType *holder, uintptr_t user_data) {
|
||||
@@ -147,7 +147,7 @@ namespace ams::os {
|
||||
void InitializeMultiWaitHolder(MultiWaitHolderType *holder, NativeHandle handle) {
|
||||
AMS_ASSERT(handle != os::InvalidNativeHandle);
|
||||
|
||||
util::ConstructAt(GetReference(holder->impl_storage).holder_of_handle_storage, handle);
|
||||
util::ConstructAt(GetReference(holder->impl_storage).holder_of_native_handle_storage, handle);
|
||||
|
||||
holder->user_data = 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 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_process_code_memory_impl.hpp"
|
||||
|
||||
namespace ams::os {
|
||||
|
||||
Result MapProcessCodeMemory(u64 *out, NativeHandle handle, const ProcessMemoryRegion *regions, size_t num_regions) {
|
||||
R_RETURN(::ams::os::impl::ProcessCodeMemoryImpl::Map(out, handle, regions, num_regions));
|
||||
}
|
||||
|
||||
Result UnmapProcessCodeMemory(NativeHandle handle, u64 process_code_address, const ProcessMemoryRegion *regions, size_t num_regions) {
|
||||
R_RETURN(::ams::os::impl::ProcessCodeMemoryImpl::Unmap(handle, process_code_address, regions, num_regions));
|
||||
}
|
||||
|
||||
}
|
||||
33
libraries/libstratosphere/source/os/os_process_memory.cpp
Normal file
33
libraries/libstratosphere/source/os/os_process_memory.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 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_process_memory_impl.hpp"
|
||||
|
||||
namespace ams::os {
|
||||
|
||||
Result MapProcessMemory(void **out, NativeHandle handle, u64 process_address, size_t process_size) {
|
||||
R_RETURN(::ams::os::impl::ProcessMemoryImpl::Map(out, handle, process_address, process_size));
|
||||
}
|
||||
|
||||
void UnmapProcessMemory(void *mapped_memory, NativeHandle handle, u64 process_address, size_t process_size) {
|
||||
return ::ams::os::impl::ProcessMemoryImpl::Unmap(mapped_memory, handle, process_address, process_size);
|
||||
}
|
||||
|
||||
Result SetProcessMemoryPermission(NativeHandle handle, u64 process_address, u64 process_size, MemoryPermission perm) {
|
||||
R_RETURN(::ams::os::impl::ProcessMemoryImpl::SetMemoryPermission(handle, process_address, process_size, perm));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,9 +36,9 @@ namespace ams::os {
|
||||
auto &impl = GetMultiWaitImpl(multi_wait);
|
||||
|
||||
AMS_ASSERT(multi_wait->state == MultiWaitType::State_Initialized);
|
||||
AMS_ASSERT(!impl.IsEmpty());
|
||||
AMS_ASSERT(impl.IsListNotEmpty());
|
||||
|
||||
impl::MultiWaitHolderBase *holder_base;
|
||||
impl::MultiWaitHolderBase *holder_base = nullptr;
|
||||
ON_SCOPE_EXIT { *out = CastToMultiWaitHolder(holder_base); };
|
||||
|
||||
R_RETURN(impl.ReplyAndReceive(std::addressof(holder_base), reply_target));
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace ams::os {
|
||||
++sema->count;
|
||||
|
||||
GetReference(sema->cv_not_zero).Signal();
|
||||
GetReference(sema->waitlist).SignalAllThreads();
|
||||
GetReference(sema->waitlist).WakeupAllMultiWaitThreadsUnsafe();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,8 +131,8 @@ namespace ams::os {
|
||||
|
||||
sema->count += count;
|
||||
|
||||
GetReference(sema->cv_not_zero).Signal();
|
||||
GetReference(sema->waitlist).SignalAllThreads();
|
||||
GetReference(sema->cv_not_zero).Broadcast();
|
||||
GetReference(sema->waitlist).WakeupAllMultiWaitThreadsUnsafe();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace ams::os {
|
||||
}
|
||||
|
||||
/* Wake up whatever manager, if any. */
|
||||
GetReference(event->multi_wait_object_list_storage).SignalAllThreads();
|
||||
GetReference(event->multi_wait_object_list_storage).WakeupAllMultiWaitThreadsUnsafe();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -110,7 +110,7 @@ namespace ams::os {
|
||||
GetReference(event->cv_signaled).Broadcast();
|
||||
|
||||
/* Wake up whatever manager, if any. */
|
||||
GetReference(event->multi_wait_object_list_storage).SignalAllThreads();
|
||||
GetReference(event->multi_wait_object_list_storage).WakeupAllMultiWaitThreadsUnsafe();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ namespace ams::os {
|
||||
GetReference(event->cv_signaled).Broadcast();
|
||||
|
||||
/* Wake up whatever manager, if any. */
|
||||
GetReference(event->multi_wait_object_list_storage).SignalAllThreads();
|
||||
GetReference(event->multi_wait_object_list_storage).WakeupAllMultiWaitThreadsUnsafe();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ namespace ams::os {
|
||||
GetReference(event->cv_signaled).Broadcast();
|
||||
|
||||
/* Wake up whatever manager, if any. */
|
||||
GetReference(event->multi_wait_object_list_storage).SignalAllThreads();
|
||||
GetReference(event->multi_wait_object_list_storage).WakeupAllMultiWaitThreadsUnsafe();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ namespace ams::os {
|
||||
|
||||
/* Update. */
|
||||
auto cur_time = impl::GetCurrentTick().ToTimeSpan();
|
||||
if (impl::UpdateSignalStateAndRecalculateNextTimeToWakeupUnsafe(event, cur_time)) {
|
||||
if (impl::UpdateSignalStateAndRecalcNextTimeToWakeupUnsafe(event, cur_time)) {
|
||||
SignalTimerEventImplUnsafe(event);
|
||||
break;
|
||||
}
|
||||
@@ -201,7 +201,7 @@ namespace ams::os {
|
||||
|
||||
/* Update. */
|
||||
auto cur_time = impl::GetCurrentTick().ToTimeSpan();
|
||||
if (impl::UpdateSignalStateAndRecalculateNextTimeToWakeupUnsafe(event, cur_time)) {
|
||||
if (impl::UpdateSignalStateAndRecalcNextTimeToWakeupUnsafe(event, cur_time)) {
|
||||
SignalTimerEventImplUnsafe(event);
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ namespace ams::os {
|
||||
|
||||
/* Update. */
|
||||
auto cur_time = impl::GetCurrentTick().ToTimeSpan();
|
||||
impl::UpdateSignalStateAndRecalculateNextTimeToWakeupUnsafe(event, cur_time);
|
||||
impl::UpdateSignalStateAndRecalcNextTimeToWakeupUnsafe(event, cur_time);
|
||||
|
||||
/* Signal. */
|
||||
SignalTimerEventImplUnsafe(event);
|
||||
@@ -243,7 +243,7 @@ namespace ams::os {
|
||||
|
||||
/* Update. */
|
||||
auto cur_time = impl::GetCurrentTick().ToTimeSpan();
|
||||
if (impl::UpdateSignalStateAndRecalculateNextTimeToWakeupUnsafe(event, cur_time)) {
|
||||
if (impl::UpdateSignalStateAndRecalcNextTimeToWakeupUnsafe(event, cur_time)) {
|
||||
SignalTimerEventImplUnsafe(event);
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace ams::pwm::driver::board::nintendo::nx::impl {
|
||||
/* Configure initial settings. */
|
||||
/* NOTE: None of these results are checked. */
|
||||
this->SetEnabled(device, false);
|
||||
this->SetDuty(device, 0);
|
||||
this->SetScale(device, 0.0);
|
||||
this->SetPeriod(device, DefaultChannelPeriod);
|
||||
R_SUCCEED();
|
||||
}
|
||||
@@ -169,8 +169,16 @@ namespace ams::pwm::driver::board::nintendo::nx::impl {
|
||||
/* Convert the scale to a duty. */
|
||||
const int duty = static_cast<int>(((scale * 256.0) / 100.0) + 0.5);
|
||||
|
||||
/* Set the duty. */
|
||||
R_RETURN(this->SetDuty(device, duty));
|
||||
/* Validate the duty. */
|
||||
R_UNLESS(0 <= duty && duty <= MaxDuty, pwm::ResultInvalidArgument());
|
||||
|
||||
/* Acquire exclusive access to the device registers. */
|
||||
std::scoped_lock lk(device->SafeCastTo<PwmDeviceImpl>());
|
||||
|
||||
/* Update the duty. */
|
||||
reg::ReadWrite(this->GetRegistersFor(device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_VALUE(PWM_CSR_PWM, static_cast<u32>(duty)));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result PwmDriverImpl::GetScale(double *out, IPwmDevice *device) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
||||
.section .text._ZN3ams3svc7aarch644lp6412CreateIoPoolEPjNS0_5board8nintendo2nx10IoPoolTypeE, "ax", %progbits
|
||||
.global _ZN3ams3svc7aarch644lp6412CreateIoPoolEPjNS0_5board8nintendo2nx10IoPoolTypeE
|
||||
.type _ZN3ams3svc7aarch644lp6412CreateIoPoolEPjNS0_5board8nintendo2nx10IoPoolTypeE, %function
|
||||
.align 2
|
||||
.cfi_startproc
|
||||
_ZN3ams3svc7aarch644lp6412CreateIoPoolEPjNS0_5board8nintendo2nx10IoPoolTypeE:
|
||||
str x0, [sp, #-16]!
|
||||
svc 0x39
|
||||
ldr x2, [sp], #16
|
||||
str w1, [x2]
|
||||
ret
|
||||
.cfi_endproc
|
||||
|
||||
.section .text._ZN3ams3svc7aarch644lp6424AttachDeviceAddressSpaceENS0_5board8nintendo2nx10DeviceNameEj, "ax", %progbits
|
||||
.global _ZN3ams3svc7aarch644lp6424AttachDeviceAddressSpaceENS0_5board8nintendo2nx10DeviceNameEj
|
||||
.type _ZN3ams3svc7aarch644lp6424AttachDeviceAddressSpaceENS0_5board8nintendo2nx10DeviceNameEj, %function
|
||||
.align 2
|
||||
.cfi_startproc
|
||||
_ZN3ams3svc7aarch644lp6424AttachDeviceAddressSpaceENS0_5board8nintendo2nx10DeviceNameEj:
|
||||
svc 0x57
|
||||
ret
|
||||
.cfi_endproc
|
||||
|
||||
.section .text._ZN3ams3svc7aarch644lp6424DetachDeviceAddressSpaceENS0_5board8nintendo2nx10DeviceNameEj, "ax", %progbits
|
||||
.global _ZN3ams3svc7aarch644lp6424DetachDeviceAddressSpaceENS0_5board8nintendo2nx10DeviceNameEj
|
||||
.type _ZN3ams3svc7aarch644lp6424DetachDeviceAddressSpaceENS0_5board8nintendo2nx10DeviceNameEj, %function
|
||||
.align 2
|
||||
.cfi_startproc
|
||||
_ZN3ams3svc7aarch644lp6424DetachDeviceAddressSpaceENS0_5board8nintendo2nx10DeviceNameEj:
|
||||
svc 0x58
|
||||
ret
|
||||
.cfi_endproc
|
||||
@@ -17,10 +17,10 @@
|
||||
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 1
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MINOR 3
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 1
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 2
|
||||
|
||||
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO
|
||||
|
||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 14
|
||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 1
|
||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 0
|
||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 1
|
||||
|
||||
@@ -69,8 +69,9 @@
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_13_2_1 ATMOSPHERE_TARGET_FIRMWARE(13, 2, 1)
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_14_0_0 ATMOSPHERE_TARGET_FIRMWARE(14, 0, 0)
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_14_1_0 ATMOSPHERE_TARGET_FIRMWARE(14, 1, 0)
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_14_1_1 ATMOSPHERE_TARGET_FIRMWARE(14, 1, 1)
|
||||
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_14_1_0
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_14_1_1
|
||||
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE(0, 0, 0)
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_CURRENT
|
||||
@@ -132,6 +133,7 @@ namespace ams {
|
||||
TargetFirmware_13_2_1 = ATMOSPHERE_TARGET_FIRMWARE_13_2_1,
|
||||
TargetFirmware_14_0_0 = ATMOSPHERE_TARGET_FIRMWARE_14_0_0,
|
||||
TargetFirmware_14_1_0 = ATMOSPHERE_TARGET_FIRMWARE_14_1_0,
|
||||
TargetFirmware_14_1_1 = ATMOSPHERE_TARGET_FIRMWARE_14_1_1,
|
||||
|
||||
TargetFirmware_Current = ATMOSPHERE_TARGET_FIRMWARE_CURRENT,
|
||||
|
||||
|
||||
@@ -47,9 +47,9 @@ namespace ams::crypto {
|
||||
};
|
||||
static constexpr size_t Asn1IdentifierSize = util::size(Asn1Identifier);
|
||||
private:
|
||||
Impl m_impl;
|
||||
Impl m_impl{};
|
||||
public:
|
||||
Sha256Generator() { /* ... */ }
|
||||
Sha256Generator() = default;
|
||||
|
||||
void Initialize() {
|
||||
m_impl.Initialize();
|
||||
|
||||
@@ -41,6 +41,7 @@ namespace ams::os {
|
||||
R_DEFINE_ERROR_RESULT(SessionClosedForReceive, 510);
|
||||
R_DEFINE_ERROR_RESULT(SessionClosedForReply, 511);
|
||||
R_DEFINE_ERROR_RESULT(ReceiveListBroken, 512);
|
||||
R_DEFINE_ERROR_RESULT(InvalidProcessMemory, 513);
|
||||
|
||||
R_DEFINE_ERROR_RESULT(NotImplemented, 1000);
|
||||
R_DEFINE_ERROR_RESULT(NotSupported, 1001);
|
||||
|
||||
@@ -144,12 +144,12 @@ namespace ams::util {
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE void StoreBigEndian(T *ptr, T val) {
|
||||
*ptr = ConvertToBigEndian<T>(val);
|
||||
*static_cast<volatile T *>(ptr) = ConvertToBigEndian<T>(val);
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE void StoreLittleEndian(T *ptr, T val) {
|
||||
*ptr = ConvertToLittleEndian<T>(val);
|
||||
*static_cast<volatile T *>(ptr) = ConvertToLittleEndian<T>(val);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,6 +32,11 @@ namespace ams::util {
|
||||
}
|
||||
};
|
||||
|
||||
template<typename F>
|
||||
struct OptionalFunction {
|
||||
F &m_f;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
struct nullopt_t {
|
||||
@@ -67,6 +72,9 @@ namespace ams::util {
|
||||
|
||||
template<typename V, typename... Args>
|
||||
constexpr ALWAYS_INLINE StorageType(std::initializer_list<V> il, Args &&... args) : m_value(il, std::forward<Args>(args)...) { /* ... */ }
|
||||
|
||||
template<typename F, typename Arg>
|
||||
constexpr ALWAYS_INLINE StorageType(OptionalFunction<F> f, Arg &&arg) : m_value(std::invoke(std::forward<F>(f.m_f), std::forward<Arg>(arg))) { /* ... */ }
|
||||
};
|
||||
|
||||
template<typename U>
|
||||
@@ -82,6 +90,9 @@ namespace ams::util {
|
||||
template<typename V, typename... Args>
|
||||
constexpr ALWAYS_INLINE StorageType(std::initializer_list<V> il, Args &&... args) : m_value(il, std::forward<Args>(args)...) { /* ... */ }
|
||||
|
||||
template<typename F, typename Arg>
|
||||
constexpr ALWAYS_INLINE StorageType(OptionalFunction<F> f, Arg &&arg) : m_value(std::invoke(std::forward<F>(f.m_f), std::forward<Arg>(arg))) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE ~StorageType() { /* ... */ }
|
||||
};
|
||||
|
||||
@@ -137,6 +148,12 @@ namespace ams::util {
|
||||
std::destroy_at(std::addressof(m_payload.m_value));
|
||||
}
|
||||
|
||||
template<typename F, typename Arg>
|
||||
constexpr void Apply(impl::OptionalFunction<F> f, Arg &&arg) {
|
||||
std::construct_at(std::addressof(m_payload), f, std::forward<Arg>(arg));
|
||||
m_engaged = true;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE T &Get() { return m_payload.m_value; }
|
||||
constexpr ALWAYS_INLINE const T &Get() const { return m_payload.m_value; }
|
||||
|
||||
@@ -234,6 +251,11 @@ namespace ams::util {
|
||||
|
||||
constexpr void ResetImpl() { static_cast<Derived *>(this)->m_payload.Reset(); }
|
||||
|
||||
template<typename F, typename Arg>
|
||||
constexpr void ApplyImpl(OptionalFunction<F> f, Arg &&arg) {
|
||||
static_cast<Derived *>(this)->m_payload.Apply(f, std::forward<Arg>(arg));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsEngagedImpl() const { return static_cast<const Derived *>(this)->m_payload.m_engaged; }
|
||||
|
||||
constexpr ALWAYS_INLINE T &GetImpl() { return static_cast<Derived *>(this)->m_payload.Get(); }
|
||||
@@ -338,6 +360,11 @@ namespace ams::util {
|
||||
std::is_assignable<T &, optional<U> &>::value ||
|
||||
std::is_assignable<T &, const optional<U> &&>::value ||
|
||||
std::is_assignable<T &, optional<U> &&>::value;
|
||||
template<typename T>
|
||||
constexpr inline bool IsOptional = false;
|
||||
|
||||
template<typename T>
|
||||
constexpr inline bool IsOptional<optional<T>> = true;
|
||||
|
||||
}
|
||||
|
||||
@@ -509,7 +536,80 @@ namespace ams::util {
|
||||
return this->IsEngagedImpl() ? std::move(this->GetImpl()) : static_cast<T>(std::forward<U>(u));
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
constexpr auto and_then(F &&f) & {
|
||||
using U = typename std::remove_cvref<typename std::invoke_result<F, T &>::type>::type;
|
||||
static_assert(impl::IsOptional<typename std::remove_cvref<U>::type>);
|
||||
return this->IsEngagedImpl() ? std::invoke(std::forward<F>(f), **this) : U{};
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
constexpr auto and_then(F &&f) const & {
|
||||
using U = typename std::remove_cvref<typename std::invoke_result<F, const T &>::type>::type;
|
||||
static_assert(impl::IsOptional<typename std::remove_cvref<U>::type>);
|
||||
return this->IsEngagedImpl() ? std::invoke(std::forward<F>(f), **this) : U{};
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
constexpr auto and_then(F &&f) && {
|
||||
using U = typename std::remove_cvref<typename std::invoke_result<F, T>::type>::type;
|
||||
static_assert(impl::IsOptional<typename std::remove_cvref<U>::type>);
|
||||
return this->IsEngagedImpl() ? std::invoke(std::forward<F>(f), std::move(**this)) : U{};
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
constexpr auto and_then(F &&f) const && {
|
||||
using U = typename std::remove_cvref<typename std::invoke_result<F, const T>::type>::type;
|
||||
static_assert(impl::IsOptional<typename std::remove_cvref<U>::type>);
|
||||
return this->IsEngagedImpl() ? std::invoke(std::forward<F>(f), std::move(**this)) : U{};
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
constexpr auto transform(F &&f) & {
|
||||
using U = typename std::remove_cvref<typename std::invoke_result<F, T &>::type>::type;
|
||||
return this->IsEngagedImpl() ? optional<U>(impl::OptionalFunction<F>{f}, **this) : optional<U>{};
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
constexpr auto transform(F &&f) const & {
|
||||
using U = typename std::remove_cvref<typename std::invoke_result<F, const T &>::type>::type;
|
||||
return this->IsEngagedImpl() ? optional<U>(impl::OptionalFunction<F>{f}, **this) : optional<U>{};
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
constexpr auto transform(F &&f) && {
|
||||
using U = typename std::remove_cvref<typename std::invoke_result<F, T>::type>::type;
|
||||
return this->IsEngagedImpl() ? optional<U>(impl::OptionalFunction<F>{f}, std::move(**this)) : optional<U>{};
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
constexpr auto transform(F &&f) const && {
|
||||
using U = typename std::remove_cvref<typename std::invoke_result<F, const T>::type>::type;
|
||||
return this->IsEngagedImpl() ? optional<U>(impl::OptionalFunction<F>{f}, std::move(**this)) : optional<U>{};
|
||||
}
|
||||
|
||||
template<typename F> requires std::invocable<F> && std::copy_constructible<T>
|
||||
constexpr optional or_else(F &&f) const & {
|
||||
using U = typename std::invoke_result<F>::type;
|
||||
static_assert(std::same_as<typename std::remove_cvref_t<U>, optional>);
|
||||
return this->IsEngagedImpl() ? *this : std::forward<F>(f)();
|
||||
}
|
||||
|
||||
template<typename F> requires std::invocable<F> && std::move_constructible<T>
|
||||
constexpr optional or_else(F &&f) && {
|
||||
using U = typename std::invoke_result<F>::type;
|
||||
static_assert(std::same_as<typename std::remove_cvref_t<U>, optional>);
|
||||
return this->IsEngagedImpl() ? std::move(*this) : std::forward<F>(f)();
|
||||
}
|
||||
|
||||
constexpr void reset() { this->ResetImpl(); }
|
||||
private:
|
||||
template<typename U> friend class optional;
|
||||
|
||||
template<typename F, typename Arg>
|
||||
constexpr explicit optional(impl::OptionalFunction<F> f, Arg &&arg) {
|
||||
this->ApplyImpl(f, std::forward<Arg>(arg));
|
||||
}
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
@@ -588,16 +688,6 @@ namespace ams::util {
|
||||
template<typename T, typename U>
|
||||
constexpr inline impl::optional_ge_t<U, T> operator>=(const U &lhs, const optional<T> &rhs) { return !rhs || lhs >= *rhs; }
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename T>
|
||||
constexpr inline bool IsOptional = false;
|
||||
|
||||
template<typename T>
|
||||
constexpr inline bool IsOptional<optional<T>> = true;
|
||||
|
||||
}
|
||||
|
||||
template<typename T, typename U> requires (!impl::IsOptional<U>) && std::three_way_comparable_with<T, U>
|
||||
constexpr inline std::compare_three_way_result_t<T, U> operator<=>(const optional<T> &lhs, const U &rhs) {
|
||||
return static_cast<bool>(lhs) ? *lhs <=> rhs : std::strong_ordering::less;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user