summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/CMakeLists.txt5
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp7
-rw-r--r--src/core/core.cpp2
-rw-r--r--src/core/core_cpu.cpp8
-rw-r--r--src/core/core_cpu.h2
-rw-r--r--src/core/file_sys/bis_factory.cpp12
-rw-r--r--src/core/file_sys/bis_factory.h5
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.cpp366
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.h70
-rw-r--r--src/core/file_sys/patch_manager.cpp49
-rw-r--r--src/core/file_sys/patch_manager.h2
-rw-r--r--src/core/file_sys/program_metadata.cpp4
-rw-r--r--src/core/file_sys/program_metadata.h6
-rw-r--r--src/core/file_sys/registered_cache.cpp9
-rw-r--r--src/core/file_sys/registered_cache.h2
-rw-r--r--src/core/file_sys/romfs.cpp21
-rw-r--r--src/core/file_sys/romfs.h15
-rw-r--r--src/core/file_sys/vfs.cpp45
-rw-r--r--src/core/file_sys/vfs.h17
-rw-r--r--src/core/file_sys/vfs_concat.cpp77
-rw-r--r--src/core/file_sys/vfs_concat.h18
-rw-r--r--src/core/file_sys/vfs_layered.cpp132
-rw-r--r--src/core/file_sys/vfs_layered.h50
-rw-r--r--src/core/file_sys/vfs_real.cpp17
-rw-r--r--src/core/file_sys/vfs_real.h1
-rw-r--r--src/core/file_sys/vfs_static.h79
-rw-r--r--src/core/file_sys/vfs_vector.cpp54
-rw-r--r--src/core/file_sys/vfs_vector.h25
-rw-r--r--src/core/gdbstub/gdbstub.cpp15
-rw-r--r--src/core/hle/kernel/object.h3
-rw-r--r--src/core/hle/kernel/process.cpp105
-rw-r--r--src/core/hle/kernel/process.h67
-rw-r--r--src/core/hle/kernel/scheduler.cpp14
-rw-r--r--src/core/hle/kernel/scheduler.h4
-rw-r--r--src/core/hle/kernel/shared_memory.cpp4
-rw-r--r--src/core/hle/kernel/svc.cpp91
-rw-r--r--src/core/hle/kernel/thread.cpp61
-rw-r--r--src/core/hle/kernel/thread.h13
-rw-r--r--src/core/hle/kernel/vm_manager.cpp180
-rw-r--r--src/core/hle/kernel/vm_manager.h100
-rw-r--r--src/core/hle/kernel/wait_object.h2
-rw-r--r--src/core/hle/service/audio/audren_u.cpp10
-rw-r--r--src/core/hle/service/fatal/fatal.cpp141
-rw-r--r--src/core/hle/service/fatal/fatal.h1
-rw-r--r--src/core/hle/service/fatal/fatal_u.cpp2
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp13
-rw-r--r--src/core/hle/service/filesystem/filesystem.h2
-rw-r--r--src/core/hle/service/hid/irs.cpp158
-rw-r--r--src/core/hle/service/hid/irs.h27
-rw-r--r--src/core/hle/service/nfp/nfp.cpp1
-rw-r--r--src/core/hle/service/nim/nim.cpp1
-rw-r--r--src/core/hle/service/sm/controller.cpp3
-rw-r--r--src/core/hle/service/ssl/ssl.cpp6
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp18
-rw-r--r--src/core/loader/elf.cpp32
-rw-r--r--src/core/loader/nro.cpp12
-rw-r--r--src/core/loader/nso.cpp14
-rw-r--r--src/core/memory.cpp30
-rw-r--r--src/core/memory.h63
59 files changed, 1945 insertions, 348 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 26f727d96..23fd6e920 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -32,6 +32,8 @@ add_library(core STATIC
file_sys/control_metadata.h
file_sys/directory.h
file_sys/errors.h
+ file_sys/fsmitm_romfsbuild.cpp
+ file_sys/fsmitm_romfsbuild.h
file_sys/mode.h
file_sys/nca_metadata.cpp
file_sys/nca_metadata.h
@@ -59,10 +61,13 @@ add_library(core STATIC
file_sys/vfs.h
file_sys/vfs_concat.cpp
file_sys/vfs_concat.h
+ file_sys/vfs_layered.cpp
+ file_sys/vfs_layered.h
file_sys/vfs_offset.cpp
file_sys/vfs_offset.h
file_sys/vfs_real.cpp
file_sys/vfs_real.h
+ file_sys/vfs_static.h
file_sys/vfs_vector.cpp
file_sys/vfs_vector.h
file_sys/xts_archive.cpp
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 7be5a38de..8cad070b4 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -129,7 +129,8 @@ public:
};
std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
- auto** const page_table = Core::CurrentProcess()->vm_manager.page_table.pointers.data();
+ auto& current_process = Core::CurrentProcess();
+ auto** const page_table = current_process->vm_manager.page_table.pointers.data();
Dynarmic::A64::UserConfig config;
@@ -138,7 +139,7 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
// Memory
config.page_table = reinterpret_cast<void**>(page_table);
- config.page_table_address_space_bits = Memory::ADDRESS_SPACE_BITS;
+ config.page_table_address_space_bits = current_process->vm_manager.GetAddressSpaceWidth();
config.silently_mirror_page_table = false;
// Multi-process state
@@ -174,7 +175,7 @@ ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
std::size_t core_index)
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index},
exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(exclusive_monitor)} {
- ThreadContext ctx;
+ ThreadContext ctx{};
inner_unicorn.SaveContext(ctx);
PageTableChanged();
LoadContext(ctx);
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 50f0a42fb..7666354dc 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -64,7 +64,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
if (concat.empty())
return nullptr;
- return FileSys::ConcatenateFiles(concat, dir->GetName());
+ return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName());
}
return vfs->OpenFile(path, FileSys::Mode::Read);
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
index 21568ad50..265f8ed9c 100644
--- a/src/core/core_cpu.cpp
+++ b/src/core/core_cpu.cpp
@@ -55,16 +55,16 @@ Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64
- arm_interface = std::make_shared<ARM_Dynarmic>(exclusive_monitor, core_index);
+ arm_interface = std::make_unique<ARM_Dynarmic>(exclusive_monitor, core_index);
#else
- arm_interface = std::make_shared<ARM_Unicorn>();
+ arm_interface = std::make_unique<ARM_Unicorn>();
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
#endif
} else {
- arm_interface = std::make_shared<ARM_Unicorn>();
+ arm_interface = std::make_unique<ARM_Unicorn>();
}
- scheduler = std::make_shared<Kernel::Scheduler>(arm_interface.get());
+ scheduler = std::make_shared<Kernel::Scheduler>(*arm_interface);
}
Cpu::~Cpu() = default;
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h
index 685532965..ee7e04abc 100644
--- a/src/core/core_cpu.h
+++ b/src/core/core_cpu.h
@@ -76,7 +76,7 @@ public:
private:
void Reschedule();
- std::shared_ptr<ARM_Interface> arm_interface;
+ std::unique_ptr<ARM_Interface> arm_interface;
std::shared_ptr<CpuBarrier> cpu_barrier;
std::shared_ptr<Kernel::Scheduler> scheduler;
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 205492897..6102ef476 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -2,13 +2,14 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <fmt/format.h>
#include "core/file_sys/bis_factory.h"
#include "core/file_sys/registered_cache.h"
namespace FileSys {
-BISFactory::BISFactory(VirtualDir nand_root_)
- : nand_root(std::move(nand_root_)),
+BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_)
+ : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
sysnand_cache(std::make_shared<RegisteredCache>(
GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
usrnand_cache(std::make_shared<RegisteredCache>(
@@ -24,4 +25,11 @@ std::shared_ptr<RegisteredCache> BISFactory::GetUserNANDContents() const {
return usrnand_cache;
}
+VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
+ // LayeredFS doesn't work on updates and title id-less homebrew
+ if (title_id == 0 || (title_id & 0x800) > 0)
+ return nullptr;
+ return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id));
+}
+
} // namespace FileSys
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index 9523dd864..c352e0925 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -17,14 +17,17 @@ class RegisteredCache;
/// registered caches.
class BISFactory {
public:
- explicit BISFactory(VirtualDir nand_root);
+ explicit BISFactory(VirtualDir nand_root, VirtualDir load_root);
~BISFactory();
std::shared_ptr<RegisteredCache> GetSystemNANDContents() const;
std::shared_ptr<RegisteredCache> GetUserNANDContents() const;
+ VirtualDir GetModificationLoadRoot(u64 title_id) const;
+
private:
VirtualDir nand_root;
+ VirtualDir load_root;
std::shared_ptr<RegisteredCache> sysnand_cache;
std::shared_ptr<RegisteredCache> usrnand_cache;
diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp
new file mode 100644
index 000000000..2a913ce82
--- /dev/null
+++ b/src/core/file_sys/fsmitm_romfsbuild.cpp
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2018 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/>.
+ */
+
+/*
+ * Adapted by DarkLordZach for use/interaction with yuzu
+ *
+ * Modifications Copyright 2018 yuzu emulator team
+ * Licensed under GPLv2 or any later version
+ * Refer to the license.txt file included.
+ */
+
+#include <cstring>
+#include "common/alignment.h"
+#include "common/assert.h"
+#include "core/file_sys/fsmitm_romfsbuild.h"
+#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs_vector.h"
+
+namespace FileSys {
+
+constexpr u64 FS_MAX_PATH = 0x301;
+
+constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF;
+constexpr u32 ROMFS_FILEPARTITION_OFS = 0x200;
+
+// Types for building a RomFS.
+struct RomFSHeader {
+ u64 header_size;
+ u64 dir_hash_table_ofs;
+ u64 dir_hash_table_size;
+ u64 dir_table_ofs;
+ u64 dir_table_size;
+ u64 file_hash_table_ofs;
+ u64 file_hash_table_size;
+ u64 file_table_ofs;
+ u64 file_table_size;
+ u64 file_partition_ofs;
+};
+static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size.");
+
+struct RomFSDirectoryEntry {
+ u32 parent;
+ u32 sibling;
+ u32 child;
+ u32 file;
+ u32 hash;
+ u32 name_size;
+};
+static_assert(sizeof(RomFSDirectoryEntry) == 0x18, "RomFSDirectoryEntry has incorrect size.");
+
+struct RomFSFileEntry {
+ u32 parent;
+ u32 sibling;
+ u64 offset;
+ u64 size;
+ u32 hash;
+ u32 name_size;
+};
+static_assert(sizeof(RomFSFileEntry) == 0x20, "RomFSFileEntry has incorrect size.");
+
+struct RomFSBuildFileContext;
+
+struct RomFSBuildDirectoryContext {
+ std::string path;
+ u32 cur_path_ofs = 0;
+ u32 path_len = 0;
+ u32 entry_offset = 0;
+ std::shared_ptr<RomFSBuildDirectoryContext> parent;
+ std::shared_ptr<RomFSBuildDirectoryContext> child;
+ std::shared_ptr<RomFSBuildDirectoryContext> sibling;
+ std::shared_ptr<RomFSBuildFileContext> file;
+};
+
+struct RomFSBuildFileContext {
+ std::string path;
+ u32 cur_path_ofs = 0;
+ u32 path_len = 0;
+ u32 entry_offset = 0;
+ u64 offset = 0;
+ u64 size = 0;
+ std::shared_ptr<RomFSBuildDirectoryContext> parent;
+ std::shared_ptr<RomFSBuildFileContext> sibling;
+ VirtualFile source;
+};
+
+static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, std::size_t path_len) {
+ u32 hash = parent ^ 123456789;
+ for (u32 i = 0; i < path_len; i++) {
+ hash = (hash >> 5) | (hash << 27);
+ hash ^= path[start + i];
+ }
+
+ return hash;
+}
+
+static u64 romfs_get_hash_table_count(u64 num_entries) {
+ if (num_entries < 3) {
+ return 3;
+ }
+
+ if (num_entries < 19) {
+ return num_entries | 1;
+ }
+
+ u64 count = num_entries;
+ while (count % 2 == 0 || count % 3 == 0 || count % 5 == 0 || count % 7 == 0 ||
+ count % 11 == 0 || count % 13 == 0 || count % 17 == 0) {
+ count++;
+ }
+ return count;
+}
+
+void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs,
+ std::shared_ptr<RomFSBuildDirectoryContext> parent) {
+ std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs;
+
+ VirtualDir dir;
+
+ if (parent->path_len == 0)
+ dir = root_romfs;
+ else
+ dir = root_romfs->GetDirectoryRelative(parent->path);
+
+ const auto entries = dir->GetEntries();
+
+ for (const auto& kv : entries) {
+ if (kv.second == VfsEntryType::Directory) {
+ const auto child = std::make_shared<RomFSBuildDirectoryContext>();
+ // Set child's path.
+ child->cur_path_ofs = parent->path_len + 1;
+ child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
+ child->path = parent->path + "/" + kv.first;
+
+ // Sanity check on path_len
+ ASSERT(child->path_len < FS_MAX_PATH);
+
+ if (AddDirectory(parent, child)) {
+ child_dirs.push_back(child);
+ }
+ } else {
+ const auto child = std::make_shared<RomFSBuildFileContext>();
+ // Set child's path.
+ child->cur_path_ofs = parent->path_len + 1;
+ child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
+ child->path = parent->path + "/" + kv.first;
+
+ // Sanity check on path_len
+ ASSERT(child->path_len < FS_MAX_PATH);
+
+ child->source = root_romfs->GetFileRelative(child->path);
+
+ child->size = child->source->GetSize();
+
+ AddFile(parent, child);
+ }
+ }
+
+ for (auto& child : child_dirs) {
+ this->VisitDirectory(root_romfs, child);
+ }
+}
+
+bool RomFSBuildContext::AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
+ std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx) {
+ // Check whether it's already in the known directories.
+ const auto existing = directories.find(dir_ctx->path);
+ if (existing != directories.end())
+ return false;
+
+ // Add a new directory.
+ num_dirs++;
+ dir_table_size +=
+ sizeof(RomFSDirectoryEntry) + Common::AlignUp(dir_ctx->path_len - dir_ctx->cur_path_ofs, 4);
+ dir_ctx->parent = parent_dir_ctx;
+ directories.emplace(dir_ctx->path, dir_ctx);
+
+ return true;
+}
+
+bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
+ std::shared_ptr<RomFSBuildFileContext> file_ctx) {
+ // Check whether it's already in the known files.
+ const auto existing = files.find(file_ctx->path);
+ if (existing != files.end()) {
+ return false;
+ }
+
+ // Add a new file.
+ num_files++;
+ file_table_size +=
+ sizeof(RomFSFileEntry) + Common::AlignUp(file_ctx->path_len - file_ctx->cur_path_ofs, 4);
+ file_ctx->parent = parent_dir_ctx;
+ files.emplace(file_ctx->path, file_ctx);
+
+ return true;
+}
+
+RomFSBuildContext::RomFSBuildContext(VirtualDir base_) : base(std::move(base_)) {
+ root = std::make_shared<RomFSBuildDirectoryContext>();
+ root->path = "\0";
+ directories.emplace(root->path, root);
+ num_dirs = 1;
+ dir_table_size = 0x18;
+
+ VisitDirectory(base, root);
+}
+
+RomFSBuildContext::~RomFSBuildContext() = default;
+
+std::map<u64, VirtualFile> RomFSBuildContext::Build() {
+ const u64 dir_hash_table_entry_count = romfs_get_hash_table_count(num_dirs);
+ const u64 file_hash_table_entry_count = romfs_get_hash_table_count(num_files);
+ dir_hash_table_size = 4 * dir_hash_table_entry_count;
+ file_hash_table_size = 4 * file_hash_table_entry_count;
+
+ // Assign metadata pointers
+ RomFSHeader header{};
+
+ std::vector<u32> dir_hash_table(dir_hash_table_entry_count, ROMFS_ENTRY_EMPTY);
+ std::vector<u32> file_hash_table(file_hash_table_entry_count, ROMFS_ENTRY_EMPTY);
+
+ std::vector<u8> dir_table(dir_table_size);
+ std::vector<u8> file_table(file_table_size);
+
+ std::shared_ptr<RomFSBuildFileContext> cur_file;
+
+ // Determine file offsets.
+ u32 entry_offset = 0;
+ std::shared_ptr<RomFSBuildFileContext> prev_file = nullptr;
+ for (const auto& it : files) {
+ cur_file = it.second;
+ file_partition_size = Common::AlignUp(file_partition_size, 16);
+ cur_file->offset = file_partition_size;
+ file_partition_size += cur_file->size;
+ cur_file->entry_offset = entry_offset;
+ entry_offset += sizeof(RomFSFileEntry) +
+ Common::AlignUp(cur_file->path_len - cur_file->cur_path_ofs, 4);
+ prev_file = cur_file;
+ }
+ // Assign deferred parent/sibling ownership.
+ for (auto it = files.rbegin(); it != files.rend(); ++it) {
+ cur_file = it->second;
+ cur_file->sibling = cur_file->parent->file;
+ cur_file->parent->file = cur_file;
+ }
+
+ std::shared_ptr<RomFSBuildDirectoryContext> cur_dir;
+
+ // Determine directory offsets.
+ entry_offset = 0;
+ for (const auto& it : directories) {
+ cur_dir = it.second;
+ cur_dir->entry_offset = entry_offset;
+ entry_offset += sizeof(RomFSDirectoryEntry) +
+ Common::AlignUp(cur_dir->path_len - cur_dir->cur_path_ofs, 4);
+ }
+ // Assign deferred parent/sibling ownership.
+ for (auto it = directories.rbegin(); it->second != root; ++it) {
+ cur_dir = it->second;
+ cur_dir->sibling = cur_dir->parent->child;
+ cur_dir->parent->child = cur_dir;
+ }
+
+ std::map<u64, VirtualFile> out;
+
+ // Populate file tables.
+ for (const auto& it : files) {
+ cur_file = it.second;
+ RomFSFileEntry cur_entry{};
+
+ cur_entry.parent = cur_file->parent->entry_offset;
+ cur_entry.sibling =
+ cur_file->sibling == nullptr ? ROMFS_ENTRY_EMPTY : cur_file->sibling->entry_offset;
+ cur_entry.offset = cur_file->offset;
+ cur_entry.size = cur_file->size;
+
+ const auto name_size = cur_file->path_len - cur_file->cur_path_ofs;
+ const auto hash = romfs_calc_path_hash(cur_file->parent->entry_offset, cur_file->path,
+ cur_file->cur_path_ofs, name_size);
+ cur_entry.hash = file_hash_table[hash % file_hash_table_entry_count];
+ file_hash_table[hash % file_hash_table_entry_count] = cur_file->entry_offset;
+
+ cur_entry.name_size = name_size;
+
+ out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->source);
+ std::memcpy(file_table.data() + cur_file->entry_offset, &cur_entry, sizeof(RomFSFileEntry));
+ std::memset(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry), 0,
+ Common::AlignUp(cur_entry.name_size, 4));
+ std::memcpy(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry),
+ cur_file->path.data() + cur_file->cur_path_ofs, name_size);
+ }
+
+ // Populate dir tables.
+ for (const auto& it : directories) {
+ cur_dir = it.second;
+ RomFSDirectoryEntry cur_entry{};
+
+ cur_entry.parent = cur_dir == root ? 0 : cur_dir->parent->entry_offset;
+ cur_entry.sibling =
+ cur_dir->sibling == nullptr ? ROMFS_ENTRY_EMPTY : cur_dir->sibling->entry_offset;
+ cur_entry.child =
+ cur_dir->child == nullptr ? ROMFS_ENTRY_EMPTY : cur_dir->child->entry_offset;
+ cur_entry.file = cur_dir->file == nullptr ? ROMFS_ENTRY_EMPTY : cur_dir->file->entry_offset;
+
+ const auto name_size = cur_dir->path_len - cur_dir->cur_path_ofs;
+ const auto hash = romfs_calc_path_hash(cur_dir == root ? 0 : cur_dir->parent->entry_offset,
+ cur_dir->path, cur_dir->cur_path_ofs, name_size);
+ cur_entry.hash = dir_hash_table[hash % dir_hash_table_entry_count];
+ dir_hash_table[hash % dir_hash_table_entry_count] = cur_dir->entry_offset;
+
+ cur_entry.name_size = name_size;
+
+ std::memcpy(dir_table.data() + cur_dir->entry_offset, &cur_entry,
+ sizeof(RomFSDirectoryEntry));
+ std::memset(dir_table.data() + cur_dir->entry_offset + sizeof(RomFSDirectoryEntry), 0,
+ Common::AlignUp(cur_entry.name_size, 4));
+ std::memcpy(dir_table.data() + cur_dir->entry_offset + sizeof(RomFSDirectoryEntry),
+ cur_dir->path.data() + cur_dir->cur_path_ofs, name_size);
+ }
+
+ // Set header fields.
+ header.header_size = sizeof(RomFSHeader);
+ header.file_hash_table_size = file_hash_table_size;
+ header.file_table_size = file_table_size;
+ header.dir_hash_table_size = dir_hash_table_size;
+ header.dir_table_size = dir_table_size;
+ header.file_partition_ofs = ROMFS_FILEPARTITION_OFS;
+ header.dir_hash_table_ofs = Common::AlignUp(header.file_partition_ofs + file_partition_size, 4);
+ header.dir_table_ofs = header.dir_hash_table_ofs + header.dir_hash_table_size;
+ header.file_hash_table_ofs = header.dir_table_ofs + header.dir_table_size;
+ header.file_table_ofs = header.file_hash_table_ofs + header.file_hash_table_size;
+
+ std::vector<u8> header_data(sizeof(RomFSHeader));
+ std::memcpy(header_data.data(), &header, header_data.size());
+ out.emplace(0, std::make_shared<VectorVfsFile>(std::move(header_data)));
+
+ std::vector<u8> metadata(file_hash_table_size + file_table_size + dir_hash_table_size +
+ dir_table_size);
+ std::size_t index = 0;
+ std::memcpy(metadata.data(), dir_hash_table.data(), dir_hash_table.size() * sizeof(u32));
+ index += dir_hash_table.size() * sizeof(u32);
+ std::memcpy(metadata.data() + index, dir_table.data(), dir_table.size());
+ index += dir_table.size();
+ std::memcpy(metadata.data() + index, file_hash_table.data(),
+ file_hash_table.size() * sizeof(u32));
+ index += file_hash_table.size() * sizeof(u32);
+ std::memcpy(metadata.data() + index, file_table.data(), file_table.size());
+ out.emplace(header.dir_hash_table_ofs, std::make_shared<VectorVfsFile>(std::move(metadata)));
+
+ return out;
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/fsmitm_romfsbuild.h b/src/core/file_sys/fsmitm_romfsbuild.h
new file mode 100644
index 000000000..b0c3c123b
--- /dev/null
+++ b/src/core/file_sys/fsmitm_romfsbuild.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2018 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/>.
+ */
+
+/*
+ * Adapted by DarkLordZach for use/interaction with yuzu
+ *
+ * Modifications Copyright 2018 yuzu emulator team
+ * Licensed under GPLv2 or any later version
+ * Refer to the license.txt file included.
+ */
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <string>
+#include <boost/detail/container_fwd.hpp>
+#include "common/common_types.h"
+#include "core/file_sys/vfs.h"
+
+namespace FileSys {
+
+struct RomFSBuildDirectoryContext;
+struct RomFSBuildFileContext;
+struct RomFSDirectoryEntry;
+struct RomFSFileEntry;
+
+class RomFSBuildContext {
+public:
+ explicit RomFSBuildContext(VirtualDir base);
+ ~RomFSBuildContext();
+
+ // This finalizes the context.
+ std::map<u64, VirtualFile> Build();
+
+private:
+ VirtualDir base;
+ std::shared_ptr<RomFSBuildDirectoryContext> root;
+ std::map<std::string, std::shared_ptr<RomFSBuildDirectoryContext>, std::less<>> directories;
+ std::map<std::string, std::shared_ptr<RomFSBuildFileContext>, std::less<>> files;
+ u64 num_dirs = 0;
+ u64 num_files = 0;
+ u64 dir_table_size = 0;
+ u64 file_table_size = 0;
+ u64 dir_hash_table_size = 0;
+ u64 file_hash_table_size = 0;
+ u64 file_partition_size = 0;
+
+ void VisitDirectory(VirtualDir filesys, std::shared_ptr<RomFSBuildDirectoryContext> parent);
+
+ bool AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
+ std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx);
+ bool AddFile(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
+ std::shared_ptr<RomFSBuildFileContext> file_ctx);
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index aebc69d52..4b3b5e665 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -11,6 +11,7 @@
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
+#include "core/file_sys/vfs_layered.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
@@ -31,8 +32,9 @@ std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]);
}
-constexpr std::array<const char*, 1> PATCH_TYPE_NAMES{
+constexpr std::array<const char*, 2> PATCH_TYPE_NAMES{
"Update",
+ "LayeredFS",
};
std::string FormatPatchTypeName(PatchType type) {
@@ -66,6 +68,44 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
return exefs;
}
+static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
+ const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
+ if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) {
+ return;
+ }
+
+ auto extracted = ExtractRomFS(romfs);
+ if (extracted == nullptr) {
+ return;
+ }
+
+ auto patch_dirs = load_dir->GetSubdirectories();
+ std::sort(patch_dirs.begin(), patch_dirs.end(),
+ [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
+
+ std::vector<VirtualDir> layers;
+ layers.reserve(patch_dirs.size() + 1);
+ for (const auto& subdir : patch_dirs) {
+ auto romfs_dir = subdir->GetSubdirectory("romfs");
+ if (romfs_dir != nullptr)
+ layers.push_back(std::move(romfs_dir));
+ }
+ layers.push_back(std::move(extracted));
+
+ auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
+ if (layered == nullptr) {
+ return;
+ }
+
+ auto packed = CreateRomFS(std::move(layered));
+ if (packed == nullptr) {
+ return;
+ }
+
+ LOG_INFO(Loader, " RomFS: LayeredFS patches applied successfully");
+ romfs = std::move(packed);
+}
+
VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
ContentRecordType type) const {
LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id,
@@ -89,6 +129,9 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
}
}
+ // LayeredFS
+ ApplyLayeredFS(romfs, title_id, type);
+
return romfs;
}
@@ -114,6 +157,10 @@ std::map<PatchType, std::string> PatchManager::GetPatchVersionNames() const {
}
}
+ const auto lfs_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
+ if (lfs_dir != nullptr && lfs_dir->GetSize() > 0)
+ out.insert_or_assign(PatchType::LayeredFS, "");
+
return out;
}
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index 209cab1dc..464f17515 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -26,6 +26,7 @@ std::string FormatTitleVersion(u32 version,
enum class PatchType {
Update,
+ LayeredFS,
};
std::string FormatPatchTypeName(PatchType type);
@@ -42,6 +43,7 @@ public:
// Currently tracked RomFS patches:
// - Game Updates
+ // - LayeredFS
VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset,
ContentRecordType type = ContentRecordType::Program) const;
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index 02319ce0f..8903ed1d3 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -83,10 +83,12 @@ void ProgramMetadata::Print() const {
auto address_space = "Unknown";
switch (npdm_header.address_space_type) {
- case ProgramAddressSpaceType::Is64Bit:
+ case ProgramAddressSpaceType::Is36Bit:
+ case ProgramAddressSpaceType::Is39Bit:
address_space = "64-bit";
break;
case ProgramAddressSpaceType::Is32Bit:
+ case ProgramAddressSpaceType::Is32BitNoMap:
address_space = "32-bit";
break;
}
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
index 1143e36c4..e4470d6f0 100644
--- a/src/core/file_sys/program_metadata.h
+++ b/src/core/file_sys/program_metadata.h
@@ -17,8 +17,10 @@ enum class ResultStatus : u16;
namespace FileSys {
enum class ProgramAddressSpaceType : u8 {
- Is64Bit = 1,
- Is32Bit = 2,
+ Is32Bit = 0,
+ Is36Bit = 1,
+ Is32BitNoMap = 2,
+ Is39Bit = 3,
};
enum class ProgramFilePermission : u64 {
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index dad7ae10b..e9b040689 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -18,6 +18,10 @@
#include "core/loader/loader.h"
namespace FileSys {
+
+// The size of blocks to use when vfs raw copying into nand.
+constexpr size_t VFS_RC_LARGE_COPY_BLOCK = 0x400000;
+
std::string RegisteredCacheEntry::DebugInfo() const {
return fmt::format("title_id={:016X}, content_type={:02X}", title_id, static_cast<u8>(type));
}
@@ -121,7 +125,7 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
if (concat.empty())
return nullptr;
- file = FileSys::ConcatenateFiles(concat);
+ file = ConcatenatedVfsFile::MakeConcatenatedFile(concat, concat.front()->GetName());
}
return file;
@@ -480,7 +484,8 @@ InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const Vfs
auto out = dir->CreateFileRelative(path);
if (out == nullptr)
return InstallResult::ErrorCopyFailed;
- return copy(in, out) ? InstallResult::Success : InstallResult::ErrorCopyFailed;
+ return copy(in, out, VFS_RC_LARGE_COPY_BLOCK) ? InstallResult::Success
+ : InstallResult::ErrorCopyFailed;
}
bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index f487b0cf0..c0cd59fc5 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -27,7 +27,7 @@ struct ContentRecord;
using NcaID = std::array<u8, 0x10>;
using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
-using VfsCopyFunction = std::function<bool(VirtualFile, VirtualFile)>;
+using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile&, size_t)>;
enum class InstallResult {
Success,
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp
index 9f6e41cdf..5910f7046 100644
--- a/src/core/file_sys/romfs.cpp
+++ b/src/core/file_sys/romfs.cpp
@@ -4,8 +4,10 @@
#include "common/common_types.h"
#include "common/swap.h"
+#include "core/file_sys/fsmitm_romfsbuild.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_offset.h"
#include "core/file_sys/vfs_vector.h"
@@ -98,7 +100,7 @@ void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file
}
}
-VirtualDir ExtractRomFS(VirtualFile file) {
+VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
RomFSHeader header{};
if (file->ReadObject(&header) != sizeof(RomFSHeader))
return nullptr;
@@ -117,9 +119,22 @@ VirtualDir ExtractRomFS(VirtualFile file) {
VirtualDir out = std::move(root);
- while (out->GetSubdirectory("") != nullptr)
- out = out->GetSubdirectory("");
+ while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) {
+ if (out->GetSubdirectories().front()->GetName() == "data" &&
+ type == RomFSExtractionType::Truncated)
+ break;
+ out = out->GetSubdirectories().front();
+ }
return out;
}
+
+VirtualFile CreateRomFS(VirtualDir dir) {
+ if (dir == nullptr)
+ return nullptr;
+
+ RomFSBuildContext ctx{dir};
+ return ConcatenatedVfsFile::MakeConcatenatedFile(0, ctx.Build(), dir->GetName());
+}
+
} // namespace FileSys
diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h
index e54a7d7a9..ecd1eb725 100644
--- a/src/core/file_sys/romfs.h
+++ b/src/core/file_sys/romfs.h
@@ -5,6 +5,7 @@
#pragma once
#include <array>
+#include <map>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
@@ -12,6 +13,8 @@
namespace FileSys {
+struct RomFSHeader;
+
struct IVFCLevel {
u64_le offset;
u64_le size;
@@ -29,8 +32,18 @@ struct IVFCHeader {
};
static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size.");
+enum class RomFSExtractionType {
+ Full, // Includes data directory
+ Truncated, // Traverses into data directory
+};
+
// Converts a RomFS binary blob to VFS Filesystem
// Returns nullptr on failure
-VirtualDir ExtractRomFS(VirtualFile file);
+VirtualDir ExtractRomFS(VirtualFile file,
+ RomFSExtractionType type = RomFSExtractionType::Truncated);
+
+// Converts a VFS filesystem into a RomFS binary
+// Returns nullptr on failure
+VirtualFile CreateRomFS(VirtualDir dir);
} // namespace FileSys
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index d7b52abfd..bfe50da73 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -399,6 +399,15 @@ bool VfsDirectory::Copy(std::string_view src, std::string_view dest) {
return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize();
}
+std::map<std::string, VfsEntryType, std::less<>> VfsDirectory::GetEntries() const {
+ std::map<std::string, VfsEntryType, std::less<>> out;
+ for (const auto& dir : GetSubdirectories())
+ out.emplace(dir->GetName(), VfsEntryType::Directory);
+ for (const auto& file : GetFiles())
+ out.emplace(file->GetName(), VfsEntryType::File);
+ return out;
+}
+
std::string VfsDirectory::GetFullPath() const {
if (IsRoot())
return GetName();
@@ -454,13 +463,41 @@ bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t
return true;
}
-bool VfsRawCopy(VirtualFile src, VirtualFile dest) {
- if (src == nullptr || dest == nullptr)
+bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size) {
+ if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
return false;
if (!dest->Resize(src->GetSize()))
return false;
- std::vector<u8> data = src->ReadAllBytes();
- return dest->WriteBytes(data, 0) == data.size();
+
+ std::vector<u8> temp(std::min(block_size, src->GetSize()));
+ for (std::size_t i = 0; i < src->GetSize(); i += block_size) {
+ const auto read = std::min(block_size, src->GetSize() - i);
+ const auto block = src->Read(temp.data(), read, i);
+
+ if (dest->Write(temp.data(), read, i) != read)
+ return false;
+ }
+
+ return true;
+}
+
+bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size) {
+ if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
+ return false;
+
+ for (const auto& file : src->GetFiles()) {
+ const auto out = dest->CreateFile(file->GetName());
+ if (!VfsRawCopy(file, out, block_size))
+ return false;
+ }
+
+ for (const auto& dir : src->GetSubdirectories()) {
+ const auto out = dest->CreateSubdirectory(dir->GetName());
+ if (!VfsRawCopyD(dir, out, block_size))
+ return false;
+ }
+
+ return true;
}
VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path) {
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index 74489b452..270291631 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -4,6 +4,7 @@
#pragma once
+#include <map>
#include <memory>
#include <string>
#include <string_view>
@@ -265,6 +266,10 @@ public:
// dest.
virtual bool Copy(std::string_view src, std::string_view dest);
+ // Gets all of the entries directly in the directory (files and dirs), returning a map between
+ // item name -> type.
+ virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const;
+
// Interprets the file with name file instead as a directory of type directory.
// The directory must have a constructor that takes a single argument of type
// std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a
@@ -310,13 +315,19 @@ public:
bool Rename(std::string_view name) override;
};
-// Compare the two files, byte-for-byte, in increments specificed by block_size
-bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t block_size = 0x200);
+// Compare the two files, byte-for-byte, in increments specified by block_size
+bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2,
+ std::size_t block_size = 0x1000);
// A method that copies the raw data between two different implementations of VirtualFile. If you
// are using the same implementation, it is probably better to use the Copy method in the parent
// directory of src/dest.
-bool VfsRawCopy(VirtualFile src, VirtualFile dest);
+bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size = 0x1000);
+
+// A method that performs a similar function to VfsRawCopy above, but instead copies entire
+// directories. It suffers the same performance penalties as above and an implementation-specific
+// Copy should always be preferred.
+bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size = 0x1000);
// Checks if the directory at path relative to rel exists. If it does, returns that. If it does not
// it attempts to create it and returns the new dir or nullptr on failure.
diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs_concat.cpp
index dc7a279a9..16d801c0c 100644
--- a/src/core/file_sys/vfs_concat.cpp
+++ b/src/core/file_sys/vfs_concat.cpp
@@ -5,17 +5,22 @@
#include <algorithm>
#include <utility>
+#include "common/assert.h"
#include "core/file_sys/vfs_concat.h"
+#include "core/file_sys/vfs_static.h"
namespace FileSys {
-VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name) {
- if (files.empty())
- return nullptr;
- if (files.size() == 1)
- return files[0];
+static bool VerifyConcatenationMapContinuity(const std::map<u64, VirtualFile>& map) {
+ const auto last_valid = --map.end();
+ for (auto iter = map.begin(); iter != last_valid;) {
+ const auto old = iter++;
+ if (old->first + old->second->GetSize() != iter->first) {
+ return false;
+ }
+ }
- return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name)));
+ return map.begin()->first == 0;
}
ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::string name)
@@ -27,8 +32,48 @@ ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::s
}
}
+ConcatenatedVfsFile::ConcatenatedVfsFile(std::map<u64, VirtualFile> files_, std::string name)
+ : files(std::move(files_)), name(std::move(name)) {
+ ASSERT(VerifyConcatenationMapContinuity(files));
+}
+
ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;
+VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::vector<VirtualFile> files,
+ std::string name) {
+ if (files.empty())
+ return nullptr;
+ if (files.size() == 1)
+ return files[0];
+
+ return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name)));
+}
+
+VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
+ std::map<u64, VirtualFile> files,
+ std::string name) {
+ if (files.empty())
+ return nullptr;
+ if (files.size() == 1)
+ return files.begin()->second;
+
+ const auto last_valid = --files.end();
+ for (auto iter = files.begin(); iter != last_valid;) {
+ const auto old = iter++;
+ if (old->first + old->second->GetSize() != iter->first) {
+ files.emplace(old->first + old->second->GetSize(),
+ std::make_shared<StaticVfsFile>(filler_byte, iter->first - old->first -
+ old->second->GetSize()));
+ }
+ }
+
+ // Ensure the map starts at offset 0 (start of file), otherwise pad to fill.
+ if (files.begin()->first != 0)
+ files.emplace(0, std::make_shared<StaticVfsFile>(filler_byte, files.begin()->first));
+
+ return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name)));
+}
+
std::string ConcatenatedVfsFile::GetName() const {
if (files.empty())
return "";
@@ -62,7 +107,7 @@ bool ConcatenatedVfsFile::IsReadable() const {
}
std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
- auto entry = files.end();
+ auto entry = --files.end();
for (auto iter = files.begin(); iter != files.end(); ++iter) {
if (iter->first > offset) {
entry = --iter;
@@ -70,20 +115,17 @@ std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t
}
}
- // Check if the entry should be the last one. The loop above will make it end().
- if (entry == files.end() && offset < files.rbegin()->first + files.rbegin()->second->GetSize())
- --entry;
-
- if (entry == files.end())
+ if (entry->first + entry->second->GetSize() <= offset)
return 0;
- const auto remaining = entry->second->GetSize() + offset - entry->first;
- if (length > remaining) {
- return entry->second->Read(data, remaining, offset - entry->first) +
- Read(data + remaining, length - remaining, offset + remaining);
+ const auto read_in =
+ std::min<u64>(entry->first + entry->second->GetSize() - offset, entry->second->GetSize());
+ if (length > read_in) {
+ return entry->second->Read(data, read_in, offset - entry->first) +
+ Read(data + read_in, length - read_in, offset + read_in);
}
- return entry->second->Read(data, length, offset - entry->first);
+ return entry->second->Read(data, std::min<u64>(read_in, length), offset - entry->first);
}
std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
@@ -93,4 +135,5 @@ std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::
bool ConcatenatedVfsFile::Rename(std::string_view name) {
return false;
}
+
} // namespace FileSys
diff --git a/src/core/file_sys/vfs_concat.h b/src/core/file_sys/vfs_concat.h
index 717d04bdc..c90f9d5d1 100644
--- a/src/core/file_sys/vfs_concat.h
+++ b/src/core/file_sys/vfs_concat.h
@@ -4,26 +4,30 @@
#pragma once
+#include <map>
#include <memory>
#include <string_view>
-#include <boost/container/flat_map.hpp>
#include "core/file_sys/vfs.h"
namespace FileSys {
-// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
-VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name = "");
-
// Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently
// read-only.
class ConcatenatedVfsFile : public VfsFile {
- friend VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name);
-
ConcatenatedVfsFile(std::vector<VirtualFile> files, std::string name);
+ ConcatenatedVfsFile(std::map<u64, VirtualFile> files, std::string name);
public:
~ConcatenatedVfsFile() override;
+ /// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
+ static VirtualFile MakeConcatenatedFile(std::vector<VirtualFile> files, std::string name);
+
+ /// Convenience function that turns a map of offsets to files into a concatenated file, filling
+ /// gaps with a given filler byte.
+ static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::map<u64, VirtualFile> files,
+ std::string name);
+
std::string GetName() const override;
std::size_t GetSize() const override;
bool Resize(std::size_t new_size) override;
@@ -36,7 +40,7 @@ public:
private:
// Maps starting offset to file -- more efficient.
- boost::container::flat_map<u64, VirtualFile> files;
+ std::map<u64, VirtualFile> files;
std::string name;
};
diff --git a/src/core/file_sys/vfs_layered.cpp b/src/core/file_sys/vfs_layered.cpp
new file mode 100644
index 000000000..bfee01725
--- /dev/null
+++ b/src/core/file_sys/vfs_layered.cpp
@@ -0,0 +1,132 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <utility>
+#include "core/file_sys/vfs_layered.h"
+
+namespace FileSys {
+
+LayeredVfsDirectory::LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name)
+ : dirs(std::move(dirs)), name(std::move(name)) {}
+
+LayeredVfsDirectory::~LayeredVfsDirectory() = default;
+
+VirtualDir LayeredVfsDirectory::MakeLayeredDirectory(std::vector<VirtualDir> dirs,
+ std::string name) {
+ if (dirs.empty())
+ return nullptr;
+ if (dirs.size() == 1)
+ return dirs[0];
+
+ return std::shared_ptr<VfsDirectory>(new LayeredVfsDirectory(std::move(dirs), std::move(name)));
+}
+
+std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFileRelative(std::string_view path) const {
+ for (const auto& layer : dirs) {
+ const auto file = layer->GetFileRelative(path);
+ if (file != nullptr)
+ return file;
+ }
+
+ return nullptr;
+}
+
+std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetDirectoryRelative(
+ std::string_view path) const {
+ std::vector<VirtualDir> out;
+ for (const auto& layer : dirs) {
+ auto dir = layer->GetDirectoryRelative(path);
+ if (dir != nullptr)
+ out.push_back(std::move(dir));
+ }
+
+ return MakeLayeredDirectory(std::move(out));
+}
+
+std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFile(std::string_view name) const {
+ return GetFileRelative(name);
+}
+
+std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetSubdirectory(std::string_view name) const {
+ return GetDirectoryRelative(name);
+}
+
+std::string LayeredVfsDirectory::GetFullPath() const {
+ return dirs[0]->GetFullPath();
+}
+
+std::vector<std::shared_ptr<VfsFile>> LayeredVfsDirectory::GetFiles() const {
+ std::vector<VirtualFile> out;
+ for (const auto& layer : dirs) {
+ for (const auto& file : layer->GetFiles()) {
+ if (std::find_if(out.begin(), out.end(), [&file](const VirtualFile& comp) {
+ return comp->GetName() == file->GetName();
+ }) == out.end()) {
+ out.push_back(file);
+ }
+ }
+ }
+
+ return out;
+}
+
+std::vector<std::shared_ptr<VfsDirectory>> LayeredVfsDirectory::GetSubdirectories() const {
+ std::vector<std::string> names;
+ for (const auto& layer : dirs) {
+ for (const auto& sd : layer->GetSubdirectories()) {
+ if (std::find(names.begin(), names.end(), sd->GetName()) == names.end())
+ names.push_back(sd->GetName());
+ }
+ }
+
+ std::vector<VirtualDir> out;
+ out.reserve(names.size());
+ for (const auto& subdir : names)
+ out.push_back(GetSubdirectory(subdir));
+
+ return out;
+}
+
+bool LayeredVfsDirectory::IsWritable() const {
+ return false;
+}
+
+bool LayeredVfsDirectory::IsReadable() const {
+ return true;
+}
+
+std::string LayeredVfsDirectory::GetName() const {
+ return name.empty() ? dirs[0]->GetName() : name;
+}
+
+std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetParentDirectory() const {
+ return dirs[0]->GetParentDirectory();
+}
+
+std::shared_ptr<VfsDirectory> LayeredVfsDirectory::CreateSubdirectory(std::string_view name) {
+ return nullptr;
+}
+
+std::shared_ptr<VfsFile> LayeredVfsDirectory::CreateFile(std::string_view name) {
+ return nullptr;
+}
+
+bool LayeredVfsDirectory::DeleteSubdirectory(std::string_view name) {
+ return false;
+}
+
+bool LayeredVfsDirectory::DeleteFile(std::string_view name) {
+ return false;
+}
+
+bool LayeredVfsDirectory::Rename(std::string_view name_) {
+ name = name_;
+ return true;
+}
+
+bool LayeredVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
+ return false;
+}
+} // namespace FileSys
diff --git a/src/core/file_sys/vfs_layered.h b/src/core/file_sys/vfs_layered.h
new file mode 100644
index 000000000..d85310f57
--- /dev/null
+++ b/src/core/file_sys/vfs_layered.h
@@ -0,0 +1,50 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include "core/file_sys/vfs.h"
+
+namespace FileSys {
+
+// Class that stacks multiple VfsDirectories on top of each other, attempting to read from the first
+// one and falling back to the one after. The highest priority directory (overwrites all others)
+// should be element 0 in the dirs vector.
+class LayeredVfsDirectory : public VfsDirectory {
+ LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name);
+
+public:
+ ~LayeredVfsDirectory() override;
+
+ /// Wrapper function to allow for more efficient handling of dirs.size() == 0, 1 cases.
+ static VirtualDir MakeLayeredDirectory(std::vector<VirtualDir> dirs, std::string name = "");
+
+ std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override;
+ std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override;
+ std::shared_ptr<VfsFile> GetFile(std::string_view name) const override;
+ std::shared_ptr<VfsDirectory> GetSubdirectory(std::string_view name) const override;
+ std::string GetFullPath() const override;
+
+ std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
+ std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
+ bool IsWritable() const override;
+ bool IsReadable() const override;
+ std::string GetName() const override;
+ std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
+ std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override;
+ std::shared_ptr<VfsFile> CreateFile(std::string_view name) override;
+ bool DeleteSubdirectory(std::string_view name) override;
+ bool DeleteFile(std::string_view name) override;
+ bool Rename(std::string_view name) override;
+
+protected:
+ bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
+
+private:
+ std::vector<VirtualDir> dirs;
+ std::string name;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 5e242e20f..9defad04c 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -413,6 +413,23 @@ std::string RealVfsDirectory::GetFullPath() const {
return out;
}
+std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const {
+ if (perms == Mode::Append)
+ return {};
+
+ std::map<std::string, VfsEntryType, std::less<>> out;
+ FileUtil::ForeachDirectoryEntry(
+ nullptr, path,
+ [&out](u64* entries_out, const std::string& directory, const std::string& filename) {
+ const std::string full_path = directory + DIR_SEP + filename;
+ out.emplace(filename, FileUtil::IsDirectory(full_path) ? VfsEntryType::Directory
+ : VfsEntryType::File);
+ return true;
+ });
+
+ return out;
+}
+
bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index 681c43e82..5b61db90d 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -98,6 +98,7 @@ public:
bool DeleteFile(std::string_view name) override;
bool Rename(std::string_view name) override;
std::string GetFullPath() const override;
+ std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
diff --git a/src/core/file_sys/vfs_static.h b/src/core/file_sys/vfs_static.h
new file mode 100644
index 000000000..44fab51d1
--- /dev/null
+++ b/src/core/file_sys/vfs_static.h
@@ -0,0 +1,79 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <algorithm>
+#include <memory>
+#include <string_view>
+
+#include "core/file_sys/vfs.h"
+
+namespace FileSys {
+
+class StaticVfsFile : public VfsFile {
+public:
+ explicit StaticVfsFile(u8 value, std::size_t size = 0, std::string name = "",
+ VirtualDir parent = nullptr)
+ : value{value}, size{size}, name{std::move(name)}, parent{std::move(parent)} {}
+
+ std::string GetName() const override {
+ return name;
+ }
+
+ std::size_t GetSize() const override {
+ return size;
+ }
+
+ bool Resize(std::size_t new_size) override {
+ size = new_size;
+ return true;
+ }
+
+ std::shared_ptr<VfsDirectory> GetContainingDirectory() const override {
+ return parent;
+ }
+
+ bool IsWritable() const override {
+ return false;
+ }
+
+ bool IsReadable() const override {
+ return true;
+ }
+
+ std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override {
+ const auto read = std::min(length, size - offset);
+ std::fill(data, data + read, value);
+ return read;
+ }
+
+ std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override {
+ return 0;
+ }
+
+ boost::optional<u8> ReadByte(std::size_t offset) const override {
+ if (offset < size)
+ return value;
+ return boost::none;
+ }
+
+ std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override {
+ const auto read = std::min(length, size - offset);
+ return std::vector<u8>(read, value);
+ }
+
+ bool Rename(std::string_view new_name) override {
+ name = new_name;
+ return true;
+ }
+
+private:
+ u8 value;
+ std::size_t size;
+ std::string name;
+ VirtualDir parent;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp
index ec7f735b5..389c7e003 100644
--- a/src/core/file_sys/vfs_vector.cpp
+++ b/src/core/file_sys/vfs_vector.cpp
@@ -3,10 +3,64 @@
// Refer to the license.txt file included.
#include <algorithm>
+#include <cstring>
#include <utility>
#include "core/file_sys/vfs_vector.h"
namespace FileSys {
+VectorVfsFile::VectorVfsFile(std::vector<u8> initial_data, std::string name, VirtualDir parent)
+ : data(std::move(initial_data)), parent(std::move(parent)), name(std::move(name)) {}
+
+VectorVfsFile::~VectorVfsFile() = default;
+
+std::string VectorVfsFile::GetName() const {
+ return name;
+}
+
+size_t VectorVfsFile::GetSize() const {
+ return data.size();
+}
+
+bool VectorVfsFile::Resize(size_t new_size) {
+ data.resize(new_size);
+ return true;
+}
+
+std::shared_ptr<VfsDirectory> VectorVfsFile::GetContainingDirectory() const {
+ return parent;
+}
+
+bool VectorVfsFile::IsWritable() const {
+ return true;
+}
+
+bool VectorVfsFile::IsReadable() const {
+ return true;
+}
+
+std::size_t VectorVfsFile::Read(u8* data_, std::size_t length, std::size_t offset) const {
+ const auto read = std::min(length, data.size() - offset);
+ std::memcpy(data_, data.data() + offset, read);
+ return read;
+}
+
+std::size_t VectorVfsFile::Write(const u8* data_, std::size_t length, std::size_t offset) {
+ if (offset + length > data.size())
+ data.resize(offset + length);
+ const auto write = std::min(length, data.size() - offset);
+ std::memcpy(data.data(), data_, write);
+ return write;
+}
+
+bool VectorVfsFile::Rename(std::string_view name_) {
+ name = name_;
+ return true;
+}
+
+void VectorVfsFile::Assign(std::vector<u8> new_data) {
+ data = std::move(new_data);
+}
+
VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_,
std::vector<VirtualDir> dirs_, std::string name_,
VirtualDir parent_)
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h
index cba44a7a6..48a414c98 100644
--- a/src/core/file_sys/vfs_vector.h
+++ b/src/core/file_sys/vfs_vector.h
@@ -8,6 +8,31 @@
namespace FileSys {
+// An implementation of VfsFile that is backed by a vector optionally supplied upon construction
+class VectorVfsFile : public VfsFile {
+public:
+ explicit VectorVfsFile(std::vector<u8> initial_data = {}, std::string name = "",
+ VirtualDir parent = nullptr);
+ ~VectorVfsFile() override;
+
+ std::string GetName() const override;
+ std::size_t GetSize() const override;
+ bool Resize(std::size_t new_size) override;
+ std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
+ bool IsWritable() const override;
+ bool IsReadable() const override;
+ std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
+ std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
+ bool Rename(std::string_view name) override;
+
+ virtual void Assign(std::vector<u8> new_data);
+
+private:
+ std::vector<u8> data;
+ VirtualDir parent;
+ std::string name;
+};
+
// An implementation of VfsDirectory that maintains two vectors for subdirectories and files.
// Vector data is supplied upon construction.
class VectorVfsDirectory : public VfsDirectory {
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 0ecdd9f82..d8c7b3492 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -37,7 +37,9 @@
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/gdbstub/gdbstub.h"
+#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
+#include "core/hle/kernel/vm_manager.h"
#include "core/loader/loader.h"
#include "core/memory.h"
@@ -585,7 +587,8 @@ static void HandleQuery() {
strlen("Xfer:features:read:target.xml:")) == 0) {
SendReply(target_xml);
} else if (strncmp(query, "Offsets", strlen("Offsets")) == 0) {
- std::string buffer = fmt::format("TextSeg={:0x}", Memory::PROCESS_IMAGE_VADDR);
+ const VAddr base_address = Core::CurrentProcess()->vm_manager.GetCodeRegionBaseAddress();
+ std::string buffer = fmt::format("TextSeg={:0x}", base_address);
SendReply(buffer.c_str());
} else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
std::string val = "m";
@@ -893,11 +896,11 @@ static void ReadMemory() {
static u8 reply[GDB_BUFFER_SIZE - 4];
auto start_offset = command_buffer + 1;
- auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
- VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
+ const auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
+ const VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
start_offset = addr_pos + 1;
- u64 len =
+ const u64 len =
HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset));
LOG_DEBUG(Debug_GDBStub, "gdb: addr: {:016X} len: {:016X}", addr, len);
@@ -906,7 +909,9 @@ static void ReadMemory() {
SendReply("E01");
}
- if (addr < Memory::PROCESS_IMAGE_VADDR || addr >= Memory::MAP_REGION_VADDR_END) {
+ const auto& vm_manager = Core::CurrentProcess()->vm_manager;
+ if (addr < vm_manager.GetCodeRegionBaseAddress() ||
+ addr >= vm_manager.GetMapRegionEndAddress()) {
return SendReply("E00");
}
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h
index b054cbf7d..9eb72315c 100644
--- a/src/core/hle/kernel/object.h
+++ b/src/core/hle/kernel/object.h
@@ -6,7 +6,6 @@
#include <atomic>
#include <string>
-#include <utility>
#include <boost/smart_ptr/intrusive_ptr.hpp>
@@ -97,7 +96,7 @@ using SharedPtr = boost::intrusive_ptr<T>;
template <typename T>
inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) {
if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
- return boost::static_pointer_cast<T>(std::move(object));
+ return boost::static_pointer_cast<T>(object);
}
return nullptr;
}
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 914bbe0a1..a8e3098ca 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -7,10 +7,13 @@
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
+#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
@@ -32,14 +35,21 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
process->name = std::move(name);
process->flags.raw = 0;
process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
+ process->resource_limit = kernel.ResourceLimitForCategory(ResourceLimitCategory::APPLICATION);
process->status = ProcessStatus::Created;
process->program_id = 0;
process->process_id = kernel.CreateNewProcessID();
+ process->svc_access_mask.set();
kernel.AppendNewProcess(process);
return process;
}
+void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
+ program_id = metadata.GetTitleID();
+ vm_manager.Reset(metadata.GetAddressSpaceType());
+}
+
void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) {
for (std::size_t i = 0; i < len; ++i) {
u32 descriptor = kernel_caps[i];
@@ -117,7 +127,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
// TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part
// of the user address space.
vm_manager
- .MapMemoryBlock(Memory::STACK_AREA_VADDR_END - stack_size,
+ .MapMemoryBlock(vm_manager.GetTLSIORegionEndAddress() - stack_size,
std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size,
MemoryState::Mapped)
.Unwrap();
@@ -128,6 +138,91 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, *this);
}
+void Process::PrepareForTermination() {
+ status = ProcessStatus::Exited;
+
+ const auto stop_threads = [this](const std::vector<SharedPtr<Thread>>& thread_list) {
+ for (auto& thread : thread_list) {
+ if (thread->owner_process != this)
+ continue;
+
+ if (thread == GetCurrentThread())
+ continue;
+
+ // TODO(Subv): When are the other running/ready threads terminated?
+ ASSERT_MSG(thread->status == ThreadStatus::WaitSynchAny ||
+ thread->status == ThreadStatus::WaitSynchAll,
+ "Exiting processes with non-waiting threads is currently unimplemented");
+
+ thread->Stop();
+ }
+ };
+
+ auto& system = Core::System::GetInstance();
+ stop_threads(system.Scheduler(0)->GetThreadList());
+ stop_threads(system.Scheduler(1)->GetThreadList());
+ stop_threads(system.Scheduler(2)->GetThreadList());
+ stop_threads(system.Scheduler(3)->GetThreadList());
+}
+
+/**
+ * Finds a free location for the TLS section of a thread.
+ * @param tls_slots The TLS page array of the thread's owner process.
+ * Returns a tuple of (page, slot, alloc_needed) where:
+ * page: The index of the first allocated TLS page that has free slots.
+ * slot: The index of the first free slot in the indicated page.
+ * alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full).
+ */
+static std::tuple<std::size_t, std::size_t, bool> FindFreeThreadLocalSlot(
+ const std::vector<std::bitset<8>>& tls_slots) {
+ // Iterate over all the allocated pages, and try to find one where not all slots are used.
+ for (std::size_t page = 0; page < tls_slots.size(); ++page) {
+ const auto& page_tls_slots = tls_slots[page];
+ if (!page_tls_slots.all()) {
+ // We found a page with at least one free slot, find which slot it is
+ for (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) {
+ if (!page_tls_slots.test(slot)) {
+ return std::make_tuple(page, slot, false);
+ }
+ }
+ }
+ }
+
+ return std::make_tuple(0, 0, true);
+}
+
+VAddr Process::MarkNextAvailableTLSSlotAsUsed(Thread& thread) {
+ auto [available_page, available_slot, needs_allocation] = FindFreeThreadLocalSlot(tls_slots);
+ const VAddr tls_begin = vm_manager.GetTLSIORegionBaseAddress();
+
+ if (needs_allocation) {
+ tls_slots.emplace_back(0); // The page is completely available at the start
+ available_page = tls_slots.size() - 1;
+ available_slot = 0; // Use the first slot in the new page
+
+ // Allocate some memory from the end of the linear heap for this region.
+ auto& tls_memory = thread.GetTLSMemory();
+ tls_memory->insert(tls_memory->end(), Memory::PAGE_SIZE, 0);
+
+ vm_manager.RefreshMemoryBlockMappings(tls_memory.get());
+
+ vm_manager.MapMemoryBlock(tls_begin + available_page * Memory::PAGE_SIZE, tls_memory, 0,
+ Memory::PAGE_SIZE, MemoryState::ThreadLocal);
+ }
+
+ tls_slots[available_page].set(available_slot);
+
+ return tls_begin + available_page * Memory::PAGE_SIZE + available_slot * Memory::TLS_ENTRY_SIZE;
+}
+
+void Process::FreeTLSSlot(VAddr tls_address) {
+ const VAddr tls_base = tls_address - vm_manager.GetTLSIORegionBaseAddress();
+ const VAddr tls_page = tls_base / Memory::PAGE_SIZE;
+ const VAddr tls_slot = (tls_base % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
+
+ tls_slots[tls_page].reset(tls_slot);
+}
+
void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
MemoryState memory_state) {
@@ -145,8 +240,8 @@ void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
}
ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
- if (target < Memory::HEAP_VADDR || target + size > Memory::HEAP_VADDR_END ||
- target + size < target) {
+ if (target < vm_manager.GetHeapRegionBaseAddress() ||
+ target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
return ERR_INVALID_ADDRESS;
}
@@ -181,8 +276,8 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per
}
ResultCode Process::HeapFree(VAddr target, u32 size) {
- if (target < Memory::HEAP_VADDR || target + size > Memory::HEAP_VADDR_END ||
- target + size < target) {
+ if (target < vm_manager.GetHeapRegionBaseAddress() ||
+ target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
return ERR_INVALID_ADDRESS;
}
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 81538f70c..adb03c228 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -17,6 +17,10 @@
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/vm_manager.h"
+namespace FileSys {
+class ProgramMetadata;
+}
+
namespace Kernel {
class KernelCore;
@@ -131,6 +135,24 @@ public:
return HANDLE_TYPE;
}
+ /// Gets the current status of the process
+ ProcessStatus GetStatus() const {
+ return status;
+ }
+
+ /// Gets the unique ID that identifies this particular process.
+ u32 GetProcessID() const {
+ return process_id;
+ }
+
+ /**
+ * Loads process-specifics configuration info with metadata provided
+ * by an executable.
+ *
+ * @param metadata The provided metadata to load process specific info.
+ */
+ void LoadFromMetadata(const FileSys::ProgramMetadata& metadata);
+
/// Title ID corresponding to the process
u64 program_id;
@@ -154,11 +176,6 @@ public:
u32 allowed_processor_mask = THREADPROCESSORID_DEFAULT_MASK;
u32 allowed_thread_priority_mask = 0xFFFFFFFF;
u32 is_virtual_address_memory_enabled = 0;
- /// Current status of the process
- ProcessStatus status;
-
- /// The ID of this process
- u32 process_id = 0;
/**
* Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them
@@ -171,13 +188,42 @@ public:
*/
void Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size);
+ /**
+ * Prepares a process for termination by stopping all of its threads
+ * and clearing any other resources.
+ */
+ void PrepareForTermination();
+
void LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr);
///////////////////////////////////////////////////////////////////////////////////////////////
// Memory Management
+ // Marks the next available region as used and returns the address of the slot.
+ VAddr MarkNextAvailableTLSSlotAsUsed(Thread& thread);
+
+ // Frees a used TLS slot identified by the given address
+ void FreeTLSSlot(VAddr tls_address);
+
+ ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
+ ResultCode HeapFree(VAddr target, u32 size);
+
+ ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
+
+ ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
+
VMManager vm_manager;
+private:
+ explicit Process(KernelCore& kernel);
+ ~Process() override;
+
+ /// Current status of the process
+ ProcessStatus status;
+
+ /// The ID of this process
+ u32 process_id = 0;
+
// Memory used to back the allocations in the regular heap. A single vector is used to cover
// the entire virtual address space extents that bound the allocations, including any holes.
// This makes deallocation and reallocation of holes fast and keeps process memory contiguous
@@ -197,17 +243,6 @@ public:
std::vector<std::bitset<8>> tls_slots;
std::string name;
-
- ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
- ResultCode HeapFree(VAddr target, u32 size);
-
- ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
-
- ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
-
-private:
- explicit Process(KernelCore& kernel);
- ~Process() override;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index 69c812f16..9faf903cf 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -17,7 +17,7 @@ namespace Kernel {
std::mutex Scheduler::scheduler_mutex;
-Scheduler::Scheduler(Core::ARM_Interface* cpu_core) : cpu_core(cpu_core) {}
+Scheduler::Scheduler(Core::ARM_Interface& cpu_core) : cpu_core(cpu_core) {}
Scheduler::~Scheduler() {
for (auto& thread : thread_list) {
@@ -59,9 +59,9 @@ void Scheduler::SwitchContext(Thread* new_thread) {
// Save context for previous thread
if (previous_thread) {
previous_thread->last_running_ticks = CoreTiming::GetTicks();
- cpu_core->SaveContext(previous_thread->context);
+ cpu_core.SaveContext(previous_thread->context);
// Save the TPIDR_EL0 system register in case it was modified.
- previous_thread->tpidr_el0 = cpu_core->GetTPIDR_EL0();
+ previous_thread->tpidr_el0 = cpu_core.GetTPIDR_EL0();
if (previous_thread->status == ThreadStatus::Running) {
// This is only the case when a reschedule is triggered without the current thread
@@ -91,10 +91,10 @@ void Scheduler::SwitchContext(Thread* new_thread) {
SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table);
}
- cpu_core->LoadContext(new_thread->context);
- cpu_core->SetTlsAddress(new_thread->GetTLSAddress());
- cpu_core->SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
- cpu_core->ClearExclusiveState();
+ cpu_core.LoadContext(new_thread->context);
+ cpu_core.SetTlsAddress(new_thread->GetTLSAddress());
+ cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
+ cpu_core.ClearExclusiveState();
} else {
current_thread = nullptr;
// Note: We do not reset the current process and current page table when idling because
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
index 744990c9b..2c94641ec 100644
--- a/src/core/hle/kernel/scheduler.h
+++ b/src/core/hle/kernel/scheduler.h
@@ -19,7 +19,7 @@ namespace Kernel {
class Scheduler final {
public:
- explicit Scheduler(Core::ARM_Interface* cpu_core);
+ explicit Scheduler(Core::ARM_Interface& cpu_core);
~Scheduler();
/// Returns whether there are any threads that are ready to run.
@@ -72,7 +72,7 @@ private:
SharedPtr<Thread> current_thread = nullptr;
- Core::ARM_Interface* cpu_core;
+ Core::ARM_Interface& cpu_core;
static std::mutex scheduler_mutex;
};
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index abb1d09cd..9b78c8cb5 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -8,6 +8,7 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
+#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/memory.h"
@@ -71,7 +72,8 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
shared_memory->other_permissions = other_permissions;
shared_memory->backing_block = std::move(heap_block);
shared_memory->backing_block_offset = offset;
- shared_memory->base_address = Memory::HEAP_VADDR + offset;
+ shared_memory->base_address =
+ kernel.CurrentProcess()->vm_manager.GetHeapRegionBaseAddress() + offset;
return shared_memory;
}
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 371fc439e..44bbaf0c8 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -51,8 +51,9 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
}
auto& process = *Core::CurrentProcess();
+ const VAddr heap_base = process.vm_manager.GetHeapRegionBaseAddress();
CASCADE_RESULT(*heap_addr,
- process.HeapAllocate(Memory::HEAP_VADDR, heap_size, VMAPermission::ReadWrite));
+ process.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite));
return RESULT_SUCCESS;
}
@@ -169,7 +170,7 @@ static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
return ERR_INVALID_HANDLE;
}
- *process_id = process->process_id;
+ *process_id = process->GetProcessID();
return RESULT_SUCCESS;
}
@@ -325,26 +326,27 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
info_sub_id, handle);
- const auto& vm_manager = Core::CurrentProcess()->vm_manager;
+ const auto& current_process = Core::CurrentProcess();
+ const auto& vm_manager = current_process->vm_manager;
switch (static_cast<GetInfoType>(info_id)) {
case GetInfoType::AllowedCpuIdBitmask:
- *result = Core::CurrentProcess()->allowed_processor_mask;
+ *result = current_process->allowed_processor_mask;
break;
case GetInfoType::AllowedThreadPrioBitmask:
- *result = Core::CurrentProcess()->allowed_thread_priority_mask;
+ *result = current_process->allowed_thread_priority_mask;
break;
case GetInfoType::MapRegionBaseAddr:
- *result = Memory::MAP_REGION_VADDR;
+ *result = vm_manager.GetMapRegionBaseAddress();
break;
case GetInfoType::MapRegionSize:
- *result = Memory::MAP_REGION_SIZE;
+ *result = vm_manager.GetMapRegionSize();
break;
case GetInfoType::HeapRegionBaseAddr:
- *result = Memory::HEAP_VADDR;
+ *result = vm_manager.GetHeapRegionBaseAddress();
break;
case GetInfoType::HeapRegionSize:
- *result = Memory::HEAP_SIZE;
+ *result = vm_manager.GetHeapRegionSize();
break;
case GetInfoType::TotalMemoryUsage:
*result = vm_manager.GetTotalMemoryUsage();
@@ -359,22 +361,35 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
*result = 0;
break;
case GetInfoType::AddressSpaceBaseAddr:
- *result = vm_manager.GetAddressSpaceBaseAddr();
+ *result = vm_manager.GetCodeRegionBaseAddress();
break;
- case GetInfoType::AddressSpaceSize:
- *result = vm_manager.GetAddressSpaceSize();
+ case GetInfoType::AddressSpaceSize: {
+ const u64 width = vm_manager.GetAddressSpaceWidth();
+
+ switch (width) {
+ case 32:
+ *result = 0xFFE00000;
+ break;
+ case 36:
+ *result = 0xFF8000000;
+ break;
+ case 39:
+ *result = 0x7FF8000000;
+ break;
+ }
break;
+ }
case GetInfoType::NewMapRegionBaseAddr:
- *result = Memory::NEW_MAP_REGION_VADDR;
+ *result = vm_manager.GetNewMapRegionBaseAddress();
break;
case GetInfoType::NewMapRegionSize:
- *result = Memory::NEW_MAP_REGION_SIZE;
+ *result = vm_manager.GetNewMapRegionSize();
break;
case GetInfoType::IsVirtualAddressMemoryEnabled:
- *result = Core::CurrentProcess()->is_virtual_address_memory_enabled;
+ *result = current_process->is_virtual_address_memory_enabled;
break;
case GetInfoType::TitleId:
- *result = Core::CurrentProcess()->program_id;
+ *result = current_process->program_id;
break;
case GetInfoType::PrivilegedProcessId:
LOG_WARNING(Kernel_SVC,
@@ -530,35 +545,13 @@ static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAdd
/// Exits the current process
static void ExitProcess() {
- LOG_INFO(Kernel_SVC, "Process {} exiting", Core::CurrentProcess()->process_id);
+ auto& current_process = Core::CurrentProcess();
- ASSERT_MSG(Core::CurrentProcess()->status == ProcessStatus::Running,
+ LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
+ ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running,
"Process has already exited");
- Core::CurrentProcess()->status = ProcessStatus::Exited;
-
- auto stop_threads = [](const std::vector<SharedPtr<Thread>>& thread_list) {
- for (auto& thread : thread_list) {
- if (thread->owner_process != Core::CurrentProcess())
- continue;
-
- if (thread == GetCurrentThread())
- continue;
-
- // TODO(Subv): When are the other running/ready threads terminated?
- ASSERT_MSG(thread->status == ThreadStatus::WaitSynchAny ||
- thread->status == ThreadStatus::WaitSynchAll,
- "Exiting processes with non-waiting threads is currently unimplemented");
-
- thread->Stop();
- }
- };
-
- auto& system = Core::System::GetInstance();
- stop_threads(system.Scheduler(0)->GetThreadList());
- stop_threads(system.Scheduler(1)->GetThreadList());
- stop_threads(system.Scheduler(2)->GetThreadList());
- stop_threads(system.Scheduler(3)->GetThreadList());
+ current_process->PrepareForTermination();
// Kill the current thread
GetCurrentThread()->Stop();
@@ -1039,7 +1032,7 @@ static const FunctionDef SVC_Table[] = {
{0x2B, nullptr, "FlushDataCache"},
{0x2C, nullptr, "MapPhysicalMemory"},
{0x2D, nullptr, "UnmapPhysicalMemory"},
- {0x2E, nullptr, "GetNextThreadInfo"},
+ {0x2E, nullptr, "GetFutureThreadInfo"},
{0x2F, nullptr, "GetLastThreadInfo"},
{0x30, nullptr, "GetResourceLimitLimitValue"},
{0x31, nullptr, "GetResourceLimitCurrentValue"},
@@ -1065,11 +1058,11 @@ static const FunctionDef SVC_Table[] = {
{0x45, nullptr, "CreateEvent"},
{0x46, nullptr, "Unknown"},
{0x47, nullptr, "Unknown"},
- {0x48, nullptr, "AllocateUnsafeMemory"},
- {0x49, nullptr, "FreeUnsafeMemory"},
- {0x4A, nullptr, "SetUnsafeAllocationLimit"},
- {0x4B, nullptr, "CreateJitMemory"},
- {0x4C, nullptr, "MapJitMemory"},
+ {0x48, nullptr, "MapPhysicalMemoryUnsafe"},
+ {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"},
+ {0x4A, nullptr, "SetUnsafeLimit"},
+ {0x4B, nullptr, "CreateCodeMemory"},
+ {0x4C, nullptr, "ControlCodeMemory"},
{0x4D, nullptr, "SleepSystem"},
{0x4E, nullptr, "ReadWriteRegister"},
{0x4F, nullptr, "SetProcessActivity"},
@@ -1104,7 +1097,7 @@ static const FunctionDef SVC_Table[] = {
{0x6C, nullptr, "SetHardwareBreakPoint"},
{0x6D, nullptr, "GetDebugThreadParam"},
{0x6E, nullptr, "Unknown"},
- {0x6F, nullptr, "GetMemoryInfo"},
+ {0x6F, nullptr, "GetSystemInfo"},
{0x70, nullptr, "CreatePort"},
{0x71, nullptr, "ManageNamedPort"},
{0x72, nullptr, "ConnectToPort"},
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index c2d7535c9..064ed908d 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -65,10 +65,7 @@ void Thread::Stop() {
wait_objects.clear();
// Mark the TLS slot in the thread's page as free.
- const u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE;
- const u64 tls_slot =
- ((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
- Core::CurrentProcess()->tls_slots[tls_page].reset(tls_slot);
+ owner_process->FreeTLSSlot(tls_address);
}
void WaitCurrentThread_Sleep() {
@@ -178,32 +175,6 @@ void Thread::ResumeFromWait() {
}
/**
- * Finds a free location for the TLS section of a thread.
- * @param tls_slots The TLS page array of the thread's owner process.
- * Returns a tuple of (page, slot, alloc_needed) where:
- * page: The index of the first allocated TLS page that has free slots.
- * slot: The index of the first free slot in the indicated page.
- * alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full).
- */
-static std::tuple<std::size_t, std::size_t, bool> GetFreeThreadLocalSlot(
- const std::vector<std::bitset<8>>& tls_slots) {
- // Iterate over all the allocated pages, and try to find one where not all slots are used.
- for (std::size_t page = 0; page < tls_slots.size(); ++page) {
- const auto& page_tls_slots = tls_slots[page];
- if (!page_tls_slots.all()) {
- // We found a page with at least one free slot, find which slot it is
- for (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) {
- if (!page_tls_slots.test(slot)) {
- return std::make_tuple(page, slot, false);
- }
- }
- }
- }
-
- return std::make_tuple(0, 0, true);
-}
-
-/**
* Resets a thread context, making it ready to be scheduled and run by the CPU
* @param context Thread context to reset
* @param stack_top Address of the top of the stack
@@ -264,32 +235,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
thread->owner_process = owner_process;
thread->scheduler = Core::System::GetInstance().Scheduler(processor_id);
thread->scheduler->AddThread(thread, priority);
-
- // Find the next available TLS index, and mark it as used
- auto& tls_slots = owner_process->tls_slots;
-
- auto [available_page, available_slot, needs_allocation] = GetFreeThreadLocalSlot(tls_slots);
- if (needs_allocation) {
- tls_slots.emplace_back(0); // The page is completely available at the start
- available_page = tls_slots.size() - 1;
- available_slot = 0; // Use the first slot in the new page
-
- // Allocate some memory from the end of the linear heap for this region.
- const std::size_t offset = thread->tls_memory->size();
- thread->tls_memory->insert(thread->tls_memory->end(), Memory::PAGE_SIZE, 0);
-
- auto& vm_manager = owner_process->vm_manager;
- vm_manager.RefreshMemoryBlockMappings(thread->tls_memory.get());
-
- vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE,
- thread->tls_memory, 0, Memory::PAGE_SIZE,
- MemoryState::ThreadLocal);
- }
-
- // Mark the slot as used
- tls_slots[available_page].set(available_slot);
- thread->tls_address = Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE +
- available_slot * Memory::TLS_ENTRY_SIZE;
+ thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
// to initialize the context
@@ -316,8 +262,9 @@ SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 pri
SetCurrentPageTable(&owner_process.vm_manager.page_table);
// Initialize new "main" thread
+ const VAddr stack_top = owner_process.vm_manager.GetTLSIORegionEndAddress();
auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0,
- Memory::STACK_AREA_VADDR_END, &owner_process);
+ stack_top, &owner_process);
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 91e9b79ec..4250144c3 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -62,6 +62,9 @@ enum class ThreadWakeupReason {
class Thread final : public WaitObject {
public:
+ using TLSMemory = std::vector<u8>;
+ using TLSMemoryPtr = std::shared_ptr<TLSMemory>;
+
/**
* Creates and returns a new thread. The new thread is immediately scheduled
* @param kernel The kernel instance this thread will be created under.
@@ -134,6 +137,14 @@ public:
return thread_id;
}
+ TLSMemoryPtr& GetTLSMemory() {
+ return tls_memory;
+ }
+
+ const TLSMemoryPtr& GetTLSMemory() const {
+ return tls_memory;
+ }
+
/**
* Resumes a thread from waiting
*/
@@ -269,7 +280,7 @@ private:
explicit Thread(KernelCore& kernel);
~Thread() override;
- std::shared_ptr<std::vector<u8>> tls_memory = std::make_shared<std::vector<u8>>();
+ TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>();
};
/**
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 608cbd57b..e412309fd 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -9,6 +9,7 @@
#include "common/logging/log.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
+#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
@@ -54,30 +55,32 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
}
VMManager::VMManager() {
- Reset();
+ // Default to assuming a 39-bit address space. This way we have a sane
+ // starting point with executables that don't provide metadata.
+ Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
}
VMManager::~VMManager() {
- Reset();
+ Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
}
-void VMManager::Reset() {
- vma_map.clear();
+void VMManager::Reset(FileSys::ProgramAddressSpaceType type) {
+ Clear();
+
+ InitializeMemoryRegionRanges(type);
+
+ page_table.Resize(address_space_width);
// Initialize the map with a single free region covering the entire managed space.
VirtualMemoryArea initial_vma;
- initial_vma.size = MAX_ADDRESS;
+ initial_vma.size = address_space_end;
vma_map.emplace(initial_vma.base, initial_vma);
- page_table.pointers.fill(nullptr);
- page_table.special_regions.clear();
- page_table.attributes.fill(Memory::PageType::Unmapped);
-
UpdatePageTableForVMA(initial_vma);
}
VMManager::VMAHandle VMManager::FindVMA(VAddr target) const {
- if (target >= MAX_ADDRESS) {
+ if (target >= address_space_end) {
return vma_map.end();
} else {
return std::prev(vma_map.upper_bound(target));
@@ -291,7 +294,7 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u64 size) {
const VAddr target_end = target + size;
ASSERT(target_end >= target);
- ASSERT(target_end <= MAX_ADDRESS);
+ ASSERT(target_end <= address_space_end);
ASSERT(size > 0);
VMAIter begin_vma = StripIterConstness(FindVMA(target));
@@ -382,6 +385,85 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
}
}
+void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType type) {
+ u64 map_region_size = 0;
+ u64 heap_region_size = 0;
+ u64 new_map_region_size = 0;
+ u64 tls_io_region_size = 0;
+
+ switch (type) {
+ case FileSys::ProgramAddressSpaceType::Is32Bit:
+ address_space_width = 32;
+ code_region_base = 0x200000;
+ code_region_end = code_region_base + 0x3FE00000;
+ map_region_size = 0x40000000;
+ heap_region_size = 0x40000000;
+ break;
+ case FileSys::ProgramAddressSpaceType::Is36Bit:
+ address_space_width = 36;
+ code_region_base = 0x8000000;
+ code_region_end = code_region_base + 0x78000000;
+ map_region_size = 0x180000000;
+ heap_region_size = 0x180000000;
+ break;
+ case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
+ address_space_width = 32;
+ code_region_base = 0x200000;
+ code_region_end = code_region_base + 0x3FE00000;
+ map_region_size = 0;
+ heap_region_size = 0x80000000;
+ break;
+ case FileSys::ProgramAddressSpaceType::Is39Bit:
+ address_space_width = 39;
+ code_region_base = 0x8000000;
+ code_region_end = code_region_base + 0x80000000;
+ map_region_size = 0x1000000000;
+ heap_region_size = 0x180000000;
+ new_map_region_size = 0x80000000;
+ tls_io_region_size = 0x1000000000;
+ break;
+ default:
+ UNREACHABLE_MSG("Invalid address space type specified: {}", static_cast<u32>(type));
+ return;
+ }
+
+ address_space_base = 0;
+ address_space_end = 1ULL << address_space_width;
+
+ map_region_base = code_region_end;
+ map_region_end = map_region_base + map_region_size;
+
+ heap_region_base = map_region_end;
+ heap_region_end = heap_region_base + heap_region_size;
+
+ new_map_region_base = heap_region_end;
+ new_map_region_end = new_map_region_base + new_map_region_size;
+
+ tls_io_region_base = new_map_region_end;
+ tls_io_region_end = tls_io_region_base + tls_io_region_size;
+
+ if (new_map_region_size == 0) {
+ new_map_region_base = address_space_base;
+ new_map_region_end = address_space_end;
+ }
+}
+
+void VMManager::Clear() {
+ ClearVMAMap();
+ ClearPageTable();
+}
+
+void VMManager::ClearVMAMap() {
+ vma_map.clear();
+}
+
+void VMManager::ClearPageTable() {
+ std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr);
+ page_table.special_regions.clear();
+ std::fill(page_table.attributes.begin(), page_table.attributes.end(),
+ Memory::PageType::Unmapped);
+}
+
u64 VMManager::GetTotalMemoryUsage() const {
LOG_WARNING(Kernel, "(STUBBED) called");
return 0xF8000000;
@@ -392,14 +474,80 @@ u64 VMManager::GetTotalHeapUsage() const {
return 0x0;
}
-VAddr VMManager::GetAddressSpaceBaseAddr() const {
- LOG_WARNING(Kernel, "(STUBBED) called");
- return 0x8000000;
+VAddr VMManager::GetAddressSpaceBaseAddress() const {
+ return address_space_base;
+}
+
+VAddr VMManager::GetAddressSpaceEndAddress() const {
+ return address_space_end;
}
u64 VMManager::GetAddressSpaceSize() const {
- LOG_WARNING(Kernel, "(STUBBED) called");
- return MAX_ADDRESS;
+ return address_space_end - address_space_base;
+}
+
+u64 VMManager::GetAddressSpaceWidth() const {
+ return address_space_width;
+}
+
+VAddr VMManager::GetCodeRegionBaseAddress() const {
+ return code_region_base;
+}
+
+VAddr VMManager::GetCodeRegionEndAddress() const {
+ return code_region_end;
+}
+
+u64 VMManager::GetCodeRegionSize() const {
+ return code_region_end - code_region_base;
+}
+
+VAddr VMManager::GetHeapRegionBaseAddress() const {
+ return heap_region_base;
+}
+
+VAddr VMManager::GetHeapRegionEndAddress() const {
+ return heap_region_end;
+}
+
+u64 VMManager::GetHeapRegionSize() const {
+ return heap_region_end - heap_region_base;
+}
+
+VAddr VMManager::GetMapRegionBaseAddress() const {
+ return map_region_base;
+}
+
+VAddr VMManager::GetMapRegionEndAddress() const {
+ return map_region_end;
+}
+
+u64 VMManager::GetMapRegionSize() const {
+ return map_region_end - map_region_base;
+}
+
+VAddr VMManager::GetNewMapRegionBaseAddress() const {
+ return new_map_region_base;
+}
+
+VAddr VMManager::GetNewMapRegionEndAddress() const {
+ return new_map_region_end;
+}
+
+u64 VMManager::GetNewMapRegionSize() const {
+ return new_map_region_end - new_map_region_base;
+}
+
+VAddr VMManager::GetTLSIORegionBaseAddress() const {
+ return tls_io_region_base;
+}
+
+VAddr VMManager::GetTLSIORegionEndAddress() const {
+ return tls_io_region_end;
+}
+
+u64 VMManager::GetTLSIORegionSize() const {
+ return tls_io_region_end - tls_io_region_base;
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index de75036c0..015559a64 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -12,6 +12,10 @@
#include "core/memory.h"
#include "core/memory_hook.h"
+namespace FileSys {
+enum class ProgramAddressSpaceType : u8;
+}
+
namespace Kernel {
enum class VMAType : u8 {
@@ -111,12 +115,6 @@ struct VirtualMemoryArea {
class VMManager final {
public:
/**
- * The maximum amount of address space managed by the kernel.
- * @todo This was selected arbitrarily, and should be verified for Switch OS.
- */
- static constexpr VAddr MAX_ADDRESS{0x1000000000ULL};
-
- /**
* A map covering the entirety of the managed address space, keyed by the `base` field of each
* VMA. It must always be modified by splitting or merging VMAs, so that the invariant
* `elem.base + elem.size == next.base` is preserved, and mergeable regions must always be
@@ -130,7 +128,7 @@ public:
~VMManager();
/// Clears the address space map, re-initializing with a single free area.
- void Reset();
+ void Reset(FileSys::ProgramAddressSpaceType type);
/// Finds the VMA in which the given address is included in, or `vma_map.end()`.
VMAHandle FindVMA(VAddr target) const;
@@ -195,12 +193,63 @@ public:
/// Gets the total heap usage, used by svcGetInfo
u64 GetTotalHeapUsage() const;
- /// Gets the total address space base address, used by svcGetInfo
- VAddr GetAddressSpaceBaseAddr() const;
+ /// Gets the address space base address
+ VAddr GetAddressSpaceBaseAddress() const;
- /// Gets the total address space address size, used by svcGetInfo
+ /// Gets the address space end address
+ VAddr GetAddressSpaceEndAddress() const;
+
+ /// Gets the total address space address size in bytes
u64 GetAddressSpaceSize() const;
+ /// Gets the address space width in bits.
+ u64 GetAddressSpaceWidth() const;
+
+ /// Gets the base address of the code region.
+ VAddr GetCodeRegionBaseAddress() const;
+
+ /// Gets the end address of the code region.
+ VAddr GetCodeRegionEndAddress() const;
+
+ /// Gets the total size of the code region in bytes.
+ u64 GetCodeRegionSize() const;
+
+ /// Gets the base address of the heap region.
+ VAddr GetHeapRegionBaseAddress() const;
+
+ /// Gets the end address of the heap region;
+ VAddr GetHeapRegionEndAddress() const;
+
+ /// Gets the total size of the heap region in bytes.
+ u64 GetHeapRegionSize() const;
+
+ /// Gets the base address of the map region.
+ VAddr GetMapRegionBaseAddress() const;
+
+ /// Gets the end address of the map region.
+ VAddr GetMapRegionEndAddress() const;
+
+ /// Gets the total size of the map region in bytes.
+ u64 GetMapRegionSize() const;
+
+ /// Gets the base address of the new map region.
+ VAddr GetNewMapRegionBaseAddress() const;
+
+ /// Gets the end address of the new map region.
+ VAddr GetNewMapRegionEndAddress() const;
+
+ /// Gets the total size of the new map region in bytes.
+ u64 GetNewMapRegionSize() const;
+
+ /// Gets the base address of the TLS IO region.
+ VAddr GetTLSIORegionBaseAddress() const;
+
+ /// Gets the end address of the TLS IO region.
+ VAddr GetTLSIORegionEndAddress() const;
+
+ /// Gets the total size of the TLS IO region in bytes.
+ u64 GetTLSIORegionSize() const;
+
/// Each VMManager has its own page table, which is set as the main one when the owning process
/// is scheduled.
Memory::PageTable page_table;
@@ -240,5 +289,36 @@ private:
/// Updates the pages corresponding to this VMA so they match the VMA's attributes.
void UpdatePageTableForVMA(const VirtualMemoryArea& vma);
+
+ /// Initializes memory region ranges to adhere to a given address space type.
+ void InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType type);
+
+ /// Clears the underlying map and page table.
+ void Clear();
+
+ /// Clears out the VMA map, unmapping any previously mapped ranges.
+ void ClearVMAMap();
+
+ /// Clears out the page table
+ void ClearPageTable();
+
+ u32 address_space_width = 0;
+ VAddr address_space_base = 0;
+ VAddr address_space_end = 0;
+
+ VAddr code_region_base = 0;
+ VAddr code_region_end = 0;
+
+ VAddr heap_region_base = 0;
+ VAddr heap_region_end = 0;
+
+ VAddr map_region_base = 0;
+ VAddr map_region_end = 0;
+
+ VAddr new_map_region_base = 0;
+ VAddr new_map_region_end = 0;
+
+ VAddr tls_io_region_base = 0;
+ VAddr tls_io_region_end = 0;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/wait_object.h b/src/core/hle/kernel/wait_object.h
index 0bd97133c..f4367ee28 100644
--- a/src/core/hle/kernel/wait_object.h
+++ b/src/core/hle/kernel/wait_object.h
@@ -69,7 +69,7 @@ private:
template <>
inline SharedPtr<WaitObject> DynamicObjectCast<WaitObject>(SharedPtr<Object> object) {
if (object != nullptr && object->IsWaitable()) {
- return boost::static_pointer_cast<WaitObject>(std::move(object));
+ return boost::static_pointer_cast<WaitObject>(object);
}
return nullptr;
}
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 06ac6372d..6073f4ecd 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -10,6 +10,7 @@
#include "common/alignment.h"
#include "common/common_funcs.h"
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/hle_ipc.h"
@@ -25,7 +26,7 @@ public:
{0, &IAudioRenderer::GetAudioRendererSampleRate, "GetAudioRendererSampleRate"},
{1, &IAudioRenderer::GetAudioRendererSampleCount, "GetAudioRendererSampleCount"},
{2, &IAudioRenderer::GetAudioRendererMixBufferCount, "GetAudioRendererMixBufferCount"},
- {3, nullptr, "GetAudioRendererState"},
+ {3, &IAudioRenderer::GetAudioRendererState, "GetAudioRendererState"},
{4, &IAudioRenderer::RequestUpdateAudioRenderer, "RequestUpdateAudioRenderer"},
{5, &IAudioRenderer::StartAudioRenderer, "StartAudioRenderer"},
{6, &IAudioRenderer::StopAudioRenderer, "StopAudioRenderer"},
@@ -62,6 +63,13 @@ private:
LOG_DEBUG(Service_Audio, "called");
}
+ void GetAudioRendererState(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(static_cast<u32>(renderer->GetStreamState()));
+ LOG_DEBUG(Service_Audio, "called");
+ }
+
void GetAudioRendererMixBufferCount(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index b436ce4e6..2212b2cdd 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -2,8 +2,17 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <array>
+#include <cstring>
+#include <ctime>
+#include <fmt/time.h>
+#include "common/file_util.h"
#include "common/logging/log.h"
+#include "common/scm_rev.h"
+#include "common/swap.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/process.h"
#include "core/hle/service/fatal/fatal.h"
#include "core/hle/service/fatal/fatal_p.h"
#include "core/hle/service/fatal/fatal_u.h"
@@ -15,16 +24,142 @@ Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
Module::Interface::~Interface() = default;
+struct FatalInfo {
+ std::array<u64_le, 31> registers{}; // TODO(ogniK): See if this actually is registers or
+ // not(find a game which has non zero valeus)
+ u64_le unk0{};
+ u64_le unk1{};
+ u64_le unk2{};
+ u64_le unk3{};
+ u64_le unk4{};
+ u64_le unk5{};
+ u64_le unk6{};
+
+ std::array<u64_le, 32> backtrace{};
+ u64_le unk7{};
+ u64_le unk8{};
+ u32_le backtrace_size{};
+ u32_le unk9{};
+ u32_le unk10{}; // TODO(ogniK): Is this even used or is it just padding?
+};
+static_assert(sizeof(FatalInfo) == 0x250, "FatalInfo is an invalid size");
+
+enum class FatalType : u32 {
+ ErrorReportAndScreen = 0,
+ ErrorReport = 1,
+ ErrorScreen = 2,
+};
+
+static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
+ const auto title_id = Core::CurrentProcess()->program_id;
+ std::string crash_report =
+ fmt::format("Yuzu {}-{} crash report\n"
+ "Title ID: {:016x}\n"
+ "Result: 0x{:X} ({:04}-{:04d})\n"
+ "\n",
+ Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw,
+ 2000 + static_cast<u32>(error_code.module.Value()),
+ static_cast<u32>(error_code.description.Value()), info.unk8, info.unk7);
+ if (info.backtrace_size != 0x0) {
+ crash_report += "Registers:\n";
+ // TODO(ogniK): This is just a guess, find a game which actually has non zero values
+ for (size_t i = 0; i < info.registers.size(); i++) {
+ crash_report +=
+ fmt::format(" X[{:02d}]: {:016x}\n", i, info.registers[i]);
+ }
+ crash_report += fmt::format(" Unknown 0: {:016x}\n", info.unk0);
+ crash_report += fmt::format(" Unknown 1: {:016x}\n", info.unk1);
+ crash_report += fmt::format(" Unknown 2: {:016x}\n", info.unk2);
+ crash_report += fmt::format(" Unknown 3: {:016x}\n", info.unk3);
+ crash_report += fmt::format(" Unknown 4: {:016x}\n", info.unk4);
+ crash_report += fmt::format(" Unknown 5: {:016x}\n", info.unk5);
+ crash_report += fmt::format(" Unknown 6: {:016x}\n", info.unk6);
+ crash_report += "\nBacktrace:\n";
+ for (size_t i = 0; i < info.backtrace_size; i++) {
+ crash_report +=
+ fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]);
+ }
+ crash_report += fmt::format("\nUnknown 7: 0x{:016x}\n", info.unk7);
+ crash_report += fmt::format("Unknown 8: 0x{:016x}\n", info.unk8);
+ crash_report += fmt::format("Unknown 9: 0x{:016x}\n", info.unk9);
+ crash_report += fmt::format("Unknown 10: 0x{:016x}\n", info.unk10);
+ }
+
+ LOG_ERROR(Service_Fatal, "{}", crash_report);
+
+ const std::string crashreport_dir =
+ FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + "crash_logs";
+
+ if (!FileUtil::CreateFullPath(crashreport_dir)) {
+ LOG_ERROR(
+ Service_Fatal,
+ "Unable to create crash report directory. Possible log directory permissions issue.");
+ return;
+ }
+
+ const std::time_t t = std::time(nullptr);
+ const std::string crashreport_filename =
+ fmt::format("{}/{:016x}-{:%F-%H%M%S}.log", crashreport_dir, title_id, *std::localtime(&t));
+
+ auto file = FileUtil::IOFile(crashreport_filename, "wb");
+ if (file.IsOpen()) {
+ file.WriteString(crash_report);
+ LOG_ERROR(Service_Fatal, "Saving error report to {}", crashreport_filename);
+ } else {
+ LOG_ERROR(Service_Fatal, "Failed to save error report to {}", crashreport_filename);
+ }
+}
+
+static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) {
+ LOG_ERROR(Service_Fatal, "Threw fatal error type {}", static_cast<u32>(fatal_type));
+ switch (fatal_type) {
+ case FatalType::ErrorReportAndScreen:
+ GenerateErrorReport(error_code, info);
+ [[fallthrough]];
+ case FatalType::ErrorScreen:
+ // Since we have no fatal:u error screen. We should just kill execution instead
+ ASSERT(false);
+ break;
+ // Should not throw a fatal screen but should generate an error report
+ case FatalType::ErrorReport:
+ GenerateErrorReport(error_code, info);
+ break;
+ };
+}
+
+void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) {
+ LOG_ERROR(Service_Fatal, "called");
+ IPC::RequestParser rp{ctx};
+ auto error_code = rp.Pop<ResultCode>();
+
+ ThrowFatalError(error_code, FatalType::ErrorScreen, {});
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
+ LOG_ERROR(Service_Fatal, "called");
IPC::RequestParser rp(ctx);
- u32 error_code = rp.Pop<u32>();
- LOG_WARNING(Service_Fatal, "(STUBBED) called, error_code=0x{:X}", error_code);
+ auto error_code = rp.Pop<ResultCode>();
+ auto fatal_type = rp.PopEnum<FatalType>();
+
+ ThrowFatalError(error_code, fatal_type, {}); // No info is passed with ThrowFatalWithPolicy
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Fatal, "(STUBBED) called");
+ LOG_ERROR(Service_Fatal, "called");
+ IPC::RequestParser rp(ctx);
+ auto error_code = rp.Pop<ResultCode>();
+ auto fatal_type = rp.PopEnum<FatalType>();
+ auto fatal_info = ctx.ReadBuffer();
+ FatalInfo info{};
+
+ ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!");
+ std::memcpy(&info, fatal_info.data(), sizeof(FatalInfo));
+
+ ThrowFatalError(error_code, fatal_type, info);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
diff --git a/src/core/hle/service/fatal/fatal.h b/src/core/hle/service/fatal/fatal.h
index 4d9a5be52..09371ff7f 100644
--- a/src/core/hle/service/fatal/fatal.h
+++ b/src/core/hle/service/fatal/fatal.h
@@ -15,6 +15,7 @@ public:
explicit Interface(std::shared_ptr<Module> module, const char* name);
~Interface() override;
+ void ThrowFatal(Kernel::HLERequestContext& ctx);
void ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx);
void ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/fatal/fatal_u.cpp b/src/core/hle/service/fatal/fatal_u.cpp
index befc307cf..1572a2051 100644
--- a/src/core/hle/service/fatal/fatal_u.cpp
+++ b/src/core/hle/service/fatal/fatal_u.cpp
@@ -8,7 +8,7 @@ namespace Service::Fatal {
Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "fatal:u") {
static const FunctionInfo functions[] = {
- {0, nullptr, "ThrowFatal"},
+ {0, &Fatal_U::ThrowFatal, "ThrowFatal"},
{1, &Fatal_U::ThrowFatalWithPolicy, "ThrowFatalWithPolicy"},
{2, &Fatal_U::ThrowFatalWithCpuContext, "ThrowFatalWithCpuContext"},
};
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index d349ee686..aed2abb71 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -343,6 +343,15 @@ std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents() {
return sdmc_factory->GetSDMCContents();
}
+FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
+ LOG_TRACE(Service_FS, "Opening mod load root for tid={:016X}", title_id);
+
+ if (bis_factory == nullptr)
+ return nullptr;
+
+ return bis_factory->GetModificationLoadRoot(title_id);
+}
+
void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) {
if (overwrite) {
bis_factory = nullptr;
@@ -354,9 +363,11 @@ void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) {
FileSys::Mode::ReadWrite);
auto sd_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir),
FileSys::Mode::ReadWrite);
+ auto load_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
+ FileSys::Mode::ReadWrite);
if (bis_factory == nullptr)
- bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory);
+ bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory);
if (save_data_factory == nullptr)
save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
if (sdmc_factory == nullptr)
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index aab65a2b8..7039a2247 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -52,6 +52,8 @@ std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents();
std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents();
std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents();
+FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
+
// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
// above is called.
void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite = true);
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index e587ad0d8..872e3c344 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -2,6 +2,11 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/swap.h"
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/hid/irs.h"
namespace Service::HID {
@@ -9,28 +14,145 @@ namespace Service::HID {
IRS::IRS() : ServiceFramework{"irs"} {
// clang-format off
static const FunctionInfo functions[] = {
- {302, nullptr, "ActivateIrsensor"},
- {303, nullptr, "DeactivateIrsensor"},
- {304, nullptr, "GetIrsensorSharedMemoryHandle"},
- {305, nullptr, "StopImageProcessor"},
- {306, nullptr, "RunMomentProcessor"},
- {307, nullptr, "RunClusteringProcessor"},
- {308, nullptr, "RunImageTransferProcessor"},
- {309, nullptr, "GetImageTransferProcessorState"},
- {310, nullptr, "RunTeraPluginProcessor"},
- {311, nullptr, "GetNpadIrCameraHandle"},
- {312, nullptr, "RunPointingProcessor"},
- {313, nullptr, "SuspendImageProcessor"},
- {314, nullptr, "CheckFirmwareVersion"},
- {315, nullptr, "SetFunctionLevel"},
- {316, nullptr, "RunImageTransferExProcessor"},
- {317, nullptr, "RunIrLedProcessor"},
- {318, nullptr, "StopImageProcessorAsync"},
- {319, nullptr, "ActivateIrsensorWithFunctionLevel"},
+ {302, &IRS::ActivateIrsensor, "ActivateIrsensor"},
+ {303, &IRS::DeactivateIrsensor, "DeactivateIrsensor"},
+ {304, &IRS::GetIrsensorSharedMemoryHandle, "GetIrsensorSharedMemoryHandle"},
+ {305, &IRS::StopImageProcessor, "StopImageProcessor"},
+ {306, &IRS::RunMomentProcessor, "RunMomentProcessor"},
+ {307, &IRS::RunClusteringProcessor, "RunClusteringProcessor"},
+ {308, &IRS::RunImageTransferProcessor, "RunImageTransferProcessor"},
+ {309, &IRS::GetImageTransferProcessorState, "GetImageTransferProcessorState"},
+ {310, &IRS::RunTeraPluginProcessor, "RunTeraPluginProcessor"},
+ {311, &IRS::GetNpadIrCameraHandle, "GetNpadIrCameraHandle"},
+ {312, &IRS::RunPointingProcessor, "RunPointingProcessor"},
+ {313, &IRS::SuspendImageProcessor, "SuspendImageProcessor"},
+ {314, &IRS::CheckFirmwareVersion, "CheckFirmwareVersion"},
+ {315, &IRS::SetFunctionLevel, "SetFunctionLevel"},
+ {316, &IRS::RunImageTransferExProcessor, "RunImageTransferExProcessor"},
+ {317, &IRS::RunIrLedProcessor, "RunIrLedProcessor"},
+ {318, &IRS::StopImageProcessorAsync, "StopImageProcessorAsync"},
+ {319, &IRS::ActivateIrsensorWithFunctionLevel, "ActivateIrsensorWithFunctionLevel"},
};
// clang-format on
RegisterHandlers(functions);
+
+ auto& kernel = Core::System::GetInstance().Kernel();
+ shared_mem = Kernel::SharedMemory::Create(
+ kernel, nullptr, 0x8000, Kernel::MemoryPermission::ReadWrite,
+ Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "IRS:SharedMemory");
+}
+
+void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+}
+
+void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+}
+
+void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(shared_mem);
+ LOG_DEBUG(Service_IRS, "called");
+}
+
+void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+}
+
+void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+}
+
+void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+}
+
+void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+}
+
+void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 5};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<u64>(CoreTiming::GetTicks());
+ rb.PushRaw<u32>(0);
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+}
+
+void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+}
+
+void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<u32>(device_handle);
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+}
+
+void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+}
+
+void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+}
+
+void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+}
+
+void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+}
+
+void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+}
+
+void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+}
+
+void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+}
+
+void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
}
IRS::~IRS() = default;
diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h
index 6fb16b45d..12de6bfb3 100644
--- a/src/core/hle/service/hid/irs.h
+++ b/src/core/hle/service/hid/irs.h
@@ -4,14 +4,41 @@
#pragma once
+#include "core/hle/kernel/object.h"
#include "core/hle/service/service.h"
+namespace Kernel {
+class SharedMemory;
+}
+
namespace Service::HID {
class IRS final : public ServiceFramework<IRS> {
public:
explicit IRS();
~IRS() override;
+
+private:
+ void ActivateIrsensor(Kernel::HLERequestContext& ctx);
+ void DeactivateIrsensor(Kernel::HLERequestContext& ctx);
+ void GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx);
+ void StopImageProcessor(Kernel::HLERequestContext& ctx);
+ void RunMomentProcessor(Kernel::HLERequestContext& ctx);
+ void RunClusteringProcessor(Kernel::HLERequestContext& ctx);
+ void RunImageTransferProcessor(Kernel::HLERequestContext& ctx);
+ void GetImageTransferProcessorState(Kernel::HLERequestContext& ctx);
+ void RunTeraPluginProcessor(Kernel::HLERequestContext& ctx);
+ void GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx);
+ void RunPointingProcessor(Kernel::HLERequestContext& ctx);
+ void SuspendImageProcessor(Kernel::HLERequestContext& ctx);
+ void CheckFirmwareVersion(Kernel::HLERequestContext& ctx);
+ void SetFunctionLevel(Kernel::HLERequestContext& ctx);
+ void RunImageTransferExProcessor(Kernel::HLERequestContext& ctx);
+ void RunIrLedProcessor(Kernel::HLERequestContext& ctx);
+ void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
+ void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
+ Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
+ const u32 device_handle{0xABCD};
};
class IRS_SYS final : public ServiceFramework<IRS_SYS> {
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index f8d2127d9..8c07a05c2 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/hid/hid.h"
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index c1737defa..261ad539c 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -4,6 +4,7 @@
#include <chrono>
#include <ctime>
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/nim/nim.h"
diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp
index cdf328a26..98f6e4111 100644
--- a/src/core/hle/service/sm/controller.cpp
+++ b/src/core/hle/service/sm/controller.cpp
@@ -2,8 +2,11 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/client_session.h"
+#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h"
#include "core/hle/service/sm/controller.h"
diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp
index fe0a318ee..bc4f7a437 100644
--- a/src/core/hle/service/ssl/ssl.cpp
+++ b/src/core/hle/service/ssl/ssl.cpp
@@ -103,6 +103,7 @@ public:
}
private:
+ u32 ssl_version{};
void CreateContext(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_SSL, "(STUBBED) called");
@@ -112,10 +113,9 @@ private:
}
void SetInterfaceVersion(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_SSL, "(STUBBED) called");
+ LOG_DEBUG(Service_SSL, "called");
IPC::RequestParser rp{ctx};
- u32 unk1 = rp.Pop<u32>(); // Probably minor/major?
- u32 unk2 = rp.Pop<u32>(); // TODO(ogniK): Figure out what this does
+ ssl_version = rp.Pop<u32>();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 2b8f78136..7e8035d0f 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -14,11 +14,9 @@
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
-#include "core/hle/kernel/resource_limit.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/deconstructed_rom_directory.h"
#include "core/loader/nso.h"
-#include "core/memory.h"
namespace Loader {
@@ -127,12 +125,16 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
metadata.Print();
const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
- if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit) {
+ if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit ||
+ arch_bits == FileSys::ProgramAddressSpaceType::Is32BitNoMap) {
return ResultStatus::Error32BitISA;
}
+ process->LoadFromMetadata(metadata);
+
// Load NSO modules
- VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR};
+ const VAddr base_address = process->vm_manager.GetCodeRegionBaseAddress();
+ VAddr next_load_addr = base_address;
for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
"subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
const FileSys::VirtualFile module_file = dir->GetFile(module);
@@ -145,13 +147,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
}
}
- auto& kernel = Core::System::GetInstance().Kernel();
- process->program_id = metadata.GetTitleID();
- process->svc_access_mask.set();
- process->resource_limit =
- kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION);
- process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(),
- metadata.GetMainThreadStackSize());
+ process->Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize());
// Find the RomFS by searching for a ".romfs" file in this directory
const auto& files = dir->GetFiles();
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 0e2af20b4..ff1221574 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -12,7 +12,7 @@
#include "core/core.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
-#include "core/hle/kernel/resource_limit.h"
+#include "core/hle/kernel/vm_manager.h"
#include "core/loader/elf.h"
#include "core/memory.h"
@@ -189,7 +189,7 @@ private:
u32* sectionAddrs;
bool relocate;
- u32 entryPoint;
+ VAddr entryPoint;
public:
explicit ElfReader(void* ptr);
@@ -205,13 +205,13 @@ public:
ElfMachine GetMachine() const {
return (ElfMachine)(header->e_machine);
}
- u32 GetEntryPoint() const {
+ VAddr GetEntryPoint() const {
return entryPoint;
}
u32 GetFlags() const {
return (u32)(header->e_flags);
}
- SharedPtr<CodeSet> LoadInto(u32 vaddr);
+ SharedPtr<CodeSet> LoadInto(VAddr vaddr);
int GetNumSegments() const {
return (int)(header->e_phnum);
@@ -274,7 +274,7 @@ const char* ElfReader::GetSectionName(int section) const {
return nullptr;
}
-SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) {
+SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx);
// Should we relocate?
@@ -289,11 +289,11 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) {
LOG_DEBUG(Loader, "{} segments:", header->e_phnum);
// First pass : Get the bits into RAM
- u32 base_addr = relocate ? vaddr : 0;
+ const VAddr base_addr = relocate ? vaddr : 0;
- u32 total_image_size = 0;
+ u64 total_image_size = 0;
for (unsigned int i = 0; i < header->e_phnum; ++i) {
- Elf32_Phdr* p = &segments[i];
+ const Elf32_Phdr* p = &segments[i];
if (p->p_type == PT_LOAD) {
total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF;
}
@@ -306,7 +306,7 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) {
SharedPtr<CodeSet> codeset = CodeSet::Create(kernel, "");
for (unsigned int i = 0; i < header->e_phnum; ++i) {
- Elf32_Phdr* p = &segments[i];
+ const Elf32_Phdr* p = &segments[i];
LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type,
p->p_vaddr, p->p_filesz, p->p_memsz);
@@ -333,8 +333,8 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) {
continue;
}
- u32 segment_addr = base_addr + p->p_vaddr;
- u32 aligned_size = (p->p_memsz + 0xFFF) & ~0xFFF;
+ const VAddr segment_addr = base_addr + p->p_vaddr;
+ const u32 aligned_size = (p->p_memsz + 0xFFF) & ~0xFFF;
codeset_segment->offset = current_image_position;
codeset_segment->addr = segment_addr;
@@ -395,18 +395,12 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (buffer.size() != file->GetSize())
return ResultStatus::ErrorIncorrectELFFileSize;
+ const VAddr base_address = process->vm_manager.GetCodeRegionBaseAddress();
ElfReader elf_reader(&buffer[0]);
- SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR);
+ SharedPtr<CodeSet> codeset = elf_reader.LoadInto(base_address);
codeset->name = file->GetName();
process->LoadModule(codeset, codeset->entrypoint);
- process->svc_access_mask.set();
-
- // Attach the default resource limit (APPLICATION) to the process
- auto& kernel = Core::System::GetInstance().Kernel();
- process->resource_limit =
- kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION);
-
process->Run(codeset->entrypoint, 48, Memory::DEFAULT_STACK_SIZE);
is_loaded = true;
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index c49ec34ab..b72871efa 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -16,7 +16,7 @@
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
-#include "core/hle/kernel/resource_limit.h"
+#include "core/hle/kernel/vm_manager.h"
#include "core/loader/nro.h"
#include "core/memory.h"
@@ -181,17 +181,13 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
}
// Load NRO
- static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR};
+ const VAddr base_address = process->vm_manager.GetCodeRegionBaseAddress();
- if (!LoadNro(file, base_addr)) {
+ if (!LoadNro(file, base_address)) {
return ResultStatus::ErrorLoadingNRO;
}
- auto& kernel = Core::System::GetInstance().Kernel();
- process->svc_access_mask.set();
- process->resource_limit =
- kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION);
- process->Run(base_addr, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
+ process->Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
is_loaded = true;
return ResultStatus::Success;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 78a4438c4..1a6876a22 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -13,7 +13,7 @@
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
-#include "core/hle/kernel/resource_limit.h"
+#include "core/hle/kernel/vm_manager.h"
#include "core/loader/nso.h"
#include "core/memory.h"
@@ -159,15 +159,11 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
}
// Load module
- LoadModule(file, Memory::PROCESS_IMAGE_VADDR);
- LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), Memory::PROCESS_IMAGE_VADDR);
+ const VAddr base_address = process->vm_manager.GetCodeRegionBaseAddress();
+ LoadModule(file, base_address);
+ LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
- auto& kernel = Core::System::GetInstance().Kernel();
- process->svc_access_mask.set();
- process->resource_limit =
- kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION);
- process->Run(Memory::PROCESS_IMAGE_VADDR, Kernel::THREADPRIO_DEFAULT,
- Memory::DEFAULT_STACK_SIZE);
+ process->Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
is_loaded = true;
return ResultStatus::Success;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 316b46820..6430daad4 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -3,7 +3,6 @@
// Refer to the license.txt file included.
#include <algorithm>
-#include <array>
#include <cstring>
#include <utility>
@@ -15,11 +14,11 @@
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/vm_manager.h"
#include "core/hle/lock.h"
#include "core/memory.h"
#include "core/memory_setup.h"
#include "video_core/renderer_base.h"
-#include "video_core/video_core.h"
namespace Memory {
@@ -41,6 +40,21 @@ PageTable* GetCurrentPageTable() {
return current_page_table;
}
+PageTable::PageTable() = default;
+
+PageTable::PageTable(std::size_t address_space_width_in_bits) {
+ Resize(address_space_width_in_bits);
+}
+
+PageTable::~PageTable() = default;
+
+void PageTable::Resize(std::size_t address_space_width_in_bits) {
+ const std::size_t num_page_table_entries = 1ULL << (address_space_width_in_bits - PAGE_BITS);
+
+ pointers.resize(num_page_table_entries);
+ attributes.resize(num_page_table_entries);
+}
+
static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, PageType type) {
LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE,
(base + size) * PAGE_SIZE);
@@ -50,7 +64,7 @@ static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, Pa
VAddr end = base + size;
while (base != end) {
- ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at {:016X}", base);
+ ASSERT_MSG(base < page_table.pointers.size(), "out of range mapping at {:016X}", base);
page_table.attributes[base] = type;
page_table.pointers[base] = memory;
@@ -323,7 +337,7 @@ void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
return;
}
- VAddr end = start + size;
+ const VAddr end = start + size;
const auto CheckRegion = [&](VAddr region_start, VAddr region_end) {
if (start >= region_end || end <= region_start) {
@@ -333,7 +347,7 @@ void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
const VAddr overlap_start = std::max(start, region_start);
const VAddr overlap_end = std::min(end, region_end);
- const u64 overlap_size = overlap_end - overlap_start;
+ const VAddr overlap_size = overlap_end - overlap_start;
auto& rasterizer = system_instance.Renderer().Rasterizer();
switch (mode) {
@@ -349,8 +363,10 @@ void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
}
};
- CheckRegion(PROCESS_IMAGE_VADDR, PROCESS_IMAGE_VADDR_END);
- CheckRegion(HEAP_VADDR, HEAP_VADDR_END);
+ const auto& vm_manager = Core::CurrentProcess()->vm_manager;
+
+ CheckRegion(vm_manager.GetCodeRegionBaseAddress(), vm_manager.GetCodeRegionEndAddress());
+ CheckRegion(vm_manager.GetHeapRegionBaseAddress(), vm_manager.GetHeapRegionEndAddress());
}
u8 Read8(const VAddr addr) {
diff --git a/src/core/memory.h b/src/core/memory.h
index 2a27c0251..1acf5ce8c 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -4,10 +4,10 @@
#pragma once
-#include <array>
#include <cstddef>
#include <string>
#include <tuple>
+#include <vector>
#include <boost/icl/interval_map.hpp>
#include "common/common_types.h"
#include "core/memory_hook.h"
@@ -23,10 +23,8 @@ namespace Memory {
* be mapped.
*/
constexpr std::size_t PAGE_BITS = 12;
-constexpr u64 PAGE_SIZE = 1 << PAGE_BITS;
+constexpr u64 PAGE_SIZE = 1ULL << PAGE_BITS;
constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
-constexpr std::size_t ADDRESS_SPACE_BITS = 36;
-constexpr std::size_t PAGE_TABLE_NUM_ENTRIES = 1ULL << (ADDRESS_SPACE_BITS - PAGE_BITS);
enum class PageType : u8 {
/// Page is unmapped and should cause an access error.
@@ -62,32 +60,39 @@ struct SpecialRegion {
* mimics the way a real CPU page table works.
*/
struct PageTable {
+ explicit PageTable();
+ explicit PageTable(std::size_t address_space_width_in_bits);
+ ~PageTable();
+
+ /**
+ * Resizes the page table to be able to accomodate enough pages within
+ * a given address space.
+ *
+ * @param address_space_width_in_bits The address size width in bits.
+ */
+ void Resize(std::size_t address_space_width_in_bits);
+
/**
- * Array of memory pointers backing each page. An entry can only be non-null if the
- * corresponding entry in the `attributes` array is of type `Memory`.
+ * Vector of memory pointers backing each page. An entry can only be non-null if the
+ * corresponding entry in the `attributes` vector is of type `Memory`.
*/
- std::array<u8*, PAGE_TABLE_NUM_ENTRIES> pointers;
+ std::vector<u8*> pointers;
/**
- * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of
- * type `Special`.
+ * Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is
+ * of type `Special`.
*/
boost::icl::interval_map<VAddr, std::set<SpecialRegion>> special_regions;
/**
- * Array of fine grained page attributes. If it is set to any value other than `Memory`, then
+ * Vector of fine grained page attributes. If it is set to any value other than `Memory`, then
* the corresponding entry in `pointers` MUST be set to null.
*/
- std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes;
+ std::vector<PageType> attributes;
};
/// Virtual user-space memory regions
enum : VAddr {
- /// Where the application text, data and bss reside.
- PROCESS_IMAGE_VADDR = 0x08000000,
- PROCESS_IMAGE_MAX_SIZE = 0x08000000,
- PROCESS_IMAGE_VADDR_END = PROCESS_IMAGE_VADDR + PROCESS_IMAGE_MAX_SIZE,
-
/// Read-only page containing kernel and system configuration values.
CONFIG_MEMORY_VADDR = 0x1FF80000,
CONFIG_MEMORY_SIZE = 0x00001000,
@@ -98,36 +103,12 @@ enum : VAddr {
SHARED_PAGE_SIZE = 0x00001000,
SHARED_PAGE_VADDR_END = SHARED_PAGE_VADDR + SHARED_PAGE_SIZE,
- /// Area where TLS (Thread-Local Storage) buffers are allocated.
- TLS_AREA_VADDR = 0x40000000,
+ /// TLS (Thread-Local Storage) related.
TLS_ENTRY_SIZE = 0x200,
- TLS_AREA_SIZE = 0x10000000,
- TLS_AREA_VADDR_END = TLS_AREA_VADDR + TLS_AREA_SIZE,
/// Application stack
- STACK_AREA_VADDR = TLS_AREA_VADDR_END,
- STACK_AREA_SIZE = 0x10000000,
- STACK_AREA_VADDR_END = STACK_AREA_VADDR + STACK_AREA_SIZE,
DEFAULT_STACK_SIZE = 0x100000,
- /// Application heap
- /// Size is confirmed to be a static value on fw 3.0.0
- HEAP_VADDR = 0x108000000,
- HEAP_SIZE = 0x180000000,
- HEAP_VADDR_END = HEAP_VADDR + HEAP_SIZE,
-
- /// New map region
- /// Size is confirmed to be a static value on fw 3.0.0
- NEW_MAP_REGION_VADDR = HEAP_VADDR_END,
- NEW_MAP_REGION_SIZE = 0x80000000,
- NEW_MAP_REGION_VADDR_END = NEW_MAP_REGION_VADDR + NEW_MAP_REGION_SIZE,
-
- /// Map region
- /// Size is confirmed to be a static value on fw 3.0.0
- MAP_REGION_VADDR = NEW_MAP_REGION_VADDR_END,
- MAP_REGION_SIZE = 0x1000000000,
- MAP_REGION_VADDR_END = MAP_REGION_VADDR + MAP_REGION_SIZE,
-
/// Kernel Virtual Address Range
KERNEL_REGION_VADDR = 0xFFFFFF8000000000,
KERNEL_REGION_SIZE = 0x7FFFE00000,