Compare commits

..

18 Commits

Author SHA1 Message Date
Michael Scire
7557b7eb92 util: prevent optimizer from removing endian-swapped writes 2022-05-08 15:32:59 -07:00
Michael Scire
187745abd5 ams: address some warnings when building with gcc-12 2022-05-07 17:09:22 -07:00
Michael Scire
4db485083b strat: update for code changes found in boot (closes #1797) 2022-05-05 17:45:55 -07:00
Michael Scire
e96972c939 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "b91294d3b"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "b91294d3b"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2022-04-29 16:15:06 -07:00
Michael Scire
3545c0aac2 ams: fs accuracy fixes, bump to 1.3.2 2022-04-29 16:14:01 -07:00
Michael Scire
d85875b910 os: fix various regressions since 1.3.1 2022-04-29 15:46:55 -07:00
Michael Scire
b1367942a2 os: fix minor bug (and simplify) MapProcessCodeMemory 2022-04-18 01:43:49 -07:00
Michael Scire
c2c0a2e169 ro/os: use os primitives for MapProcessCodeMemory 2022-04-18 01:39:22 -07:00
Michael Scire
f5052b4bca loader: update for changes in 14.0.0 2022-04-17 20:11:05 -07:00
Michael Scire
70d67bb115 loader: use os apis for interacting with process memory 2022-04-17 18:51:36 -07:00
Michael Scire
9056e0b05f strat: fix linux clang build 2022-04-17 14:01:03 -07:00
Michael Scire
895b6d0470 optional: add c++23 monadic interface 2022-04-17 12:17:25 -07:00
Michael Scire
dfba595cdc fs: fix null check in AesXtsStorageExternal 2022-04-16 12:28:40 -07:00
Michael Scire
175a34da43 os: silence a maybe-uninit warning 2022-04-16 12:28:21 -07:00
Michael Scire
02b126c2be os: refactor multi wait apis to better match Nintendo's latest implementation 2022-04-12 16:47:36 -07:00
Michael Scire
b45671fd35 fs: QueryEntry uses NonSecure buffers 2022-04-08 11:23:39 -07:00
Michael Scire
106599895d fs: fix memory leak when path is reallocated (closes #1842) 2022-04-08 11:02:17 -07:00
Michael Scire
80154b0a54 os: broadcast, not signal, on release more than 1 sema 2022-04-07 12:17:13 -07:00
82 changed files with 1208 additions and 1387 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -25,6 +25,8 @@
#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>

View File

@@ -23,11 +23,13 @@ namespace ams::os {
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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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;
@@ -174,15 +178,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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,6 +16,12 @@
#pragma once
#include <stratosphere.hpp>
namespace ams::ro::impl {
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);
};
}

View File

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

View 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/>.
*/
#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);
};
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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_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));
}
}

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -18,7 +18,7 @@
namespace ams::crypto {
void GenerateSha256(void *dst, size_t dst_size, const void *src, size_t src_size) {
Sha256Generator gen;
Sha256Generator gen{};
gen.Initialize();
gen.Update(src, src_size);

View File

@@ -1,399 +0,0 @@
/*
* 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/>.
*/
struct MinimumProgramVersion {
ncm::ProgramId program_id;
u32 version;
};
constexpr u32 MakeSystemVersion(u32 major, u32 minor, u32 micro) {
return (major << 26) | (minor << 20) | (micro << 16);
}
constexpr MinimumProgramVersion g_MinimumProgramVersions810[] = {
{ncm::SystemProgramId::Settings, 1},
{ncm::SystemProgramId::Bus, 1},
{ncm::SystemProgramId::Audio, 1},
{ncm::SystemProgramId::NvServices, 1},
{ncm::SystemProgramId::Ns, 1},
{ncm::SystemProgramId::Ssl, 1},
{ncm::SystemProgramId::Es, 1},
{ncm::SystemProgramId::Creport, 1},
{ncm::SystemProgramId::Ro, 1},
};
constexpr size_t g_MinimumProgramVersionsCount810 = util::size(g_MinimumProgramVersions810);
constexpr MinimumProgramVersion g_MinimumProgramVersions900[] = {
/* All non-Development System Modules. */
{ncm::SystemProgramId::Usb, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Tma, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Boot2, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Settings, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Bus, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Bluetooth, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Bcat, MakeSystemVersion(9, 0, 0)},
/* {ncm::SystemProgramId::Dmnt, MakeSystemVersion(9, 0, 0)}, */
{ncm::SystemProgramId::Friends, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Nifm, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Ptm, MakeSystemVersion(9, 0, 0)},
/* {ncm::SystemProgramId::Shell, MakeSystemVersion(9, 0, 0)}, */
{ncm::SystemProgramId::BsdSockets, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Hid, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Audio, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::LogManager, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Wlan, MakeSystemVersion(9, 0, 0)},
/* {ncm::SystemProgramId::Cs, MakeSystemVersion(9, 0, 0)}, */
{ncm::SystemProgramId::Ldn, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::NvServices, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Pcv, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Ppc, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::NvnFlinger, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Pcie, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Account, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Ns, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Nfc, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Psc, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::CapSrv, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Am, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Ssl, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Nim, MakeSystemVersion(9, 0, 0)},
/* {ncm::SystemProgramId::Cec, MakeSystemVersion(9, 0, 0)}, */
/* {ncm::SystemProgramId::Tspm, MakeSystemVersion(9, 0, 0)}, */
/* {ncm::SystemProgramId::Spl, MakeSystemVersion(9, 0, 0)}, */
{ncm::SystemProgramId::Lbl, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Btm, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Erpt, MakeSystemVersion(9, 0, 0)},
/* {ncm::SystemProgramId::Time, MakeSystemVersion(9, 0, 0)}, */
{ncm::SystemProgramId::Vi, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Pctl, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Npns, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Eupld, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Glue, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Eclct, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Es, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Fatal, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Grc, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Creport, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Ro, MakeSystemVersion(9, 0, 0)},
/* {ncm::SystemProgramId::Profiler, MakeSystemVersion(9, 0, 0)}, */
{ncm::SystemProgramId::Sdb, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Migration, MakeSystemVersion(9, 0, 0)},
/* {ncm::SystemProgramId::Jit, MakeSystemVersion(9, 0, 0)}, */
{ncm::SystemProgramId::JpegDec, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::SafeMode, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Olsc, MakeSystemVersion(9, 0, 0)},
/* {ncm::SystemProgramId::Dt, MakeSystemVersion(9, 0, 0)}, */
/* {ncm::SystemProgramId::Nd, MakeSystemVersion(9, 0, 0)}, */
{ncm::SystemProgramId::Ngct, MakeSystemVersion(9, 0, 0)},
/* All Web Applets. */
{ncm::WebAppletId::Web, MakeSystemVersion(9, 0, 0)},
{ncm::WebAppletId::Shop, MakeSystemVersion(9, 0, 0)},
{ncm::WebAppletId::OfflineWeb, MakeSystemVersion(9, 0, 0)},
{ncm::WebAppletId::LoginShare, MakeSystemVersion(9, 0, 0)},
{ncm::WebAppletId::WifiWebAuth, MakeSystemVersion(9, 0, 0)},
};
constexpr size_t g_MinimumProgramVersionsCount900 = util::size(g_MinimumProgramVersions900);
constexpr MinimumProgramVersion g_MinimumProgramVersions910[] = {
/* All non-Development System Modules. */
{ncm::SystemProgramId::Usb, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Tma, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Boot2, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Settings, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Bus, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Bluetooth, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Bcat, MakeSystemVersion(9, 1, 0)},
/* {ncm::SystemProgramId::Dmnt, MakeSystemVersion(9, 0, 0)}, */
{ncm::SystemProgramId::Friends, MakeSystemVersion(9, 1, 0)},
{ncm::SystemProgramId::Nifm, MakeSystemVersion(9, 1, 0)},
{ncm::SystemProgramId::Ptm, MakeSystemVersion(9, 0, 0)},
/* {ncm::SystemProgramId::Shell, MakeSystemVersion(9, 0, 0)}, */
{ncm::SystemProgramId::BsdSockets, MakeSystemVersion(9, 1, 0)},
{ncm::SystemProgramId::Hid, MakeSystemVersion(9, 1, 0)},
{ncm::SystemProgramId::Audio, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::LogManager, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Wlan, MakeSystemVersion(9, 1, 0)},
/* {ncm::SystemProgramId::Cs, MakeSystemVersion(9, 0, 0)}, */
{ncm::SystemProgramId::Ldn, MakeSystemVersion(9, 1, 0)},
{ncm::SystemProgramId::NvServices, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Pcv, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Ppc, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::NvnFlinger, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Pcie, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Account, MakeSystemVersion(9, 1, 0)},
{ncm::SystemProgramId::Ns, MakeSystemVersion(9, 1, 0)},
{ncm::SystemProgramId::Nfc, MakeSystemVersion(9, 1, 0)},
{ncm::SystemProgramId::Psc, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::CapSrv, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Am, MakeSystemVersion(9, 1, 0)},
{ncm::SystemProgramId::Ssl, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Nim, MakeSystemVersion(9, 1, 0)},
/* {ncm::SystemProgramId::Cec, MakeSystemVersion(9, 0, 0)}, */
/* {ncm::SystemProgramId::Tspm, MakeSystemVersion(9, 0, 0)}, */
/* {ncm::SystemProgramId::Spl, MakeSystemVersion(9, 0, 0)}, */
{ncm::SystemProgramId::Lbl, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Btm, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Erpt, MakeSystemVersion(9, 1, 0)},
/* {ncm::SystemProgramId::Time, MakeSystemVersion(9, 0, 0)}, */
{ncm::SystemProgramId::Vi, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Pctl, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Npns, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Eupld, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Glue, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Eclct, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Es, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Fatal, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Grc, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Creport, MakeSystemVersion(9, 1, 0)},
{ncm::SystemProgramId::Ro, MakeSystemVersion(9, 1, 0)},
/* {ncm::SystemProgramId::Profiler, MakeSystemVersion(9, 0, 0)}, */
{ncm::SystemProgramId::Sdb, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Migration, MakeSystemVersion(9, 1, 0)},
/* {ncm::SystemProgramId::Jit, MakeSystemVersion(9, 0, 0)}, */
{ncm::SystemProgramId::JpegDec, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::SafeMode, MakeSystemVersion(9, 0, 0)},
{ncm::SystemProgramId::Olsc, MakeSystemVersion(9, 1, 0)},
/* {ncm::SystemProgramId::Dt, MakeSystemVersion(9, 0, 0)}, */
/* {ncm::SystemProgramId::Nd, MakeSystemVersion(9, 0, 0)}, */
{ncm::SystemProgramId::Ngct, MakeSystemVersion(9, 1, 0)},
/* All Web Applets. */
{ncm::WebAppletId::Web, MakeSystemVersion(9, 1, 0)},
{ncm::WebAppletId::Shop, MakeSystemVersion(9, 1, 0)},
{ncm::WebAppletId::OfflineWeb, MakeSystemVersion(9, 1, 0)},
{ncm::WebAppletId::LoginShare, MakeSystemVersion(9, 1, 0)},
{ncm::WebAppletId::WifiWebAuth, MakeSystemVersion(9, 1, 0)},
};
constexpr size_t g_MinimumProgramVersionsCount910 = util::size(g_MinimumProgramVersions910);
constexpr MinimumProgramVersion g_MinimumProgramVersions1000[] = {
/* All non-Development System Modules. */
{ncm::SystemProgramId::Usb, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Tma, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Boot2, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Settings, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Bus, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Bluetooth, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Bcat, MakeSystemVersion(10, 0, 0)},
/* {ncm::SystemProgramId::Dmnt, MakeSystemVersion(10, 0, 0)}, */
{ncm::SystemProgramId::Friends, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Nifm, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Ptm, MakeSystemVersion(10, 0, 0)},
/* {ncm::SystemProgramId::Shell, MakeSystemVersion(10, 0, 0)}, */
{ncm::SystemProgramId::BsdSockets, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Hid, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Audio, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::LogManager, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Wlan, MakeSystemVersion(10, 0, 0)},
/* {ncm::SystemProgramId::Cs, MakeSystemVersion(10, 0, 0)}, */
{ncm::SystemProgramId::Ldn, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::NvServices, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Pcv, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Ppc, MakeSystemVersion( 9, 0, 0)},
{ncm::SystemProgramId::NvnFlinger, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Pcie, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Account, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Ns, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Nfc, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Psc, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::CapSrv, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Am, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Ssl, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Nim, MakeSystemVersion(10, 0, 0)},
/* {ncm::SystemProgramId::Cec, MakeSystemVersion(10, 0, 0)}, */
/* {ncm::SystemProgramId::Tspm, MakeSystemVersion(10, 0, 0)}, */
/* {ncm::SystemProgramId::Spl, MakeSystemVersion(10, 0, 0)}, */
{ncm::SystemProgramId::Lbl, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Btm, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Erpt, MakeSystemVersion(10, 0, 0)},
/* {ncm::SystemProgramId::Time, MakeSystemVersion(10, 0, 0)}, */
{ncm::SystemProgramId::Vi, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Pctl, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Npns, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Eupld, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Glue, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Eclct, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Es, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Fatal, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Grc, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Creport, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Ro, MakeSystemVersion(10, 0, 0)},
/* {ncm::SystemProgramId::Profiler, MakeSystemVersion(10, 0, 0)}, */
{ncm::SystemProgramId::Sdb, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Migration, MakeSystemVersion(10, 0, 0)},
/* {ncm::SystemProgramId::Jit, MakeSystemVersion(10, 0, 0)}, */
{ncm::SystemProgramId::JpegDec, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::SafeMode, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Olsc, MakeSystemVersion(10, 0, 0)},
/* {ncm::SystemProgramId::Dt, MakeSystemVersion(10, 0, 0)}, */
/* {ncm::SystemProgramId::Nd, MakeSystemVersion(10, 0, 0)}, */
{ncm::SystemProgramId::Ngct, MakeSystemVersion(10, 0, 0)},
/* {ncm::SystemProgramId::Pgl, MakeSystemVersion(10, 0, 0)}, */
/* All Web Applets. */
{ncm::WebAppletId::Web, MakeSystemVersion(10, 0, 0)},
{ncm::WebAppletId::Shop, MakeSystemVersion(10, 0, 0)},
{ncm::WebAppletId::OfflineWeb, MakeSystemVersion(10, 0, 0)},
{ncm::WebAppletId::LoginShare, MakeSystemVersion(10, 0, 0)},
{ncm::WebAppletId::WifiWebAuth, MakeSystemVersion(10, 0, 0)},
};
constexpr size_t g_MinimumProgramVersionsCount1000 = util::size(g_MinimumProgramVersions1000);
constexpr MinimumProgramVersion g_MinimumProgramVersions1010[] = {
/* All non-Development System Modules. */
{ncm::SystemProgramId::Usb, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Tma, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Boot2, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Settings, MakeSystemVersion(10, 1, 0)},
{ncm::SystemProgramId::Bus, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Bluetooth, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Bcat, MakeSystemVersion(10, 0, 0)},
/* {ncm::SystemProgramId::Dmnt, MakeSystemVersion(10, 0, 0)}, */
{ncm::SystemProgramId::Friends, MakeSystemVersion(10, 1, 0)},
{ncm::SystemProgramId::Nifm, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Ptm, MakeSystemVersion(10, 0, 2)},
/* {ncm::SystemProgramId::Shell, MakeSystemVersion(10, 0, 0)}, */
{ncm::SystemProgramId::BsdSockets, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Hid, MakeSystemVersion(10, 1, 0)},
{ncm::SystemProgramId::Audio, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::LogManager, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Wlan, MakeSystemVersion(10, 1, 0)},
/* {ncm::SystemProgramId::Cs, MakeSystemVersion(10, 0, 0)}, */
{ncm::SystemProgramId::Ldn, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::NvServices, MakeSystemVersion(10, 1, 0)},
{ncm::SystemProgramId::Pcv, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Ppc, MakeSystemVersion( 9, 0, 0)},
{ncm::SystemProgramId::NvnFlinger, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Pcie, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Account, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Ns, MakeSystemVersion(10, 1, 0)},
{ncm::SystemProgramId::Nfc, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Psc, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::CapSrv, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Am, MakeSystemVersion(10, 1, 0)},
{ncm::SystemProgramId::Ssl, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Nim, MakeSystemVersion(10, 1, 0)},
/* {ncm::SystemProgramId::Cec, MakeSystemVersion(10, 0, 0)}, */
/* {ncm::SystemProgramId::Tspm, MakeSystemVersion(10, 0, 0)}, */
/* {ncm::SystemProgramId::Spl, MakeSystemVersion(10, 0, 0)}, */
{ncm::SystemProgramId::Lbl, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Btm, MakeSystemVersion(10, 1, 0)},
{ncm::SystemProgramId::Erpt, MakeSystemVersion(10, 0, 0)},
/* {ncm::SystemProgramId::Time, MakeSystemVersion(10, 0, 0)}, */
{ncm::SystemProgramId::Vi, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Pctl, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Npns, MakeSystemVersion(10, 1, 0)},
{ncm::SystemProgramId::Eupld, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Glue, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Eclct, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Es, MakeSystemVersion(10, 1, 0)},
{ncm::SystemProgramId::Fatal, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Grc, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Creport, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Ro, MakeSystemVersion(10, 0, 0)},
/* {ncm::SystemProgramId::Profiler, MakeSystemVersion(10, 0, 0)}, */
{ncm::SystemProgramId::Sdb, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Migration, MakeSystemVersion(10, 1, 0)},
/* {ncm::SystemProgramId::Jit, MakeSystemVersion(10, 0, 0)}, */
{ncm::SystemProgramId::JpegDec, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::SafeMode, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Olsc, MakeSystemVersion(10, 1, 0)},
/* {ncm::SystemProgramId::Dt, MakeSystemVersion(10, 0, 0)}, */
/* {ncm::SystemProgramId::Nd, MakeSystemVersion(10, 0, 0)}, */
{ncm::SystemProgramId::Ngct, MakeSystemVersion(10, 0, 0)},
/* {ncm::SystemProgramId::Pgl, MakeSystemVersion(10, 0, 0)}, */
/* All Web Applets. */
{ncm::WebAppletId::Web, MakeSystemVersion(10, 0, 0)},
{ncm::WebAppletId::Shop, MakeSystemVersion(10, 0, 0)},
{ncm::WebAppletId::OfflineWeb, MakeSystemVersion(10, 0, 0)},
{ncm::WebAppletId::LoginShare, MakeSystemVersion(10, 0, 0)},
{ncm::WebAppletId::WifiWebAuth, MakeSystemVersion(10, 0, 0)},
};
constexpr size_t g_MinimumProgramVersionsCount1010 = util::size(g_MinimumProgramVersions1010);
constexpr MinimumProgramVersion g_MinimumProgramVersions1100[] = {
/* All non-Development System Modules. */
{ncm::SystemProgramId::Usb, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Tma, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Boot2, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Settings, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Bus, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Bluetooth, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Bcat, MakeSystemVersion(11, 0, 0)},
/* {ncm::SystemProgramId::Dmnt, MakeSystemVersion(11, 0, 0)}, */
{ncm::SystemProgramId::Friends, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Nifm, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Ptm, MakeSystemVersion(11, 0, 0)},
/* {ncm::SystemProgramId::Shell, MakeSystemVersion(11, 0, 0)}, */
{ncm::SystemProgramId::BsdSockets, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Hid, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Audio, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::LogManager, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Wlan, MakeSystemVersion(11, 0, 0)},
/* {ncm::SystemProgramId::Cs, MakeSystemVersion(11, 0, 0)}, */
{ncm::SystemProgramId::Ldn, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::NvServices, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Pcv, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Ppc, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::NvnFlinger, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Pcie, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Account, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Ns, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Nfc, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Psc, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::CapSrv, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Am, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Ssl, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Nim, MakeSystemVersion(11, 0, 0)},
/* {ncm::SystemProgramId::Cec, MakeSystemVersion(11, 0, 0)}, */
/* {ncm::SystemProgramId::Tspm, MakeSystemVersion(11, 0, 0)}, */
/* {ncm::SystemProgramId::Spl, MakeSystemVersion(11, 0, 0)}, */
{ncm::SystemProgramId::Lbl, MakeSystemVersion(10, 0, 0)},
{ncm::SystemProgramId::Btm, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Erpt, MakeSystemVersion(11, 0, 0)},
/* {ncm::SystemProgramId::Time, MakeSystemVersion(11, 0, 0)}, */
{ncm::SystemProgramId::Vi, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Pctl, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Npns, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Eupld, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Glue, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Eclct, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Es, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Fatal, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Grc, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Creport, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Ro, MakeSystemVersion(11, 0, 0)},
/* {ncm::SystemProgramId::Profiler, MakeSystemVersion(11, 0, 0)}, */
{ncm::SystemProgramId::Sdb, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Migration, MakeSystemVersion(11, 0, 0)},
/* {ncm::SystemProgramId::Jit, MakeSystemVersion(11, 0, 0)}, */
{ncm::SystemProgramId::JpegDec, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::SafeMode, MakeSystemVersion(11, 0, 0)},
{ncm::SystemProgramId::Olsc, MakeSystemVersion(11, 0, 0)},
/* {ncm::SystemProgramId::Dt, MakeSystemVersion(11, 0, 0)}, */
/* {ncm::SystemProgramId::Nd, MakeSystemVersion(11, 0, 0)}, */
{ncm::SystemProgramId::Ngct, MakeSystemVersion(11, 0, 0)},
/* {ncm::SystemProgramId::Pgl, MakeSystemVersion(11, 0, 0)}, */
/* All Web Applets. */
{ncm::WebAppletId::Web, MakeSystemVersion(11, 0, 0)},
{ncm::WebAppletId::Shop, MakeSystemVersion(11, 0, 0)},
{ncm::WebAppletId::OfflineWeb, MakeSystemVersion(11, 0, 0)},
{ncm::WebAppletId::LoginShare, MakeSystemVersion(11, 0, 0)},
{ncm::WebAppletId::WifiWebAuth, MakeSystemVersion(11, 0, 0)},
};
constexpr size_t g_MinimumProgramVersionsCount1100 = util::size(g_MinimumProgramVersions1100);

View File

@@ -1,54 +0,0 @@
/*
* 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::ldr {
class AutoCloseMap {
NON_COPYABLE(AutoCloseMap);
NON_MOVEABLE(AutoCloseMap);
private:
Result m_result;
uintptr_t m_map_address;
os::NativeHandle m_handle;
u64 m_address;
u64 m_size;
public:
AutoCloseMap(uintptr_t map, os::NativeHandle handle, u64 addr, u64 size) : m_map_address(map), m_handle(handle), m_address(addr), m_size(size) {
m_result = svc::MapProcessMemory(m_map_address, m_handle, m_address, m_size);
}
~AutoCloseMap() {
if (m_handle != os::InvalidNativeHandle && R_SUCCEEDED(m_result)) {
R_ABORT_UNLESS(svc::UnmapProcessMemory(m_map_address, m_handle, m_address, m_size));
}
}
Result GetResult() const {
return m_result;
}
bool IsSuccess() const {
return R_SUCCEEDED(m_result);
}
void Cancel() {
m_handle = os::InvalidNativeHandle;
}
};
}

View File

@@ -186,7 +186,7 @@ namespace ams::ldr {
);
enum class MemoryRegionType : u32 {
None = 0,
NoMapping = 0,
KernelTraceBuffer = 1,
OnMemoryBootImage = 2,
DTB = 3,
@@ -200,17 +200,31 @@ namespace ams::ldr {
DEFINE_CAPABILITY_FIELD(Region2, ReadOnly1, 6, MemoryRegionType);
DEFINE_CAPABILITY_FIELD(ReadOnly2, Region2, 1, bool);
bool IsValid(const util::BitPack32 *kac, size_t kac_count) const {
for (size_t i = 0; i < kac_count; i++) {
if (GetCapabilityId(kac[i]) == Id) {
const auto restriction = Decode(kac[i]);
static bool IsValidRegionType(const util::BitPack32 *kac, size_t kac_count, MemoryRegionType region_type, bool is_read_only) {
if (region_type != MemoryRegionType::NoMapping) {
for (size_t i = 0; i < kac_count; i++) {
if (GetCapabilityId(kac[i]) == Id) {
const auto restriction = Decode(kac[i]);
if (this->GetValue() == restriction.GetValue()) {
return true;
if ((restriction.GetRegion0() == region_type && (is_read_only || !restriction.GetReadOnly0())) ||
(restriction.GetRegion1() == region_type && (is_read_only || !restriction.GetReadOnly1())) ||
(restriction.GetRegion2() == region_type && (is_read_only || !restriction.GetReadOnly2())))
{
return true;
}
}
}
return false;
} else {
return true;
}
return false;
}
bool IsValid(const util::BitPack32 *kac, size_t kac_count) const {
return IsValidRegionType(kac, kac_count, this->GetRegion0(), this->GetReadOnly0()) &&
IsValidRegionType(kac, kac_count, this->GetRegion1(), this->GetReadOnly1()) &&
IsValidRegionType(kac, kac_count, this->GetRegion2(), this->GetReadOnly2());
}
);

View File

@@ -89,6 +89,13 @@ namespace ams::ldr {
R_UNLESS((acid->flags & Acid::AcidFlag_Production) != 0, ldr::ResultInvalidMeta());
}
/* Validate that the acid version is correct. */
constexpr u8 MinimumValueForAcid209 = 14; /* TODO: What is the actual meaning of this value? */
if (acid->unknown_209 < MinimumValueForAcid209) {
R_UNLESS(acid->version == 0, ldr::ResultInvalidMeta());
R_UNLESS(acid->unknown_209 == 0, ldr::ResultInvalidMeta());
}
/* Validate Fac, Sac, Kac. */
R_TRY(ValidateSubregion(sizeof(Acid), size, acid->fac_offset, acid->fac_size));
R_TRY(ValidateSubregion(sizeof(Acid), size, acid->sac_offset, acid->sac_size));

View File

@@ -14,7 +14,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "ldr_auto_close.hpp"
#include "ldr_capabilities.hpp"
#include "ldr_content_management.hpp"
#include "ldr_development_manager.hpp"
@@ -82,9 +81,6 @@ namespace ams::ldr {
bool g_has_nso[Nso_Count];
NsoHeader g_nso_headers[Nso_Count];
/* Anti-downgrade. */
#include "ldr_anti_downgrade_tables.inc"
Result ValidateProgramVersion(ncm::ProgramId program_id, u32 version) {
/* No version verification is done before 8.1.0. */
R_SUCCEED_IF(hos::GetVersion() < hos::Version_8_1_0);
@@ -92,40 +88,9 @@ namespace ams::ldr {
/* No verification is done if development. */
R_SUCCEED_IF(IsDevelopmentForAntiDowngradeCheck());
/* Do version-dependent validation, if compiled to do so. */
#ifdef LDR_VALIDATE_PROCESS_VERSION
const MinimumProgramVersion *entries = nullptr;
size_t num_entries = 0;
const auto hos_version = hos::GetVersion();
if (hos_version >= hos::Version_11_0_0) {
entries = g_MinimumProgramVersions1100;
num_entries = g_MinimumProgramVersionsCount1100;
} else if (hos_version >= hos::Version_10_1_0) {
entries = g_MinimumProgramVersions1010;
num_entries = g_MinimumProgramVersionsCount1010;
} else if (hos_version >= hos::Version_10_0_0) {
entries = g_MinimumProgramVersions1000;
num_entries = g_MinimumProgramVersionsCount1000;
} else if (hos_version >= hos::Version_9_1_0) {
entries = g_MinimumProgramVersions910;
num_entries = g_MinimumProgramVersionsCount910;
} else if (hos_version >= hos::Version_9_0_0) {
entries = g_MinimumProgramVersions900;
num_entries = g_MinimumProgramVersionsCount900;
} else if (hos_version >= hos::Version_8_1_0) {
entries = g_MinimumProgramVersions810;
num_entries = g_MinimumProgramVersionsCount810;
}
for (size_t i = 0; i < num_entries; i++) {
if (entries[i].program_id == program_id) {
R_UNLESS(entries[i].version <= version, ldr::ResultInvalidVersion());
}
}
#else
/* TODO: Anti-downgrade checking does not make very much sense for us. Should we do anything? */
AMS_UNUSED(program_id, version);
#endif
R_SUCCEED();
}
@@ -176,7 +141,7 @@ namespace ams::ldr {
return static_cast<Acid::PoolPartition>((meta->acid->flags & Acid::AcidFlag_PoolPartitionMask) >> Acid::AcidFlag_PoolPartitionShift);
}
Result LoadNsoHeaders(NsoHeader *nso_headers, bool *has_nso) {
Result LoadAutoLoadHeaders(NsoHeader *nso_headers, bool *has_nso) {
/* Clear NSOs. */
std::memset(nso_headers, 0, sizeof(*nso_headers) * Nso_Count);
std::memset(has_nso, 0, sizeof(*has_nso) * Nso_Count);
@@ -198,7 +163,7 @@ namespace ams::ldr {
R_SUCCEED();
}
Result ValidateNsoHeaders(const NsoHeader *nso_headers, const bool *has_nso) {
Result CheckAutoLoad(const NsoHeader *nso_headers, const bool *has_nso) {
/* We must always have a main. */
R_UNLESS(has_nso[Nso_Main], ldr::ResultInvalidNso());
@@ -217,6 +182,60 @@ namespace ams::ldr {
R_SUCCEED();
}
constexpr const ncm::ProgramId UnqualifiedApprovalProgramIds[] = {
{ 0x010003F003A34000 }, /* Pokemon: Let's Go, Pikachu! */
{ 0x0100152000022000 }, /* Mario Kart 8 Deluxe */
{ 0x0100165003504000 }, /* Nintendo Labo Toy-Con 04: VR Kit */
{ 0x0100187003A36000 }, /* Pokemon: Let's Go, Eevee! */
{ 0x01002E5008C56000 }, /* Pokemon Sword [Live Tournament] */
{ 0x01002FF008C24000 }, /* Ring Fit Adventure */
{ 0x010049900F546001 }, /* Super Mario 3D All-Stars: Super Mario 64 */
{ 0x010057D00ECE4000 }, /* Nintendo Switch Online (Nintendo 64) [for Japan] */
{ 0x01006F8002326000 }, /* Animal Crossing: New Horizons */
{ 0x01006FB00F50E000 }, /* [???] */
{ 0x010070300F50C000 }, /* [???] */
{ 0x010075100E8EC000 }, /* 马力欧卡丁车8 豪华版 [Mario Kart 8 Deluxe for China] */
{ 0x01008DB008C2C000 }, /* Pokemon Shield */
{ 0x01009AD008C4C000 }, /* Pokemon: Let's Go, Pikachu! [Kiosk] */
{ 0x0100A66003384000 }, /* Hulu */
{ 0x0100ABF008968000 }, /* Pokemon Sword */
{ 0x0100C9A00ECE6000 }, /* Nintendo Switch Online (Nintendo 64) [for America] */
{ 0x0100ED100BA3A000 }, /* Mario Kart Live: Home Circuit */
{ 0x0100F38011CFE000 }, /* Animal Crossing: New Horizons Island Transfer Tool */
{ 0x0100F6B011028000 }, /* 健身环大冒险 [Ring Fit Adventure for China] */
};
/* Check that the unqualified approval programs are sorted. */
static_assert([]() -> bool {
for (size_t i = 0; i < util::size(UnqualifiedApprovalProgramIds) - 1; ++i) {
if (UnqualifiedApprovalProgramIds[i].value >= UnqualifiedApprovalProgramIds[i + 1].value) {
return false;
}
}
return true;
}());
bool IsUnqualifiedApprovalProgramId(ncm::ProgramId program_id) {
/* Check if the program id is one with unqualified approval. */
return std::binary_search(std::begin(UnqualifiedApprovalProgramIds), std::end(UnqualifiedApprovalProgramIds), program_id);
}
bool IsUnqualifiedApproval(const Meta *meta) {
/* If the meta has unqualified approval flag, it's unqualified approval. */
if (meta->acid->flags & ldr::Acid::AcidFlag_UnqualifiedApproval) {
return true;
}
/* If the unqualified approval flag is not set, the program must be an application. */
if (!IsApplication(meta)) {
return false;
}
/* The program id must be a force unqualified approval program id. */
return IsUnqualifiedApprovalProgramId(meta->acid->program_id_min) && meta->acid->program_id_min == meta->acid->program_id_max;
}
Result ValidateMeta(const Meta *meta, const ncm::ProgramLocation &loc, const fs::CodeVerificationData &code_verification_data) {
/* Validate version. */
R_TRY(ValidateProgramVersion(loc.program_id, meta->npdm->version));
@@ -229,7 +248,7 @@ namespace ams::ldr {
R_TRY(TestCapability(static_cast<const util::BitPack32 *>(meta->acid_kac), meta->acid->kac_size / sizeof(util::BitPack32), static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)));
/* If we have data to validate, validate it. */
if (code_verification_data.has_data && meta->check_verification_data) {
if (meta->check_verification_data) {
const u8 *sig = code_verification_data.signature;
const size_t sig_size = sizeof(code_verification_data.signature);
const u8 *mod = static_cast<u8 *>(meta->modulus);
@@ -240,7 +259,15 @@ namespace ams::ldr {
const size_t hsh_size = sizeof(code_verification_data.target_hash);
const bool is_signature_valid = crypto::VerifyRsa2048PssSha256WithHash(sig, sig_size, mod, mod_size, exp, exp_size, hsh, hsh_size);
R_UNLESS(is_signature_valid, ldr::ResultInvalidNcaSignature());
/* If the signature check fails, we need to check if this is allowable. */
if (!is_signature_valid) {
/* We have to enforce signature checks on prod and when we have a signature to check on dev. */
R_UNLESS(IsDevelopmentForAcidProductionCheck(), ldr::ResultInvalidNcaSignature());
R_UNLESS(!code_verification_data.has_data, ldr::ResultInvalidNcaSignature());
/* There was no signature to check on dev. Check if this is acceptable. */
R_UNLESS(IsUnqualifiedApproval(meta), ldr::ResultInvalidNcaSignature());
}
}
/* All good. */
@@ -299,6 +326,8 @@ namespace ams::ldr {
/* 5.0.0+ Set Pool Partition. */
if (hos::GetVersion() >= hos::Version_5_0_0) {
/* TODO: Nintendo no longer accepts Applet when pool partition == application. Would this break hbl/anything else in the hb ecosystem? */
/* TODO: Nintendo uses a helper bool MakeSvcPoolPartitionFlag(u32 *out, Acid::PoolPartition partition); */
switch (GetPoolPartition(meta)) {
case Acid::PoolPartition_Application:
if (IsApplet(meta)) {
@@ -370,63 +399,6 @@ namespace ams::ldr {
R_SUCCEED();
}
ALWAYS_INLINE u64 GetCurrentProcessInfo(svc::InfoType info_type) {
u64 value;
R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), info_type, svc::PseudoHandle::CurrentProcess, 0));
return value;
}
Result SearchFreeRegion(uintptr_t *out, size_t mapping_size) {
/* Get address space extents. */
const uintptr_t heap_start = GetCurrentProcessInfo(svc::InfoType_HeapRegionAddress);
const size_t heap_size = GetCurrentProcessInfo(svc::InfoType_HeapRegionSize);
const uintptr_t alias_start = GetCurrentProcessInfo(svc::InfoType_AliasRegionAddress);
const size_t alias_size = GetCurrentProcessInfo(svc::InfoType_AliasRegionSize);
const uintptr_t aslr_start = GetCurrentProcessInfo(svc::InfoType_AslrRegionAddress);
const size_t aslr_size = GetCurrentProcessInfo(svc::InfoType_AslrRegionSize);
/* Iterate upwards to find a free region. */
uintptr_t address = aslr_start;
while (true) {
/* Declare variables for memory querying. */
svc::MemoryInfo mem_info;
svc::PageInfo page_info;
/* Check that we're still within bounds. */
R_UNLESS(address < address + mapping_size, svc::ResultOutOfMemory());
/* If we're within the heap region, skip to the end of the heap region. */
if (heap_size != 0 && !(address + mapping_size - 1 < heap_start || heap_start + heap_size - 1 < address)) {
R_UNLESS(address < heap_start + heap_size, svc::ResultOutOfMemory());
address = heap_start + heap_size;
continue;
}
/* If we're within the alias region, skip to the end of the alias region. */
if (alias_size != 0 && !(address + mapping_size - 1 < alias_start || alias_start + alias_size - 1 < address)) {
R_UNLESS(address < alias_start + alias_size, svc::ResultOutOfMemory());
address = alias_start + alias_size;
continue;
}
/* Get the current memory range. */
R_ABORT_UNLESS(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), address));
/* If the memory range is free and big enough, use it. */
if (mem_info.state == svc::MemoryState_Free && mapping_size <= ((mem_info.base_address + mem_info.size) - address)) {
*out = address;
R_SUCCEED();
}
/* Check that we can advance. */
R_UNLESS(address < mem_info.base_address + mem_info.size, svc::ResultOutOfMemory());
R_UNLESS(mem_info.base_address + mem_info.size - 1 < aslr_start + aslr_size - 1, svc::ResultOutOfMemory());
/* Advance. */
address = mem_info.base_address + mem_info.size;
}
}
Result DecideAddressSpaceLayout(ProcessInfo *out, svc::CreateProcessParameter *out_param, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument) {
/* Clear output. */
out->args_address = 0;
@@ -517,25 +489,7 @@ namespace ams::ldr {
R_SUCCEED();
}
Result CreateProcessImpl(ProcessInfo *out, const Meta *meta, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) {
/* Get CreateProcessParameter. */
svc::CreateProcessParameter param;
R_TRY(GetCreateProcessParameter(std::addressof(param), meta, flags, resource_limit));
/* Decide on an NSO layout. */
R_TRY(DecideAddressSpaceLayout(out, std::addressof(param), nso_headers, has_nso, argument));
/* Actually create process. */
svc::Handle process_handle;
R_TRY(svc::CreateProcess(std::addressof(process_handle), std::addressof(param), static_cast<const u32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(u32)));
/* Set the output handle. */
out->process_handle = process_handle;
R_SUCCEED();
}
Result LoadNsoSegment(fs::FileHandle file, const NsoHeader::SegmentInfo *segment, size_t file_size, const u8 *file_hash, bool is_compressed, bool check_hash, uintptr_t map_base, uintptr_t map_end) {
Result LoadAutoLoadModuleSegment(fs::FileHandle file, const NsoHeader::SegmentInfo *segment, size_t file_size, const u8 *file_hash, bool is_compressed, bool check_hash, uintptr_t map_base, uintptr_t map_end) {
/* Select read size based on compression. */
if (!is_compressed) {
file_size = segment->size;
@@ -568,25 +522,29 @@ namespace ams::ldr {
R_SUCCEED();
}
Result LoadAutoLoadModule(os::NativeHandle process_handle, fs::FileHandle file, uintptr_t map_address, const NsoHeader *nso_header, uintptr_t nso_address, size_t nso_size) {
Result LoadAutoLoadModule(os::NativeHandle process_handle, fs::FileHandle file, const NsoHeader *nso_header, uintptr_t nso_address, size_t nso_size) {
/* Map and read data from file. */
{
AutoCloseMap map(map_address, process_handle, nso_address, nso_size);
R_TRY(map.GetResult());
/* Map the process memory. */
void *mapped_memory = nullptr;
R_TRY(os::MapProcessMemory(std::addressof(mapped_memory), process_handle, nso_address, nso_size));
ON_SCOPE_EXIT { os::UnmapProcessMemory(mapped_memory, process_handle, nso_address, nso_size); };
const uintptr_t map_address = reinterpret_cast<uintptr_t>(mapped_memory);
/* Load NSO segments. */
R_TRY(LoadNsoSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Text]), nso_header->text_compressed_size, nso_header->text_hash, (nso_header->flags & NsoHeader::Flag_CompressedText) != 0,
(nso_header->flags & NsoHeader::Flag_CheckHashText) != 0, map_address + nso_header->text_dst_offset, map_address + nso_size));
R_TRY(LoadNsoSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Ro]), nso_header->ro_compressed_size, nso_header->ro_hash, (nso_header->flags & NsoHeader::Flag_CompressedRo) != 0,
(nso_header->flags & NsoHeader::Flag_CheckHashRo) != 0, map_address + nso_header->ro_dst_offset, map_address + nso_size));
R_TRY(LoadNsoSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Rw]), nso_header->rw_compressed_size, nso_header->rw_hash, (nso_header->flags & NsoHeader::Flag_CompressedRw) != 0,
(nso_header->flags & NsoHeader::Flag_CheckHashRw) != 0, map_address + nso_header->rw_dst_offset, map_address + nso_size));
R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Text]), nso_header->text_compressed_size, nso_header->text_hash, (nso_header->flags & NsoHeader::Flag_CompressedText) != 0,
(nso_header->flags & NsoHeader::Flag_CheckHashText) != 0, map_address + nso_header->text_dst_offset, map_address + nso_size));
R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Ro]), nso_header->ro_compressed_size, nso_header->ro_hash, (nso_header->flags & NsoHeader::Flag_CompressedRo) != 0,
(nso_header->flags & NsoHeader::Flag_CheckHashRo) != 0, map_address + nso_header->ro_dst_offset, map_address + nso_size));
R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Rw]), nso_header->rw_compressed_size, nso_header->rw_hash, (nso_header->flags & NsoHeader::Flag_CompressedRw) != 0,
(nso_header->flags & NsoHeader::Flag_CheckHashRw) != 0, map_address + nso_header->rw_dst_offset, map_address + nso_size));
/* Clear unused space to zero. */
const size_t text_end = nso_header->text_dst_offset + nso_header->text_size;
const size_t ro_end = nso_header->ro_dst_offset + nso_header->ro_size;
const size_t rw_end = nso_header->rw_dst_offset + nso_header->rw_size;
std::memset(reinterpret_cast<void *>(map_address), 0, nso_header->text_dst_offset);
std::memset(reinterpret_cast<void *>(map_address + 0), 0, nso_header->text_dst_offset);
std::memset(reinterpret_cast<void *>(map_address + text_end), 0, nso_header->ro_dst_offset - text_end);
std::memset(reinterpret_cast<void *>(map_address + ro_end), 0, nso_header->rw_dst_offset - ro_end);
std::memset(reinterpret_cast<void *>(map_address + rw_end), 0, nso_header->bss_size);
@@ -603,13 +561,13 @@ namespace ams::ldr {
const size_t ro_size = util::AlignUp(nso_header->ro_size, os::MemoryPageSize);
const size_t rw_size = util::AlignUp(nso_header->rw_size + nso_header->bss_size, os::MemoryPageSize);
if (text_size) {
R_TRY(svc::SetProcessMemoryPermission(process_handle, nso_address + nso_header->text_dst_offset, text_size, svc::MemoryPermission_ReadExecute));
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->text_dst_offset, text_size, os::MemoryPermission_ReadExecute));
}
if (ro_size) {
R_TRY(svc::SetProcessMemoryPermission(process_handle, nso_address + nso_header->ro_dst_offset, ro_size, svc::MemoryPermission_Read));
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->ro_dst_offset, ro_size, os::MemoryPermission_ReadOnly));
}
if (rw_size) {
R_TRY(svc::SetProcessMemoryPermission(process_handle, nso_address + nso_header->rw_dst_offset, rw_size, svc::MemoryPermission_ReadWrite));
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->rw_dst_offset, rw_size, os::MemoryPermission_ReadWrite));
}
R_SUCCEED();
@@ -623,10 +581,7 @@ namespace ams::ldr {
R_TRY(fs::OpenFile(std::addressof(file), GetNsoPath(i), fs::OpenMode_Read));
ON_SCOPE_EXIT { fs::CloseFile(file); };
uintptr_t map_address;
R_TRY(SearchFreeRegion(std::addressof(map_address), process_info->nso_size[i]));
R_TRY(LoadAutoLoadModule(process_info->process_handle, file, map_address, nso_headers + i, process_info->nso_address[i], process_info->nso_size[i]));
R_TRY(LoadAutoLoadModule(process_info->process_handle, file, nso_headers + i, process_info->nso_address[i], process_info->nso_size[i]));
}
}
@@ -634,13 +589,11 @@ namespace ams::ldr {
if (argument != nullptr) {
/* Write argument data into memory. */
{
uintptr_t map_address;
R_TRY(SearchFreeRegion(std::addressof(map_address), process_info->args_size));
void *map_address = nullptr;
R_TRY(os::MapProcessMemory(std::addressof(map_address), process_info->process_handle, process_info->args_address, process_info->args_size));
ON_SCOPE_EXIT { os::UnmapProcessMemory(map_address, process_info->process_handle, process_info->args_address, process_info->args_size); };
AutoCloseMap map(map_address, process_info->process_handle, process_info->args_address, process_info->args_size);
R_TRY(map.GetResult());
ProgramArguments *args = reinterpret_cast<ProgramArguments *>(map_address);
ProgramArguments *args = static_cast<ProgramArguments *>(map_address);
std::memset(args, 0, sizeof(*args));
args->allocated_size = process_info->args_size;
args->arguments_size = argument->argument_size;
@@ -648,12 +601,33 @@ namespace ams::ldr {
}
/* Set argument region permissions. */
R_TRY(svc::SetProcessMemoryPermission(process_info->process_handle, process_info->args_address, process_info->args_size, svc::MemoryPermission_ReadWrite));
/* NOTE: Nintendo uses svc::SetProcessMemoryPermission directly here. */
R_TRY(os::SetProcessMemoryPermission(process_info->process_handle, process_info->args_address, process_info->args_size, os::MemoryPermission_ReadWrite));
}
R_SUCCEED();
}
Result CreateProcessAndLoadAutoLoadModules(ProcessInfo *out, const Meta *meta, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) {
/* Get CreateProcessParameter. */
svc::CreateProcessParameter param;
R_TRY(GetCreateProcessParameter(std::addressof(param), meta, flags, resource_limit));
/* Decide on an NSO layout. */
R_TRY(DecideAddressSpaceLayout(out, std::addressof(param), nso_headers, has_nso, argument));
/* Actually create process. */
svc::Handle process_handle;
R_TRY(svc::CreateProcess(std::addressof(process_handle), std::addressof(param), static_cast<const u32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(u32)));
/* Set the output handle, and ensure that if we fail after this point we clean it up. */
out->process_handle = process_handle;
ON_RESULT_FAILURE { svc::CloseHandle(process_handle); };
/* Load all auto load modules. */
R_RETURN(LoadAutoLoadModules(out, nso_headers, has_nso, argument));
}
}
/* Process Creation API. */
@@ -670,22 +644,13 @@ namespace ams::ldr {
/* Validate meta. */
R_TRY(ValidateMeta(std::addressof(meta), loc, mount.GetCodeVerificationData()));
/* Load, validate NSOs. */
R_TRY(LoadNsoHeaders(g_nso_headers, g_has_nso));
R_TRY(ValidateNsoHeaders(g_nso_headers, g_has_nso));
/* Load, validate NSO headers. */
R_TRY(LoadAutoLoadHeaders(g_nso_headers, g_has_nso));
R_TRY(CheckAutoLoad(g_nso_headers, g_has_nso));
/* Actually create process. */
/* Actually create the process and load NSOs into process memory. */
ProcessInfo info;
R_TRY(CreateProcessImpl(std::addressof(info), std::addressof(meta), g_nso_headers, g_has_nso, argument, flags, resource_limit));
/* Load NSOs into process memory. */
{
/* Ensure we close the process handle, if we fail. */
ON_RESULT_FAILURE { os::CloseNativeHandle(info.process_handle); };
/* Load all NSOs. */
R_TRY(LoadAutoLoadModules(std::addressof(info), g_nso_headers, g_has_nso, argument));
}
R_TRY(CreateProcessAndLoadAutoLoadModules(std::addressof(info), std::addressof(meta), g_nso_headers, g_has_nso, argument, flags, resource_limit));
/* Register NSOs with the RoManager. */
{

View File

@@ -1,83 +0,0 @@
/*
* 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 "ro_map_utils.hpp"
namespace ams::ro::impl {
namespace {
ALWAYS_INLINE u64 GetCurrentProcessInfo(svc::InfoType info_type) {
u64 value;
R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), info_type, svc::PseudoHandle::CurrentProcess, 0));
return value;
}
}
Result SearchFreeRegion(uintptr_t *out, size_t mapping_size) {
/* Get address space extents. */
const uintptr_t heap_start = GetCurrentProcessInfo(svc::InfoType_HeapRegionAddress);
const size_t heap_size = GetCurrentProcessInfo(svc::InfoType_HeapRegionSize);
const uintptr_t alias_start = GetCurrentProcessInfo(svc::InfoType_AliasRegionAddress);
const size_t alias_size = GetCurrentProcessInfo(svc::InfoType_AliasRegionSize);
const uintptr_t aslr_start = GetCurrentProcessInfo(svc::InfoType_AslrRegionAddress);
const size_t aslr_size = GetCurrentProcessInfo(svc::InfoType_AslrRegionSize);
/* Iterate upwards to find a free region. */
uintptr_t address = aslr_start;
while (true) {
/* Declare variables for memory querying. */
svc::MemoryInfo mem_info;
svc::PageInfo page_info;
/* Check that we're still within bounds. */
R_UNLESS(address < address + mapping_size, ro::ResultOutOfAddressSpace());
/* If we're within the heap region, skip to the end of the heap region. */
if (heap_size != 0 && !(address + mapping_size - 1 < heap_start || heap_start + heap_size - 1 < address)) {
R_UNLESS(address < heap_start + heap_size, ro::ResultOutOfAddressSpace());
address = heap_start + heap_size;
continue;
}
/* If we're within the alias region, skip to the end of the alias region. */
if (alias_size != 0 && !(address + mapping_size - 1 < alias_start || alias_start + alias_size - 1 < address)) {
R_UNLESS(address < alias_start + alias_size, ro::ResultOutOfAddressSpace());
address = alias_start + alias_size;
continue;
}
/* Get the current memory range. */
R_ABORT_UNLESS(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), address));
/* If the memory range is free and big enough, use it. */
if (mem_info.state == svc::MemoryState_Free && mapping_size <= ((mem_info.base_address + mem_info.size) - address)) {
*out = address;
R_SUCCEED();
}
/* Check that we can advance. */
R_UNLESS(address < mem_info.base_address + mem_info.size, ro::ResultOutOfAddressSpace());
R_UNLESS(mem_info.base_address + mem_info.size - 1 < aslr_start + aslr_size - 1, ro::ResultOutOfAddressSpace());
/* Advance. */
address = mem_info.base_address + mem_info.size;
}
}
}

