Compare commits

...

17 Commits
mem ... nim

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

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

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

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

* fs: merge FileHandleStorage into file_storage TU

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

* fssystem: PartitionFileSystemMetaCore cleanup

* fs: add IFile::DryWrite, update results

* fssystem: implement PartitionFileSystemCore

* fssystem: cleanup PartitionFileSystemCore

* fssystem: implement Sha256PartitionFileSystem

Co-authored-by: Michael Scire <SciresM@gmail.com>
2020-03-27 03:40:52 -07:00
Michael Scire
0af2758fde fs.mitm: use new namespace types for saves 2020-03-24 17:50:36 -07:00
99 changed files with 7168 additions and 285 deletions

View File

@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
branch = master
commit = 30f3e4c33d2767126c36a51b9259051c6c42d6b5
parent = 82eab9c8d0093e4d8907a668e9335981f6f4949b
commit = a40d4593d7e46ea296802e5c50fd0da2d9f92a76
parent = 6de1361c8be886eda16faf87c73b9e4d01187bca
method = merge
cmdver = 0.4.1

View File

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

View File

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

View File

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

View File

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

View File

@@ -41,6 +41,7 @@
#include <stratosphere/fs/fs_code.hpp>
#include <stratosphere/fs/fs_content.hpp>
#include <stratosphere/fs/fs_content_storage.hpp>
#include <stratosphere/fs/fs_image_directory.hpp>
#include <stratosphere/fs/fs_game_card.hpp>
#include <stratosphere/fs/fs_save_data_types.hpp>
#include <stratosphere/fs/fs_save_data_management.hpp>

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) 2018-2020 Adubbz, Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "fssystem_partition_file_system_meta.hpp"
#include "../fs/fsa/fs_ifile.hpp"
#include "../fs/fsa/fs_idirectory.hpp"
#include "../fs/fsa/fs_ifilesystem.hpp"
namespace ams::fssystem {
template<typename MetaType>
class PartitionFileSystemCore : public fs::impl::Newable, public fs::fsa::IFileSystem {
NON_COPYABLE(PartitionFileSystemCore);
NON_MOVEABLE(PartitionFileSystemCore);
private:
class PartitionFile;
class PartitionDirectory;
private:
fs::IStorage *base_storage;
MetaType *meta_data;
bool initialized;
size_t meta_data_size;
std::unique_ptr<MetaType> unique_meta_data;
std::shared_ptr<fs::IStorage> shared_storage;
private:
Result Initialize(fs::IStorage *base_storage, MemoryResource *allocator);
public:
PartitionFileSystemCore();
virtual ~PartitionFileSystemCore() override;
Result Initialize(std::unique_ptr<MetaType> &&meta_data, std::shared_ptr<fs::IStorage> base_storage);
Result Initialize(MetaType *meta_data, std::shared_ptr<fs::IStorage> base_storage);
Result Initialize(fs::IStorage *base_storage);
Result Initialize(std::shared_ptr<fs::IStorage> base_storage);
Result Initialize(std::shared_ptr<fs::IStorage> base_storage, MemoryResource *allocator);
Result GetFileBaseOffset(s64 *out_offset, const char *path);
virtual Result CreateFileImpl(const char *path, s64 size, int option) override;
virtual Result DeleteFileImpl(const char *path) override;
virtual Result CreateDirectoryImpl(const char *path) override;
virtual Result DeleteDirectoryImpl(const char *path) override;
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override;
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override;
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override;
virtual Result GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) override;
virtual Result OpenFileImpl(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) override;
virtual Result OpenDirectoryImpl(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) override;
virtual Result CommitImpl() override;
virtual Result CleanDirectoryRecursivelyImpl(const char *path) override;
/* These aren't accessible as commands. */
virtual Result CommitProvisionallyImpl(s64 counter) override;
};
using PartitionFileSystem = PartitionFileSystemCore<PartitionFileSystemMeta>;
using Sha256PartitionFileSystem = PartitionFileSystemCore<Sha256PartitionFileSystemMeta>;
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (c) 2018-2020 Adubbz, Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/fs/fs_common.hpp>
#include <stratosphere/fs/impl/fs_newable.hpp>
namespace ams::fssystem {
namespace impl {
struct PartitionFileSystemFormat {
#pragma pack(push, 1)
struct PartitionEntry {
u64 offset;
u64 size;
u32 name_offset;
u32 reserved;
};
static_assert(std::is_pod<PartitionEntry>::value);
#pragma pack(pop)
static constexpr const char VersionSignature[] = { 'P', 'F', 'S', '0' };
static constexpr size_t EntryNameLengthMax = ::ams::fs::EntryNameLengthMax;
static constexpr size_t FileDataAlignmentSize = 0x20;
using ResultSignatureVerificationFailed = fs::ResultPartitionSignatureVerificationFailed;
};
struct Sha256PartitionFileSystemFormat {
static constexpr size_t HashSize = ::ams::crypto::Sha256Generator::HashSize;
#pragma pack(push, 1)
struct PartitionEntry {
u64 offset;
u64 size;
u32 name_offset;
u32 hash_target_size;
u64 hash_target_offset;
char hash[HashSize];
};
static_assert(std::is_pod<PartitionEntry>::value);
#pragma pack(pop)
static constexpr const char VersionSignature[] = { 'H', 'F', 'S', '0' };
static constexpr size_t EntryNameLengthMax = ::ams::fs::EntryNameLengthMax;
static constexpr size_t FileDataAlignmentSize = 0x200;
using ResultSignatureVerificationFailed = fs::ResultSha256PartitionSignatureVerificationFailed;
};
}
template<typename Format>
class PartitionFileSystemMetaCore : public fs::impl::Newable {
public:
static constexpr size_t EntryNameLengthMax = Format::EntryNameLengthMax;
static constexpr size_t FileDataAlignmentSize = Format::FileDataAlignmentSize;
/* Forward declare header. */
struct PartitionFileSystemHeader;
using PartitionEntry = typename Format::PartitionEntry;
protected:
bool initialized;
PartitionFileSystemHeader *header;
PartitionEntry *entries;
char *name_table;
size_t meta_data_size;
MemoryResource *allocator;
char *buffer;
public:
PartitionFileSystemMetaCore() : initialized(false), allocator(nullptr), buffer(nullptr) { /* ... */ }
~PartitionFileSystemMetaCore();
Result Initialize(fs::IStorage *storage, MemoryResource *allocator);
Result Initialize(fs::IStorage *storage, void *header, size_t header_size);
const PartitionEntry *GetEntry(s32 index) const;
s32 GetEntryCount() const;
s32 GetEntryIndex(const char *name) const;
const char *GetEntryName(s32 index) const;
size_t GetHeaderSize() const;
size_t GetMetaDataSize() const;
public:
static Result QueryMetaDataSize(size_t *out_size, fs::IStorage *storage);
protected:
void DeallocateBuffer();
};
using PartitionFileSystemMeta = PartitionFileSystemMetaCore<impl::PartitionFileSystemFormat>;
class Sha256PartitionFileSystemMeta : public PartitionFileSystemMetaCore<impl::Sha256PartitionFileSystemFormat> {
public:
using PartitionFileSystemMetaCore<impl::Sha256PartitionFileSystemFormat>::Initialize;
Result Initialize(fs::IStorage *base_storage, MemoryResource *allocator, const void *hash, size_t hash_size, std::optional<u8> suffix = std::nullopt);
};
}

View File

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

View File

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

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/mem/mem_standard_allocator.hpp>
#include <stratosphere/mem/impl/mem_impl_heap.hpp>

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/mem/impl/mem_impl_common.hpp>
namespace ams::mem::impl::heap {
class TlsHeapCache;
class CachedHeap final {
NON_COPYABLE(CachedHeap);
private:
TlsHeapCache *tls_heap_cache;
public:
constexpr CachedHeap() : tls_heap_cache() { /* ... */ }
~CachedHeap() { this->Finalize(); }
ALWAYS_INLINE CachedHeap(CachedHeap &&rhs) : tls_heap_cache(rhs.tls_heap_cache) {
rhs.tls_heap_cache = nullptr;
}
ALWAYS_INLINE CachedHeap &operator=(CachedHeap &&rhs) {
this->Reset();
this->tls_heap_cache = rhs.tls_heap_cache;
rhs.tls_heap_cache = nullptr;
return *this;
}
void *Allocate(size_t n);
void *Allocate(size_t n, size_t align);
size_t GetAllocationSize(const void *ptr);
errno_t Free(void *p);
errno_t FreeWithSize(void *p, size_t size);
errno_t Reallocate(void *ptr, size_t size, void **p);
errno_t Shrink(void *ptr, size_t size);
void ReleaseAllCache();
void Finalize();
bool CheckCache();
errno_t QueryV(int query, std::va_list vl);
errno_t Query(int query, ...);
void Reset() { this->Finalize(); }
void Reset(TlsHeapCache *thc);
TlsHeapCache *Release();
constexpr explicit ALWAYS_INLINE operator bool() const { return this->tls_heap_cache != nullptr; }
};
}

