diff options
Diffstat (limited to 'src/core')
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,  | 