View File

@@ -1,177 +0,0 @@
/*
* 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::ro::impl {
constexpr inline auto RetrySearchCount = 512;
Result SearchFreeRegion(uintptr_t *out, size_t mapping_size);
class ProcessRegionInfo {
NON_COPYABLE(ProcessRegionInfo);
NON_MOVEABLE(ProcessRegionInfo);
private:
static constexpr size_t StackGuardSize = 4 * os::MemoryPageSize;
private:
u64 m_heap_start;
u64 m_heap_size;
u64 m_alias_start;
u64 m_alias_size;
u64 m_aslr_start;
u64 m_aslr_size;
public:
ProcessRegionInfo(os::NativeHandle process) {
R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_heap_start), svc::InfoType_HeapRegionAddress, process, 0));
R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_heap_size), svc::InfoType_HeapRegionSize, process, 0));
R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_alias_start), svc::InfoType_AliasRegionAddress, process, 0));
R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_alias_size), svc::InfoType_AliasRegionSize, process, 0));
R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_aslr_start), svc::InfoType_AslrRegionAddress, process, 0));
R_ABORT_UNLESS(svc::GetInfo(std::addressof(m_aslr_size), svc::InfoType_AslrRegionSize, process, 0));
}
u64 GetAslrRegion(u64 mapping_size) const {
/* If we can, look for a region. */
if (mapping_size <= m_aslr_size) {
for (auto i = 0; i < RetrySearchCount; ++i) {
/* Get a random address. */
const u64 address = m_aslr_start + os::GenerateRandomU64((m_aslr_size - mapping_size) / os::MemoryPageSize) * os::MemoryPageSize;
/* Check that it's not contained within heap. */
if (m_heap_size != 0 && !(address + mapping_size - 1 < m_heap_start || m_heap_start + m_heap_size - 1 < address)) {
continue;
}
/* Check that it's not contained within alias. */
if (m_alias_size != 0 && !(address + mapping_size - 1 < m_alias_start || m_alias_start + m_alias_size - 1 < address)) {
continue;
}
/* Return the address. */
return address;
}
}
/* We failed to find a region. */
return 0;
}
bool CanEmplaceGuardSpaces(os::NativeHandle process, u64 address, u64 size) {
/* NOTE: Nintendo does not check the results of these svc calls. */
svc::MemoryInfo mem_info;
svc::PageInfo page_info;
/* Check for guard availability before the region. */
R_ABORT_UNLESS(svc::QueryProcessMemory(std::addressof(mem_info), std::addressof(page_info), process, address - 1));
if (!(mem_info.state == svc::MemoryState_Free && mem_info.base_address <= address - StackGuardSize)) {
return false;
}
/* Check for guard availability after the region. */
R_ABORT_UNLESS(svc::QueryProcessMemory(std::addressof(mem_info), std::addressof(page_info), process, address + size));
if (!(mem_info.state == svc::MemoryState_Free && address + size + StackGuardSize <= mem_info.base_address + mem_info.size)) {
return false;
}
return true;
}
};
class AutoCloseMap {
private:
Result m_result;
uintptr_t m_map_address;
os::NativeHandle m_handle;
u64 m_address;
u64 m_size;
public:
AutoCloseMap(uintptr_t map, os::NativeHandle handle, u64 addr, u64 size) : m_map_address(map), m_handle(handle), m_address(addr), m_size(size) {
m_result = svc::MapProcessMemory(m_map_address, m_handle, m_address, m_size);
}
~AutoCloseMap() {
if (m_handle != os::InvalidNativeHandle && R_SUCCEEDED(m_result)) {
R_ABORT_UNLESS(svc::UnmapProcessMemory(m_map_address, m_handle, m_address, m_size));
}
}
Result GetResult() const {
return m_result;
}
bool IsSuccess() const {
return R_SUCCEEDED(m_result);
}
void Cancel() {
m_handle = os::InvalidNativeHandle;
}
};
class MappedCodeMemory {
NON_COPYABLE(MappedCodeMemory);
private:
os::NativeHandle m_handle;
Result m_result;
u64 m_dst_address;
u64 m_src_address;
u64 m_size;
public:
constexpr MappedCodeMemory() : m_handle(os::InvalidNativeHandle), m_result(ro::ResultInternalError()), m_dst_address(0), m_src_address(0), m_size(0) {
/* ... */
}
MappedCodeMemory(os::NativeHandle handle, u64 dst, u64 src, u64 size) : m_handle(handle), m_dst_address(dst), m_src_address(src), m_size(size) {
m_result = svc::MapProcessCodeMemory(m_handle, m_dst_address, m_src_address, m_size);
}
~MappedCodeMemory() {
if (m_handle != os::InvalidNativeHandle && R_SUCCEEDED(m_result) && m_size > 0) {
R_ABORT_UNLESS(svc::UnmapProcessCodeMemory(m_handle, m_dst_address, m_src_address, m_size));
}
}
MappedCodeMemory(MappedCodeMemory &&rhs) : m_handle(rhs.m_handle), m_result(rhs.m_result), m_dst_address(rhs.m_dst_address), m_src_address(rhs.m_src_address), m_size(rhs.m_size) {
rhs.m_handle = os::InvalidNativeHandle;
}
MappedCodeMemory &operator=(MappedCodeMemory &&rhs) {
m_handle = rhs.m_handle;
m_result = rhs.m_result;
m_dst_address = rhs.m_dst_address;
m_src_address = rhs.m_src_address;
m_size = rhs.m_size;
rhs.m_handle = os::InvalidNativeHandle;
return *this;
}
Result GetResult() const {
return m_result;
}
bool IsSuccess() const {
return R_SUCCEEDED(m_result);
}
void Cancel() {
m_handle = os::InvalidNativeHandle;
}
};
}