View File

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

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <errno.h>
namespace ams::mem::impl {
constexpr inline size_t MaxSize = static_cast<size_t>(std::numeric_limits<s64>::max());
using errno_t = int;
enum DumpMode {
DumpMode_Basic = (0 << 0),
DumpMode_Spans = (1 << 0),
DumpMode_Pointers = (1 << 1),
DumpMode_Pages = (1 << 2),
DumpMode_All = (DumpMode_Pages | DumpMode_Pointers | DumpMode_Spans | DumpMode_Basic),
};
enum AllocQuery {
AllocQuery_Dump = 0,
AllocQuery_PageSize = 1,
AllocQuery_AllocatedSize = 2,
AllocQuery_FreeSize = 3,
AllocQuery_SystemSize = 4,
AllocQuery_MaxAllocatableSize = 5,
AllocQuery_IsClean = 6,
AllocQuery_HeapHash = 7,
AllocQuery_UnifyFreeList = 8,
AllocQuery_SetColor = 9,
AllocQuery_GetColor = 10,
AllocQuery_SetName = 11,
AllocQuery_GetName = 12,
/* AllocQuery_Thirteen = 13, */
AllocQuery_CheckCache = 14,
AllocQuery_ClearCache = 15,
AllocQuery_FinalizeCache = 16,
AllocQuery_FreeSizeMapped = 17,
AllocQuery_MaxAllocatableSizeMapped = 18,
AllocQuery_DumpJson = 19,
};
enum HeapOption {
HeapOption_UseEnvironment = (1 << 0),
HeapOption_DisableCache = (1 << 2),
};
struct HeapHash {
size_t alloc_count;
size_t alloc_size;
size_t hash;
};
static_assert(std::is_pod<HeapHash>::value);
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
namespace ams::mem::impl {
namespace heap {
class CentralHeap;
}
using InternalCentralHeapStorage = ::ams::util::TypedStorage<::ams::mem::impl::heap::CentralHeap, sizeof(void *) * 6, alignof(void *)>;
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/mem/impl/mem_impl_common.hpp>
#include <stratosphere/mem/impl/heap/mem_impl_heap_cached_heap.hpp>
#include <stratosphere/mem/impl/heap/mem_impl_heap_central_heap.hpp>

View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 2019-2020 Adubbz, Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/os.hpp>
#include <stratosphere/mem/impl/mem_impl_declarations.hpp>
namespace ams::mem {
class StandardAllocator {
NON_COPYABLE(StandardAllocator);
NON_MOVEABLE(StandardAllocator);
public:
using WalkCallback = int (*)(void *ptr, size_t size, void *user_data);
struct AllocatorHash {
size_t allocated_count;
size_t allocated_size;
size_t hash;
};
private:
bool initialized;
bool enable_thread_cache;
uintptr_t unused;
os::TlsSlot tls_slot;
impl::InternalCentralHeapStorage central_heap_storage;
public:
StandardAllocator();
StandardAllocator(void *mem, size_t size);
StandardAllocator(void *mem, size_t size, bool enable_cache);
~StandardAllocator() {
if (this->initialized) {
this->Finalize();
}
}
void Initialize(void *mem, size_t size);
void Initialize(void *mem, size_t size, bool enable_cache);
void Finalize();
void *Allocate(size_t size);
void *Allocate(size_t size, size_t alignment);
void Free(void *ptr);
void *Reallocate(void *ptr, size_t new_size);
size_t Shrink(void *ptr, size_t new_size);
void ClearThreadCache() const;
void CleanUpManagementArea() const;
size_t GetSizeOf(const void *ptr) const;
size_t GetTotalFreeSize() const;
size_t GetAllocatableSize() const;
void WalkAllocatedBlocks(WalkCallback callback, void *user_data) const;
void Dump() const;
AllocatorHash Hash() const;
};
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/nim/nim_system_update_task_id.hpp>
namespace ams::nim {
/* Management. */
void InitializeForNetworkInstallManager();
void FinalizeForNetworkInstallManager();
/* Service API. */
Result DestroySystemUpdateTask(const SystemUpdateTaskId& id);
s32 ListSystemUpdateTask(SystemUpdateTaskId *out_list, size_t out_list_size);
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2019-2020 Adubbz, Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/nim/nim_task_id_common.hpp>
namespace ams::nim {
AMS_NIM_DEFINE_TASK_ID(SystemUpdateTaskId, 8);
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2019-2020 Adubbz, Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/nim/nim_task_id_common.hpp>
namespace ams::nim {
#define AMS_NIM_DEFINE_TASK_ID(ID, ALIGN) \
struct alignas(ALIGN) ID { \
util::Uuid uuid; \
\
ALWAYS_INLINE bool IsValid() const { \
return this->uuid != util::InvalidUuid; \
} \
}; \
\
static_assert(alignof(ID) == ALIGN); \
\
ALWAYS_INLINE bool operator==(const ID &lhs, const ID &rhs) { \
return lhs.uuid == rhs.uuid; \
} \
\
ALWAYS_INLINE bool operator!=(const ID &lhs, const ID &rhs) { \
return lhs.uuid != rhs.uuid; \
} \
\
constexpr inline ID Invalid##ID = { util::InvalidUuid }
}

View File

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

View File

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

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_common_types.hpp>
#include <stratosphere/os/os_memory_common.hpp>
namespace ams::os {
Result AllocateMemoryBlock(uintptr_t *out_address, size_t size);
void FreeMemoryBlock(uintptr_t address, size_t size);
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_common_types.hpp>
#include <stratosphere/os/os_memory_common.hpp>
namespace ams::os {
void SetMemoryPermission(uintptr_t address, size_t size, MemoryPermission perm);
}

View File

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

View File

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

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/os/os_common_types.hpp>
#include <stratosphere/os/os_memory_common.hpp>
#include <stratosphere/os/os_thread_local_storage_common.hpp>
namespace ams::os {
Result AllocateTlsSlot(TlsSlot *out, TlsDestructor destructor);
void FreeTlsSlot(TlsSlot slot);
uintptr_t GetTlsValue(TlsSlot slot);
void SetTlsValue(TlsSlot slot, uintptr_t value);
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/os/os_common_types.hpp>
#include <stratosphere/os/os_memory_common.hpp>
namespace ams::os {
struct TlsSlot {
u32 _value;
};
using TlsDestructor = void (*)(uintptr_t arg);
constexpr inline size_t TlsSlotCountMax = 16;
constexpr inline size_t SdkTlsSlotCountMax = 16;
}

View File

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

View File

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

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/sf/sf_common.hpp>
#include <stratosphere/mem.hpp>
namespace ams::sf {
class StandardAllocatorMemoryResource : public MemoryResource {
private:
mem::StandardAllocator *standard_allocator;
public:
explicit StandardAllocatorMemoryResource(mem::StandardAllocator *sa) : standard_allocator(sa) { /* ... */ }
mem::StandardAllocator *GetAllocator() const { return this->standard_allocator; }
private:
virtual void *AllocateImpl(size_t size, size_t alignment) override {
return this->standard_allocator->Allocate(size, alignment);
}
virtual void DeallocateImpl(void *buffer, size_t size, size_t alignment) override {
return this->standard_allocator->Free(buffer);
}
virtual bool IsEqualImpl(const MemoryResource &resource) const {
return this == std::addressof(resource);
}
};
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "fsa/fs_mount_utils.hpp"
namespace ams::fs {
Result MountImageDirectory(const char *name, ImageDirectoryId id) {
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
/* Open the image directory. This uses libnx bindings. */
FsFileSystem fs;
R_TRY(fsOpenImageDirectoryFileSystem(std::addressof(fs), static_cast<::FsImageDirectoryId>(id)));
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<RemoteFileSystem>(fs);
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInImageDirectoryA());
/* Register. */
return fsa::Register(name, std::move(fsa));
}
}

View File

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

View File

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

View File

@@ -0,0 +1,219 @@
/*
* Copyright (c) 2018-2020 Adubbz, Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::fssystem {
template <typename Format>
struct PartitionFileSystemMetaCore<Format>::PartitionFileSystemHeader {
char signature[sizeof(Format::VersionSignature)];
s32 entry_count;
u32 name_table_size;
u32 reserved;
};
static_assert(std::is_pod<PartitionFileSystemMeta::PartitionFileSystemHeader>::value);
static_assert(sizeof(PartitionFileSystemMeta::PartitionFileSystemHeader) == 0x10);
template <typename Format>
PartitionFileSystemMetaCore<Format>::~PartitionFileSystemMetaCore() {
this->DeallocateBuffer();
}
template <typename Format>
Result PartitionFileSystemMetaCore<Format>::Initialize(fs::IStorage *storage, MemoryResource *allocator) {
/* Validate preconditions. */
AMS_ASSERT(allocator != nullptr);
/* Determine the meta data size. */
R_TRY(this->QueryMetaDataSize(std::addressof(this->meta_data_size), storage));
/* Deallocate any old meta buffer and allocate a new one. */
this->DeallocateBuffer();
this->allocator = allocator;
this->buffer = static_cast<char *>(this->allocator->Allocate(this->meta_data_size));
R_UNLESS(this->buffer != nullptr, fs::ResultAllocationFailureInPartitionFileSystemMetaA());
/* Perform regular initialization. */
return this->Initialize(storage, this->buffer, this->meta_data_size);
}
template <typename Format>
Result PartitionFileSystemMetaCore<Format>::Initialize(fs::IStorage *storage, void *meta, size_t meta_size) {
/* Validate size for header. */
R_UNLESS(meta_size >= sizeof(PartitionFileSystemHeader), fs::ResultInvalidSize());
/* Read the header. */
R_TRY(storage->Read(0, meta, sizeof(PartitionFileSystemHeader)));
/* Set and validate the header. */
this->header = reinterpret_cast<PartitionFileSystemHeader *>(meta);
R_UNLESS(crypto::IsSameBytes(this->header->signature, Format::VersionSignature, sizeof(Format::VersionSignature)), typename Format::ResultSignatureVerificationFailed());
/* Setup entries and name table. */
const size_t entries_size = this->header->entry_count * sizeof(typename Format::PartitionEntry);
this->entries = reinterpret_cast<PartitionEntry *>(static_cast<u8 *>(meta) + sizeof(PartitionFileSystemHeader));
this->name_table = static_cast<char *>(meta) + sizeof(PartitionFileSystemHeader) + entries_size;
/* Validate size for header + entries + name table. */
R_UNLESS(meta_size >= sizeof(PartitionFileSystemHeader) + entries_size + this->header->name_table_size, fs::ResultInvalidSize());
/* Read entries and name table. */
R_TRY(storage->Read(sizeof(PartitionFileSystemHeader), this->entries, entries_size + this->header->name_table_size));
/* Mark as initialized. */
this->initialized = true;
return ResultSuccess();
}
template <typename Format>
void PartitionFileSystemMetaCore<Format>::DeallocateBuffer() {
if (this->buffer != nullptr) {
AMS_ABORT_UNLESS(this->allocator != nullptr);
this->allocator->Deallocate(this->buffer, this->meta_data_size);
this->buffer = nullptr;
}
}
template <typename Format>
const typename Format::PartitionEntry *PartitionFileSystemMetaCore<Format>::GetEntry(s32 index) const {
if (this->initialized && 0 <= index && index < static_cast<s32>(this->header->entry_count)) {
return std::addressof(this->entries[index]);
}
return nullptr;
}
template <typename Format>
s32 PartitionFileSystemMetaCore<Format>::GetEntryCount() const {
if (this->initialized) {
return this->header->entry_count;
}
return 0;
}
template <typename Format>
s32 PartitionFileSystemMetaCore<Format>::GetEntryIndex(const char *name) const {
/* Fail if not initialized. */
if (!this->initialized) {
return 0;
}
for (s32 i = 0; i < static_cast<s32>(this->header->entry_count); i++) {
const auto &entry = this->entries[i];
/* Name offset is invalid. */
if (entry.name_offset >= this->header->name_table_size) {
return 0;
}
/* Compare to input name. */
const s32 max_name_len = this->header->name_table_size - entry.name_offset;
if (std::strncmp(std::addressof(this->name_table[entry.name_offset]), name, max_name_len) == 0) {
return i;
}
}
/* Not found. */
return -1;
}
template <typename Format>
const char *PartitionFileSystemMetaCore<Format>::GetEntryName(s32 index) const {
if (this->initialized && index < static_cast<s32>(this->header->entry_count)) {
return std::addressof(this->name_table[this->GetEntry(index)->name_offset]);
}
return nullptr;
}
template <typename Format>
size_t PartitionFileSystemMetaCore<Format>::GetHeaderSize() const {
return sizeof(PartitionFileSystemHeader);
}
template <typename Format>
size_t PartitionFileSystemMetaCore<Format>::GetMetaDataSize() const {
return this->meta_data_size;
}
template <typename Format>
Result PartitionFileSystemMetaCore<Format>::QueryMetaDataSize(size_t *out_size, fs::IStorage *storage) {
/* Read and validate the header. */
PartitionFileSystemHeader header;
R_TRY(storage->Read(0, std::addressof(header), sizeof(PartitionFileSystemHeader)));
R_UNLESS(crypto::IsSameBytes(std::addressof(header), Format::VersionSignature, sizeof(Format::VersionSignature)), typename Format::ResultSignatureVerificationFailed());
/* Output size. */
*out_size = sizeof(PartitionFileSystemHeader) + header.entry_count * sizeof(typename Format::PartitionEntry) + header.name_table_size;
return ResultSuccess();
}
template class PartitionFileSystemMetaCore<impl::PartitionFileSystemFormat>;
template class PartitionFileSystemMetaCore<impl::Sha256PartitionFileSystemFormat>;
Result Sha256PartitionFileSystemMeta::Initialize(fs::IStorage *base_storage, MemoryResource *allocator, const void *hash, size_t hash_size, std::optional<u8> suffix) {
/* Ensure preconditions. */
R_UNLESS(hash_size == crypto::Sha256Generator::HashSize, fs::ResultPreconditionViolation());
/* Get metadata size. */
R_TRY(QueryMetaDataSize(std::addressof(this->meta_data_size), base_storage));
/* Ensure we have no buffer. */
this->DeallocateBuffer();
/* Set allocator and allocate buffer. */
this->allocator = allocator;
this->buffer = static_cast<char *>(this->allocator->Allocate(this->meta_data_size));
R_UNLESS(this->buffer != nullptr, fs::ResultAllocationFailureInPartitionFileSystemMetaB());
/* Read metadata. */
R_TRY(base_storage->Read(0, this->buffer, this->meta_data_size));
/* Calculate hash. */
char calc_hash[crypto::Sha256Generator::HashSize];
{
crypto::Sha256Generator generator;
generator.Initialize();
generator.Update(this->buffer, this->meta_data_size);
if (suffix) {
u8 suffix_val = *suffix;
generator.Update(std::addressof(suffix_val), 1);
}
generator.GetHash(calc_hash, sizeof(calc_hash));
}
/* Ensure hash is valid. */
R_UNLESS(crypto::IsSameBytes(hash, calc_hash, sizeof(calc_hash)), fs::ResultSha256PartitionHashVerificationFailed());
/* Give access to Format */
using Format = impl::Sha256PartitionFileSystemFormat;
/* Set header. */
this->header = reinterpret_cast<PartitionFileSystemHeader *>(this->buffer);
R_UNLESS(crypto::IsSameBytes(this->header->signature, Format::VersionSignature, sizeof(Format::VersionSignature)), typename Format::ResultSignatureVerificationFailed());
/* Validate size for entries and name table. */
const size_t entries_size = this->header->entry_count * sizeof(typename Format::PartitionEntry);
R_UNLESS(this->meta_data_size >= sizeof(PartitionFileSystemHeader) + entries_size + this->header->name_table_size, fs::ResultInvalidSha256PartitionMetaDataSize());
/* Set entries and name table. */
this->entries = reinterpret_cast<PartitionEntry *>(this->buffer + sizeof(PartitionFileSystemHeader));
this->name_table = this->buffer + sizeof(PartitionFileSystemHeader) + entries_size;
/* We initialized. */
this->initialized = true;
return ResultSuccess();
}
}

View File

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

View File

@@ -0,0 +1,116 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "mem_impl_heap_platform.hpp"
#include "mem_impl_heap_tls_heap_static.hpp"
#include "mem_impl_heap_tls_heap_central.hpp"
namespace ams::mem::impl::heap {
void *CachedHeap::Allocate(size_t n) {
return this->tls_heap_cache->Allocate(n);
}
void *CachedHeap::Allocate(size_t n, size_t align) {
return this->tls_heap_cache->Allocate(n, align);
}
size_t CachedHeap::GetAllocationSize(const void *ptr) {
return this->tls_heap_cache->GetAllocationSize(ptr);
}
errno_t CachedHeap::Free(void *p) {
return this->tls_heap_cache->Free(p);
}
errno_t CachedHeap::FreeWithSize(void *p, size_t size) {
return this->tls_heap_cache->FreeWithSize(p, size);
}
errno_t CachedHeap::Reallocate(void *ptr, size_t size, void **p) {
return this->tls_heap_cache->Reallocate(ptr, size, p);
}
errno_t CachedHeap::Shrink(void *ptr, size_t size) {
return this->tls_heap_cache->Shrink(ptr, size);
}
void CachedHeap::ReleaseAllCache() {
if (this->tls_heap_cache) {
this->tls_heap_cache->ReleaseAllCache();
}
}
void CachedHeap::Finalize() {
if (this->tls_heap_cache) {
this->tls_heap_cache->Finalize();
this->tls_heap_cache = nullptr;
}
}
bool CachedHeap::CheckCache() {
bool cache = false;
auto err = this->Query(AllocQuery_CheckCache, std::addressof(cache));
AMS_ASSERT(err != 0);
return cache;
}
errno_t CachedHeap::QueryV(int _query, std::va_list vl) {
const AllocQuery query = static_cast<AllocQuery>(_query);
switch (query) {
case AllocQuery_CheckCache:
{
bool *out = va_arg(vl, bool *);
if (out) {
*out = (this->tls_heap_cache == nullptr) || this->tls_heap_cache->CheckCache();
}
return 0;
}
case AllocQuery_ClearCache:
{
this->ReleaseAllCache();
return 0;
}
case AllocQuery_FinalizeCache:
{
this->Finalize();
return 0;
}
default:
return EINVAL;
}
}
errno_t CachedHeap::Query(int query, ...) {
std::va_list vl;
va_start(vl, query);
auto err = this->QueryV(query, vl);
va_end(vl);
return err;
}
void CachedHeap::Reset(TlsHeapCache *thc) {
this->Finalize();
this->tls_heap_cache = thc;
}
TlsHeapCache *CachedHeap::Release() {
TlsHeapCache *ret = this->tls_heap_cache;
this->tls_heap_cache = nullptr;
return ret;
}
}

View File

@@ -0,0 +1,409 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "mem_impl_heap_platform.hpp"
#include "mem_impl_heap_tls_heap_static.hpp"
#include "mem_impl_heap_tls_heap_central.hpp"
namespace ams::mem::impl::heap {
errno_t CentralHeap::Initialize(void *start, size_t size, u32 option) {
/* Validate size. */
if (size == 0 || !util::IsAligned(size, PageSize)) {
return EINVAL;
}
/* Don't allow initializing twice. */
if (this->start) {
return EEXIST;
}
if (start) {
/* We were provided with a region to use as backing memory. */
u8 *aligned_start = reinterpret_cast<u8 *>(util::AlignUp(reinterpret_cast<uintptr_t>(start), PageSize));
u8 *aligned_end = reinterpret_cast<u8 *>(util::AlignDown(reinterpret_cast<uintptr_t>(start) + size, PageSize));
if (aligned_start >= aligned_end) {
return EINVAL;
}
this->start = aligned_start;
this->end = aligned_end;
this->option = option;
this->tls_heap_central = new (this->start) TlsHeapCentral;
if (auto err = this->tls_heap_central->Initialize(this->start, this->end - this->start, false); err != 0) {
this->tls_heap_central->~TlsHeapCentral();
this->tls_heap_central = nullptr;
AMS_ASSERT(err == 0);
return err;
}
this->use_virtual_memory = false;
} else {
/* We were not provided with a region to use as backing. */
void *mem;
if (auto err = AllocateVirtualMemory(std::addressof(mem), size); err != 0) {
return err;
}
if (!util::IsAligned(reinterpret_cast<uintptr_t>(mem), PageSize)) {
FreeVirtualMemory(mem, size);
size += PageSize;
if (auto err = AllocateVirtualMemory(std::addressof(mem), size); err != 0) {
return err;
}
}
this->start = static_cast<u8 *>(mem);
this->end = this->start + size;
this->option = option;
void *central = reinterpret_cast<void *>(util::AlignUp(reinterpret_cast<uintptr_t>(mem), PageSize));
if (auto err = AllocatePhysicalMemory(central, sizeof(TlsHeapCentral)); err != 0) {
return err;
}
this->tls_heap_central = new (central) TlsHeapCentral;
if (auto err = this->tls_heap_central->Initialize(central, size, true); err != 0) {
this->tls_heap_central->~TlsHeapCentral();
this->tls_heap_central = nullptr;
AMS_ASSERT(err == 0);
return err;
}
this->use_virtual_memory = true;
}
return 0;
}
void CentralHeap::Finalize() {
if (this->tls_heap_central) {
this->tls_heap_central->~TlsHeapCentral();
}
if (this->use_virtual_memory) {
mem::impl::physical_free(util::AlignUp(static_cast<void *>(this->start), PageSize), this->end - this->start);
mem::impl::virtual_free(this->start, this->end - this->start);
}
this->tls_heap_central = nullptr;
this->use_virtual_memory = false;
this->option = 0;
this->start = nullptr;
this->end = nullptr;
}
void *CentralHeap::Allocate(size_t n, size_t align) {
if (!util::IsPowerOfTwo(align)) {
return nullptr;
}
if (n > MaxSize) {
return nullptr;
}
if (align > PageSize) {
return this->tls_heap_central->CacheLargeMemoryWithBigAlign(util::AlignUp(n, PageSize), align);
}
const size_t real_size = TlsHeapStatic::GetRealSizeFromSizeAndAlignment(util::AlignUp(n, align), align);
const auto cls = TlsHeapStatic::GetClassFromSize(real_size);
if (!cls) {
return this->tls_heap_central->CacheLargeMemory(real_size);
}
if (real_size == 0) {
return nullptr;
}
AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
return this->tls_heap_central->CacheSmallMemory(cls, align);
}
size_t CentralHeap::GetAllocationSize(const void *ptr) {
const auto cls = this->tls_heap_central->GetClassFromPointer(ptr);
if (cls > 0) {
/* Check that the pointer has alignment from out allocator. */
if (!util::IsAligned(reinterpret_cast<uintptr_t>(ptr), MinimumAlignment)) {
return 0;
}
AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
return TlsHeapStatic::GetChunkSize(cls);
} else if (ptr != nullptr) {
return this->tls_heap_central->GetAllocationSize(ptr);
} else {
return 0;
}
}
errno_t CentralHeap::Free(void *ptr) {
/* Allow Free(nullptr) */
if (ptr == nullptr) {
return 0;
}
/* Check that the pointer has alignment from out allocator. */
if(!util::IsAligned(reinterpret_cast<uintptr_t>(ptr), MinimumAlignment)) {
AMS_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(ptr), MinimumAlignment));
return EFAULT;
}
const auto cls = this->tls_heap_central->GetClassFromPointer(ptr);
if (cls >= 0) {
AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
if (cls) {
return this->tls_heap_central->UncacheSmallMemory(ptr);
} else {
return this->tls_heap_central->UncacheLargeMemory(ptr);
}
} else {
AMS_ASSERT(cls >= 0);
return EFAULT;
}
}
errno_t CentralHeap::FreeWithSize(void *ptr, size_t size) {
if (TlsHeapStatic::GetClassFromSize(size)) {
return this->tls_heap_central->UncacheSmallMemory(ptr);
} else {
return this->tls_heap_central->UncacheLargeMemory(ptr);
}
}
errno_t CentralHeap::Reallocate(void *ptr, size_t size, void **p) {
AMS_ASSERT(ptr != nullptr && size != 0);
if (!size) {
return EINVAL;
}
if (size > MaxSize) {
return ENOMEM;
}
const auto cls_from_size = TlsHeapStatic::GetClassFromSize(size);
const auto cls_from_ptr = this->tls_heap_central->GetClassFromPointer(ptr);
if (cls_from_ptr) {
if (cls_from_ptr <= 0) {
return EFAULT;
} else if (cls_from_size && cls_from_size <= cls_from_ptr) {
*p = ptr;
return 0;
} else {
const size_t new_chunk_size = TlsHeapStatic::GetChunkSize(cls_from_ptr);
*p = this->Allocate(new_chunk_size);
if (*p) {
std::memcpy(*p, ptr, size);
return this->tls_heap_central->UncacheSmallMemory(ptr);
} else {
return ENOMEM;
}
}
} else if (cls_from_size) {
*p = this->Allocate(size);
if (*p) {
std::memcpy(*p, ptr, size);
return this->tls_heap_central->UncacheLargeMemory(ptr);
} else {
return ENOMEM;
}
} else {
return this->tls_heap_central->ReallocateLargeMemory(ptr, size, p);
}
}
errno_t CentralHeap::Shrink(void *ptr, size_t size) {
AMS_ASSERT(ptr != nullptr && size != 0);
if (!size) {
return EINVAL;
}
if (size > MaxSize) {
return ENOMEM;
}
const auto cls_from_size = TlsHeapStatic::GetClassFromSize(size);
const auto cls_from_ptr = this->tls_heap_central->GetClassFromPointer(ptr);
if (cls_from_ptr) {
if (cls_from_ptr <= 0) {
return EFAULT;
} else if (cls_from_size && cls_from_size <= cls_from_ptr) {
return 0;
} else {
return EINVAL;
}
} else if (cls_from_size) {
return this->tls_heap_central->ShrinkLargeMemory(ptr, PageSize);
} else {
return this->tls_heap_central->ShrinkLargeMemory(ptr, size);
}
}
bool CentralHeap::MakeCache(CachedHeap *cached_heap) {
if (cached_heap == nullptr) {
return false;
}
AMS_ASSERT(this->tls_heap_central != nullptr);
const auto cls = TlsHeapStatic::GetClassFromSize(sizeof(*cached_heap));
void *tls_heap_cache = this->tls_heap_central->CacheSmallMemoryForSystem(cls);
if (tls_heap_cache == nullptr) {
return false;
}
new (tls_heap_cache) TlsHeapCache(this->tls_heap_central, this->option);
if (this->tls_heap_central->AddThreadCache(reinterpret_cast<TlsHeapCache *>(tls_heap_cache)) != 0) {
this->tls_heap_central->UncacheSmallMemory(tls_heap_cache);
return false;
}
cached_heap->Reset(reinterpret_cast<TlsHeapCache *>(tls_heap_cache));
return true;
}
errno_t CentralHeap::WalkAllocatedPointers(HeapWalkCallback callback, void *user_data) {
if (!callback || !this->tls_heap_central) {
return EINVAL;
}
return this->tls_heap_central->WalkAllocatedPointers(callback, user_data);
}
errno_t CentralHeap::QueryV(int query, std::va_list vl) {
return this->QueryVImpl(query, std::addressof(vl));
}
errno_t CentralHeap::Query(int query, ...) {
std::va_list vl;
va_start(vl, query);
auto err = this->QueryVImpl(query, std::addressof(vl));
va_end(vl);
return err;
}
errno_t CentralHeap::QueryVImpl(int _query, std::va_list *vl_ptr) {
const AllocQuery query = static_cast<AllocQuery>(_query);
switch (query) {
case AllocQuery_Dump:
case AllocQuery_DumpJson:
{
auto dump_mode = static_cast<DumpMode>(va_arg(*vl_ptr, int));
auto fd = va_arg(*vl_ptr, int);
if (this->tls_heap_central) {
this->tls_heap_central->Dump(dump_mode, fd, query == AllocQuery_DumpJson);
}
return 0;
}
case AllocQuery_PageSize:
{
size_t *out = va_arg(*vl_ptr, size_t *);
if (out) {
*out = PageSize;
}
return 0;
}
case AllocQuery_AllocatedSize:
case AllocQuery_FreeSize:
case AllocQuery_SystemSize:
case AllocQuery_MaxAllocatableSize:
{
size_t *out = va_arg(*vl_ptr, size_t *);
if (!out) {
return 0;
}
if (!this->tls_heap_central) {
*out = 0;
return 0;
}
TlsHeapMemStats stats;
this->tls_heap_central->GetMemStats(std::addressof(stats));
switch (query) {
case AllocQuery_AllocatedSize:
default:
*out = stats.allocated_size;
break;
case AllocQuery_FreeSize:
*out = stats.free_size;
break;
case AllocQuery_SystemSize:
*out = stats.system_size;
break;
case AllocQuery_MaxAllocatableSize:
*out = stats.max_allocatable_size;
break;
}
return 0;
}
case AllocQuery_IsClean:
{
int *out = va_arg(*vl_ptr, int *);
if (out) {
*out = !this->tls_heap_central || this->tls_heap_central->IsClean();
}
return 0;
}
case AllocQuery_HeapHash:
{
HeapHash *out = va_arg(*vl_ptr, HeapHash *);
if (out) {
if (this->tls_heap_central) {
this->tls_heap_central->CalculateHeapHash(out);
} else {
*out = {};
}
}
return 0;
}
case AllocQuery_UnifyFreeList:
/* NOTE: Nintendo does not check that the ptr is not null for this query, even though they do for other queries. */
this->tls_heap_central->IsClean();
return 0;
case AllocQuery_SetColor:
{
/* NOTE: Nintendo does not check that the ptr is not null for this query, even though they do for other queries. */
void *ptr = va_arg(*vl_ptr, void *);
int color = va_arg(*vl_ptr, int);
return this->tls_heap_central->SetColor(ptr, color);
}
case AllocQuery_GetColor:
{
/* NOTE: Nintendo does not check that the ptr is not null for this query, even though they do for other queries. */
void *ptr = va_arg(*vl_ptr, void *);
int *out = va_arg(*vl_ptr, int *);
return this->tls_heap_central->GetColor(ptr, out);
}
case AllocQuery_SetName:
{
/* NOTE: Nintendo does not check that the ptr is not null for this query, even though they do for other queries. */
void *ptr = va_arg(*vl_ptr, void *);
const char *name = va_arg(*vl_ptr, const char *);
return this->tls_heap_central->SetName(ptr, name);
}
case AllocQuery_GetName:
{
/* NOTE: Nintendo does not check that the ptr is not null for this query, even though they do for other queries. */
void *ptr = va_arg(*vl_ptr, void *);
char *dst = va_arg(*vl_ptr, char *);
size_t dst_size = va_arg(*vl_ptr, size_t);
return this->tls_heap_central->GetName(ptr, dst, dst_size);
}
case AllocQuery_FreeSizeMapped:
case AllocQuery_MaxAllocatableSizeMapped:
{
/* NOTE: Nintendo does not check that the ptr is not null for this query, even though they do for other queries. */
size_t *out = va_arg(*vl_ptr, size_t *);
size_t free_size;
size_t max_allocatable_size;
auto err = this->tls_heap_central->GetMappedMemStats(std::addressof(free_size), std::addressof(max_allocatable_size));
if (err == 0) {
if (query == AllocQuery_FreeSizeMapped) {
*out = free_size;
} else {
*out = max_allocatable_size;
}
}
return err;
}
default:
return EINVAL;
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "../mem_impl_platform.hpp"
namespace ams::mem::impl::heap {
using Prot = mem::impl::Prot;
inline errno_t AllocateVirtualMemory(void **ptr, size_t size) {
return ::ams::mem::impl::virtual_alloc(ptr, size);
}
inline errno_t FreeVirtualMemory(void *ptr, size_t size) {
return ::ams::mem::impl::virtual_free(ptr, size);
}
inline errno_t AllocatePhysicalMemory(void *ptr, size_t size) {
return ::ams::mem::impl::physical_alloc(ptr, size, static_cast<Prot>(Prot_read | Prot_write));
}
inline errno_t FreePhysicalMemory(void *ptr, size_t size) {
return ::ams::mem::impl::physical_free(ptr, size);
}
}

View File

@@ -0,0 +1,557 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "mem_impl_heap_platform.hpp"
#include "mem_impl_heap_tls_heap_static.hpp"
#include "mem_impl_heap_tls_heap_cache.hpp"
#include "mem_impl_heap_tls_heap_central.hpp"
namespace ams::mem::impl::heap {
TlsHeapCache::TlsHeapCache(TlsHeapCentral *central, u32 option) {
/* Choose function impls based on option. */
if ((option & HeapOption_DisableCache) != 0) {
this->allocate = AllocateImpl<false>;
this->allocate_aligned = AllocateAlignedImpl<false>;
this->free = FreeImpl<false>;
this->free_with_size = FreeWithSizeImpl<false>;
this->get_allocation_size = GetAllocationSizeImpl<false>;
this->reallocate = ReallocateImpl<false>;
this->shrink = ShrinkImpl<false>;
} else {
this->allocate = AllocateImpl<true>;
this->allocate_aligned = AllocateAlignedImpl<true>;
this->free = FreeImpl<true>;
this->free_with_size = FreeWithSizeImpl<true>;
this->get_allocation_size = GetAllocationSizeImpl<true>;
this->reallocate = ReallocateImpl<true>;
this->shrink = ShrinkImpl<true>;
}
/* Generate random bytes to mangle pointers. */
if (auto err = gen_random(std::addressof(this->mangle_val), sizeof(this->mangle_val)); err != 0) {
s64 epoch_time;
epochtime(std::addressof(epoch_time));
this->mangle_val = reinterpret_cast<uintptr_t>(std::addressof(epoch_time)) ^ static_cast<u64>(epoch_time);
}
/* Set member variables. */
this->central = central;
this->total_heap_size = central->GetTotalHeapSize();
this->heap_option = option;
this->total_cached_size = 0;
this->largest_class = 0;
/* Setup chunks. */
for (size_t i = 0; i < TlsHeapStatic::NumClassInfo; i++) {
this->small_mem_lists[i] = nullptr;
this->cached_size[i] = 0;
this->chunk_count[i] = 1;
}
/* Set fixed chunk counts for particularly small chunks. */
this->chunk_count[1] = MaxChunkCount;
this->chunk_count[2] = MaxChunkCount;
this->chunk_count[3] = MaxChunkCount;
this->chunk_count[4] = MaxChunkCount / 2;
this->chunk_count[5] = MaxChunkCount / 2;
this->chunk_count[6] = MaxChunkCount / 2;
this->chunk_count[7] = MaxChunkCount / 4;
this->chunk_count[8] = MaxChunkCount / 4;
this->chunk_count[9] = MaxChunkCount / 4;
}
void TlsHeapCache::Finalize() {
/* Free all small mem lists. */
this->ReleaseAllCache();
/* Remove this cache from the owner central heap. */
this->central->RemoveThreadCache(this);
this->central->UncacheSmallMemory(this);
}
bool TlsHeapCache::CheckCache() const {
for (size_t i = 0; i < util::size(this->small_mem_lists); i++) {
void *ptr = this->small_mem_lists[i];
if (ptr) {
s64 depth = -static_cast<s64>(this->cached_size[i] / TlsHeapStatic::GetChunkSize(i));
while (ptr) {
ptr = *reinterpret_cast<void **>(this->ManglePointer(ptr));
if ((++depth) == 0) {
AMS_ASSERT(ptr == nullptr);
break;
}
}
}
}
return true;
}
void TlsHeapCache::ReleaseAllCache() {
for (size_t i = 0; i < util::size(this->small_mem_lists); i++) {
if (this->small_mem_lists[i]) {
this->central->UncacheSmallMemoryList(this, this->small_mem_lists[i]);
this->small_mem_lists[i] = nullptr;
this->cached_size[i] = 0;
}
}
this->total_cached_size = 0;
this->largest_class = 0;
}
template<>
void *TlsHeapCache::AllocateImpl<false>(TlsHeapCache *_this, size_t size) {
/* Validate allocation size. */
if (size == 0 || size > MaxSize) {
return nullptr;
}
if (const size_t cls = TlsHeapStatic::GetClassFromSize(size); cls != 0) {
AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
return _this->central->CacheSmallMemory(cls);
} else {
/* If allocating a huge size, release our cache. */
if (size >= _this->total_heap_size / 4) {
_this->ReleaseAllCache();
}
return _this->central->CacheLargeMemory(size);
}
}
template<>
void *TlsHeapCache::AllocateImpl<true>(TlsHeapCache *_this, size_t size) {
/* Validate allocation size. */
if (size == 0 || size > MaxSize) {
return nullptr;
}
if (size_t cls = TlsHeapStatic::GetClassFromSize(size); cls != 0) {
AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
/* Allocate a chunk. */
void *ptr = _this->small_mem_lists[cls];
if (ptr == nullptr) {
const size_t prev_cls = cls;
size_t count = _this->chunk_count[cls];
size_t n = _this->central->CacheSmallMemoryList(_this, std::addressof(cls), count, std::addressof(ptr));
if (n == 0) {
return nullptr;
}
if (cls == prev_cls) {
if (count < MaxChunkCount) {
count++;
}
_this->chunk_count[cls] = std::max(count, n);
} else {
AMS_ASSERT(n == 1);
}
const size_t csize = TlsHeapStatic::GetChunkSize(cls) * (n - 1);
_this->cached_size[cls] += csize;
if (_this->cached_size[cls] > _this->cached_size[_this->largest_class]) {
_this->largest_class = cls;
}
_this->total_cached_size += csize;
}
/* Demangle our pointer, update free list. */
ptr = _this->ManglePointer(ptr);
_this->small_mem_lists[cls] = *reinterpret_cast<void **>(ptr);
return ptr;
} else {
/* If allocating a huge size, release our cache. */
if (size >= _this->total_heap_size / 4) {
_this->ReleaseAllCache();
}
return _this->central->CacheLargeMemory(size);
}
}
template<>
void *TlsHeapCache::AllocateAlignedImpl<false>(TlsHeapCache *_this, size_t size, size_t align) {
/* Ensure valid alignment. */
if (!util::IsPowerOfTwo(align)) {
return nullptr;
}
/* NOTE: Nintendo does not check size == 0 here, despite doing so in Alloc */
if (size > MaxSize) {
return nullptr;
}
/* Handle big alignment. */
if (align > TlsHeapStatic::PageSize) {
return _this->central->CacheLargeMemoryWithBigAlign(util::AlignUp(size, TlsHeapStatic::PageSize), align);
}
const size_t real_size = TlsHeapStatic::GetRealSizeFromSizeAndAlignment(util::AlignUp(size, align), align);
if (const size_t cls = TlsHeapStatic::GetClassFromSize(real_size); cls != 0) {
if (real_size == 0) {
return nullptr;
}
AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
return _this->central->CacheSmallMemory(cls, align);
} else {
/* If allocating a huge size, release our cache. */
if (real_size >= _this->total_heap_size / 4) {
_this->ReleaseAllCache();
}
return _this->central->CacheLargeMemory(real_size);
}
}
template<>
void *TlsHeapCache::AllocateAlignedImpl<true>(TlsHeapCache *_this, size_t size, size_t align) {
/* Ensure valid alignment. */
if (!util::IsPowerOfTwo(align)) {
return nullptr;
}
/* NOTE: Nintendo does not check size == 0 here, despite doing so in Alloc */
if (size > MaxSize) {
return nullptr;
}
/* Handle big alignment. */
if (align > TlsHeapStatic::PageSize) {
return _this->central->CacheLargeMemoryWithBigAlign(util::AlignUp(size, TlsHeapStatic::PageSize), align);
}
const size_t real_size = TlsHeapStatic::GetRealSizeFromSizeAndAlignment(util::AlignUp(size, align), align);
if (size_t cls = TlsHeapStatic::GetClassFromSize(real_size); cls != 0) {
if (real_size == 0) {
return nullptr;
}
AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
/* Allocate a chunk. */
void *ptr = _this->small_mem_lists[cls];
if (ptr == nullptr) {
const size_t prev_cls = cls;
size_t count = _this->chunk_count[cls];
size_t n = _this->central->CacheSmallMemoryList(_this, std::addressof(cls), count, std::addressof(ptr), align);
if (n == 0) {
return nullptr;
}
if (cls == prev_cls) {
if (count < MaxChunkCount) {
count++;
}
_this->chunk_count[cls] = std::max(count, n);
} else {
AMS_ASSERT(n == 1);
}
const s32 csize = TlsHeapStatic::GetChunkSize(cls) * (n - 1);
_this->total_cached_size += csize;
_this->cached_size[cls] += csize;
if (_this->cached_size[cls] > _this->cached_size[_this->largest_class]) {
_this->largest_class = cls;
}
}
/* Demangle our pointer, update free list. */
ptr = _this->ManglePointer(ptr);
_this->small_mem_lists[cls] = *reinterpret_cast<void **>(ptr);
return ptr;
} else {
/* If allocating a huge size, release our cache. */
if (size >= _this->total_heap_size / 4) {
_this->ReleaseAllCache();
}
return _this->central->CacheLargeMemory(size);
}
}
template<>
errno_t TlsHeapCache::FreeImpl<false>(TlsHeapCache *_this, void *ptr) {
const size_t cls = _this->central->GetClassFromPointer(ptr);
if (cls == 0) {
return _this->central->UncacheLargeMemory(ptr);
}
AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
if (static_cast<s32>(cls) >= 0) {
return _this->central->UncacheSmallMemory(ptr);
} else if (ptr == nullptr) {
return 0;
} else {
return EFAULT;
}
}
template<>
errno_t TlsHeapCache::FreeImpl<true>(TlsHeapCache *_this, void *ptr) {
const size_t cls = _this->central->GetClassFromPointer(ptr);
if (cls == 0) {
return _this->central->UncacheLargeMemory(ptr);
}
AMS_ASSERT(cls < TlsHeapStatic::NumClassInfo);
if (static_cast<s32>(cls) >= 0) {
*reinterpret_cast<void **>(ptr) = _this->small_mem_lists[cls];
_this->small_mem_lists[cls] = _this->ManglePointer(ptr);
const s32 csize = TlsHeapStatic::GetChunkSize(cls);
_this->total_cached_size += csize;
_this->cached_size[cls] += csize;
if (_this->cached_size[cls] > _this->cached_size[_this->largest_class]) {
_this->largest_class = cls;
}
errno_t err = 0;
if (!_this->central->CheckCachedSize(_this->total_cached_size)) {
_this->central->UncacheSmallMemoryList(_this, _this->small_mem_lists[_this->largest_class]);
_this->small_mem_lists[_this->largest_class] = nullptr;
_this->total_cached_size -= _this->cached_size[_this->largest_class];
_this->cached_size[_this->largest_class] = 0;
s32 largest_class = 0;
s32 biggest_size = -1;
for (size_t i = 0; i < TlsHeapStatic::NumClassInfo; i++) {
if (biggest_size < _this->cached_size[i]) {
biggest_size = _this->cached_size[i];
largest_class = static_cast<s32>(i);
}
}
_this->largest_class = largest_class;
}
return err;
} else if (ptr == nullptr) {
return 0;
} else {
return EFAULT;
}
}
template<>
errno_t TlsHeapCache::FreeWithSizeImpl<false>(TlsHeapCache *_this, void *ptr, size_t size) {
if (ptr == nullptr) {
return 0;
}
const size_t cls = TlsHeapStatic::GetClassFromSize(size);
if (cls == 0) {
return _this->central->UncacheLargeMemory(ptr);
} else {
return _this->central->UncacheSmallMemory(ptr);
}
}
template<>
errno_t TlsHeapCache::FreeWithSizeImpl<true>(TlsHeapCache *_this, void *ptr, size_t size) {
if (ptr == nullptr) {
return 0;
}
const size_t cls = TlsHeapStatic::GetClassFromSize(size);
if (cls == 0) {
return _this->central->UncacheLargeMemory(ptr);
} else {
*reinterpret_cast<void **>(ptr) = _this->small_mem_lists[cls];
_this->small_mem_lists[cls] = _this->ManglePointer(ptr);
const s32 csize = TlsHeapStatic::GetChunkSize(cls);
_this->total_cached_size += csize;
_this->cached_size[cls] += csize;
if (_this->cached_size[cls] > _this->cached_size[_this->largest_class]) {
_this->largest_class = cls;
}
errno_t err = 0;
if (!_this->central->CheckCachedSize(_this->total_cached_size)) {
_this->central->UncacheSmallMemoryList(_this, _this->small_mem_lists[_this->largest_class]);
_this->small_mem_lists[_this->largest_class] = nullptr;
_this->total_cached_size -= _this->cached_size[_this->largest_class];
_this->cached_size[_this->largest_class] = 0;
s32 largest_class = 0;
s32 biggest_size = -1;
for (size_t i = 0; i < TlsHeapStatic::NumClassInfo; i++) {
if (biggest_size < _this->cached_size[i]) {
biggest_size = _this->cached_size[i];
largest_class = static_cast<s32>(i);
}
}
_this->largest_class = largest_class;
}
return err;
}
}
template<>
size_t TlsHeapCache::GetAllocationSizeImpl<false>(TlsHeapCache *_this, const void *ptr) {
return _this->GetAllocationSizeCommonImpl(ptr);
}
template<>
size_t TlsHeapCache::GetAllocationSizeImpl<true>(TlsHeapCache *_this, const void *ptr) {
return _this->GetAllocationSizeCommonImpl(ptr);
}
size_t TlsHeapCache::GetAllocationSizeCommonImpl(const void *ptr) const {
const s32 cls = this->central->GetClassFromPointer(ptr);
if (cls > 0) {
if (!util::IsAligned(ptr, alignof(u64))) {
/* All pointers we allocate have alignment at least 8. */
return 0;
}
/* Validate class. */
AMS_ASSERT(cls < static_cast<s32>(TlsHeapStatic::NumClassInfo));
if (cls < 0) {
return 0;
}
return TlsHeapStatic::GetChunkSize(cls);
} else if (ptr != nullptr) {
return this->central->GetAllocationSize(ptr);
} else {
return 0;
}
}
template<>
errno_t TlsHeapCache::ReallocateImpl<false>(TlsHeapCache *_this, void *ptr, size_t size, void **p) {
AMS_ASSERT(ptr != nullptr && size != 0);
if (size > MaxSize) {
return ENOMEM;
}
size_t alloc_size, copy_size;
const s32 cls_from_size = TlsHeapStatic::GetClassFromSize(size);
const s32 cls_from_ptr = _this->central->GetClassFromPointer(ptr);
if (cls_from_ptr < 0) {
/* error case. */
return EFAULT;
} else if (cls_from_size) {
if (cls_from_ptr > 0) {
if (cls_from_size <= cls_from_ptr) {
*p = ptr;
return 0;
} else {
alloc_size = size;
copy_size = TlsHeapStatic::GetChunkSize(cls_from_ptr);
}
} else /* if (cls_from_ptr == 0) */ {
alloc_size = size;
copy_size = size;
}
} else if (cls_from_ptr == 0) {
return _this->central->ReallocateLargeMemory(ptr, size, p);
} else /* if (cls_from_ptr > 0) */ {
alloc_size = size;
copy_size = TlsHeapStatic::GetChunkSize(cls_from_ptr);
}
*p = AllocateImpl<false>(_this, alloc_size);
if (*p == nullptr) {
return ENOMEM;
}
std::memcpy(*p, ptr, copy_size);
return FreeImpl<false>(_this, ptr);
}
template<>
errno_t TlsHeapCache::ReallocateImpl<true>(TlsHeapCache *_this, void *ptr, size_t size, void **p) {
AMS_ASSERT(ptr != nullptr && size != 0);
if (size > MaxSize) {
return ENOMEM;
}
size_t alloc_size, copy_size;
const s32 cls_from_size = TlsHeapStatic::GetClassFromSize(size);
const s32 cls_from_ptr = _this->central->GetClassFromPointer(ptr);
if (cls_from_ptr < 0) {
/* error case. */
return EFAULT;
} else if (cls_from_size) {
if (cls_from_ptr > 0) {
if (cls_from_size <= cls_from_ptr) {
*p = ptr;
return 0;
} else {
alloc_size = size;
copy_size = TlsHeapStatic::GetChunkSize(cls_from_ptr);
}
} else /* if (cls_from_ptr == 0) */ {
alloc_size = size;
copy_size = size;
}
} else if (cls_from_ptr == 0) {
return _this->central->ReallocateLargeMemory(ptr, size, p);
} else /* if (cls_from_ptr > 0) */ {
alloc_size = size;
copy_size = TlsHeapStatic::GetChunkSize(cls_from_ptr);
}
*p = AllocateImpl<true>(_this, alloc_size);
if (*p == nullptr) {
return ENOMEM;
}
std::memcpy(*p, ptr, copy_size);
return FreeImpl<true>(_this, ptr);
}
template<>
errno_t TlsHeapCache::ShrinkImpl<false>(TlsHeapCache *_this, void *ptr, size_t size) {
return _this->ShrinkCommonImpl(ptr, size);
}
template<>
errno_t TlsHeapCache::ShrinkImpl<true>(TlsHeapCache *_this, void *ptr, size_t size) {
return _this->ShrinkCommonImpl(ptr, size);
}
errno_t TlsHeapCache::ShrinkCommonImpl(void *ptr, size_t size) const {
AMS_ASSERT(ptr != nullptr && size != 0);
if (size > MaxSize) {
return ENOMEM;
}
const s32 cls_from_size = TlsHeapStatic::GetClassFromSize(size);
const s32 cls_from_ptr = this->central->GetClassFromPointer(ptr);
if (cls_from_ptr) {
if (cls_from_ptr <= 0) {
return EFAULT;
} else if (cls_from_size && cls_from_size <= cls_from_ptr) {
return 0;
} else {
return EINVAL;
}
} else if (cls_from_size) {
return this->central->ShrinkLargeMemory(ptr, TlsHeapStatic::PageSize);
} else {
return this->central->ShrinkLargeMemory(ptr, size);
}
}
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "mem_impl_heap_platform.hpp"
#include "mem_impl_heap_tls_heap_static.hpp"
namespace ams::mem::impl::heap {
class TlsHeapCentral;
#define FOREACH_TLS_HEAP_CACHE_FUNC(HANDLER) \
HANDLER(void *, Allocate, allocate, size_t size); \
HANDLER(void *, AllocateAligned, allocate_aligned, size_t size, size_t align); \
HANDLER(errno_t, Free, free, void *ptr); \
HANDLER(errno_t, FreeWithSize, free_with_size, void *ptr, size_t size); \
HANDLER(size_t, GetAllocationSize, get_allocation_size, const void *ptr); \
HANDLER(errno_t, Reallocate, reallocate, void *ptr, size_t size, void **p); \
HANDLER(errno_t, Shrink, shrink, void *ptr, size_t size);
class TlsHeapCache {
public:
static constexpr size_t MaxChunkCount = BITSIZEOF(u64);
public:
#define TLS_HEAP_CACHE_DECLARE_TYPEDEF(RETURN, NAME, MEMBER_NAME, ...) \
using NAME##Func = RETURN (*)(TlsHeapCache *, ## __VA_ARGS__)
FOREACH_TLS_HEAP_CACHE_FUNC(TLS_HEAP_CACHE_DECLARE_TYPEDEF)
#undef TLS_HEAP_CACHE_DECLARE_TYPEDEF
private:
#define TLS_HEAP_CACHE_DECLARE_MEMBER(RETURN, NAME, MEMBER_NAME, ...) \
NAME##Func MEMBER_NAME;
FOREACH_TLS_HEAP_CACHE_FUNC(TLS_HEAP_CACHE_DECLARE_MEMBER)
#undef TLS_HEAP_CACHE_DECLARE_MEMBER
uintptr_t mangle_val;
TlsHeapCentral *central;
size_t total_heap_size;
u32 heap_option;
s32 total_cached_size;
s32 largest_class;
void *small_mem_lists[TlsHeapStatic::NumClassInfo];
s32 cached_size[TlsHeapStatic::NumClassInfo];
u8 chunk_count[TlsHeapStatic::NumClassInfo];
public:
TlsHeapCache(TlsHeapCentral *central, u32 option);
void Finalize();
void *ManglePointer(void *ptr) const {
return reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(ptr) ^ this->mangle_val);
}
bool CheckCache() const;
void ReleaseAllCache();
public:
/* TODO: Better handler with type info to macro this? */
ALWAYS_INLINE void *Allocate(size_t size) { return this->allocate(this, size); }
ALWAYS_INLINE void *Allocate(size_t size, size_t align) { return this->allocate_aligned(this, size, align); }
ALWAYS_INLINE errno_t Free(void *ptr) { return this->free(this, ptr); }
ALWAYS_INLINE errno_t FreeWithSize(void *ptr, size_t size) { return this->free_with_size(this, ptr, size); }
ALWAYS_INLINE size_t GetAllocationSize(const void *ptr) { return this->get_allocation_size(this, ptr); }
ALWAYS_INLINE errno_t Reallocate(void *ptr, size_t size, void **p) { return this->reallocate(this, ptr, size, p); }
ALWAYS_INLINE errno_t Shrink(void *ptr, size_t size) { return this->shrink(this, ptr, size); }
private:
#define TLS_HEAP_CACHE_DECLARE_TEMPLATE(RETURN, NAME, MEMBER_NAME, ...) \
template<bool Cache> static RETURN NAME##Impl(TlsHeapCache *_this, ## __VA_ARGS__ )
FOREACH_TLS_HEAP_CACHE_FUNC(TLS_HEAP_CACHE_DECLARE_TEMPLATE)
#undef TLS_HEAP_CACHE_DECLARE_TEMPLATE
size_t GetAllocationSizeCommonImpl(const void *ptr) const;
errno_t ShrinkCommonImpl(void *ptr, size_t size) const;
};
#define TLS_HEAP_CACHE_DECLARE_INSTANTIATION(RETURN, NAME, MEMBER_NAME, ...) \
template<> RETURN TlsHeapCache::NAME##Impl<false>(TlsHeapCache *_this, ##__VA_ARGS__); \
template<> RETURN TlsHeapCache::NAME##Impl<true>(TlsHeapCache *_this, ##__VA_ARGS__)
FOREACH_TLS_HEAP_CACHE_FUNC(TLS_HEAP_CACHE_DECLARE_INSTANTIATION)
#undef FOREACH_TLS_HEAP_CACHE_FUNC
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,547 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "mem_impl_heap_platform.hpp"
#include "mem_impl_heap_tls_heap_static.hpp"
#include "mem_impl_heap_tls_heap_cache.hpp"
namespace ams::mem::impl::heap {
/* Simple intrusive list. */
template<typename T>
struct ListHeader {
T *list_next;
};
template<typename T>
struct ListElement : public ListHeader<T> {
T *list_prev;
};
template<typename T>
constexpr inline void ListClearLink(ListHeader<T> *l) {
l->list_next = nullptr;
}
template<typename T>
constexpr inline void ListClearLink(ListElement<T> *l) {
l->list_next = nullptr;
l->list_prev = nullptr;
}
template<typename T>
constexpr inline T *ListGetNext(const ListHeader<T> *l) {
return l->list_next;
}
template<typename T>
constexpr inline T *ListGetNext(const ListElement<T> *l) {
return l->list_next;
}
template<typename T>
constexpr inline T *ListGetPrev(const ListElement<T> *l) {
return l->list_prev;
}
template<typename T>
constexpr inline void ListInsertAfter(ListHeader<T> *hdr, T *e) {
e->list_next = hdr->list_next;
e->list_prev = static_cast<T *>(hdr);
if (hdr->list_next != nullptr) {
hdr->list_next->list_prev = e;
}
hdr->list_next = e;
}
template<typename T>
constexpr inline void ListRemoveSelf(T *e) {
if (e->list_next != nullptr) {
e->list_next->list_prev = e->list_prev;
}
if (e->list_prev != nullptr) {
e->list_prev->list_next = e->list_next;
}
e->list_next = nullptr;
e->list_prev = nullptr;
}
struct Span : public ListElement<Span> {
struct SmallMemory {
SmallMemory *next;
};
enum Status : u8 {
Status_NotUsed = 0,
Status_InUse = 1,
Status_InFreeList = 2,
Status_InUseSystem = 3,
};
u16 object_count;
u8 page_class;
u8 status;
s32 id;
union {
uintptr_t u;
void *p;
SmallMemory *sm;
char *cp;
} start;
uintptr_t num_pages;
union {
struct {
SmallMemory *objects;
u64 is_allocated[8];
} small;
struct {
u8 color[3];
char name[0x10];
} large;
struct {
u32 zero;
} large_clear;
} aux;
};
struct SpanPage : public ListElement<SpanPage> {
struct Info {
u64 alloc_bitmap;
u16 free_count;
u8 is_sticky;
Span span_of_spanpage;
} info;
Span spans[(TlsHeapStatic::PageSize - sizeof(Info) - sizeof(ListElement<SpanPage>)) / sizeof(Span)];
static constexpr size_t MaxSpanCount = sizeof(spans) / sizeof(spans[0]);
};
static_assert(sizeof(SpanPage) <= TlsHeapStatic::PageSize);
static constexpr ALWAYS_INLINE bool CanAllocateSpan(const SpanPage *span_page) {
return span_page->info.alloc_bitmap != ~(decltype(span_page->info.alloc_bitmap){});
}
struct SpanTable {
uintptr_t total_pages;
Span **page_to_span;
u8 *pageclass_cache;
};
struct TlsHeapMemStats {
size_t allocated_size;
size_t free_size;
size_t system_size;
size_t max_allocatable_size;
};
ALWAYS_INLINE Span *GetSpanFromPointer(const SpanTable *table, const void *ptr) {
const size_t idx = TlsHeapStatic::GetPageIndex(reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(table));
if (idx < table->total_pages) {
return table->page_to_span[idx];
} else {
return nullptr;
}
}
ALWAYS_INLINE SpanPage *GetSpanPage(Span *span) {
return reinterpret_cast<SpanPage *>(TlsHeapStatic::AlignDownPage(reinterpret_cast<uintptr_t>(span)));
}
ALWAYS_INLINE Span *GetSpanPageSpan(SpanPage *span_page) {
return std::addressof(span_page->info.span_of_spanpage);
}
ALWAYS_INLINE Span *GetPrevSpan(const SpanTable *span_table, const Span *span) {
return GetSpanFromPointer(span_table, reinterpret_cast<const void *>(span->start.u - 1));
}
ALWAYS_INLINE Span *GetNextSpan(const SpanTable *span_table, const Span *span) {
return GetSpanFromPointer(span_table, reinterpret_cast<const void *>(span->start.u + span->num_pages * TlsHeapStatic::PageSize));
}
class TlsHeapCentral {
private:
using FreeListAvailableWord = u64;
static constexpr size_t FreeListCount = 0x100;
static constexpr size_t NumFreeListBitmaps = FreeListCount / BITSIZEOF(FreeListAvailableWord);
static constexpr ALWAYS_INLINE size_t FreeListAvailableIndex(size_t which) {
return which / BITSIZEOF(FreeListAvailableWord);
}
static constexpr ALWAYS_INLINE size_t FreeListAvailableBit(size_t which) {
return which % BITSIZEOF(FreeListAvailableWord);
}
static constexpr ALWAYS_INLINE FreeListAvailableWord FreeListAvailableMask(size_t which) {
return static_cast<FreeListAvailableWord>(1) << FreeListAvailableBit(which);
}
static_assert(NumFreeListBitmaps * BITSIZEOF(FreeListAvailableWord) == FreeListCount);
private:
SpanTable span_table;
u8 *physical_page_flags;
s32 num_threads;
s32 static_thread_quota;
s32 dynamic_thread_quota;
bool use_virtual_memory;
os::RecursiveMutex lock;
ListHeader<SpanPage> spanpage_list;
ListHeader<SpanPage> full_spanpage_list;
ListHeader<Span> freelists[FreeListCount];
FreeListAvailableWord freelists_bitmap[NumFreeListBitmaps];
ListHeader<Span> smallmem_lists[TlsHeapStatic::NumClassInfo];
public:
TlsHeapCentral() {
this->span_table.total_pages = 0;
}
errno_t Initialize(void *start, size_t size, bool use_virtual_memory);
bool IsClean();
errno_t ReallocateLargeMemory(void *ptr, size_t size, void **p);
errno_t ShrinkLargeMemory(void *ptr, size_t size);
void CalculateHeapHash(HeapHash *out);
errno_t AddThreadCache(TlsHeapCache *cache) {
std::scoped_lock lk(this->lock);
/* Add thread and recalculate. */
this->num_threads++;
this->dynamic_thread_quota = this->GetTotalHeapSize() / (2 * this->num_threads);
return 0;
}
errno_t RemoveThreadCache(TlsHeapCache *cache) {
std::scoped_lock lk(this->lock);
/* Remove thread and recalculate. */
this->num_threads--;
this->dynamic_thread_quota = this->GetTotalHeapSize() / (2 * this->num_threads);
return 0;
}
void *CacheLargeMemory(size_t size) {
std::scoped_lock lk(this->lock);
const size_t num_pages = util::AlignUp(size, TlsHeapStatic::PageSize) / TlsHeapStatic::PageSize;
if (Span *span = this->AllocatePagesImpl(num_pages); span != nullptr) {
return span->start.p;
} else {
return nullptr;
}
}
void *CacheLargeMemoryWithBigAlign(size_t size, size_t align) {
std::scoped_lock lk(this->lock);
const size_t num_pages = util::AlignUp(size, TlsHeapStatic::PageSize) / TlsHeapStatic::PageSize;
Span *span = nullptr;
if (align > TlsHeapStatic::PageSize) {
span = this->AllocatePagesWithBigAlignImpl(num_pages, align);
} else {
span = this->AllocatePagesImpl(num_pages);
}
if (span != nullptr) {
return span->start.p;
} else {
return nullptr;
}
}
void *CacheSmallMemory(size_t cls, size_t align = 0) {
std::scoped_lock lk(this->lock);
return this->CacheSmallMemoryImpl(cls, align, false);
}
void *CacheSmallMemoryForSystem(size_t cls) {
std::scoped_lock lk(this->lock);
return this->CacheSmallMemoryImpl(cls, 0, true);
}
size_t CacheSmallMemoryList(TlsHeapCache *cache, size_t *cls, size_t count, void **p, size_t align = 0) {
std::scoped_lock lk(this->lock);
s32 cpu_id = 0;
if (*cls < 8) {
getcpu(std::addressof(cpu_id));
}
return this->CacheSmallMemoryListImpl(cache, cls, count, p, cpu_id, 0);
}
bool CheckCachedSize(s32 size) const {
return size < this->dynamic_thread_quota && size < this->static_thread_quota;
}
void Dump(DumpMode dump_mode, int fd, bool json) {
std::scoped_lock lk(this->lock);
return this->DumpImpl(dump_mode, fd, json);
}
size_t GetAllocationSize(const void *ptr) {
if (TlsHeapStatic::IsPageAligned(ptr)) {
Span *span = nullptr;
{
std::scoped_lock lk(this->lock);
span = GetSpanFromPointer(std::addressof(this->span_table), ptr);
}
if (span != nullptr) {
return span->num_pages * TlsHeapStatic::PageSize;
} else {
AMS_ASSERT(span != nullptr);
return 0;
}
} else {
/* TODO: Handle error? */
return 0;
}
}
size_t GetClassFromPointer(const void *ptr) {
std::atomic_thread_fence(std::memory_order_acquire);
const size_t idx = (reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(this)) / TlsHeapStatic::PageSize;
if (idx < this->span_table.total_pages) {
if (ptr != nullptr) {
std::scoped_lock lk(this->lock);
Span *span = GetSpanFromPointer(std::addressof(this->span_table), ptr);
if (span != nullptr) {
AMS_ASSERT(span->page_class == this->span_table.pageclass_cache[idx]);
} else {
AMS_ASSERT(span != nullptr);
}
}
return this->span_table.pageclass_cache[idx];
} else {
/* TODO: Handle error? */
return 0xFFFFFFFF;
}
}
errno_t GetColor(const void *ptr, int *out) {
if (out == nullptr) {
return EINVAL;
}
std::scoped_lock lk(this->lock);
if (Span *span = GetSpanFromPointer(std::addressof(this->span_table), ptr); span != nullptr && !span->page_class) {
*out = (span->aux.large.color[0] << 0) | (span->aux.large.color[1] << 0) | (span->aux.large.color[2] << 16);
return 0;
} else {
return EINVAL;
}
}
errno_t SetColor(const void *ptr, int color) {
std::scoped_lock lk(this->lock);
if (Span *span = GetSpanFromPointer(std::addressof(this->span_table), ptr); span != nullptr && !span->page_class) {
span->aux.large.color[0] = (color >> 0) & 0xFF;
span->aux.large.color[1] = (color >> 8) & 0xFF;
span->aux.large.color[2] = (color >> 16) & 0xFF;
return 0;
} else {
return EINVAL;
}
}
errno_t GetMappedMemStats(size_t *out_free_size, size_t *out_max_allocatable_size) {
std::scoped_lock lk(this->lock);
return this->GetMappedMemStatsImpl(out_free_size, out_max_allocatable_size);
}
errno_t GetMemStats(TlsHeapMemStats *out) {
std::scoped_lock lk(this->lock);
return this->GetMemStatsImpl(out);
}
errno_t GetName(const void *ptr, char *dst, size_t dst_size) {
std::scoped_lock lk(this->lock);
if (Span *span = GetSpanFromPointer(std::addressof(this->span_table), ptr); span != nullptr && !span->page_class) {
strlcpy(dst, span->aux.large.name, dst_size);
return 0;
} else {
return EINVAL;
}
}
errno_t SetName(const void *ptr, const char *name) {
std::scoped_lock lk(this->lock);
if (Span *span = GetSpanFromPointer(std::addressof(this->span_table), ptr); span != nullptr && !span->page_class) {
strlcpy(span->aux.large.name, name, sizeof(span->aux.large.name));
return 0;
} else {
return EINVAL;
}
}
size_t GetTotalHeapSize() const {
return this->span_table.total_pages * TlsHeapStatic::PageSize;
}
errno_t UncacheLargeMemory(void *ptr) {
if (TlsHeapStatic::IsPageAligned(ptr)) {
std::scoped_lock lk(this->lock);
if (Span *span = GetSpanFromPointer(std::addressof(this->span_table), ptr); span != nullptr) {
this->FreePagesImpl(span);
return 0;
} else {
return EFAULT;
}
} else {
return EFAULT;
}
}
errno_t UncacheSmallMemory(void *ptr) {
std::scoped_lock lk(this->lock);
return this->UncacheSmallMemoryImpl(ptr);
}
errno_t UncacheSmallMemoryList(TlsHeapCache *cache, void *ptr) {
std::scoped_lock lk(this->lock);
while (true) {
if (ptr == nullptr) {
return 0;
}
ptr = cache->ManglePointer(ptr);
void *next = *reinterpret_cast<void **>(ptr);
if (auto err = this->UncacheSmallMemoryImpl(ptr); err != 0) {
return err;
}
ptr = next;
}
}
errno_t WalkAllocatedPointers(HeapWalkCallback callback, void *user_data) {
/* Explicitly handle locking, as we will release the lock during callback. */
this->lock.lock();
ON_SCOPE_EXIT { this->lock.unlock(); };
return this->WalkAllocatedPointersImpl(callback, user_data);
}
private:
SpanPage *AllocateSpanPage();
Span *AllocateSpanFromSpanPage(SpanPage *sp);
Span *SplitSpan(Span *span, size_t num_pages, Span *new_span);
void MergeFreeSpans(Span *span, Span *span_to_merge, uintptr_t start);
bool DestroySpanPageIfEmpty(SpanPage *sp, bool full);
Span *GetFirstSpan() const;
Span *MakeFreeSpan(size_t num_pages);
Span *SearchFreeSpan(size_t num_pages) const;
void FreeSpanToSpanPage(Span *span, SpanPage *sp);
void FreeSpanToSpanPage(Span *span);
void MergeIntoFreeList(Span *&span);
errno_t AllocatePhysical(void *start, size_t size);
errno_t FreePhysical(void *start, size_t size);
private:
Span *AllocatePagesImpl(size_t num_pages);
Span *AllocatePagesWithBigAlignImpl(size_t num_pages, size_t align);
void FreePagesImpl(Span *span);
void *CacheSmallMemoryImpl(size_t cls, size_t align, bool for_system);
errno_t UncacheSmallMemoryImpl(void *ptr);
size_t CacheSmallMemoryListImpl(TlsHeapCache *cache, size_t *cls, size_t count, void **p, s32 cpu_id, size_t align);
errno_t WalkAllocatedPointersImpl(HeapWalkCallback callback, void *user_data);
errno_t GetMappedMemStatsImpl(size_t *out_free_size, size_t *out_max_allocatable_size);
errno_t GetMemStatsImpl(TlsHeapMemStats *out);
void DumpImpl(DumpMode dump_mode, int fd, bool json);
private:
size_t FreeListFirstNonEmpty(size_t start) const {
if (start < FreeListCount) {
for (size_t i = FreeListAvailableIndex(start); i < util::size(this->freelists_bitmap); i++) {
const FreeListAvailableWord masked = this->freelists_bitmap[i] & ~(FreeListAvailableMask(start) - 1);
if (masked) {
const size_t b = __builtin_ctzll(masked);
const size_t res = i * BITSIZEOF(FreeListAvailableWord) + b;
AMS_ASSERT(res < FreeListCount);
return res;
}
start = (i + 1) * BITSIZEOF(FreeListAvailableWord);
}
}
return FreeListCount;
}
ALWAYS_INLINE void AddToFreeBlockList(Span *span) {
AMS_ASSERT(GetSpanPageSpan(GetSpanPage(span)) != span);
AMS_ASSERT(span->status == Span::Status_InFreeList);
const size_t which = std::min(span->num_pages, FreeListCount) - 1;
ListInsertAfter(std::addressof(this->freelists[which]), span);
this->freelists_bitmap[FreeListAvailableIndex(which)] |= FreeListAvailableMask(which);
}
ALWAYS_INLINE void RemoveFromFreeBlockList(Span *span) {
const size_t which = std::min(span->num_pages, FreeListCount) - 1;
ListRemoveSelf(span);
if (!ListGetNext(std::addressof(this->freelists[which]))) {
this->freelists_bitmap[FreeListAvailableIndex(which)] &= ~FreeListAvailableMask(which);
}
}
Span *AllocateSpanStruct() {
SpanPage *sp = ListGetNext(std::addressof(this->spanpage_list));
while (sp && (sp->info.is_sticky || !CanAllocateSpan(sp))) {
sp = ListGetNext(sp);
}
if (sp == nullptr) {
sp = this->AllocateSpanPage();
}
if (sp != nullptr) {
return this->AllocateSpanFromSpanPage(sp);
} else {
return nullptr;
}
}
s32 CallWalkCallback(HeapWalkCallback callback, void *ptr, size_t size, void *user_data) {
this->lock.unlock();
int res = callback(ptr, size, user_data);
this->lock.lock();
if (res) {
return 0;
} else {
return -1;
}
}
};
}

View File

@@ -0,0 +1,210 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "mem_impl_heap_platform.hpp"
namespace ams::mem::impl::heap {
class TlsHeapStatic {
public:
struct ClassInfo {
u16 num_pages;
u16 chunk_size;
};
static constexpr size_t NumClassInfo = 57;
static constexpr size_t MaxSizeWithClass = 0xC00;
static constexpr size_t ChunkGranularity = 0x10;
static constexpr size_t PageSize = 4_KB;
static constexpr size_t PhysicalPageSize = 256_KB;
public:
static constexpr inline std::array<ClassInfo, NumClassInfo> ClassInfos = {
ClassInfo{ .num_pages = 0, .chunk_size = 0x000, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x010, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x020, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x030, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x040, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x050, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x060, },
ClassInfo{ .num_pages = 2, .chunk_size = 0x070, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x080, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x090, },
ClassInfo{ .num_pages = 2, .chunk_size = 0x0A0, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x0B0, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x0C0, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x0D0, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x0E0, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x0F0, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x100, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x110, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x120, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x130, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x140, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x150, },
ClassInfo{ .num_pages = 2, .chunk_size = 0x160, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x170, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x180, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x190, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x1A0, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x1B0, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x1C0, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x1D0, },
ClassInfo{ .num_pages = 2, .chunk_size = 0x1E0, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x200, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x210, },
ClassInfo{ .num_pages = 2, .chunk_size = 0x220, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x240, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x260, },
ClassInfo{ .num_pages = 2, .chunk_size = 0x270, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x280, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x2A0, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x2D0, },
ClassInfo{ .num_pages = 2, .chunk_size = 0x2E0, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x300, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x330, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x360, },
ClassInfo{ .num_pages = 2, .chunk_size = 0x380, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x3B0, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x400, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x450, },
ClassInfo{ .num_pages = 2, .chunk_size = 0x490, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x4C0, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x550, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x600, },
ClassInfo{ .num_pages = 2, .chunk_size = 0x660, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x6D0, },
ClassInfo{ .num_pages = 1, .chunk_size = 0x800, },
ClassInfo{ .num_pages = 3, .chunk_size = 0x990, },
ClassInfo{ .num_pages = 2, .chunk_size = 0xAA0, },
};
static constexpr inline std::array<size_t, MaxSizeWithClass / ChunkGranularity> SizeToClass = [] {
std::array<size_t, MaxSizeWithClass / ChunkGranularity> arr = {};
arr[0] = 1;
for (size_t i = 1; i < arr.size(); i++) {
const size_t cur_size = i * ChunkGranularity;
for (size_t j = 0; j < ClassInfos.size(); j++) {
if (ClassInfos[j].chunk_size >= cur_size) {
arr[i] = j;
break;
}
}
}
return arr;
}();
public:
static constexpr ALWAYS_INLINE size_t GetClassFromSize(size_t size) {
AMS_ASSERT(size <= MaxSize);
const size_t idx = util::AlignUp(size, ChunkGranularity) / ChunkGranularity;
if (idx < MaxSizeWithClass / ChunkGranularity) {
return SizeToClass[idx];
} else {
return 0;
}
}
static constexpr ALWAYS_INLINE size_t GetRealSizeFromSizeAndAlignment(size_t size, size_t align) {
AMS_ASSERT(size <= MaxSize);
const size_t idx = util::AlignUp(size, ChunkGranularity) / ChunkGranularity;
if (size == 0 || idx >= MaxSizeWithClass / ChunkGranularity) {
return size;
}
const auto cls = SizeToClass[idx];
if (!cls) {
return PageSize;
}
AMS_ASSERT(align != 0);
const size_t mask = align - 1;
for (auto i = cls; i < ClassInfos.size(); i++) {
if ((ClassInfos[i].chunk_size & mask) == 0) {
return ClassInfos[i].chunk_size;
}
}
return PageSize;
}
static constexpr ALWAYS_INLINE bool IsPageAligned(uintptr_t ptr) {
return util::IsAligned(ptr, PageSize);
}
static ALWAYS_INLINE bool IsPageAligned(const void *ptr) {
return IsPageAligned(reinterpret_cast<uintptr_t>(ptr));
}
static constexpr ALWAYS_INLINE size_t GetPageIndex(uintptr_t ptr) {
return ptr / PageSize;
}
static constexpr ALWAYS_INLINE size_t GetPhysicalPageIndex(uintptr_t ptr) {
return ptr / PhysicalPageSize;
}
static constexpr ALWAYS_INLINE uintptr_t AlignUpPage(uintptr_t ptr) {
return util::AlignUp(ptr, PageSize);
}
template<typename T>
static ALWAYS_INLINE T *AlignUpPage(T *ptr) {
static_assert(std::is_pod<T>::value);
static_assert(util::IsAligned(PageSize, alignof(T)));
return reinterpret_cast<T *>(AlignUpPage(reinterpret_cast<uintptr_t>(ptr)));
}
static constexpr ALWAYS_INLINE uintptr_t AlignDownPage(uintptr_t ptr) {
return util::AlignDown(ptr, PageSize);
}
template<typename T>
static ALWAYS_INLINE T *AlignDownPage(T *ptr) {
static_assert(std::is_pod<T>::value);
static_assert(util::IsAligned(PageSize, alignof(T)));
return reinterpret_cast<T *>(AlignDownPage(reinterpret_cast<uintptr_t>(ptr)));
}
static constexpr ALWAYS_INLINE uintptr_t AlignUpPhysicalPage(uintptr_t ptr) {
return util::AlignUp(ptr, PhysicalPageSize);
}
template<typename T>
static ALWAYS_INLINE T *AlignUpPhysicalPage(T *ptr) {
static_assert(std::is_pod<T>::value);
static_assert(util::IsAligned(PhysicalPageSize, alignof(T)));
return reinterpret_cast<T *>(AlignUpPhysicalPage(reinterpret_cast<uintptr_t>(ptr)));
}
static constexpr ALWAYS_INLINE uintptr_t AlignDownPhysicalPage(uintptr_t ptr) {
return util::AlignDown(ptr, PhysicalPageSize);
}
template<typename T>
static ALWAYS_INLINE T *AlignDownPhysicalPage(T *ptr) {
static_assert(std::is_pod<T>::value);
static_assert(util::IsAligned(PhysicalPageSize, alignof(T)));
return reinterpret_cast<T *>(AlignDownPhysicalPage(reinterpret_cast<uintptr_t>(ptr)));
}
static constexpr ALWAYS_INLINE size_t GetChunkSize(size_t cls) {
return ClassInfos[cls].chunk_size;
}
static constexpr ALWAYS_INLINE size_t GetNumPages(size_t cls) {
return ClassInfos[cls].num_pages;
}
};
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::mem::impl {
enum Prot {
Prot_none = (0 << 0),
Prot_read = (1 << 0),
Prot_write = (1 << 1),
Prot_exec = (1 << 2),
};
errno_t virtual_alloc(void **ptr, size_t size);
errno_t virtual_free(void *ptr, size_t size);
errno_t physical_alloc(void *ptr, size_t size, Prot prot);
errno_t physical_free(void *ptr, size_t size);
size_t strlcpy(char *dst, const char *src, size_t size);
errno_t gen_random(void *dst, size_t dst_size);
errno_t epochtime(s64 *dst);
errno_t getcpu(s32 *out);
}

View File

@@ -0,0 +1,161 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "mem_impl_platform.hpp"
namespace ams::mem::impl {
namespace {
os::Mutex g_virt_mem_enabled_lock;
bool g_virt_mem_enabled_detected;
bool g_virt_mem_enabled;
void EnsureVirtualAddressMemoryDetected() {
std::scoped_lock lk(g_virt_mem_enabled_lock);
if (AMS_LIKELY(g_virt_mem_enabled_detected)) {
return;
}
g_virt_mem_enabled = os::IsVirtualAddressMemoryEnabled();
}
ALWAYS_INLINE bool IsVirtualAddressMemoryEnabled() {
EnsureVirtualAddressMemoryDetected();
return g_virt_mem_enabled;
}
ALWAYS_INLINE errno_t ConvertResult(Result result) {
/* TODO: Actually implement this in a meaningful way. */
if (R_FAILED(result)) {
return EINVAL;
}
return 0;
}
ALWAYS_INLINE os::MemoryPermission ConvertToOsPermission(Prot prot) {
static_assert(static_cast<int>(Prot_read) == static_cast<int>(os::MemoryPermission_ReadOnly));
static_assert(static_cast<int>(Prot_write) == static_cast<int>(os::MemoryPermission_WriteOnly));
return static_cast<os::MemoryPermission>(prot & os::MemoryPermission_ReadWrite);
}
}
errno_t virtual_alloc(void **ptr, size_t size) {
/* Ensure size isn't too large. */
if (size > mem::impl::MaxSize) {
return EINVAL;
}
/* Allocate virtual memory. */
uintptr_t addr;
if (IsVirtualAddressMemoryEnabled()) {
/* TODO: Support virtual address memory. */
AMS_ABORT("Virtual address memory not supported yet");
} else {
if (auto err = ConvertResult(os::AllocateMemoryBlock(std::addressof(addr), util::AlignUp(size, os::MemoryBlockUnitSize))); err != 0) {
return err;
}
os::SetMemoryPermission(addr, size, os::MemoryPermission_None);
}
return 0;
}
errno_t virtual_free(void *ptr, size_t size) {
/* Ensure size isn't zero. */
if (size == 0) {
return EINVAL;
}
if (IsVirtualAddressMemoryEnabled()) {
/* TODO: Support virtual address memory. */
AMS_ABORT("Virtual address memory not supported yet");
} else {
os::FreeMemoryBlock(reinterpret_cast<uintptr_t>(ptr), util::AlignUp(size, os::MemoryBlockUnitSize));
}
return 0;
}
errno_t physical_alloc(void *ptr, size_t size, Prot prot) {
/* Detect empty allocation. */
const uintptr_t aligned_start = util::AlignDown(reinterpret_cast<uintptr_t>(ptr), os::MemoryPageSize);
const uintptr_t aligned_end = util::AlignUp(reinterpret_cast<uintptr_t>(ptr) + size, os::MemoryPageSize);
const size_t aligned_size = aligned_end - aligned_start;
if (aligned_end <= aligned_start) {
return 0;
}
if (IsVirtualAddressMemoryEnabled()) {
/* TODO: Support virtual address memory. */
AMS_ABORT("Virtual address memory not supported yet");
} else {
os::SetMemoryPermission(aligned_start, aligned_size, ConvertToOsPermission(prot));
}
return 0;
}
errno_t physical_free(void *ptr, size_t size) {
/* Detect empty allocation. */
const uintptr_t aligned_start = util::AlignDown(reinterpret_cast<uintptr_t>(ptr), os::MemoryPageSize);
const uintptr_t aligned_end = util::AlignUp(reinterpret_cast<uintptr_t>(ptr) + size, os::MemoryPageSize);
const size_t aligned_size = aligned_end - aligned_start;
if (aligned_end <= aligned_start) {
return 0;
}
if (IsVirtualAddressMemoryEnabled()) {
/* TODO: Support virtual address memory. */
AMS_ABORT("Virtual address memory not supported yet");
} else {
os::SetMemoryPermission(aligned_start, aligned_size, os::MemoryPermission_None);
}
return 0;
}
size_t strlcpy(char *dst, const char *src, size_t size) {
const size_t src_size = std::strlen(src);
if (src_size >= size) {
if (size) {
std::memcpy(dst, src, size - 1);
dst[size - 1] = 0;
}
} else {
std::memcpy(dst, src, src_size + 1);
}
return src_size;
}
errno_t gen_random(void *dst, size_t dst_size) {
os::GenerateRandomBytes(dst, dst_size);
return 0;
}
errno_t epochtime(s64 *dst) {
/* TODO: What is this calc? */
auto ts = os::ConvertToTimeSpan(os::GetSystemTick());
*dst = (ts.GetNanoSeconds() / INT64_C(100)) + INT64_C(0x8A09F909AE60000);
return 0;
}
errno_t getcpu(s32 *out) {
*out = os::GetCurrentCoreNumber();
return 0;
}
}

View File

@@ -0,0 +1,344 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/mem_impl_platform.hpp"
#include "impl/heap/mem_impl_heap_tls_heap_static.hpp"
#include "impl/heap/mem_impl_heap_tls_heap_cache.hpp"
#include "impl/heap/mem_impl_heap_tls_heap_central.hpp"
namespace ams::mem {
constexpr inline size_t DefaultAlignment = alignof(std::max_align_t);
constexpr inline size_t MinimumAllocatorSize = 16_KB;
namespace {
void ThreadDestroy(uintptr_t arg) {
if (arg) {
reinterpret_cast<impl::heap::TlsHeapCache *>(arg)->Finalize();
}
}
ALWAYS_INLINE impl::heap::CentralHeap *GetCentral(const impl::InternalCentralHeapStorage *storage) {
return reinterpret_cast<impl::heap::CentralHeap *>(const_cast<impl::InternalCentralHeapStorage *>(storage));
}
ALWAYS_INLINE impl::heap::CentralHeap *GetCentral(const impl::InternalCentralHeapStorage &storage) {
return GetCentral(std::addressof(storage));
}
ALWAYS_INLINE void GetCache(impl::heap::CentralHeap *central, os::TlsSlot slot) {
impl::heap::CachedHeap tmp_cache;
if (central->MakeCache(std::addressof(tmp_cache))) {
impl::heap::TlsHeapCache *cache = tmp_cache.Release();
os::SetTlsValue(slot, reinterpret_cast<uintptr_t>(cache));
}
}
struct InternalHash {
size_t allocated_count;
size_t allocated_size;
crypto::Sha1Generator sha1;
};
int InternalHashCallback(void *ptr, size_t size, void *user_data) {
InternalHash *hash = reinterpret_cast<InternalHash *>(user_data);
hash->sha1.Update(reinterpret_cast<void *>(std::addressof(ptr)), sizeof(ptr));
hash->sha1.Update(reinterpret_cast<void *>(std::addressof(size)), sizeof(size));
hash->allocated_count++;
hash->allocated_size += size;
return 1;
}
}
StandardAllocator::StandardAllocator() : initialized(false), enable_thread_cache(false), unused(0) {
static_assert(sizeof(impl::heap::CentralHeap) <= sizeof(this->central_heap_storage));
new (std::addressof(this->central_heap_storage)) impl::heap::CentralHeap;
}
StandardAllocator::StandardAllocator(void *mem, size_t size) : StandardAllocator() {
this->Initialize(mem, size);
}
StandardAllocator::StandardAllocator(void *mem, size_t size, bool enable_cache) : StandardAllocator() {
this->Initialize(mem, size, enable_cache);
}
void StandardAllocator::Initialize(void *mem, size_t size) {
this->Initialize(mem, size, false);
}
void StandardAllocator::Initialize(void *mem, size_t size, bool enable_cache) {
AMS_ABORT_UNLESS(!this->initialized);
const uintptr_t aligned_start = util::AlignUp(reinterpret_cast<uintptr_t>(mem), impl::heap::TlsHeapStatic::PageSize);
const uintptr_t aligned_end = util::AlignDown(reinterpret_cast<uintptr_t>(mem) + size, impl::heap::TlsHeapStatic::PageSize);
const size_t aligned_size = aligned_end - aligned_start;
if (mem == nullptr) {
AMS_ABORT_UNLESS(os::IsVirtualAddressMemoryEnabled());
AMS_ABORT_UNLESS(GetCentral(this->central_heap_storage)->Initialize(nullptr, size, 0) == 0);
} else {
AMS_ABORT_UNLESS(aligned_start < aligned_end);
AMS_ABORT_UNLESS(aligned_size >= MinimumAllocatorSize);
AMS_ABORT_UNLESS(GetCentral(this->central_heap_storage)->Initialize(reinterpret_cast<void *>(aligned_start), aligned_size, 0) == 0);
}
this->enable_thread_cache = enable_cache;
if (this->enable_thread_cache) {
R_ABORT_UNLESS(os::AllocateTlsSlot(std::addressof(this->tls_slot), ThreadDestroy));
}
this->initialized = true;
}
void StandardAllocator::Finalize() {
AMS_ABORT_UNLESS(this->initialized);
if (this->enable_thread_cache) {
os::FreeTlsSlot(this->tls_slot);
}
GetCentral(this->central_heap_storage)->Finalize();
this->initialized = false;
}
void *StandardAllocator::Allocate(size_t size) {
AMS_ASSERT(this->initialized);
return this->Allocate(size, DefaultAlignment);
}
void *StandardAllocator::Allocate(size_t size, size_t alignment) {
AMS_ASSERT(this->initialized);
impl::heap::TlsHeapCache *heap_cache = nullptr;
if (this->enable_thread_cache) {
heap_cache = reinterpret_cast<impl::heap::TlsHeapCache *>(os::GetTlsValue(this->tls_slot));
if (!heap_cache) {
GetCache(GetCentral(this->central_heap_storage), this->tls_slot);
heap_cache = reinterpret_cast<impl::heap::TlsHeapCache *>(os::GetTlsValue(this->tls_slot));
}
}
void *ptr = nullptr;
if (heap_cache) {
ptr = heap_cache->Allocate(size, alignment);
if (ptr) {
return ptr;
}
impl::heap::CachedHeap cache;
cache.Reset(heap_cache);
cache.Query(impl::AllocQuery_FinalizeCache);
os::SetTlsValue(this->tls_slot, 0);
}
return GetCentral(this->central_heap_storage)->Allocate(size, alignment);
}
void StandardAllocator::Free(void *ptr) {
AMS_ASSERT(this->initialized);
if (ptr == nullptr) {
return;
}
if (this->enable_thread_cache) {
impl::heap::TlsHeapCache *heap_cache = reinterpret_cast<impl::heap::TlsHeapCache *>(os::GetTlsValue(this->tls_slot));
if (heap_cache) {
heap_cache->Free(ptr);
return;
}
}
auto err = GetCentral(this->central_heap_storage)->Free(ptr);
AMS_ASSERT(err == 0);
}
void *StandardAllocator::Reallocate(void *ptr, size_t new_size) {
AMS_ASSERT(this->initialized);
if (new_size > impl::MaxSize) {
return nullptr;
}
if (ptr == nullptr) {
return this->Allocate(new_size);
}
if (new_size == 0) {
this->Free(ptr);
return nullptr;
}
size_t aligned_new_size = util::AlignUp(new_size, DefaultAlignment);
impl::heap::TlsHeapCache *heap_cache = nullptr;
if (this->enable_thread_cache) {
heap_cache = reinterpret_cast<impl::heap::TlsHeapCache *>(os::GetTlsValue(this->tls_slot));
if (!heap_cache) {
GetCache(GetCentral(this->central_heap_storage), this->tls_slot);
heap_cache = reinterpret_cast<impl::heap::TlsHeapCache *>(os::GetTlsValue(this->tls_slot));
}
}
void *p = nullptr;
impl::errno_t err;
if (heap_cache) {
err = heap_cache->Reallocate(ptr, aligned_new_size, std::addressof(p));
} else {
err = GetCentral(this->central_heap_storage)->Reallocate(ptr, aligned_new_size, std::addressof(p));
}
if (err == 0) {
return p;
} else {
return nullptr;
}
}
size_t StandardAllocator::Shrink(void *ptr, size_t new_size) {
AMS_ASSERT(this->initialized);
if (this->enable_thread_cache) {
impl::heap::TlsHeapCache *heap_cache = reinterpret_cast<impl::heap::TlsHeapCache *>(os::GetTlsValue(this->tls_slot));
if (heap_cache) {
if (heap_cache->Shrink(ptr, new_size) == 0) {
return heap_cache->GetAllocationSize(ptr);
} else {
return 0;
}
}
}
if (GetCentral(this->central_heap_storage)->Shrink(ptr, new_size) == 0) {
return GetCentral(this->central_heap_storage)->GetAllocationSize(ptr);
} else {
return 0;
}
}
void StandardAllocator::ClearThreadCache() const {
if (this->enable_thread_cache) {
impl::heap::TlsHeapCache *heap_cache = reinterpret_cast<impl::heap::TlsHeapCache *>(os::GetTlsValue(this->tls_slot));
impl::heap::CachedHeap cache;
cache.Reset(heap_cache);
cache.Query(impl::AllocQuery_ClearCache);
cache.Release();
}
}
void StandardAllocator::CleanUpManagementArea() const {
AMS_ASSERT(this->initialized);
auto err = GetCentral(this->central_heap_storage)->Query(impl::AllocQuery_UnifyFreeList);
AMS_ASSERT(err == 0);
}
size_t StandardAllocator::GetSizeOf(const void *ptr) const {
AMS_ASSERT(this->initialized);
if (!util::IsAligned(reinterpret_cast<uintptr_t>(ptr), DefaultAlignment)) {
return 0;
}
impl::heap::TlsHeapCache *heap_cache = nullptr;
if (this->enable_thread_cache) {
heap_cache = reinterpret_cast<impl::heap::TlsHeapCache *>(os::GetTlsValue(this->tls_slot));
if (!heap_cache) {
GetCache(GetCentral(this->central_heap_storage), this->tls_slot);
heap_cache = reinterpret_cast<impl::heap::TlsHeapCache *>(os::GetTlsValue(this->tls_slot));
}
}
if (heap_cache) {
return heap_cache->GetAllocationSize(ptr);
} else {
return GetCentral(this->central_heap_storage)->GetAllocationSize(ptr);
}
}
size_t StandardAllocator::GetTotalFreeSize() const {
size_t size = 0;
auto err = GetCentral(this->central_heap_storage)->Query(impl::AllocQuery_FreeSizeMapped, std::addressof(size));
if (err != 0) {
err = GetCentral(this->central_heap_storage)->Query(impl::AllocQuery_FreeSize, std::addressof(size));
}
AMS_ASSERT(err == 0);
return size;
}
size_t StandardAllocator::GetAllocatableSize() const {
size_t size = 0;
auto err = GetCentral(this->central_heap_storage)->Query(impl::AllocQuery_MaxAllocatableSizeMapped, std::addressof(size));
if (err != 0) {
err = GetCentral(this->central_heap_storage)->Query(impl::AllocQuery_MaxAllocatableSize, std::addressof(size));
}
AMS_ASSERT(err == 0);
return size;
}
void StandardAllocator::WalkAllocatedBlocks(WalkCallback callback, void *user_data) const {
AMS_ASSERT(this->initialized);
this->ClearThreadCache();
GetCentral(this->central_heap_storage)->WalkAllocatedPointers(callback, user_data);
}
void StandardAllocator::Dump() const {
AMS_ASSERT(this->initialized);
size_t tmp;
auto err = GetCentral(this->central_heap_storage)->Query(impl::AllocQuery_MaxAllocatableSizeMapped, std::addressof(tmp));
if (err == 0) {
GetCentral(this->central_heap_storage)->Query(impl::AllocQuery_Dump, impl::DumpMode_Spans | impl::DumpMode_Pointers, 1);
} else {
GetCentral(this->central_heap_storage)->Query(impl::AllocQuery_Dump, impl::DumpMode_All, 1);
}
}
StandardAllocator::AllocatorHash StandardAllocator::Hash() const {
AMS_ASSERT(this->initialized);
AllocatorHash alloc_hash;
{
char temp_hash[crypto::Sha1Generator::HashSize];
InternalHash internal_hash;
internal_hash.allocated_count = 0;
internal_hash.allocated_size = 0;
internal_hash.sha1.Initialize();
this->WalkAllocatedBlocks(InternalHashCallback, reinterpret_cast<void *>(std::addressof(internal_hash)));
alloc_hash.allocated_count = internal_hash.allocated_count;
alloc_hash.allocated_size = internal_hash.allocated_size;
internal_hash.sha1.GetHash(temp_hash, sizeof(temp_hash));
std::memcpy(std::addressof(alloc_hash.hash), temp_hash, sizeof(alloc_hash.hash));
}
return alloc_hash;
}
}

View File

@@ -73,12 +73,11 @@ namespace ams::ncm {
/* Allocate space to hold the converted meta. */
const size_t meta_size = package_meta_reader.CalculateConvertContentMetaSize();
void *meta = std::malloc(meta_size);
ON_SCOPE_EXIT { std::free(meta); };
std::unique_ptr<char[]> meta(new (std::nothrow) char[meta_size]);
/* Convert the meta from packaged form to normal form. */
package_meta_reader.ConvertToContentMeta(meta, meta_size, meta_info);
ncm::ContentMetaReader meta_reader(meta, meta_size);
package_meta_reader.ConvertToContentMeta(meta.get(), meta_size, meta_info);
ncm::ContentMetaReader meta_reader(meta.get(), meta_size);
/* Insert the new metas into the database. */
R_TRY(this->db->Set(package_meta_reader.GetKey(), meta_reader.GetData(), meta_reader.GetSize()));

View File

@@ -24,6 +24,14 @@ namespace ams::ncm {
namespace {
alignas(os::MemoryPageSize) u8 g_system_content_meta_database_heap[512_KB];
alignas(os::MemoryPageSize) u8 g_gamecard_content_meta_database_heap[512_KB];
alignas(os::MemoryPageSize) u8 g_sd_and_user_content_meta_database_heap[2_MB + 512_KB];
ContentMetaMemoryResource g_system_content_meta_memory_resource(g_system_content_meta_database_heap, sizeof(g_system_content_meta_database_heap));
ContentMetaMemoryResource g_gamecard_content_meta_memory_resource(g_gamecard_content_meta_database_heap, sizeof(g_gamecard_content_meta_database_heap));
ContentMetaMemoryResource g_sd_and_user_content_meta_memory_resource(g_sd_and_user_content_meta_database_heap, sizeof(g_sd_and_user_content_meta_database_heap));
constexpr fs::SystemSaveDataId BuiltInSystemSaveDataId = 0x8000000000000120;
constexpr u64 BuiltInSystemSaveDataSize = 0x6c000;
constexpr u64 BuiltInSystemSaveDataJournalSize = 0x6c000;
@@ -216,10 +224,11 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ContentManagerImpl::InitializeContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, StorageId storage_id, const SystemSaveDataInfo &info, size_t max_content_metas) {
Result ContentManagerImpl::InitializeContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, StorageId storage_id, const SystemSaveDataInfo &info, size_t max_content_metas, ContentMetaMemoryResource *memory_resource) {
out->storage_id = storage_id;
out->info = info;
out->max_content_metas = max_content_metas;
out->memory_resource = memory_resource;
out->content_meta_database = nullptr;
out->kvs = std::nullopt;
@@ -231,9 +240,10 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ContentManagerImpl::InitializeGameCardContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, size_t max_content_metas) {
Result ContentManagerImpl::InitializeGameCardContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, size_t max_content_metas, ContentMetaMemoryResource *memory_resource) {
out->storage_id = StorageId::GameCard;
out->max_content_metas = max_content_metas;
out->memory_resource = memory_resource;
out->content_meta_database = nullptr;
out->kvs = std::nullopt;
@@ -332,7 +342,7 @@ namespace ams::ncm {
R_TRY(this->ActivateContentStorage(StorageId::BuiltInSystem));
/* Next, the BuiltInSystem content meta entry. */
R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[this->num_content_meta_entries++], StorageId::BuiltInSystem, BuiltInSystemSystemSaveDataInfo, MaxBuiltInSystemContentMetaCount));
R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[this->num_content_meta_entries++], StorageId::BuiltInSystem, BuiltInSystemSystemSaveDataInfo, MaxBuiltInSystemContentMetaCount, std::addressof(g_system_content_meta_memory_resource)));
if (R_FAILED(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem))) {
R_TRY(this->CreateContentMetaDatabase(StorageId::BuiltInSystem));
@@ -360,18 +370,18 @@ namespace ams::ncm {
/* Now for BuiltInUser's content storage and content meta entries. */
R_TRY(this->InitializeContentStorageRoot(&this->content_storage_roots[this->num_content_storage_entries++], StorageId::BuiltInUser, fs::ContentStorageId::User));
R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[this->num_content_meta_entries++], StorageId::BuiltInUser, BuiltInUserSystemSaveDataInfo, MaxBuiltInUserContentMetaCount));
R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[this->num_content_meta_entries++], StorageId::BuiltInUser, BuiltInUserSystemSaveDataInfo, MaxBuiltInUserContentMetaCount, std::addressof(g_sd_and_user_content_meta_memory_resource)));
/* Beyond this point, N uses hardcoded indices. */
/* Next SdCard's content storage and content meta entries. */
R_TRY(this->InitializeContentStorageRoot(&this->content_storage_roots[2], StorageId::SdCard, fs::ContentStorageId::SdCard));
R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[2], StorageId::SdCard, SdCardSystemSaveDataInfo, MaxSdCardContentMetaCount));
R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[2], StorageId::SdCard, SdCardSystemSaveDataInfo, MaxSdCardContentMetaCount, std::addressof(g_sd_and_user_content_meta_memory_resource)));
/* GameCard's content storage and content meta entries. */
/* N doesn't set a content storage id for game cards, so we'll just use 0 (System). */
R_TRY(this->InitializeGameCardContentStorageRoot(&this->content_storage_roots[3]));
R_TRY(this->InitializeGameCardContentMetaDatabaseRoot(&this->content_meta_database_roots[3], MaxGameCardContentMetaCount));
R_TRY(this->InitializeGameCardContentMetaDatabaseRoot(&this->content_meta_database_roots[3], MaxGameCardContentMetaCount, std::addressof(g_gamecard_content_meta_memory_resource)));
this->initialized = true;
return ResultSuccess();
@@ -609,7 +619,7 @@ namespace ams::ncm {
if (storage_id == StorageId::GameCard) {
/* Initialize the key value store. */
R_TRY(root->kvs->Initialize(root->max_content_metas));
R_TRY(root->kvs->Initialize(root->max_content_metas, root->memory_resource->Get()));
/* Create an on memory content meta database for game cards. */
root->content_meta_database = std::make_shared<OnMemoryContentMetaDatabaseImpl>(std::addressof(*root->kvs));
@@ -621,7 +631,7 @@ namespace ams::ncm {
auto mount_guard = SCOPE_GUARD { fs::Unmount(root->mount_name); };
/* Initialize and load the key value store from the filesystem. */
R_TRY(root->kvs->Initialize(root->path, root->max_content_metas));
R_TRY(root->kvs->Initialize(root->path, root->max_content_metas, root->memory_resource->Get()));
R_TRY(root->kvs->Load());
/* Create the content meta database. */

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2019-2020 Adubbz, Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::nim {
namespace {
bool g_initialized;
}
/* Management. */
void InitializeForNetworkInstallManager() {
AMS_ASSERT(!g_initialized);
R_ABORT_UNLESS(nimInitialize());
g_initialized = true;
}
void FinalizeForNetworkInstallManager() {
AMS_ASSERT(g_initialized);
nimExit();
g_initialized = false;
}
/* Service API. */
Result DestroySystemUpdateTask(const SystemUpdateTaskId& id) {
static_assert(sizeof(SystemUpdateTaskId) == sizeof(::NimSystemUpdateTaskId));
return nimDestroySystemUpdateTask(reinterpret_cast<const ::NimSystemUpdateTaskId *>(std::addressof(id)));
}
s32 ListSystemUpdateTask(SystemUpdateTaskId *out_list, size_t out_list_size) {
static_assert(sizeof(SystemUpdateTaskId) == sizeof(::NimSystemUpdateTaskId));
s32 count;
R_ABORT_UNLESS(nimListSystemUpdateTask(std::addressof(count), reinterpret_cast<::NimSystemUpdateTaskId *>(out_list), out_list_size));
return count;
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::os::impl {
void SetMemoryPermissionImpl(uintptr_t address, size_t size, MemoryPermission perm);
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::os::impl {
namespace {
void SetMemoryPermissionBySvc(uintptr_t start, size_t size, svc::MemoryPermission perm) {
uintptr_t cur_address = start;
size_t remaining_size = size;
while (remaining_size > 0) {
svc::MemoryInfo mem_info;
svc::PageInfo page_info;
R_ABORT_UNLESS(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), cur_address));
size_t cur_size = std::min(mem_info.addr + mem_info.size - cur_address, remaining_size);
if (mem_info.perm != perm) {
R_ABORT_UNLESS(svc::SetMemoryPermission(cur_address, cur_size, perm));
}
cur_address += cur_size;
remaining_size -= cur_size;
}
}
}
void SetMemoryPermissionImpl(uintptr_t address, size_t size, MemoryPermission perm) {
switch (perm) {
case MemoryPermission_None:
return SetMemoryPermissionBySvc(address, size, svc::MemoryPermission_None);
case MemoryPermission_ReadOnly:
return SetMemoryPermissionBySvc(address, size, svc::MemoryPermission_Read);
case MemoryPermission_ReadWrite:
return SetMemoryPermissionBySvc(address, size, svc::MemoryPermission_ReadWrite);
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::os {
Result AllocateMemoryBlock(uintptr_t *out_address, size_t size) {
AMS_ABORT("Not implemented yet");
}
void FreeMemoryBlock(uintptr_t address, size_t size) {
AMS_ABORT("Not implemented yet");
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/os_memory_permission_impl.hpp"
namespace ams::os {
void SetMemoryPermission(uintptr_t address, size_t size, MemoryPermission perm) {
return impl::SetMemoryPermissionImpl(address, size, perm);
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::os {
/* TODO: How will this work without libnx? */
namespace {
using LibnxTlsDestructor = void (*)(void *);
}
Result AllocateTlsSlot(TlsSlot *out, TlsDestructor destructor) {
s32 slot = ::threadTlsAlloc(reinterpret_cast<LibnxTlsDestructor>(destructor));
R_UNLESS(slot >= 0, os::ResultOutOfResource());
*out = { static_cast<u32>(slot) };
return ResultSuccess();
}
void FreeTlsSlot(TlsSlot slot) {
::threadTlsFree(static_cast<s32>(slot._value));
}
uintptr_t GetTlsValue(TlsSlot slot) {
return reinterpret_cast<uintptr_t>(::threadTlsGet(static_cast<s32>(slot._value)));
}
void SetTlsValue(TlsSlot slot, uintptr_t value) {
::threadTlsSet(static_cast<s32>(slot._value), reinterpret_cast<void *>(value));
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::os {
namespace {
/* TODO: Remove, add VammManager */
size_t GetSystemResourceSize() {
u64 v;
if (R_SUCCEEDED(svcGetInfo(std::addressof(v), InfoType_SystemResourceSizeTotal, CUR_PROCESS_HANDLE, 0))) {
return v;
} else {
return 0;
}
}
}
bool IsVirtualAddressMemoryEnabled() {
return GetSystemResourceSize() > 0;
}
}

View File

@@ -33,7 +33,6 @@ namespace ams::updater {
bool HasEks(BootImageUpdateType boot_image_update_type);
bool HasAutoRcmPreserve(BootImageUpdateType boot_image_update_type);
ncm::ContentMetaType GetContentMetaType(BootModeType mode);
Result GetBootImagePackageId(ncm::SystemDataId *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size);
/* Verification Prototypes. */
Result GetVerificationState(VerificationState *out, void *work_buffer, size_t work_buffer_size);
@@ -42,8 +41,7 @@ namespace ams::updater {
Result VerifyBootImagesSafe(ncm::SystemDataId data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
/* Update Prototypes. */
Result SetVerificationNeeded(BootModeType mode, bool needed, void *work_buffer, size_t work_buffer_size);
Result UpdateBootImages(ncm::SystemDataId data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
Result SetVerificationNeeded(BootModeType mode, void *work_buffer, size_t work_buffer_size, bool needed);
Result UpdateBootImagesNormal(ncm::SystemDataId data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
Result UpdateBootImagesSafe(ncm::SystemDataId data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
@@ -123,45 +121,12 @@ namespace ams::updater {
R_CATCH(ResultNeedsRepairBootImages) {
/* Perform repair. */
*out_repaired = true;
R_TRY(UpdateBootImages(bip_data_id, mode, work_buffer, work_buffer_size, boot_image_update_type));
R_TRY(UpdateBootImagesFromPackage(bip_data_id, mode, work_buffer, work_buffer_size, boot_image_update_type));
}
} R_END_TRY_CATCH;
/* We've either just verified or just repaired. Either way, we don't need to verify any more. */
return SetVerificationNeeded(mode, false, work_buffer, work_buffer_size);
}
Result GetBootImagePackageId(ncm::SystemDataId *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size) {
/* Ensure we can read content metas. */
constexpr size_t MaxContentMetas = 0x40;
AMS_ABORT_UNLESS(work_buffer_size >= sizeof(ncm::ContentMetaKey) * MaxContentMetas);
/* Open NAND System meta database, list contents. */
ncm::ContentMetaDatabase db;
R_TRY(ncm::OpenContentMetaDatabase(std::addressof(db), ncm::StorageId::BuiltInSystem));
ncm::ContentMetaKey *keys = reinterpret_cast<ncm::ContentMetaKey *>(work_buffer);
const auto content_meta_type = GetContentMetaType(mode);
auto count = db.ListContentMeta(keys, MaxContentMetas, content_meta_type);
R_UNLESS(count.total > 0, ResultBootImagePackageNotFound());
/* Output is sorted, return the lowest valid exfat entry. */
if (count.total > 1) {
for (auto i = 0; i < count.total; i++) {
u8 attr;
R_TRY(db.GetAttributes(std::addressof(attr), keys[i]));
if (attr & ncm::ContentMetaAttribute_IncludesExFatDriver) {
out_data_id->value = keys[i].id;
return ResultSuccess();
}
}
}
/* If there's only one entry or no exfat entries, return that entry. */
out_data_id->value = keys[0].id;
return ResultSuccess();
return SetVerificationNeeded(mode, work_buffer, work_buffer_size, false);
}
Result VerifyBootImages(ncm::SystemDataId data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) {
@@ -281,16 +246,6 @@ namespace ams::updater {
return ResultSuccess();
}
Result UpdateBootImages(ncm::SystemDataId data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) {
switch (mode) {
case BootModeType::Normal:
return UpdateBootImagesNormal(data_id, work_buffer, work_buffer_size, boot_image_update_type);
case BootModeType::Safe:
return UpdateBootImagesSafe(data_id, work_buffer, work_buffer_size, boot_image_update_type);
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
Result UpdateBootImagesNormal(ncm::SystemDataId data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) {
/* Ensure work buffer is big enough for us to do what we want to do. */
R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size));
@@ -409,7 +364,7 @@ namespace ams::updater {
return ResultSuccess();
}
Result SetVerificationNeeded(BootModeType mode, bool needed, void *work_buffer, size_t work_buffer_size) {
Result SetVerificationNeeded(BootModeType mode, void *work_buffer, size_t work_buffer_size, bool needed) {
/* Ensure work buffer is big enough for us to do what we want to do. */
R_TRY(ValidateWorkBuffer(work_buffer, work_buffer_size));
@@ -485,6 +440,67 @@ namespace ams::updater {
}
}
BootImageUpdateType GetBootImageUpdateType(int boot_image_update_type) {
switch (boot_image_update_type) {
case 0:
return BootImageUpdateType::Erista;
case 1:
return BootImageUpdateType::Mariko;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
Result GetBootImagePackageId(ncm::SystemDataId *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size) {
/* Ensure we can read content metas. */
constexpr size_t MaxContentMetas = 0x40;
AMS_ABORT_UNLESS(work_buffer_size >= sizeof(ncm::ContentMetaKey) * MaxContentMetas);
/* Open NAND System meta database, list contents. */
ncm::ContentMetaDatabase db;
R_TRY(ncm::OpenContentMetaDatabase(std::addressof(db), ncm::StorageId::BuiltInSystem));
ncm::ContentMetaKey *keys = reinterpret_cast<ncm::ContentMetaKey *>(work_buffer);
const auto content_meta_type = GetContentMetaType(mode);
auto count = db.ListContentMeta(keys, MaxContentMetas, content_meta_type);
R_UNLESS(count.total > 0, ResultBootImagePackageNotFound());
/* Output is sorted, return the lowest valid exfat entry. */
if (count.total > 1) {
for (auto i = 0; i < count.total; i++) {
u8 attr;
R_TRY(db.GetAttributes(std::addressof(attr), keys[i]));
if (attr & ncm::ContentMetaAttribute_IncludesExFatDriver) {
out_data_id->value = keys[i].id;
return ResultSuccess();
}
}
}
/* If there's only one entry or no exfat entries, return that entry. */
out_data_id->value = keys[0].id;
return ResultSuccess();
}
Result MarkVerifyingRequired(BootModeType mode, void *work_buffer, size_t work_buffer_size) {
return SetVerificationNeeded(mode, work_buffer, work_buffer_size, true);
}
Result MarkVerified(BootModeType mode, void *work_buffer, size_t work_buffer_size) {
return SetVerificationNeeded(mode, work_buffer, work_buffer_size, false);
}
Result UpdateBootImagesFromPackage(ncm::SystemDataId data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) {
switch (mode) {
case BootModeType::Normal:
return UpdateBootImagesNormal(data_id, work_buffer, work_buffer_size, boot_image_update_type);
case BootModeType::Safe:
return UpdateBootImagesSafe(data_id, work_buffer, work_buffer_size, boot_image_update_type);
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
Result VerifyBootImagesAndRepairIfNeeded(bool *out_repaired_normal, bool *out_repaired_safe, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) {
/* Always set output to false before doing anything else. */
*out_repaired_normal = false;

View File

@@ -19,6 +19,7 @@
#include <vapours/assert.hpp>
#include <vapours/literals.hpp>
#include <vapours/allocator.hpp>
#include <vapours/timespan.hpp>
#include <vapours/span.hpp>

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
namespace ams {
constexpr inline size_t DefaultAlignment = alignof(max_align_t);
using AllocateFunction = void *(*)(size_t);
using AllocateFunctionWithUserData = void *(*)(size_t, void *);
using AlignedAllocateFunction = void *(*)(size_t, size_t);
using AlignedAllocateFunctionWithUserData = void *(*)(size_t, size_t, void *);
using DeallocateFunction = void (*)(void *, size_t);
using FreeFunction = void (*)(void *);
using FreeFunctionWithUserData = void (*)(void *, void *);
class MemoryResource {
public:
ALWAYS_INLINE void *allocate(size_t size, size_t alignment = DefaultAlignment) {
return this->AllocateImpl(size, alignment);
}
ALWAYS_INLINE void deallocate(void *buffer, size_t size, size_t alignment = DefaultAlignment) {
this->DeallocateImpl(buffer, size, alignment);
}
ALWAYS_INLINE bool is_equal(const MemoryResource &resource) const {
return this->IsEqualImpl(resource);
}
ALWAYS_INLINE void *Allocate(size_t size, size_t alignment = DefaultAlignment) {
return this->AllocateImpl(size, alignment);
}
ALWAYS_INLINE void Deallocate(void *buffer, size_t size, size_t alignment = DefaultAlignment) {
this->DeallocateImpl(buffer, size, alignment);
}
ALWAYS_INLINE bool IsEqual(const MemoryResource &resource) const {
return this->IsEqualImpl(resource);
}
protected:
virtual void *AllocateImpl(size_t size, size_t alignment) = 0;
virtual void DeallocateImpl(void *buffer, size_t size, size_t alignment) = 0;
virtual bool IsEqualImpl(const MemoryResource &resource) const = 0;
};
}

View File

@@ -46,11 +46,11 @@ namespace ams::diag {
#endif
#ifdef AMS_ENABLE_ASSERTIONS
#define AMS_ASSERT_IMPL(expr, ...) \
({ \
if (const bool __tmp_ams_assert_val = (expr); AMS_UNLIKELY(!__tmp_ams_assert_val)) { \
AMS_CALL_ASSERT_FAIL_IMPL(#expr, ## __VA_ARGS__); \
} \
#define AMS_ASSERT_IMPL(expr, ...) \
({ \
if (const bool __tmp_ams_assert_val = static_cast<bool>(expr); AMS_UNLIKELY(!__tmp_ams_assert_val)) { \
AMS_CALL_ASSERT_FAIL_IMPL(#expr, ## __VA_ARGS__); \
} \
})
#else
#define AMS_ASSERT_IMPL(expr, ...) AMS_UNUSED(expr, ## __VA_ARGS__)
@@ -68,9 +68,9 @@ namespace ams::diag {
#define AMS_ABORT(...) AMS_CALL_ABORT_IMPL("", ## __VA_ARGS__)
#define AMS_ABORT_UNLESS(expr, ...) \
({ \
if (const bool __tmp_ams_assert_val = (expr); AMS_UNLIKELY(!__tmp_ams_assert_val)) { \
AMS_CALL_ABORT_IMPL(#expr, ##__VA_ARGS__); \
} \
#define AMS_ABORT_UNLESS(expr, ...) \
({ \
if (const bool __tmp_ams_assert_val = static_cast<bool>(expr); AMS_UNLIKELY(!__tmp_ams_assert_val)) { \
AMS_CALL_ABORT_IMPL(#expr, ##__VA_ARGS__); \
} \
})

View File

@@ -19,6 +19,7 @@
#include <vapours/crypto/crypto_memory_compare.hpp>
#include <vapours/crypto/crypto_memory_clear.hpp>
#include <vapours/crypto/crypto_sha1_generator.hpp>
#include <vapours/crypto/crypto_sha256_generator.hpp>
#include <vapours/crypto/crypto_rsa_pss_sha256_verifier.hpp>
#include <vapours/crypto/crypto_rsa_oaep_sha256_decoder.hpp>

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/util.hpp>
#include <vapours/crypto/impl/crypto_sha1_impl.hpp>
namespace ams::crypto {
class Sha1Generator {
NON_COPYABLE(Sha1Generator);
NON_MOVEABLE(Sha1Generator);
private:
using Impl = impl::Sha1Impl;
public:
static constexpr size_t HashSize = Impl::HashSize;
static constexpr size_t BlockSize = Impl::BlockSize;
static constexpr inline u8 Asn1Identifier[] = {
0x30, 0x21, /* Sequence, size 0x21 */
0x30, 0x09, /* Sequence, size 0x09 */
0x06, 0x05, /* Object Identifier */
0x2B, 0x0E, 0x03, 0x02, 0x1A, /* SHA-1 */
0x05, 0x00, /* Null */
0x04, 0x14, /* Octet string, size 0x14 */
};
static constexpr size_t Asn1IdentifierSize = util::size(Asn1Identifier);
private:
Impl impl;
public:
Sha1Generator() { /* ... */ }
void Initialize() {
this->impl.Initialize();
}
void Update(const void *data, size_t size) {
this->impl.Update(data, size);
}
void GetHash(void *dst, size_t size) {
this->impl.GetHash(dst, size);
}
};
void GenerateSha1Hash(void *dst, size_t dst_size, const void *src, size_t src_size);
}

View File

@@ -28,6 +28,8 @@ namespace ams::crypto {
};
class Sha256Generator {
NON_COPYABLE(Sha256Generator);
NON_MOVEABLE(Sha256Generator);
private:
using Impl = impl::Sha256Impl;
public:
@@ -39,7 +41,7 @@ namespace ams::crypto {
0x30, 0x0D, /* Sequence, size 0x0D */
0x06, 0x09, /* Object Identifier */
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, /* SHA-256 */
0x00, /* Null */
0x05, 0x00, /* Null */
0x04, 0x20, /* Octet string, size 0x20 */
};
static constexpr size_t Asn1IdentifierSize = util::size(Asn1Identifier);

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/util.hpp>
#include <vapours/crypto/impl/crypto_hash_function.hpp>
#include <vapours/crypto/crypto_memory_clear.hpp>
namespace ams::crypto::impl {
class Sha1Impl {
public:
static constexpr size_t HashSize = 0x14;
static constexpr size_t BlockSize = 0x40;
private:
struct State {
u32 intermediate_hash[HashSize / sizeof(u32)];
u8 buffer[BlockSize];
u64 bits_consumed;
size_t num_buffered;
bool finalized;
};
private:
State state;
public:
Sha1Impl() { /* ... */ }
~Sha1Impl() {
static_assert(std::is_trivially_destructible<State>::value);
ClearMemory(std::addressof(this->state), sizeof(this->state));
}
void Initialize();
void Update(const void *data, size_t size);
void GetHash(void *dst, size_t size);
};
/* static_assert(HashFunction<Sha1Impl>); */
}

View File

@@ -38,7 +38,7 @@
#define WRAP_TEMPLATE_CONSTANT(...) ([] { using U = union { static constexpr auto GetValue() { return __VA_ARGS__; } }; return U{}; }())
#define UNWRAP_TEMPLATE_CONSTANT(tpnm) (tpnm::GetValue())
#define CONCATENATE_IMPL(S1, s2) s1##s2
#define CONCATENATE_IMPL(s1, s2) s1##s2
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
#define BITSIZEOF(x) (sizeof(x) * CHAR_BIT)

View File

@@ -68,13 +68,21 @@ namespace ams::fs {
R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardB, 3226);
R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardC, 3227);
R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardD, 3228);
R_DEFINE_ERROR_RESULT(AllocationFailureInImageDirectoryA, 3232);
R_DEFINE_ERROR_RESULT(AllocationFailureInSdCardA, 3244);
R_DEFINE_ERROR_RESULT(AllocationFailureInSdCardB, 3245);
R_DEFINE_ERROR_RESULT(AllocationFailureInSystemSaveDataA, 3246);
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemA, 3247);
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemB, 3248);
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemC, 3249);
R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemCreatorA, 3280);
R_DEFINE_ERROR_RESULT(AllocationFailureInDirectorySaveDataFileSystem, 3321);
R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemA, 3347);
R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemB, 3348);
R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemC, 3349);
R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemMetaA, 3350);
R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemMetaB, 3351);
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemD, 3352);
R_DEFINE_ERROR_RESULT(AllocationFailureInSubDirectoryFileSystem, 3355);
R_DEFINE_ERROR_RESULT(AllocationFailureInRegisterA, 3365);
@@ -238,6 +246,8 @@ namespace ams::fs {
R_DEFINE_ERROR_RANGE(InvalidOperationForOpenMode, 6200, 6299);
R_DEFINE_ERROR_RESULT(FileExtensionWithoutOpenModeAllowAppend, 6201);
R_DEFINE_ERROR_RESULT(ReadNotPermitted, 6202);
R_DEFINE_ERROR_RESULT(WriteNotPermitted, 6203);
R_DEFINE_ERROR_RANGE(UnsupportedOperation, 6300, 6399);
R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageA, 6302);
@@ -245,6 +255,7 @@ namespace ams::fs {
R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageA, 6304);
R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageB, 6305);
R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileStorageA, 6306);
R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileStorageB, 6307);
R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileServiceObjectAdapterA, 6362);
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemA, 6364);
R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemB, 6365);
@@ -256,6 +267,10 @@ namespace ams::fs {
R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileSystemTemplateC, 6371);
R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileA, 6372);
R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileB, 6373);
R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileSystemA, 6374);
R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileSystemB, 6375);
R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileA, 6376);
R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileB, 6377);
R_DEFINE_ERROR_RANGE(PermissionDenied, 6400, 6449);

View File

@@ -23,7 +23,7 @@ namespace ams::pm {
R_DEFINE_ERROR_RESULT(ProcessNotFound, 1);
R_DEFINE_ERROR_RESULT(AlreadyStarted, 2);
R_DEFINE_ERROR_RESULT(NotExited, 3);
R_DEFINE_ERROR_RESULT(NotTerminated, 3);
R_DEFINE_ERROR_RESULT(DebugHookInUse, 4);
R_DEFINE_ERROR_RESULT(ApplicationRunning, 5);
R_DEFINE_ERROR_RESULT(InvalidSize, 6);

View File

@@ -60,6 +60,8 @@ namespace ams::svc {
T pointer;
public:
constexpr ALWAYS_INLINE UserPointer(T p) : pointer(p) { /* ... */ }
constexpr ALWAYS_INLINE T GetPointerUnsafe() { return this->pointer; }
};
template<typename T>
@@ -168,11 +170,11 @@ namespace ams::svc {
InitialProcessIdRangeInfo_Maximum = 1,
};
enum PhysicalMemoryInfo : u64 {
PhysicalMemoryInfo_Application = 0,
PhysicalMemoryInfo_Applet = 1,
PhysicalMemoryInfo_System = 2,
PhysicalMemoryInfo_SystemUnsafe = 3,
enum PhysicalMemorySystemInfo : u64 {
PhysicalMemorySystemInfo_Application = 0,
PhysicalMemorySystemInfo_Applet = 1,
PhysicalMemorySystemInfo_System = 2,
PhysicalMemorySystemInfo_SystemUnsafe = 3,
};
enum LastThreadInfoFlag : u32 {

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <vapours.hpp>
namespace ams::crypto {
void GenerateSha1Hash(void *dst, size_t dst_size, const void *src, size_t src_size) {
Sha1Generator gen;
gen.Initialize();
gen.Update(src, src_size);
gen.GetHash(dst, dst_size);
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <vapours.hpp>
namespace ams::crypto::impl {
#ifdef ATMOSPHERE_IS_STRATOSPHERE
void Sha1Impl::Initialize() {
static_assert(sizeof(this->state) == sizeof(::Sha1Context));
::sha1ContextCreate(reinterpret_cast<::Sha1Context *>(std::addressof(this->state)));
}
void Sha1Impl::Update(const void *data, size_t size) {
static_assert(sizeof(this->state) == sizeof(::Sha1Context));
::sha1ContextUpdate(reinterpret_cast<::Sha1Context *>(std::addressof(this->state)), data, size);
}
void Sha1Impl::GetHash(void *dst, size_t size) {
static_assert(sizeof(this->state) == sizeof(::Sha1Context));
AMS_ASSERT(size >= HashSize);
::sha1ContextGetHash(reinterpret_cast<::Sha1Context *>(std::addressof(this->state)), dst);
}
#else
/* TODO: Non-EL0 implementation. */
#endif
}

View File

@@ -176,7 +176,7 @@ namespace ams::mitm::fs {
return ResultSuccess();
}
Result FsMitmService::OpenSaveDataFileSystem(sf::Out<std::shared_ptr<IFileSystemInterface>> out, u8 _space_id, const FsSaveDataAttribute &attribute) {
Result FsMitmService::OpenSaveDataFileSystem(sf::Out<std::shared_ptr<IFileSystemInterface>> out, u8 _space_id, const fs::SaveDataAttribute &attribute) {
/* We only want to intercept saves for games, right now. */
const bool is_game_or_hbl = this->client_info.override_status.IsHbl() || ncm::IsApplicationId(this->client_info.program_id);
R_UNLESS(is_game_or_hbl, sm::mitm::ResultShouldForwardToSession());
@@ -188,14 +188,15 @@ namespace ams::mitm::fs {
R_UNLESS(cfg::HasContentSpecificFlag(this->client_info.program_id, "redirect_save"), sm::mitm::ResultShouldForwardToSession());
/* Only redirect account savedata. */
R_UNLESS(attribute.save_data_type == FsSaveDataType_Account, sm::mitm::ResultShouldForwardToSession());
R_UNLESS(attribute.type == fs::SaveDataType::Account, sm::mitm::ResultShouldForwardToSession());
/* Get enum type for space id. */
auto space_id = static_cast<FsSaveDataSpaceId>(_space_id);
/* Verify we can open the save. */
static_assert(sizeof(fs::SaveDataAttribute) == sizeof(::FsSaveDataAttribute));
FsFileSystem save_fs;
R_UNLESS(R_SUCCEEDED(fsOpenSaveDataFileSystemFwd(this->forward_service.get(), &save_fs, space_id, &attribute)), sm::mitm::ResultShouldForwardToSession());
R_UNLESS(R_SUCCEEDED(fsOpenSaveDataFileSystemFwd(this->forward_service.get(), &save_fs, space_id, reinterpret_cast<const FsSaveDataAttribute *>(&attribute))), sm::mitm::ResultShouldForwardToSession());
std::unique_ptr<fs::fsa::IFileSystem> save_ifs = std::make_unique<fs::RemoteFileSystem>(save_fs);
/* Mount the SD card using fs.mitm's session. */
@@ -205,7 +206,7 @@ namespace ams::mitm::fs {
std::shared_ptr<fs::fsa::IFileSystem> sd_ifs = std::make_shared<fs::RemoteFileSystem>(sd_fs);
/* Verify that we can open the save directory, and that it exists. */
const ncm::ProgramId application_id = attribute.application_id == 0 ? this->client_info.program_id : ncm::ProgramId{attribute.application_id};
const ncm::ProgramId application_id = attribute.program_id == ncm::InvalidProgramId ? this->client_info.program_id : attribute.program_id;
char save_dir_path[fs::EntryNameLengthMax + 1];
R_TRY(mitm::fs::SaveUtil::GetDirectorySaveDataPath(save_dir_path, sizeof(save_dir_path), application_id, space_id, attribute));

View File

@@ -82,7 +82,7 @@ namespace ams::mitm::fs {
Result OpenFileSystemWithPatch(sf::Out<std::shared_ptr<IFileSystemInterface>> out, ncm::ProgramId program_id, u32 _filesystem_type);
Result OpenFileSystemWithId(sf::Out<std::shared_ptr<IFileSystemInterface>> out, const fssrv::sf::Path &path, ncm::ProgramId program_id, u32 _filesystem_type);
Result OpenSdCardFileSystem(sf::Out<std::shared_ptr<IFileSystemInterface>> out);
Result OpenSaveDataFileSystem(sf::Out<std::shared_ptr<IFileSystemInterface>> out, u8 space_id, const FsSaveDataAttribute &attribute);
Result OpenSaveDataFileSystem(sf::Out<std::shared_ptr<IFileSystemInterface>> out, u8 space_id, const ams::fs::SaveDataAttribute &attribute);
Result OpenBisStorage(sf::Out<std::shared_ptr<IStorageInterface>> out, u32 bis_partition_id);
Result OpenDataStorageByCurrentProcess(sf::Out<std::shared_ptr<IStorageInterface>> out);
Result OpenDataStorageByDataId(sf::Out<std::shared_ptr<IStorageInterface>> out, ncm::DataId data_id, u8 storage_id);

View File

@@ -49,7 +49,7 @@ namespace ams::mitm::fs {
void ProcessForServerOnAllThreads() {
/* Initialize threads. */
if constexpr (NumExtraThreads > 0) {
const u32 priority = os::GetCurrentThreadPriority();
const s32 priority = os::GetCurrentThreadPriority();
for (size_t i = 0; i < NumExtraThreads; i++) {
R_ABORT_UNLESS(g_extra_threads[i].Initialize(LoopServerThread, nullptr, g_extra_thread_stacks[i], ThreadStackSize, priority));
}

View File

@@ -22,24 +22,24 @@ namespace ams::mitm::fs {
namespace {
Result GetSaveDataSpaceIdString(const char **out_str, u8 space_id) {
switch (space_id) {
case FsSaveDataSpaceId_System:
case FsSaveDataSpaceId_ProperSystem:
switch (static_cast<SaveDataSpaceId>(space_id)) {
case SaveDataSpaceId::System:
case SaveDataSpaceId::ProperSystem:
*out_str = "sys";
break;
case FsSaveDataSpaceId_User:
case SaveDataSpaceId::User:
*out_str = "user";
break;
case FsSaveDataSpaceId_SdSystem:
case SaveDataSpaceId::SdSystem:
*out_str = "sd_sys";
break;
case FsSaveDataSpaceId_Temporary:
case SaveDataSpaceId::Temporary:
*out_str = "temp";
break;
case FsSaveDataSpaceId_SdUser:
case SaveDataSpaceId::SdUser:
*out_str = "sd_user";
break;
case FsSaveDataSpaceId_SafeMode:
case SaveDataSpaceId::SafeMode:
*out_str = "safe";
break;
default:
@@ -49,27 +49,27 @@ namespace ams::mitm::fs {
return ResultSuccess();
}
Result GetSaveDataTypeString(const char **out_str, u8 save_data_type) {
Result GetSaveDataTypeString(const char **out_str, SaveDataType save_data_type) {
switch (save_data_type) {
case FsSaveDataType_System:
case SaveDataType::System:
*out_str = "system";
break;
case FsSaveDataType_Account:
case SaveDataType::Account:
*out_str = "account";
break;
case FsSaveDataType_Bcat:
case SaveDataType::Bcat:
*out_str = "bcat";
break;
case FsSaveDataType_Device:
case SaveDataType::Device:
*out_str = "device";
break;
case FsSaveDataType_Temporary:
case SaveDataType::Temporary:
*out_str = "temp";
break;
case FsSaveDataType_Cache:
case SaveDataType::Cache:
*out_str = "cache";
break;
case FsSaveDataType_SystemBcat:
case SaveDataType::SystemBcat:
*out_str = "system_bcat";
break;
default:
@@ -80,30 +80,29 @@ namespace ams::mitm::fs {
return ResultSuccess();
}
constexpr inline bool IsEmptyAccountId(const AccountUid &uid) {
constexpr AccountUid empty_uid = {};
return std::memcmp(&uid, &empty_uid, sizeof(uid)) == 0;
constexpr inline bool IsEmptyAccountId(const UserId &uid) {
return uid == InvalidUserId;
}
}
Result SaveUtil::GetDirectorySaveDataPath(char *dst, size_t dst_size, ncm::ProgramId program_id, u8 space_id, const FsSaveDataAttribute &attribute) {
Result SaveUtil::GetDirectorySaveDataPath(char *dst, size_t dst_size, ncm::ProgramId program_id, u8 space_id, const fs::SaveDataAttribute &attribute) {
/* Saves should be separate for emunand vs sysnand. */
const char *emummc_str = emummc::IsActive() ? "emummc" : "sysmmc";
/* Get space_id, save_data_type strings. */
const char *space_id_str, *save_type_str;
R_TRY(GetSaveDataSpaceIdString(&space_id_str, space_id));
R_TRY(GetSaveDataTypeString(&save_type_str, attribute.save_data_type));
R_TRY(GetSaveDataTypeString(&save_type_str, attribute.type));
/* Initialize the path. */
const bool is_system = attribute.system_save_data_id != 0 && IsEmptyAccountId(attribute.uid);
const bool is_system = attribute.system_save_data_id != InvalidSystemSaveDataId && IsEmptyAccountId(attribute.user_id);
size_t out_path_len;
if (is_system) {
out_path_len = static_cast<size_t>(std::snprintf(dst, dst_size, "/atmosphere/saves/%s/%s/%s/%016lx", emummc_str, space_id_str, save_type_str, attribute.system_save_data_id));
} else {
out_path_len = static_cast<size_t>(std::snprintf(dst, dst_size, "/atmosphere/saves/%s/%s/%s/%016lx/%016lx%016lx", emummc_str, space_id_str, save_type_str, static_cast<u64>(program_id), attribute.uid.uid[1], attribute.uid.uid[0]));
out_path_len = static_cast<size_t>(std::snprintf(dst, dst_size, "/atmosphere/saves/%s/%s/%s/%016lx/%016lx%016lx", emummc_str, space_id_str, save_type_str, static_cast<u64>(program_id), attribute.user_id.data[1], attribute.user_id.data[0]));
}
R_UNLESS(out_path_len < dst_size, fs::ResultTooLongPath());

View File

@@ -20,7 +20,7 @@ namespace ams::mitm::fs {
class SaveUtil {
public:
static Result GetDirectorySaveDataPath(char *dst, size_t dst_size, ncm::ProgramId program_id, u8 space_id, const FsSaveDataAttribute &attribute);
static Result GetDirectorySaveDataPath(char *dst, size_t dst_size, ncm::ProgramId program_id, u8 space_id, const ams::fs::SaveDataAttribute &attribute);
};
}

View File

@@ -246,17 +246,17 @@ namespace ams::creport {
this->thread_count = 0;
/* Get thread list. */
u32 num_threads;
s32 num_threads;
u64 thread_ids[ThreadCountMax];
{
if (R_FAILED(svcGetThreadList(&num_threads, thread_ids, ThreadCountMax, debug_handle))) {
if (R_FAILED(svc::GetThreadList(&num_threads, thread_ids, ThreadCountMax, debug_handle))) {
return;
}
num_threads = std::min(size_t(num_threads), ThreadCountMax);
}
/* Parse thread infos. */
for (size_t i = 0; i < num_threads; i++) {
for (s32 i = 0; i < num_threads; i++) {
if (this->threads[this->thread_count].ReadFromProcess(debug_handle, tls_map, thread_ids[i], is_64_bit)) {
this->thread_count++;
}

View File

@@ -139,7 +139,7 @@ int main(int argc, char **argv)
/* Initialize threads. */
if constexpr (NumExtraThreads > 0) {
const u32 priority = os::GetCurrentThreadPriority();
const s32 priority = os::GetCurrentThreadPriority();
for (size_t i = 0; i < NumExtraThreads; i++) {
R_ABORT_UNLESS(g_extra_threads[i].Initialize(LoopServerThread, nullptr, g_extra_thread_stacks[i], ThreadStackSize, priority));
}

View File

@@ -213,14 +213,14 @@ namespace ams::fatal::srv {
ThreadContext thread_ctx;
{
/* We start by trying to get a list of threads. */
u32 thread_count;
s32 thread_count;
u64 thread_ids[0x60];
if (R_FAILED(svcGetThreadList(&thread_count, thread_ids, 0x60, debug_handle.Get()))) {
if (R_FAILED(svc::GetThreadList(&thread_count, thread_ids, 0x60, debug_handle.Get()))) {
return;
}
/* We need to locate the thread that's called fatal. */
for (u32 i = 0; i < thread_count; i++) {
for (s32 i = 0; i < thread_count; i++) {
const u64 cur_thread_id = thread_ids[i];
if (thread_id_to_tls.find(cur_thread_id) == thread_id_to_tls.end()) {
continue;

View File

@@ -21,7 +21,7 @@ extern "C" {
u32 __nx_applet_type = AppletType_None;
#define INNER_HEAP_SIZE 0x400000
#define INNER_HEAP_SIZE 0x8000
size_t nx_inner_heap_size = INNER_HEAP_SIZE;
char nx_inner_heap[INNER_HEAP_SIZE];
@@ -49,6 +49,25 @@ namespace ams {
using namespace ams;
namespace {
u8 g_heap_memory[1_MB];
lmem::HeapHandle g_heap_handle;
void *Allocate(size_t size) {
return lmem::AllocateFromExpHeap(g_heap_handle, size);
}
void Deallocate(void *p, size_t size) {
lmem::FreeToExpHeap(g_heap_handle, p);
}
void InitializeHeap() {
g_heap_handle = lmem::CreateExpHeap(g_heap_memory, sizeof(g_heap_memory), lmem::CreateOption_None);
}
}
void __libnx_exception_handler(ThreadExceptionDump *ctx) {
ams::CrashHandler(ctx);
}
@@ -63,11 +82,15 @@ void __libnx_initheap(void) {
fake_heap_start = (char*)addr;
fake_heap_end = (char*)addr + size;
InitializeHeap();
}
void __appInit(void) {
hos::SetVersionForLibnx();
fs::SetAllocator(Allocate, Deallocate);
sm::DoWithSession([&]() {
R_ABORT_UNLESS(fsInitialize());
R_ABORT_UNLESS(splInitialize());
@@ -82,6 +105,30 @@ void __appExit(void) {
fsExit();
}
void *operator new(size_t size) {
return Allocate(size);
}
void *operator new(size_t size, const std::nothrow_t &) {
return Allocate(size);
}
void operator delete(void *p) {
return Deallocate(p, 0);
}
void *operator new[](size_t size) {
return Allocate(size);
}
void *operator new[](size_t size, const std::nothrow_t &) {
return Allocate(size);
}
void operator delete[](void *p) {
return Deallocate(p, 0);
}
namespace {
struct ContentManagerServerOptions {

View File

@@ -17,7 +17,7 @@
namespace ams::pm::impl {
ProcessInfo::ProcessInfo(Handle h, os::ProcessId pid, ldr::PinId pin, const ncm::ProgramLocation &l, const cfg::OverrideStatus &s) : process_id(pid), pin_id(pin), loc(l), status(s), handle(h), state(ProcessState_Created), flags(0), waitable_holder(h) {
ProcessInfo::ProcessInfo(Handle h, os::ProcessId pid, ldr::PinId pin, const ncm::ProgramLocation &l, const cfg::OverrideStatus &s) : process_id(pid), pin_id(pin), loc(l), status(s), handle(h), state(svc::ProcessState_Created), flags(0), waitable_holder(h) {
this->waitable_holder.SetUserData(reinterpret_cast<uintptr_t>(this));
}

View File

@@ -43,7 +43,7 @@ namespace ams::pm::impl {
const ncm::ProgramLocation loc;
const cfg::OverrideStatus status;
Handle handle;
ProcessState state;
svc::ProcessState state;
u32 flags;
os::WaitableHolder waitable_holder;
private:
@@ -87,20 +87,20 @@ namespace ams::pm::impl {
return this->status;
}
ProcessState GetState() const {
svc::ProcessState GetState() const {
return this->state;
}
void SetState(ProcessState state) {
void SetState(svc::ProcessState state) {
this->state = state;
}
bool HasStarted() const {
return this->state != ProcessState_Created && this->state != ProcessState_CreatedAttached;
return this->state != svc::ProcessState_Created && this->state != svc::ProcessState_CreatedAttached;
}
bool HasExited() const {
return this->state == ProcessState_Exited;
bool HasTerminated() const {
return this->state == svc::ProcessState_Terminated;
}
#define DEFINE_FLAG_SET(flag) \

View File

@@ -97,7 +97,7 @@ namespace ams::pm::impl {
Started = 2,
Exception = 3,
DebugRunning = 4,
DebugSuspended = 5,
DebugBreak = 5,
};
enum class ProcessEventDeprecated {
@@ -105,7 +105,7 @@ namespace ams::pm::impl {
Exception = 1,
Exited = 2,
DebugRunning = 3,
DebugSuspended = 4,
DebugBreak = 4,
Started = 5,
};
@@ -124,8 +124,8 @@ namespace ams::pm::impl {
return static_cast<u32>(ProcessEventDeprecated::Exception);
case ProcessEvent::DebugRunning:
return static_cast<u32>(ProcessEventDeprecated::DebugRunning);
case ProcessEvent::DebugSuspended:
return static_cast<u32>(ProcessEventDeprecated::DebugSuspended);
case ProcessEvent::DebugBreak:
return static_cast<u32>(ProcessEventDeprecated::DebugBreak);
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
@@ -263,7 +263,7 @@ namespace ams::pm::impl {
Result StartProcess(ProcessInfo *process_info, const ldr::ProgramInfo *program_info) {
R_TRY(svcStartProcess(process_info->GetHandle(), program_info->main_thread_priority, program_info->default_cpu_id, program_info->main_thread_stack_size));
process_info->SetState(ProcessState_Running);
process_info->SetState(svc::ProcessState_Running);
return ResultSuccess();
}
@@ -372,25 +372,25 @@ namespace ams::pm::impl {
svcResetSignal(process_info->GetHandle());
/* Update the process's state. */
const ProcessState old_state = process_info->GetState();
const svc::ProcessState old_state = process_info->GetState();
{
u64 tmp = 0;
R_ABORT_UNLESS(svcGetProcessInfo(&tmp, process_info->GetHandle(), ProcessInfoType_ProcessState));
process_info->SetState(static_cast<ProcessState>(tmp));
s64 tmp = 0;
R_ABORT_UNLESS(svc::GetProcessInfo(&tmp, process_info->GetHandle(), svc::ProcessInfoType_ProcessState));
process_info->SetState(static_cast<svc::ProcessState>(tmp));
}
const ProcessState new_state = process_info->GetState();
const svc::ProcessState new_state = process_info->GetState();
/* If we're transitioning away from crashed, clear waiting attached. */
if (old_state == ProcessState_Crashed && new_state != ProcessState_Crashed) {
if (old_state == svc::ProcessState_Crashed && new_state != svc::ProcessState_Crashed) {
process_info->ClearExceptionWaitingAttach();
}
switch (new_state) {
case ProcessState_Created:
case ProcessState_CreatedAttached:
case ProcessState_Exiting:
case svc::ProcessState_Created:
case svc::ProcessState_CreatedAttached:
case svc::ProcessState_Terminating:
break;
case ProcessState_Running:
case svc::ProcessState_Running:
if (process_info->ShouldSignalOnDebugEvent()) {
process_info->ClearSuspended();
process_info->SetSuspendedStateChanged();
@@ -401,18 +401,18 @@ namespace ams::pm::impl {
g_process_event.Signal();
}
break;
case ProcessState_Crashed:
case svc::ProcessState_Crashed:
process_info->SetExceptionOccurred();
g_process_event.Signal();
break;
case ProcessState_RunningAttached:
case svc::ProcessState_RunningAttached:
if (process_info->ShouldSignalOnDebugEvent()) {
process_info->ClearSuspended();
process_info->SetSuspendedStateChanged();
g_process_event.Signal();
}
break;
case ProcessState_Exited:
case svc::ProcessState_Terminated:
/* Free process resources, unlink from waitable manager. */
process_info->Cleanup();
@@ -438,7 +438,7 @@ namespace ams::pm::impl {
}
}
break;
case ProcessState_DebugSuspended:
case svc::ProcessState_DebugBreak:
if (process_info->ShouldSignalOnDebugEvent()) {
process_info->SetSuspended();
process_info->SetSuspendedStateChanged();
@@ -535,7 +535,7 @@ namespace ams::pm::impl {
if (process.HasSuspendedStateChanged()) {
process.ClearSuspendedStateChanged();
if (process.IsSuspended()) {
out->event = GetProcessEventValue(ProcessEvent::DebugSuspended);
out->event = GetProcessEventValue(ProcessEvent::DebugBreak);
} else {
out->event = GetProcessEventValue(ProcessEvent::DebugRunning);
}
@@ -548,7 +548,7 @@ namespace ams::pm::impl {
out->process_id = process.GetProcessId();
return ResultSuccess();
}
if (hos::GetVersion() < hos::Version_500 && process.ShouldSignalOnExit() && process.HasExited()) {
if (hos::GetVersion() < hos::Version_500 && process.ShouldSignalOnExit() && process.HasTerminated()) {
out->event = GetProcessEventValue(ProcessEvent::Exited);
out->process_id = process.GetProcessId();
return ResultSuccess();
@@ -579,8 +579,8 @@ namespace ams::pm::impl {
ProcessListAccessor list(g_process_list);
auto process_info = list->Find(process_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
R_UNLESS(process_info->HasExited(), pm::ResultNotExited());
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
R_UNLESS(process_info->HasTerminated(), pm::ResultNotTerminated());
CleanupProcessInfo(list, process_info);
return ResultSuccess();
@@ -725,8 +725,8 @@ namespace ams::pm::impl {
return resource::BoostApplicationThreadResourceLimit();
}
Result AtmosphereGetCurrentLimitInfo(u64 *out_cur_val, u64 *out_lim_val, u32 group, u32 resource) {
return resource::GetResourceLimitValues(out_cur_val, out_lim_val, static_cast<ResourceLimitGroup>(group), static_cast<LimitableResource>(resource));
Result AtmosphereGetCurrentLimitInfo(s64 *out_cur_val, s64 *out_lim_val, u32 group, u32 resource) {
return resource::GetResourceLimitValues(out_cur_val, out_lim_val, static_cast<ResourceLimitGroup>(group), static_cast<svc::LimitableResource>(resource));
}
}

View File

@@ -51,6 +51,6 @@ namespace ams::pm::impl {
/* Resource Limit API. */
Result BoostSystemMemoryResourceLimit(u64 boost_size);
Result BoostApplicationThreadResourceLimit();
Result AtmosphereGetCurrentLimitInfo(u64 *out_cur_val, u64 *out_lim_val, u32 group, u32 resource);
Result AtmosphereGetCurrentLimitInfo(s64 *out_cur_val, s64 *out_lim_val, u32 group, u32 resource);
}

View File

@@ -19,14 +19,13 @@ namespace ams::pm::resource {
namespace {
constexpr LimitableResource LimitableResources[] = {
LimitableResource_Memory,
LimitableResource_Threads,
LimitableResource_Events,
LimitableResource_TransferMemories,
LimitableResource_Sessions,
constexpr svc::LimitableResource LimitableResources[] = {
svc::LimitableResource_PhysicalMemoryMax,
svc::LimitableResource_ThreadCountMax,
svc::LimitableResource_EventCountMax,
svc::LimitableResource_TransferMemoryCountMax,
svc::LimitableResource_SessionCountMax,
};
constexpr size_t LimitableResource_Count = util::size(LimitableResources);
/* Definitions for limit differences over time. */
constexpr size_t ExtraSystemThreadCount400 = 100;
@@ -48,27 +47,27 @@ namespace ams::pm::resource {
u64 g_system_memory_boost_size = 0;
u64 g_extra_application_threads_available = 0;
u64 g_resource_limits[ResourceLimitGroup_Count][LimitableResource_Count] = {
u64 g_resource_limits[ResourceLimitGroup_Count][svc::LimitableResource_Count] = {
[ResourceLimitGroup_System] = {
[LimitableResource_Memory] = 0, /* Initialized by more complicated logic later. */
[LimitableResource_Threads] = 508,
[LimitableResource_Events] = 600,
[LimitableResource_TransferMemories] = 128,
[LimitableResource_Sessions] = 794,
[svc::LimitableResource_PhysicalMemoryMax] = 0, /* Initialized by more complicated logic later. */
[svc::LimitableResource_ThreadCountMax] = 508,
[svc::LimitableResource_EventCountMax] = 600,
[svc::LimitableResource_TransferMemoryCountMax] = 128,
[svc::LimitableResource_SessionCountMax] = 794,
},
[ResourceLimitGroup_Application] = {
[LimitableResource_Memory] = 0, /* Initialized by more complicated logic later. */
[LimitableResource_Threads] = 96,
[LimitableResource_Events] = 0,
[LimitableResource_TransferMemories] = 32,
[LimitableResource_Sessions] = 1,
[svc::LimitableResource_PhysicalMemoryMax] = 0, /* Initialized by more complicated logic later. */
[svc::LimitableResource_ThreadCountMax] = 96,
[svc::LimitableResource_EventCountMax] = 0,
[svc::LimitableResource_TransferMemoryCountMax] = 32,
[svc::LimitableResource_SessionCountMax] = 1,
},
[ResourceLimitGroup_Applet] = {
[LimitableResource_Memory] = 0, /* Initialized by more complicated logic later. */
[LimitableResource_Threads] = 96,
[LimitableResource_Events] = 0,
[LimitableResource_TransferMemories] = 32,
[LimitableResource_Sessions] = 5,
[svc::LimitableResource_PhysicalMemoryMax] = 0, /* Initialized by more complicated logic later. */
[svc::LimitableResource_ThreadCountMax] = 96,
[svc::LimitableResource_EventCountMax] = 0,
[svc::LimitableResource_TransferMemoryCountMax] = 32,
[svc::LimitableResource_SessionCountMax] = 5,
},
};
@@ -102,13 +101,13 @@ namespace ams::pm::resource {
/* Helpers. */
Result SetMemoryResourceLimitLimitValue(ResourceLimitGroup group, u64 new_memory_limit) {
const u64 old_memory_limit = g_resource_limits[group][LimitableResource_Memory];
g_resource_limits[group][LimitableResource_Memory] = new_memory_limit;
const u64 old_memory_limit = g_resource_limits[group][svc::LimitableResource_PhysicalMemoryMax];
g_resource_limits[group][svc::LimitableResource_PhysicalMemoryMax] = new_memory_limit;
{
/* If we fail, restore the old memory limit. */
auto limit_guard = SCOPE_GUARD { g_resource_limits[group][LimitableResource_Memory] = old_memory_limit; };
R_TRY(svcSetResourceLimitLimitValue(GetResourceLimitHandle(group), LimitableResource_Memory, g_resource_limits[group][LimitableResource_Memory]));
auto limit_guard = SCOPE_GUARD { g_resource_limits[group][svc::LimitableResource_PhysicalMemoryMax] = old_memory_limit; };
R_TRY(svc::SetResourceLimitLimitValue(GetResourceLimitHandle(group), svc::LimitableResource_PhysicalMemoryMax, g_resource_limits[group][svc::LimitableResource_PhysicalMemoryMax]));
limit_guard.Cancel();
}
@@ -120,12 +119,12 @@ namespace ams::pm::resource {
R_TRY(SetMemoryResourceLimitLimitValue(group, new_memory_limit));
/* Set other limit values. */
for (size_t i = 0; i < LimitableResource_Count; i++) {
for (size_t i = 0; i < svc::LimitableResource_Count; i++) {
const auto resource = LimitableResources[i];
if (resource == LimitableResource_Memory) {
if (resource == svc::LimitableResource_PhysicalMemoryMax) {
continue;
}
R_TRY(svcSetResourceLimitLimitValue(GetResourceLimitHandle(group), resource, g_resource_limits[group][resource]));
R_TRY(svc::SetResourceLimitLimitValue(GetResourceLimitHandle(group), resource, g_resource_limits[group][resource]));
}
return ResultSuccess();
}
@@ -143,16 +142,16 @@ namespace ams::pm::resource {
void WaitResourceAvailable(ResourceLimitGroup group) {
const Handle reslimit_hnd = GetResourceLimitHandle(group);
for (size_t i = 0; i < LimitableResource_Count; i++) {
for (size_t i = 0; i < svc::LimitableResource_Count; i++) {
const auto resource = LimitableResources[i];
u64 value = 0;
s64 value = 0;
while (true) {
R_ABORT_UNLESS(svcGetResourceLimitCurrentValue(&value, reslimit_hnd, resource));
R_ABORT_UNLESS(svc::GetResourceLimitCurrentValue(&value, reslimit_hnd, resource));
if (value == 0) {
break;
}
svcSleepThread(1'000'000ul);
svc::SleepThread(1'000'000ul);
}
}
}
@@ -160,11 +159,11 @@ namespace ams::pm::resource {
void WaitApplicationMemoryAvailable() {
u64 value = 0;
while (true) {
R_ABORT_UNLESS(svcGetSystemInfo(&value, SystemInfoType_UsedPhysicalMemorySize, INVALID_HANDLE, PhysicalMemoryInfo_Application));
R_ABORT_UNLESS(svc::GetSystemInfo(&value, svc::SystemInfoType_UsedPhysicalMemorySize, INVALID_HANDLE, svc::PhysicalMemorySystemInfo_Application));
if (value == 0) {
break;
}
svcSleepThread(1'000'000ul);
svc::SleepThread(1'000'000ul);
}
}
@@ -176,10 +175,10 @@ namespace ams::pm::resource {
for (size_t i = 0; i < ResourceLimitGroup_Count; i++) {
if (i == ResourceLimitGroup_System) {
u64 value = 0;
R_ABORT_UNLESS(svcGetInfo(&value, InfoType_ResourceLimit, INVALID_HANDLE, 0));
g_resource_limit_handles[i] = static_cast<Handle>(value);
R_ABORT_UNLESS(svc::GetInfo(&value, svc::InfoType_ResourceLimit, svc::InvalidHandle, 0));
g_resource_limit_handles[i] = static_cast<svc::Handle>(value);
} else {
R_ABORT_UNLESS(svcCreateResourceLimit(&g_resource_limit_handles[i]));
R_ABORT_UNLESS(svc::CreateResourceLimit(&g_resource_limit_handles[i]));
}
}
@@ -187,7 +186,7 @@ namespace ams::pm::resource {
const auto hos_version = hos::GetVersion();
if (hos_version >= hos::Version_400) {
/* 4.0.0 increased the system thread limit. */
g_resource_limits[ResourceLimitGroup_System][LimitableResource_Threads] += ExtraSystemThreadCount400;
g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_ThreadCountMax] += ExtraSystemThreadCount400;
/* 4.0.0 also took memory away from applet and gave it to system, for the Standard and StandardForSystemDev profiles. */
g_memory_resource_limits[spl::MemoryArrangement_Standard][ResourceLimitGroup_System] += ExtraSystemMemorySize400;
g_memory_resource_limits[spl::MemoryArrangement_Standard][ResourceLimitGroup_Applet] -= ExtraSystemMemorySize400;
@@ -203,26 +202,26 @@ namespace ams::pm::resource {
}
if (hos_version >= hos::Version_600) {
/* 6.0.0 increased the system event and session limits. */
g_resource_limits[ResourceLimitGroup_System][LimitableResource_Events] += ExtraSystemEventCount600;
g_resource_limits[ResourceLimitGroup_System][LimitableResource_Sessions] += ExtraSystemSessionCount600;
g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_EventCountMax] += ExtraSystemEventCount600;
g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_SessionCountMax] += ExtraSystemSessionCount600;
}
if (hos_version >= hos::Version_900) {
/* 9.2.0 increased the system session limit. */
/* NOTE: We don't currently support detection of minor version, so we will provide this increase on 9.0.0+. */
/* This shouldn't impact any existing behavior in undesirable ways. */
g_resource_limits[ResourceLimitGroup_System][LimitableResource_Sessions] += ExtraSystemSessionCount920;
g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_SessionCountMax] += ExtraSystemSessionCount920;
}
/* 7.0.0+: Calculate the number of extra application threads available. */
if (hos::GetVersion() >= hos::Version_700) {
/* See how many threads we have available. */
u64 total_threads_available = 0;
R_ABORT_UNLESS(svcGetResourceLimitLimitValue(&total_threads_available, GetResourceLimitHandle(ResourceLimitGroup_System), LimitableResource_Threads));
s64 total_threads_available = 0;
R_ABORT_UNLESS(svc::GetResourceLimitLimitValue(&total_threads_available, GetResourceLimitHandle(ResourceLimitGroup_System), svc::LimitableResource_ThreadCountMax));
/* See how many threads we're expecting. */
const size_t total_threads_allocated = g_resource_limits[ResourceLimitGroup_System][LimitableResource_Threads] -
g_resource_limits[ResourceLimitGroup_Application][LimitableResource_Threads] -
g_resource_limits[ResourceLimitGroup_Applet][LimitableResource_Threads];
const s64 total_threads_allocated = g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_ThreadCountMax] -
g_resource_limits[ResourceLimitGroup_Application][svc::LimitableResource_ThreadCountMax] -
g_resource_limits[ResourceLimitGroup_Applet][svc::LimitableResource_ThreadCountMax];
/* Ensure we don't over-commit threads. */
AMS_ABORT_UNLESS(total_threads_allocated <= total_threads_available);
@@ -237,16 +236,16 @@ namespace ams::pm::resource {
g_memory_arrangement = spl::MemoryArrangement_Dynamic;
/* Get total memory available. */
u64 total_memory = 0;
R_ABORT_UNLESS(svcGetResourceLimitLimitValue(&total_memory, GetResourceLimitHandle(ResourceLimitGroup_System), LimitableResource_Memory));
s64 total_memory = 0;
R_ABORT_UNLESS(svc::GetResourceLimitLimitValue(&total_memory, GetResourceLimitHandle(ResourceLimitGroup_System), svc::LimitableResource_PhysicalMemoryMax));
/* Get and save application + applet memory. */
R_ABORT_UNLESS(svcGetSystemInfo(&g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Application], SystemInfoType_TotalPhysicalMemorySize, INVALID_HANDLE, PhysicalMemoryInfo_Application));
R_ABORT_UNLESS(svcGetSystemInfo(&g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Applet], SystemInfoType_TotalPhysicalMemorySize, INVALID_HANDLE, PhysicalMemoryInfo_Applet));
R_ABORT_UNLESS(svc::GetSystemInfo(&g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Application], svc::SystemInfoType_TotalPhysicalMemorySize, svc::InvalidHandle, svc::PhysicalMemorySystemInfo_Application));
R_ABORT_UNLESS(svc::GetSystemInfo(&g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Applet], svc::SystemInfoType_TotalPhysicalMemorySize, svc::InvalidHandle, svc::PhysicalMemorySystemInfo_Applet));
const u64 application_size = g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Application];
const u64 applet_size = g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Applet];
const u64 reserved_non_system_size = (application_size + applet_size + ReservedMemorySize600);
const s64 application_size = g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Application];
const s64 applet_size = g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_Applet];
const s64 reserved_non_system_size = (application_size + applet_size + ReservedMemorySize600);
/* Ensure there's enough memory for the system region. */
AMS_ABORT_UNLESS(reserved_non_system_size < total_memory);
@@ -292,11 +291,11 @@ namespace ams::pm::resource {
if (hos::GetVersion() >= hos::Version_500) {
/* Starting in 5.0.0, PM does not allow for only one of the sets to fail. */
if (boost_size < g_system_memory_boost_size) {
R_TRY(svcSetUnsafeLimit(boost_size));
R_TRY(svc::SetUnsafeLimit(boost_size));
R_ABORT_UNLESS(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
} else {
R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
R_ABORT_UNLESS(svcSetUnsafeLimit(boost_size));
R_ABORT_UNLESS(svc::SetUnsafeLimit(boost_size));
}
} else {
const u64 new_sys_size = g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_System] + boost_size;
@@ -318,11 +317,11 @@ namespace ams::pm::resource {
Result BoostApplicationThreadResourceLimit() {
std::scoped_lock lk(g_resource_limit_lock);
/* Set new limit. */
const u64 new_thread_count = g_resource_limits[ResourceLimitGroup_Application][LimitableResource_Threads] + g_extra_application_threads_available;
R_TRY(svcSetResourceLimitLimitValue(GetResourceLimitHandle(ResourceLimitGroup_Application), LimitableResource_Threads, new_thread_count));
const s64 new_thread_count = g_resource_limits[ResourceLimitGroup_Application][svc::LimitableResource_ThreadCountMax] + g_extra_application_threads_available;
R_TRY(svc::SetResourceLimitLimitValue(GetResourceLimitHandle(ResourceLimitGroup_Application), svc::LimitableResource_ThreadCountMax, new_thread_count));
/* Record that we did so. */
g_resource_limits[ResourceLimitGroup_Application][LimitableResource_Threads] = new_thread_count;
g_resource_limits[ResourceLimitGroup_Application][svc::LimitableResource_ThreadCountMax] = new_thread_count;
g_extra_application_threads_available = 0;
return ResultSuccess();
@@ -345,14 +344,14 @@ namespace ams::pm::resource {
}
}
Result GetResourceLimitValues(u64 *out_cur, u64 *out_lim, ResourceLimitGroup group, LimitableResource resource) {
Result GetResourceLimitValues(s64 *out_cur, s64 *out_lim, ResourceLimitGroup group, svc::LimitableResource resource) {
/* Do not allow out of bounds access. */
AMS_ABORT_UNLESS(group < ResourceLimitGroup_Count);
AMS_ABORT_UNLESS(resource < LimitableResource_Count);
AMS_ABORT_UNLESS(resource < svc::LimitableResource_Count);
const Handle reslimit_hnd = GetResourceLimitHandle(group);
R_TRY(svcGetResourceLimitCurrentValue(out_cur, reslimit_hnd, resource));
R_TRY(svcGetResourceLimitLimitValue(out_lim, reslimit_hnd, resource));
R_TRY(svc::GetResourceLimitCurrentValue(out_cur, reslimit_hnd, resource));
R_TRY(svc::GetResourceLimitLimitValue(out_lim, reslimit_hnd, resource));
return ResultSuccess();
}

View File

@@ -26,6 +26,6 @@ namespace ams::pm::resource {
Handle GetResourceLimitHandle(const ldr::ProgramInfo *info);
void WaitResourceAvailable(const ldr::ProgramInfo *info);
Result GetResourceLimitValues(u64 *out_cur, u64 *out_lim, ResourceLimitGroup group, LimitableResource resource);
Result GetResourceLimitValues(s64 *out_cur, s64 *out_lim, ResourceLimitGroup group, svc::LimitableResource resource);
}

View File

@@ -58,7 +58,7 @@ namespace ams::pm::dmnt {
return impl::AtmosphereGetProcessInfo(out_process_handle.GetHandlePointer(), out_loc.GetPointer(), out_status.GetPointer(), process_id);
}
Result DebugMonitorServiceBase::AtmosphereGetCurrentLimitInfo(sf::Out<u64> out_cur_val, sf::Out<u64> out_lim_val, u32 group, u32 resource) {
Result DebugMonitorServiceBase::AtmosphereGetCurrentLimitInfo(sf::Out<s64> out_cur_val, sf::Out<s64> out_lim_val, u32 group, u32 resource) {
return impl::AtmosphereGetCurrentLimitInfo(out_cur_val.GetPointer(), out_lim_val.GetPointer(), group, resource);
}

View File

@@ -32,7 +32,7 @@ namespace ams::pm::dmnt {
/* Atmosphere extension commands. */
virtual Result AtmosphereGetProcessInfo(sf::OutCopyHandle out_process_handle, sf::Out<ncm::ProgramLocation> out_loc, sf::Out<cfg::OverrideStatus> out_status, os::ProcessId process_id);
virtual Result AtmosphereGetCurrentLimitInfo(sf::Out<u64> out_cur_val, sf::Out<u64> out_lim_val, u32 group, u32 resource);
virtual Result AtmosphereGetCurrentLimitInfo(sf::Out<s64> out_cur_val, sf::Out<s64> out_lim_val, u32 group, u32 resource);
};
/* This represents modern DebugMonitorService (5.0.0+). */

View File

@@ -115,10 +115,10 @@ namespace {
cfg::GetInitialProcessRange(&min_priv_process_id, &max_priv_process_id);
/* Get list of processes, register all privileged ones. */
u32 num_pids;
s32 num_pids;
os::ProcessId pids[ProcessCountMax];
R_ABORT_UNLESS(svcGetProcessList(&num_pids, reinterpret_cast<u64 *>(pids), ProcessCountMax));
for (size_t i = 0; i < num_pids; i++) {
R_ABORT_UNLESS(svc::GetProcessList(&num_pids, reinterpret_cast<u64 *>(pids), ProcessCountMax));
for (s32 i = 0; i < num_pids; i++) {
if (min_priv_process_id <= pids[i] && pids[i] <= max_priv_process_id) {
RegisterPrivilegedProcess(pids[i]);
}