View File

@@ -15,65 +15,38 @@
*/
#include <stratosphere.hpp>
#include "ro_nro_utils.hpp"
#include "ro_map_utils.hpp"
namespace ams::ro::impl {
Result MapNro(u64 *out_base_address, os::NativeHandle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size) {
/* Re-map the NRO/BSS as code memory in the destination process. */
MappedCodeMemory nro_mcm;
MappedCodeMemory bss_mcm;
ProcessRegionInfo region_info(process_handle);
u64 base_address;
{
const u64 memory_size = nro_heap_size + bss_heap_size;
int i;
for (i = 0; i < RetrySearchCount; ++i) {
/* Get a random address for the nro. */
base_address = region_info.GetAslrRegion(memory_size);
R_UNLESS(base_address != 0, ro::ResultOutOfAddressSpace());
namespace {
/* Map the NRO, retrying if random address was invalid. */
MappedCodeMemory tmp_nro_mcm(process_handle, base_address, nro_heap_address, nro_heap_size);
R_TRY_CATCH(tmp_nro_mcm.GetResult()) {
R_CATCH(svc::ResultInvalidCurrentMemory) { continue; }
} R_END_TRY_CATCH;
ALWAYS_INLINE size_t SetupNroProcessMemoryRegions(os::ProcessMemoryRegion *regions, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size) {
/* Reset region count. */
size_t num_regions = 0;
/* Handle bss. */
if (bss_heap_size > 0) {
/* Map BSS, retrying if random address was invalid. */
MappedCodeMemory tmp_bss_mcm(process_handle, base_address + nro_heap_size, bss_heap_address, bss_heap_size);
R_TRY_CATCH(tmp_bss_mcm.GetResult()) {
R_CATCH(svc::ResultInvalidCurrentMemory) { continue; }
} R_END_TRY_CATCH;
/* We always want a region for the nro. */
regions[num_regions++] = { nro_heap_address, nro_heap_size };
/* Check that we can have guard spaces. */
if (!region_info.CanEmplaceGuardSpaces(process_handle, base_address, memory_size)) {
continue;
}
/* We succeeded, so save the bss memory. */
bss_mcm = std::move(tmp_bss_mcm);
} else {
/* Check that we can have guard spaces. */
if (!region_info.CanEmplaceGuardSpaces(process_handle, base_address, memory_size)) {
continue;
}
}
/* We succeeded, so save the code memory. */
nro_mcm = std::move(tmp_nro_mcm);
break;
/* If we have bss, create a region for bss. */
if (bss_heap_size > 0) {
regions[num_regions++] = { bss_heap_address, bss_heap_size };
}
R_UNLESS(i != RetrySearchCount, ro::ResultOutOfAddressSpace());
return num_regions;
}
/* Cancel the automatic closing of our mappings. */
nro_mcm.Cancel();
bss_mcm.Cancel();
}
Result MapNro(u64 *out_base_address, os::NativeHandle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size) {
/* Set up the process memory regions. */
os::ProcessMemoryRegion regions[2];
const size_t num_regions = SetupNroProcessMemoryRegions(regions, nro_heap_address, nro_heap_size, bss_heap_address, bss_heap_size);
/* Re-map the nro/bss as code memory in the destination process. */
R_TRY_CATCH(os::MapProcessCodeMemory(out_base_address, process_handle, regions, num_regions)) {
R_CONVERT(os::ResultOutOfAddressSpace, ro::ResultOutOfAddressSpace())
} R_END_TRY_CATCH;
*out_base_address = base_address;
R_SUCCEED();
}
@@ -82,28 +55,20 @@ namespace ams::ro::impl {
const u64 ro_offset = rx_offset + rx_size;
const u64 rw_offset = ro_offset + ro_size;
R_TRY(svc::SetProcessMemoryPermission(process_handle, base_address + rx_offset, rx_size, svc::MemoryPermission_ReadExecute));
R_TRY(svc::SetProcessMemoryPermission(process_handle, base_address + ro_offset, ro_size, svc::MemoryPermission_Read));
R_TRY(svc::SetProcessMemoryPermission(process_handle, base_address + rw_offset, rw_size, svc::MemoryPermission_ReadWrite));
R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + rx_offset, rx_size, os::MemoryPermission_ReadExecute));
R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + ro_offset, ro_size, os::MemoryPermission_ReadOnly));
R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + rw_offset, rw_size, os::MemoryPermission_ReadWrite));
R_SUCCEED();
}
Result UnmapNro(os::NativeHandle process_handle, u64 base_address, u64 nro_heap_address, u64 bss_heap_address, u64 bss_heap_size, u64 code_size, u64 rw_size) {
/* First, unmap bss. */
if (bss_heap_size > 0) {
R_TRY(svc::UnmapProcessCodeMemory(process_handle, base_address + code_size + rw_size, bss_heap_address, bss_heap_size));
}
Result UnmapNro(os::NativeHandle process_handle, u64 base_address, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size) {
/* Set up the process memory regions. */
os::ProcessMemoryRegion regions[2];
const size_t num_regions = SetupNroProcessMemoryRegions(regions, nro_heap_address, nro_heap_size, bss_heap_address, bss_heap_size);
/* Next, unmap .rwdata */
if (rw_size > 0) {
R_TRY(svc::UnmapProcessCodeMemory(process_handle, base_address + code_size, nro_heap_address + code_size, rw_size));
}
/* Finally, unmap .text + .rodata. */
R_TRY(svc::UnmapProcessCodeMemory(process_handle, base_address, nro_heap_address, code_size));
R_SUCCEED();
/* Unmap the nro/bss. */
R_RETURN(os::UnmapProcessCodeMemory(process_handle, base_address, regions, num_regions));
}
}

View File

@@ -22,6 +22,6 @@ namespace ams::ro::impl {
/* Utilities for working with NROs. */
Result MapNro(u64 *out_base_address, os::NativeHandle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size);
Result SetNroPerms(os::NativeHandle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size);
Result UnmapNro(os::NativeHandle process_handle, u64 base_address, u64 nro_heap_address, u64 bss_heap_address, u64 bss_heap_size, u64 code_size, u64 rw_size);
Result UnmapNro(os::NativeHandle process_handle, u64 base_address, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size);
}

View File

@@ -15,7 +15,6 @@
*/
#include <stratosphere.hpp>
#include "ro_nrr_utils.hpp"
#include "ro_map_utils.hpp"
#include "ro_service_impl.hpp"
namespace ams::ro::impl {
@@ -196,51 +195,29 @@ namespace ams::ro::impl {
/* Utilities for working with NRRs. */
Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, void *out_hash, size_t out_hash_size, os::NativeHandle process_handle, ncm::ProgramId program_id, u64 nrr_heap_address, u64 nrr_heap_size, NrrKind nrr_kind, bool enforce_nrr_kind) {
/* Re-map the NRR as code memory in the destination process. */
MappedCodeMemory nrr_mcm;
ProcessRegionInfo region_info(process_handle);
u64 code_address;
{
int i;
for (i = 0; i < RetrySearchCount; ++i) {
/* Get a random address for the nrr. */
code_address = region_info.GetAslrRegion(nrr_heap_size);
R_UNLESS(code_address != 0, ro::ResultOutOfAddressSpace());
/* Re-map the nrr as code memory in the destination process. */
u64 code_address = 0;
const os::ProcessMemoryRegion region = { nrr_heap_address, nrr_heap_size };
R_TRY_CATCH(os::MapProcessCodeMemory(std::addressof(code_address), process_handle, std::addressof(region), 1)) {
R_CONVERT(os::ResultOutOfAddressSpace, ro::ResultOutOfAddressSpace())
} R_END_TRY_CATCH;
/* Map the code memory, retrying if the random address was invalid. */
MappedCodeMemory tmp_mcm(process_handle, code_address, nrr_heap_address, nrr_heap_size);
R_TRY_CATCH(tmp_mcm.GetResult()) {
R_CATCH(svc::ResultInvalidCurrentMemory) { continue; }
} R_END_TRY_CATCH;
/* If we fail, unmap the nrr code memory. */
ON_RESULT_FAILURE { R_ABORT_UNLESS(os::UnmapProcessCodeMemory(process_handle, code_address, std::addressof(region), 1)); };
/* Check that we can have guard spaces. */
if (!region_info.CanEmplaceGuardSpaces(process_handle, code_address, nrr_heap_size)) {
continue;
}
/* Map the nrr in our process. */
void *mapped_memory = nullptr;
R_TRY_CATCH(os::MapProcessMemory(std::addressof(mapped_memory), process_handle, code_address, region.size)) {
R_CONVERT(os::ResultOutOfAddressSpace, ro::ResultOutOfAddressSpace())
} R_END_TRY_CATCH;
/* We succeeded, so save the code memory. */
nrr_mcm = std::move(tmp_mcm);
break;
}
/* If we fail, unmap the nrr memory. */
ON_RESULT_FAILURE_2 { os::UnmapProcessMemory(mapped_memory, process_handle, code_address, region.size); };
R_UNLESS(i != RetrySearchCount, ro::ResultOutOfAddressSpace());
}
/* Decide where to map the NRR in our process. */
uintptr_t map_address;
R_UNLESS(R_SUCCEEDED(SearchFreeRegion(std::addressof(map_address), nrr_heap_size)), ro::ResultOutOfAddressSpace());
/* NOTE: Nintendo does not check the return value of this map. We will check, instead of aborting if it fails. */
AutoCloseMap nrr_map(map_address, process_handle, code_address, nrr_heap_size);
R_TRY(nrr_map.GetResult());
NrrHeader *nrr_header = reinterpret_cast<NrrHeader *>(map_address);
/* Validate the nrr header. */
NrrHeader *nrr_header = static_cast<NrrHeader *>(mapped_memory);
R_TRY(ValidateNrr(nrr_header, nrr_heap_size, program_id, nrr_kind, enforce_nrr_kind));
/* Cancel the automatic closing of our mappings. */
nrr_map.Cancel();
nrr_mcm.Cancel();
/* Save a copy of the hash that we verified. */
crypto::GenerateSha256(out_hash, out_hash_size, nrr_header->GetSignedArea(), nrr_header->GetSignedAreaSize());
@@ -250,16 +227,18 @@ namespace ams::ro::impl {
}
Result UnmapNrr(os::NativeHandle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address) {
R_TRY(svc::UnmapProcessMemory(reinterpret_cast<uintptr_t>(header), process_handle, mapped_code_address, nrr_heap_size));
R_TRY(svc::UnmapProcessCodeMemory(process_handle, mapped_code_address, nrr_heap_address, nrr_heap_size));
R_SUCCEED();
/* Unmap our process mapping. */
os::UnmapProcessMemory(const_cast<NrrHeader *>(header), process_handle, mapped_code_address, nrr_heap_size);
/* Unmap the code memory mapping. */
const os::ProcessMemoryRegion region = { nrr_heap_address, nrr_heap_size };
R_RETURN(os::UnmapProcessCodeMemory(process_handle, mapped_code_address, std::addressof(region), 1));
}
bool ValidateNrrHashTableEntry(const void *signed_area, size_t signed_area_size, size_t hashes_offset, size_t num_hashes, const void *nrr_hash, const u8 *hash_table, const void *desired_hash) {
crypto::Sha256Generator sha256;
sha256.Initialize();
/* Hash data before the hash table. */
const size_t pre_hash_table_size = hashes_offset - NrrHeader::GetSignedAreaOffset();
sha256.Update(signed_area, pre_hash_table_size);

View File

@@ -14,7 +14,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "ro_map_utils.hpp"
#include "ro_nrr_utils.hpp"
#include "ro_nro_utils.hpp"
#include "ro_patcher.hpp"
@@ -248,16 +247,17 @@ namespace ams::ro::impl {
}
Result ValidateNro(ModuleId *out_module_id, u64 *out_rx_size, u64 *out_ro_size, u64 *out_rw_size, u64 base_address, u64 expected_nro_size, u64 expected_bss_size) {
/* Find space to map the NRO. */
uintptr_t map_address;
R_UNLESS(R_SUCCEEDED(SearchFreeRegion(std::addressof(map_address), expected_nro_size)), ro::ResultOutOfAddressSpace());
/* Map the NRO. */
void *mapped_memory = nullptr;
R_TRY_CATCH(os::MapProcessMemory(std::addressof(mapped_memory), m_process_handle, base_address, expected_nro_size)) {
R_CONVERT(os::ResultOutOfAddressSpace, ro::ResultOutOfAddressSpace())
} R_END_TRY_CATCH;
/* Actually map the NRO. */
AutoCloseMap nro_map(map_address, m_process_handle, base_address, expected_nro_size);
R_TRY(nro_map.GetResult());
/* When we're done, unmap the memory. */
ON_SCOPE_EXIT { os::UnmapProcessMemory(mapped_memory, m_process_handle, base_address, expected_nro_size); };
/* Validate header. */
const NroHeader *header = reinterpret_cast<const NroHeader *>(map_address);
const NroHeader *header = static_cast<const NroHeader *>(mapped_memory);
R_UNLESS(header->IsMagicValid(), ro::ResultInvalidNro());
/* Read sizes from header. */
@@ -298,7 +298,7 @@ namespace ams::ro::impl {
R_UNLESS(R_FAILED(this->GetNroInfoByModuleId(nullptr, module_id)), ro::ResultAlreadyLoaded());
/* Apply patches to NRO. */
LocateAndApplyIpsPatchesToModule(module_id, reinterpret_cast<u8 *>(map_address), nro_size);
LocateAndApplyIpsPatchesToModule(module_id, static_cast<u8 *>(mapped_memory), nro_size);
/* Copy to output. */
*out_module_id = *module_id;
@@ -552,21 +552,14 @@ namespace ams::ro::impl {
/* Map the NRO. */
R_TRY(MapNro(std::addressof(nro_info->base_address), context->GetProcessHandle(), nro_address, nro_size, bss_address, bss_size));
ON_RESULT_FAILURE { UnmapNro(context->GetProcessHandle(), nro_info->base_address, nro_address, nro_size, bss_address, bss_size); };
/* Validate the NRO (parsing region extents). */
u64 rx_size = 0, ro_size = 0, rw_size = 0;
{
ON_RESULT_FAILURE { UnmapNro(context->GetProcessHandle(), nro_info->base_address, nro_address, bss_address, bss_size, nro_size, 0); };
R_TRY(context->ValidateNro(std::addressof(nro_info->module_id), std::addressof(rx_size), std::addressof(ro_size), std::addressof(rw_size), nro_info->base_address, nro_size, bss_size));
}
R_TRY(context->ValidateNro(std::addressof(nro_info->module_id), std::addressof(rx_size), std::addressof(ro_size), std::addressof(rw_size), nro_info->base_address, nro_size, bss_size));
/* Set NRO perms. */
{
ON_RESULT_FAILURE { UnmapNro(context->GetProcessHandle(), nro_info->base_address, nro_address, bss_address, bss_size, rx_size + ro_size, rw_size); };
R_TRY(SetNroPerms(context->GetProcessHandle(), nro_info->base_address, rx_size, ro_size, rw_size + bss_size));
}
R_TRY(SetNroPerms(context->GetProcessHandle(), nro_info->base_address, rx_size, ro_size, rw_size + bss_size));
context->SetNroInfoInUse(nro_info, true);
nro_info->code_size = rx_size + ro_size;
@@ -594,7 +587,7 @@ namespace ams::ro::impl {
context->SetNroInfoInUse(nro_info, false);
std::memset(nro_info, 0, sizeof(*nro_info));
}
R_RETURN(UnmapNro(context->GetProcessHandle(), nro_backup.base_address, nro_backup.nro_heap_address, nro_backup.bss_heap_address, nro_backup.bss_heap_size, nro_backup.code_size, nro_backup.rw_size));
R_RETURN(UnmapNro(context->GetProcessHandle(), nro_backup.base_address, nro_backup.nro_heap_address, nro_backup.code_size + nro_backup.rw_size, nro_backup.bss_heap_address, nro_backup.bss_heap_size));
}
/* Debug service implementations. */