diff options
84 files changed, 1108 insertions, 579 deletions
diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..ab861a396 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +dist/languages/* linguist-vendored +dist/qt_themes/* linguist-vendored +externals/* linguist-vendored +*.h linguist-language=cpp diff --git a/.travis.yml b/.travis.yml index 4d363cbc9..b0fbe3c5f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,7 @@ matrix: - os: osx env: NAME="macos build" sudo: false - osx_image: xcode9.3 + osx_image: xcode10 install: "./.travis/macos/deps.sh" script: "./.travis/macos/build.sh" after_success: "./.travis/macos/upload.sh" diff --git a/.travis/macos/build.sh b/.travis/macos/build.sh index b881fa190..e68dc1400 100755 --- a/.travis/macos/build.sh +++ b/.travis/macos/build.sh @@ -2,7 +2,7 @@ set -o pipefail -export MACOSX_DEPLOYMENT_TARGET=10.12 +export MACOSX_DEPLOYMENT_TARGET=10.13 export Qt5_DIR=$(brew --prefix)/opt/qt5 export UNICORNDIR=$(pwd)/externals/unicorn export PATH="/usr/local/opt/ccache/libexec:$PATH" diff --git a/CMakeLists.txt b/CMakeLists.txt index 25c5cb112..cd990188e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,8 +123,6 @@ else() # Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors. add_definitions(/DWIN32_LEAN_AND_MEAN) - # set up output paths for executable binaries (.exe-files, and .dll-files on DLL-capable platforms) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE) # Tweak optimization settings diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 521b19ff7..6f0ff953a 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp @@ -79,7 +79,7 @@ u32 AudioRenderer::GetMixBufferCount() const { return worker_params.mix_buffer_count; } -u32 AudioRenderer::GetState() const { +Stream::State AudioRenderer::GetStreamState() const { return stream->GetState(); } diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h index be923ee65..dfef89e1d 100644 --- a/src/audio_core/audio_renderer.h +++ b/src/audio_core/audio_renderer.h @@ -170,7 +170,7 @@ public: u32 GetSampleRate() const; u32 GetSampleCount() const; u32 GetMixBufferCount() const; - u32 GetState() const; + Stream::State GetStreamState() const; private: class VoiceState; diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index ee4aa98af..742a5e0a0 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp @@ -53,8 +53,8 @@ void Stream::Stop() { ASSERT_MSG(false, "Unimplemented"); } -u32 Stream::GetState() const { - return static_cast<u32>(state); +Stream::State Stream::GetState() const { + return state; } s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const { diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h index 43eca74e1..aebfeb51d 100644 --- a/src/audio_core/stream.h +++ b/src/audio_core/stream.h @@ -33,6 +33,12 @@ public: Multi51Channel16, }; + /// Current state of the stream + enum class State { + Stopped, + Playing, + }; + /// Callback function type, used to change guest state on a buffer being released using ReleaseCallback = std::function<void()>; @@ -73,15 +79,9 @@ public: u32 GetNumChannels() const; /// Get the state - u32 GetState() const; + State GetState() const; private: - /// Current state of the stream - enum class State { - Stopped, - Playing, - }; - /// Plays the next queued buffer in the audio stream, starting playback if necessary void PlayNextBuffer(); diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 16d528994..59da33f30 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -22,10 +22,16 @@ public: std::array<u64, 31> cpu_registers; u64 sp; u64 pc; - u64 pstate; + u32 pstate; + std::array<u8, 4> padding; std::array<u128, 32> vector_registers; - u64 fpcr; + u32 fpcr; + u32 fpsr; + u64 tpidr; }; + // Internally within the kernel, it expects the AArch64 version of the + // thread context to be 800 bytes in size. + static_assert(sizeof(ThreadContext) == 0x320); /// Runs the CPU until an event happens virtual void Run() = 0; diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 7be5a38de..05cc84458 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->VMManager().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->VMManager().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); @@ -246,15 +247,19 @@ void ARM_Dynarmic::SaveContext(ThreadContext& ctx) { ctx.pstate = jit->GetPstate(); ctx.vector_registers = jit->GetVectors(); ctx.fpcr = jit->GetFpcr(); + ctx.fpsr = jit->GetFpsr(); + ctx.tpidr = cb->tpidr_el0; } void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) { jit->SetRegisters(ctx.cpu_registers); jit->SetSP(ctx.sp); jit->SetPC(ctx.pc); - jit->SetPstate(static_cast<u32>(ctx.pstate)); + jit->SetPstate(ctx.pstate); jit->SetVectors(ctx.vector_registers); - jit->SetFpcr(static_cast<u32>(ctx.fpcr)); + jit->SetFpcr(ctx.fpcr); + jit->SetFpsr(ctx.fpsr); + SetTPIDR_EL0(ctx.tpidr); } void ARM_Dynarmic::PrepareReschedule() { diff --git a/src/core/core.cpp b/src/core/core.cpp index 50f0a42fb..b6acfb3e4 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); @@ -202,7 +202,7 @@ struct System::Impl { return init_result; } - const Loader::ResultStatus load_result{app_loader->Load(kernel.CurrentProcess())}; + const Loader::ResultStatus load_result{app_loader->Load(*kernel.CurrentProcess())}; if (load_result != Loader::ResultStatus::Success) { LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); Shutdown(); 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/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp index 21fc3d796..2a913ce82 100644 --- a/src/core/file_sys/fsmitm_romfsbuild.cpp +++ b/src/core/file_sys/fsmitm_romfsbuild.cpp @@ -23,6 +23,7 @@ */ #include <cstring> +#include "common/alignment.h" #include "common/assert.h" #include "core/file_sys/fsmitm_romfsbuild.h" #include "core/file_sys/vfs.h" @@ -73,7 +74,7 @@ static_assert(sizeof(RomFSFileEntry) == 0x20, "RomFSFileEntry has incorrect size struct RomFSBuildFileContext; struct RomFSBuildDirectoryContext { - std::string path = ""; + std::string path; u32 cur_path_ofs = 0; u32 path_len = 0; u32 entry_offset = 0; @@ -84,7 +85,7 @@ struct RomFSBuildDirectoryContext { }; struct RomFSBuildFileContext { - std::string path = ""; + std::string path; u32 cur_path_ofs = 0; u32 path_len = 0; u32 entry_offset = 0; @@ -92,12 +93,10 @@ struct RomFSBuildFileContext { u64 size = 0; std::shared_ptr<RomFSBuildDirectoryContext> parent; std::shared_ptr<RomFSBuildFileContext> sibling; - VirtualFile source = nullptr; - - RomFSBuildFileContext() : path(""), cur_path_ofs(0), path_len(0) {} + VirtualFile source; }; -static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, size_t path_len) { +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); @@ -107,13 +106,16 @@ static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, size_t return hash; } -static u32 romfs_get_hash_table_count(u32 num_entries) { +static u64 romfs_get_hash_table_count(u64 num_entries) { if (num_entries < 3) { return 3; - } else if (num_entries < 19) { + } + + if (num_entries < 19) { return num_entries | 1; } - u32 count = num_entries; + + 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++; @@ -139,7 +141,7 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, 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 + kv.first.size(); + child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); child->path = parent->path + "/" + kv.first; // Sanity check on path_len @@ -152,7 +154,7 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, 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 + kv.first.size(); + child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); child->path = parent->path + "/" + kv.first; // Sanity check on path_len @@ -181,7 +183,7 @@ bool RomFSBuildContext::AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> // Add a new directory. num_dirs++; dir_table_size += - sizeof(RomFSDirectoryEntry) + ((dir_ctx->path_len - dir_ctx->cur_path_ofs + 3) & ~3); + 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); @@ -199,7 +201,7 @@ bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> pare // Add a new file. num_files++; file_table_size += - sizeof(RomFSFileEntry) + ((file_ctx->path_len - file_ctx->cur_path_ofs + 3) & ~3); + 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); @@ -219,8 +221,8 @@ RomFSBuildContext::RomFSBuildContext(VirtualDir base_) : base(std::move(base_)) RomFSBuildContext::~RomFSBuildContext() = default; std::map<u64, VirtualFile> RomFSBuildContext::Build() { - const auto dir_hash_table_entry_count = romfs_get_hash_table_count(num_dirs); - const auto file_hash_table_entry_count = romfs_get_hash_table_count(num_files); + 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; @@ -233,12 +235,6 @@ std::map<u64, VirtualFile> RomFSBuildContext::Build() { std::vector<u8> dir_table(dir_table_size); std::vector<u8> file_table(file_table_size); - // Clear out hash tables. - for (u32 i = 0; i < dir_hash_table_entry_count; i++) - dir_hash_table[i] = ROMFS_ENTRY_EMPTY; - for (u32 i = 0; i < file_hash_table_entry_count; i++) - file_hash_table[i] = ROMFS_ENTRY_EMPTY; - std::shared_ptr<RomFSBuildFileContext> cur_file; // Determine file offsets. @@ -246,12 +242,12 @@ std::map<u64, VirtualFile> RomFSBuildContext::Build() { std::shared_ptr<RomFSBuildFileContext> prev_file = nullptr; for (const auto& it : files) { cur_file = it.second; - file_partition_size = (file_partition_size + 0xFULL) & ~0xFULL; + 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) + ((cur_file->path_len - cur_file->cur_path_ofs + 3) & ~3); + 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. @@ -268,8 +264,8 @@ std::map<u64, VirtualFile> RomFSBuildContext::Build() { for (const auto& it : directories) { cur_dir = it.second; cur_dir->entry_offset = entry_offset; - entry_offset += - sizeof(RomFSDirectoryEntry) + ((cur_dir->path_len - cur_dir->cur_path_ofs + 3) & ~3); + 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) { @@ -302,7 +298,7 @@ std::map<u64, VirtualFile> RomFSBuildContext::Build() { 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, - (cur_entry.name_size + 3) & ~3); + 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); } @@ -329,10 +325,8 @@ std::map<u64, VirtualFile> RomFSBuildContext::Build() { std::memcpy(dir_table.data() + cur_dir->entry_offset, &cur_entry, sizeof(RomFSDirectoryEntry)); - 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, - (cur_entry.name_size + 3) & ~3); + 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); } @@ -344,18 +338,18 @@ std::map<u64, VirtualFile> RomFSBuildContext::Build() { 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 = (header.file_partition_ofs + file_partition_size + 3ULL) & ~3ULL; + 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>(header_data)); + 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); - auto index = 0; + 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()); @@ -364,7 +358,7 @@ std::map<u64, VirtualFile> RomFSBuildContext::Build() { 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>(metadata)); + out.emplace(header.dir_hash_table_ofs, std::make_shared<VectorVfsFile>(std::move(metadata))); return out; } diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index af3f9a78f..4b3b5e665 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -70,38 +70,40 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { 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) { - auto extracted = ExtractRomFS(romfs); - - if (extracted != nullptr) { - 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)); - } + if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) { + return; + } - layers.push_back(std::move(extracted)); + auto extracted = ExtractRomFS(romfs); + if (extracted == nullptr) { + return; + } - const auto layered = LayerDirectories(layers); + 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(); }); - if (layered != nullptr) { - auto packed = CreateRomFS(layered); + 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)); - if (packed != nullptr) { - LOG_INFO(Loader, " RomFS: LayeredFS patches applied successfully"); - romfs = std::move(packed); - } - } - } + 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, 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 653ef2e7b..e9b040689 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -125,7 +125,7 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, if (concat.empty()) return nullptr; - file = FileSys::ConcatenateFiles(concat, concat.front()->GetName()); + file = ConcatenatedVfsFile::MakeConcatenatedFile(concat, concat.front()->GetName()); } return file; diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp index 205284a4d..5910f7046 100644 --- a/src/core/file_sys/romfs.cpp +++ b/src/core/file_sys/romfs.cpp @@ -134,7 +134,7 @@ VirtualFile CreateRomFS(VirtualDir dir) { return nullptr; RomFSBuildContext ctx{dir}; - return ConcatenateFiles<0>(ctx.Build(), dir->GetName()); + return ConcatenatedVfsFile::MakeConcatenatedFile(0, ctx.Build(), dir->GetName()); } } // namespace FileSys diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp index 3d1a3685e..d027a8d59 100644 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp @@ -34,7 +34,7 @@ ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() { if (!updatable) return MakeResult<VirtualFile>(file); - const PatchManager patch_manager(Core::CurrentProcess()->program_id); + const PatchManager patch_manager(Core::CurrentProcess()->GetTitleID()); return MakeResult<VirtualFile>(patch_manager.PatchRomFS(file, ivfc_offset)); } diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index 9b2c51bbd..47f2ab9e0 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -81,7 +81,7 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should // be interpreted as the title id of the current process. if (type == SaveDataType::SaveData && title_id == 0) - title_id = Core::CurrentProcess()->program_id; + title_id = Core::CurrentProcess()->GetTitleID(); std::string out; diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index 5fbea1739..bfe50da73 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp @@ -463,14 +463,14 @@ bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t return true; } -bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, size_t block_size) { +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> temp(std::min(block_size, src->GetSize())); - for (size_t i = 0; i < src->GetSize(); i += block_size) { + 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); @@ -481,7 +481,7 @@ bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, size_t block_si return true; } -bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, size_t block_size) { +bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size) { if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) return false; diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h index cea4aa8b8..270291631 100644 --- a/src/core/file_sys/vfs.h +++ b/src/core/file_sys/vfs.h @@ -315,18 +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, size_t block_size = 0x1000); +// 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(const VirtualFile& src, const VirtualFile& dest, size_t block_size = 0x1000); +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, size_t block_size = 0x1000); +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 d9f9911da..16d801c0c 100644 --- a/src/core/file_sys/vfs_concat.cpp +++ b/src/core/file_sys/vfs_concat.cpp @@ -7,6 +7,7 @@ #include "common/assert.h" #include "core/file_sys/vfs_concat.h" +#include "core/file_sys/vfs_static.h" namespace FileSys { @@ -22,15 +23,6 @@ static bool VerifyConcatenationMapContinuity(const std::map<u64, VirtualFile>& m return map.begin()->first == 0; } -VirtualFile ConcatenateFiles(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))); -} - ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::string name) : name(std::move(name)) { std::size_t next_offset = 0; @@ -47,6 +39,41 @@ ConcatenatedVfsFile::ConcatenatedVfsFile(std::map<u64, VirtualFile> files_, std: 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 ""; diff --git a/src/core/file_sys/vfs_concat.h b/src/core/file_sys/vfs_concat.h index 76211d38a..c90f9d5d1 100644 --- a/src/core/file_sys/vfs_concat.h +++ b/src/core/file_sys/vfs_concat.h @@ -7,26 +7,27 @@ #include <map> #include <memory> #include <string_view> -#include <boost/container/flat_map.hpp> #include "core/file_sys/vfs.h" -#include "core/file_sys/vfs_static.h" namespace FileSys { // 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); - - template <u8 filler_byte> - friend VirtualFile ConcatenateFiles(std::map<u64, 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; @@ -43,33 +44,4 @@ private: std::string name; }; -// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases. -VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name); - -// Convenience function that turns a map of offsets to files into a concatenated file, filling gaps -// with template parameter. -template <u8 filler_byte> -VirtualFile ConcatenateFiles(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))); -} - } // namespace FileSys diff --git a/src/core/file_sys/vfs_layered.cpp b/src/core/file_sys/vfs_layered.cpp index 45563d7ae..bfee01725 100644 --- a/src/core/file_sys/vfs_layered.cpp +++ b/src/core/file_sys/vfs_layered.cpp @@ -8,7 +8,13 @@ namespace FileSys { -VirtualDir LayerDirectories(std::vector<VirtualDir> dirs, std::string name) { +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) @@ -17,11 +23,6 @@ VirtualDir LayerDirectories(std::vector<VirtualDir> dirs, std::string name) { return std::shared_ptr<VfsDirectory>(new LayeredVfsDirectory(std::move(dirs), std::move(name))); } -LayeredVfsDirectory::LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name) - : dirs(std::move(dirs)), name(std::move(name)) {} - -LayeredVfsDirectory::~LayeredVfsDirectory() = default; - std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFileRelative(std::string_view path) const { for (const auto& layer : dirs) { const auto file = layer->GetFileRelative(path); @@ -41,7 +42,7 @@ std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetDirectoryRelative( out.push_back(std::move(dir)); } - return LayerDirectories(std::move(out)); + return MakeLayeredDirectory(std::move(out)); } std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFile(std::string_view name) const { diff --git a/src/core/file_sys/vfs_layered.h b/src/core/file_sys/vfs_layered.h index 4f6e341ab..d85310f57 100644 --- a/src/core/file_sys/vfs_layered.h +++ b/src/core/file_sys/vfs_layered.h @@ -9,20 +9,18 @@ namespace FileSys { -// Wrapper function to allow for more efficient handling of dirs.size() == 0, 1 cases. -VirtualDir LayerDirectories(std::vector<VirtualDir> dirs, std::string name = ""); - // 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 { - friend VirtualDir LayerDirectories(std::vector<VirtualDir> dirs, std::string name); - 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; diff --git a/src/core/file_sys/vfs_static.h b/src/core/file_sys/vfs_static.h index 4dd47ffcc..44fab51d1 100644 --- a/src/core/file_sys/vfs_static.h +++ b/src/core/file_sys/vfs_static.h @@ -12,21 +12,21 @@ namespace FileSys { -template <u8 value> class StaticVfsFile : public VfsFile { public: - explicit StaticVfsFile(size_t size = 0, std::string name = "", VirtualDir parent = nullptr) - : size(size), name(std::move(name)), parent(std::move(parent)) {} + 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; } - size_t GetSize() const override { + std::size_t GetSize() const override { return size; } - bool Resize(size_t new_size) override { + bool Resize(std::size_t new_size) override { size = new_size; return true; } @@ -43,23 +43,23 @@ public: return true; } - size_t Read(u8* data, size_t length, size_t offset) const override { + 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; } - size_t Write(const u8* data, size_t length, size_t offset) override { + std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override { return 0; } - boost::optional<u8> ReadByte(size_t offset) const override { + boost::optional<u8> ReadByte(std::size_t offset) const override { if (offset < size) return value; return boost::none; } - std::vector<u8> ReadBytes(size_t length, size_t offset) const override { + 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); } @@ -70,7 +70,8 @@ public: } private: - size_t size; + u8 value; + std::size_t size; std::string name; VirtualDir parent; }; diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp index 7033e2c88..389c7e003 100644 --- a/src/core/file_sys/vfs_vector.cpp +++ b/src/core/file_sys/vfs_vector.cpp @@ -9,7 +9,7 @@ namespace FileSys { VectorVfsFile::VectorVfsFile(std::vector<u8> initial_data, std::string name, VirtualDir parent) - : data(std::move(initial_data)), name(std::move(name)), parent(std::move(parent)) {} + : data(std::move(initial_data)), parent(std::move(parent)), name(std::move(name)) {} VectorVfsFile::~VectorVfsFile() = default; @@ -38,13 +38,13 @@ bool VectorVfsFile::IsReadable() const { return true; } -size_t VectorVfsFile::Read(u8* data_, size_t length, size_t offset) const { +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; } -size_t VectorVfsFile::Write(const u8* data_, size_t length, size_t offset) { +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); diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h index 115c3ae95..48a414c98 100644 --- a/src/core/file_sys/vfs_vector.h +++ b/src/core/file_sys/vfs_vector.h @@ -16,13 +16,13 @@ public: ~VectorVfsFile() override; std::string GetName() const override; - size_t GetSize() const override; - bool Resize(size_t new_size) 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; - size_t Read(u8* data, size_t length, size_t offset) const override; - size_t Write(const u8* data, size_t length, size_t offset) 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); diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 0ecdd9f82..5bc947010 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" @@ -248,7 +250,7 @@ static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr) } else if (id == PC_REGISTER) { thread->context.pc = val; } else if (id == PSTATE_REGISTER) { - thread->context.pstate = val; + thread->context.pstate = static_cast<u32>(val); } else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) { thread->context.vector_registers[id - (PSTATE_REGISTER + 1)][0] = val; } @@ -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()->VMManager().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()->VMManager(); + if (addr < vm_manager.GetCodeRegionBaseAddress() || + addr >= vm_manager.GetMapRegionEndAddress()) { return SendReply("E00"); } diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index 8c2be2681..e5fa67ae8 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h @@ -31,6 +31,7 @@ enum { TooLarge = 119, InvalidEnumValue = 120, NoSuchEntry = 121, + AlreadyRegistered = 122, InvalidState = 125, ResourceLimitExceeded = 132, }; @@ -58,6 +59,7 @@ constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle); constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize); +constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::AlreadyRegistered); constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel, ErrCodes::InvalidThreadPriority); 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 121f741fd..dc9fc8470 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -8,6 +8,7 @@ #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" @@ -34,14 +35,22 @@ 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(); + is_64bit_process = metadata.Is64BitProgram(); + 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]; @@ -119,7 +128,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(); @@ -185,6 +194,7 @@ static std::tuple<std::size_t, std::size_t, bool> FindFreeThreadLocalSlot( 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 @@ -197,18 +207,17 @@ VAddr Process::MarkNextAvailableTLSSlotAsUsed(Thread& thread) { vm_manager.RefreshMemoryBlockMappings(tls_memory.get()); - vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE, - tls_memory, 0, Memory::PAGE_SIZE, MemoryState::ThreadLocal); + 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 Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE + - available_slot * Memory::TLS_ENTRY_SIZE; + 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 - Memory::TLS_AREA_VADDR; + 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; @@ -232,8 +241,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; } @@ -268,8 +277,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 04d74e572..590e0c73d 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,16 @@ public: return HANDLE_TYPE; } + /// Gets a reference to the process' memory manager. + Kernel::VMManager& VMManager() { + return vm_manager; + } + + /// Gets a const reference to the process' memory manager. + const Kernel::VMManager& VMManager() const { + return vm_manager; + } + /// Gets the current status of the process ProcessStatus GetStatus() const { return status; @@ -141,29 +155,52 @@ public: return process_id; } - /// Title ID corresponding to the process - u64 program_id; + /// Gets the title ID corresponding to this process. + u64 GetTitleID() const { + return program_id; + } - /// Resource limit descriptor for this process - SharedPtr<ResourceLimit> resource_limit; + /// Gets the resource limit descriptor for this process + ResourceLimit& GetResourceLimit() { + return *resource_limit; + } - /// The process may only call SVCs which have the corresponding bit set. - std::bitset<0x80> svc_access_mask; - /// Maximum size of the handle table for the process. - unsigned int handle_table_size = 0x200; - /// Special memory ranges mapped into this processes address space. This is used to give - /// processes access to specific I/O regions and device memory. - boost::container::static_vector<AddressMapping, 8> address_mappings; - ProcessFlags flags; - /// Kernel compatibility version for this process - u16 kernel_version = 0; - /// The default CPU for this process, threads are scheduled on this cpu by default. - u8 ideal_processor = 0; - /// Bitmask of allowed CPUs that this process' threads can run on. TODO(Subv): Actually parse - /// this value from the process header. - u32 allowed_processor_mask = THREADPROCESSORID_DEFAULT_MASK; - u32 allowed_thread_priority_mask = 0xFFFFFFFF; - u32 is_virtual_address_memory_enabled = 0; + /// Gets the resource limit descriptor for this process + const ResourceLimit& GetResourceLimit() const { + return *resource_limit; + } + + /// Gets the default CPU ID for this process + u8 GetDefaultProcessorID() const { + return ideal_processor; + } + + /// Gets the bitmask of allowed CPUs that this process' threads can run on. + u32 GetAllowedProcessorMask() const { + return allowed_processor_mask; + } + + /// Gets the bitmask of allowed thread priorities. + u32 GetAllowedThreadPriorityMask() const { + return allowed_thread_priority_mask; + } + + u32 IsVirtualMemoryEnabled() const { + return is_virtual_address_memory_enabled; + } + + /// Whether this process is an AArch64 or AArch32 process. + bool Is64BitProcess() const { + return is_64bit_process; + } + + /** + * 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); /** * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them @@ -200,18 +237,43 @@ public: ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size); - VMManager vm_manager; - private: explicit Process(KernelCore& kernel); ~Process() override; + /// Memory manager for this process. + Kernel::VMManager vm_manager; + /// Current status of the process ProcessStatus status; /// The ID of this process u32 process_id = 0; + /// Title ID corresponding to the process + u64 program_id; + + /// Resource limit descriptor for this process + SharedPtr<ResourceLimit> resource_limit; + + /// The process may only call SVCs which have the corresponding bit set. + std::bitset<0x80> svc_access_mask; + /// Maximum size of the handle table for the process. + u32 handle_table_size = 0x200; + /// Special memory ranges mapped into this processes address space. This is used to give + /// processes access to specific I/O regions and device memory. + boost::container::static_vector<AddressMapping, 8> address_mappings; + ProcessFlags flags; + /// Kernel compatibility version for this process + u16 kernel_version = 0; + /// The default CPU for this process, threads are scheduled on this cpu by default. + u8 ideal_processor = 0; + /// Bitmask of allowed CPUs that this process' threads can run on. TODO(Subv): Actually parse + /// this value from the process header. + u32 allowed_processor_mask = THREADPROCESSORID_DEFAULT_MASK; + u32 allowed_thread_priority_mask = 0xFFFFFFFF; + u32 is_virtual_address_memory_enabled = 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 @@ -230,6 +292,11 @@ private: /// This vector will grow as more pages are allocated for new threads. std::vector<std::bitset<8>> tls_slots; + /// Whether or not this process is AArch64, or AArch32. + /// By default, we currently assume this is true, unless otherwise + /// specified by metadata provided to the process during loading. + bool is_64bit_process = true; + std::string name; }; diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 69c812f16..1e82cfffb 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 @@ -88,13 +88,13 @@ void Scheduler::SwitchContext(Thread* new_thread) { if (previous_process != current_thread->owner_process) { Core::CurrentProcess() = current_thread->owner_process; - SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table); + SetCurrentPageTable(&Core::CurrentProcess()->VMManager().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..d061e6155 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" @@ -34,11 +35,11 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, SharedPtr<Proce // Refresh the address mappings for the current process. if (Core::CurrentProcess() != nullptr) { - Core::CurrentProcess()->vm_manager.RefreshMemoryBlockMappings( + Core::CurrentProcess()->VMManager().RefreshMemoryBlockMappings( shared_memory->backing_block.get()); } } else { - auto& vm_manager = shared_memory->owner_process->vm_manager; + auto& vm_manager = shared_memory->owner_process->VMManager(); // The memory is already available and mapped in the owner process. auto vma = vm_manager.FindVMA(address); @@ -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()->VMManager().GetHeapRegionBaseAddress() + offset; return shared_memory; } @@ -105,7 +107,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi VAddr target_address = address; // Map the memory block into the target process - auto result = target_process->vm_manager.MapMemoryBlock( + auto result = target_process->VMManager().MapMemoryBlock( target_address, backing_block, backing_block_offset, size, MemoryState::Shared); if (result.Failed()) { LOG_ERROR( @@ -115,14 +117,14 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi return result.Code(); } - return target_process->vm_manager.ReprotectRange(target_address, size, - ConvertPermissions(permissions)); + return target_process->VMManager().ReprotectRange(target_address, size, + ConvertPermissions(permissions)); } ResultCode SharedMemory::Unmap(Process* target_process, VAddr address) { // TODO(Subv): Verify what happens if the application tries to unmap an address that is not // mapped to a SharedMemory. - return target_process->vm_manager.UnmapRange(address, size); + return target_process->VMManager().UnmapRange(address, size); } VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) { diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index c9d212a4c..1cdaa740a 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.VMManager().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; } @@ -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->VMManager(); switch (static_cast<GetInfoType>(info_id)) { case GetInfoType::AllowedCpuIdBitmask: - *result = Core::CurrentProcess()->allowed_processor_mask; + *result = current_process->GetAllowedProcessorMask(); break; case GetInfoType::AllowedThreadPrioBitmask: - *result = Core::CurrentProcess()->allowed_thread_priority_mask; + *result = current_process->GetAllowedThreadPriorityMask(); 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->IsVirtualMemoryEnabled(); break; case GetInfoType::TitleId: - *result = Core::CurrentProcess()->program_id; + *result = current_process->GetTitleID(); break; case GetInfoType::PrivilegedProcessId: LOG_WARNING(Kernel_SVC, @@ -400,8 +415,36 @@ static ResultCode SetThreadActivity(Handle handle, u32 unknown) { } /// Gets the thread context -static ResultCode GetThreadContext(Handle handle, VAddr addr) { - LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, addr=0x{:X}", handle, addr); +static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { + LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle); + + auto& kernel = Core::System::GetInstance().Kernel(); + const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle); + if (!thread) { + return ERR_INVALID_HANDLE; + } + + const auto current_process = Core::CurrentProcess(); + if (thread->owner_process != current_process) { + return ERR_INVALID_HANDLE; + } + + if (thread == GetCurrentThread()) { + return ERR_ALREADY_REGISTERED; + } + + Core::ARM_Interface::ThreadContext ctx = thread->context; + // Mask away mode bits, interrupt bits, IL bit, and other reserved bits. + ctx.pstate &= 0xFF0FFE20; + + // If 64-bit, we can just write the context registers directly and we're good. + // However, if 32-bit, we have to ensure some registers are zeroed out. + if (!current_process->Is64BitProcess()) { + std::fill(ctx.cpu_registers.begin() + 15, ctx.cpu_registers.end(), 0); + std::fill(ctx.vector_registers.begin() + 16, ctx.vector_registers.end(), u128{}); + } + + Memory::WriteBlock(thread_context, &ctx, sizeof(ctx)); return RESULT_SUCCESS; } @@ -429,8 +472,8 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { // Note: The kernel uses the current process's resource limit instead of // the one from the thread owner's resource limit. - SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit; - if (resource_limit->GetMaxResourceValue(ResourceType::Priority) > priority) { + const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit(); + if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { return ERR_NOT_AUTHORIZED; } @@ -504,9 +547,9 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_i if (!process) { return ERR_INVALID_HANDLE; } - auto vma = process->vm_manager.FindVMA(addr); + auto vma = process->VMManager().FindVMA(addr); memory_info->attributes = 0; - if (vma == Core::CurrentProcess()->vm_manager.vma_map.end()) { + if (vma == Core::CurrentProcess()->VMManager().vma_map.end()) { memory_info->base_address = 0; memory_info->permission = static_cast<u32>(VMAPermission::None); memory_info->size = 0; @@ -553,14 +596,14 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V return ERR_INVALID_THREAD_PRIORITY; } - SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit; - if (resource_limit->GetMaxResourceValue(ResourceType::Priority) > priority) { + const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit(); + if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { return ERR_NOT_AUTHORIZED; } if (processor_id == THREADPROCESSORID_DEFAULT) { // Set the target CPU to the one specified in the process' exheader. - processor_id = Core::CurrentProcess()->ideal_processor; + processor_id = Core::CurrentProcess()->GetDefaultProcessorID(); ASSERT(processor_id != THREADPROCESSORID_DEFAULT); } @@ -887,10 +930,10 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { } if (core == static_cast<u32>(THREADPROCESSORID_DEFAULT)) { - ASSERT(thread->owner_process->ideal_processor != + ASSERT(thread->owner_process->GetDefaultProcessorID() != static_cast<u8>(THREADPROCESSORID_DEFAULT)); // Set the target CPU to the one specified in the process' exheader. - core = thread->owner_process->ideal_processor; + core = thread->owner_process->GetDefaultProcessorID(); mask = 1ull << core; } diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index fea9ba5ea..22712e64f 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -64,6 +64,11 @@ void SvcWrap() { FuncReturn(func(Param(0), (s32)Param(1)).raw); } +template <ResultCode func(u64, u32)> +void SvcWrap() { + FuncReturn(func(Param(0), static_cast<u32>(Param(1))).raw); +} + template <ResultCode func(u64*, u64)> void SvcWrap() { u64 param_1 = 0; diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 315f65338..b5c16cfbb 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -259,11 +259,12 @@ void Thread::BoostPriority(u32 priority) { SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority, Process& owner_process) { // Setup page table so we can write to memory - SetCurrentPageTable(&owner_process.vm_manager.page_table); + SetCurrentPageTable(&owner_process.VMManager().page_table); // Initialize new "main" thread + const VAddr stack_top = owner_process.VMManager().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/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 80ed4b152..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" @@ -65,7 +66,7 @@ private: void GetAudioRendererState(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(renderer->GetState()); + rb.Push<u32>(static_cast<u32>(renderer->GetStreamState())); LOG_DEBUG(Service_Audio, "called"); } diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp index 6de7edf9e..2f15ac2a6 100644 --- a/src/core/hle/service/fatal/fatal.cpp +++ b/src/core/hle/service/fatal/fatal.cpp @@ -6,11 +6,11 @@ #include <cstring> #include <ctime> #include <fmt/time.h> -#include "common/common_paths.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" @@ -51,7 +51,7 @@ enum class FatalType : u32 { }; static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) { - const auto title_id = Core::CurrentProcess()->program_id; + const auto title_id = Core::CurrentProcess()->GetTitleID(); std::string crash_report = fmt::format("Yuzu {}-{} crash report\n" "Title ID: {:016x}\n" 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/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp index 1069d103f..4b2f758a8 100644 --- a/src/core/hle/service/ns/pl_u.cpp +++ b/src/core/hle/service/ns/pl_u.cpp @@ -317,9 +317,9 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { // Map backing memory for the font data - Core::CurrentProcess()->vm_manager.MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0, - SHARED_FONT_MEM_SIZE, - Kernel::MemoryState::Shared); + Core::CurrentProcess()->VMManager().MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0, + SHARED_FONT_MEM_SIZE, + Kernel::MemoryState::Shared); // Create shared font memory object auto& kernel = Core::System::GetInstance().Kernel(); 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/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 2ee60f1ec..bbc02abcc 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -612,7 +612,7 @@ public: {3000, nullptr, "ListDisplayModes"}, {3001, nullptr, "ListDisplayRgbRanges"}, {3002, nullptr, "ListDisplayContentTypes"}, - {3200, nullptr, "GetDisplayMode"}, + {3200, &ISystemDisplayService::GetDisplayMode, "GetDisplayMode"}, {3201, nullptr, "SetDisplayMode"}, {3202, nullptr, "GetDisplayUnderscan"}, {3203, nullptr, "SetDisplayUnderscan"}, @@ -663,6 +663,24 @@ private: LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:08X}, visibility={}", layer_id, visibility); } + + void GetDisplayMode(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(RESULT_SUCCESS); + + if (Settings::values.use_docked_mode) { + rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); + rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight)); + } else { + rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth)); + rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight)); + } + + rb.PushRaw<float>(60.0f); + rb.Push<u32>(0); + + LOG_DEBUG(Service_VI, "called"); + } }; class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> { diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 2b8f78136..c1824b9c3 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 { @@ -88,8 +86,7 @@ FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::Virtua return FileType::Error; } -ResultStatus AppLoader_DeconstructedRomDirectory::Load( - Kernel::SharedPtr<Kernel::Process>& process) { +ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process) { if (is_loaded) { return ResultStatus::ErrorAlreadyLoaded; } @@ -127,12 +124,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.VMManager().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 +146,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/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index 8a0dc1b1e..d109ed2b5 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h @@ -7,7 +7,6 @@ #include <string> #include "common/common_types.h" #include "core/file_sys/program_metadata.h" -#include "core/hle/kernel/object.h" #include "core/loader/loader.h" namespace Loader { @@ -38,7 +37,7 @@ public: return IdentifyType(file); } - ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; + ResultStatus Load(Kernel::Process& process) override; ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; ResultStatus ReadIcon(std::vector<u8>& buffer) override; diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 0e2af20b4..e67b49fc9 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; @@ -387,7 +387,7 @@ FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& file) { return FileType::Error; } -ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) { +ResultStatus AppLoader_ELF::Load(Kernel::Process& process) { if (is_loaded) return ResultStatus::ErrorAlreadyLoaded; @@ -395,19 +395,13 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) { if (buffer.size() != file->GetSize()) return ResultStatus::ErrorIncorrectELFFileSize; + const VAddr base_address = process.VMManager().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); + process.LoadModule(codeset, codeset->entrypoint); + process.Run(codeset->entrypoint, 48, Memory::DEFAULT_STACK_SIZE); is_loaded = true; return ResultStatus::Success; diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h index b8fb982d0..6af76441c 100644 --- a/src/core/loader/elf.h +++ b/src/core/loader/elf.h @@ -8,9 +8,6 @@ #include "common/common_types.h" #include "core/loader/loader.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Loader namespace - namespace Loader { /// Loads an ELF/AXF file @@ -29,7 +26,7 @@ public: return IdentifyType(file); } - ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; + ResultStatus Load(Kernel::Process& process) override; }; } // namespace Loader diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 843c4bb91..20e66109b 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -12,7 +12,6 @@ #include <boost/optional.hpp> #include "common/common_types.h" #include "core/file_sys/vfs.h" -#include "core/hle/kernel/object.h" namespace Kernel { struct AddressMapping; @@ -136,7 +135,7 @@ public: * @param process The newly created process. * @return The status result of the operation. */ - virtual ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) = 0; + virtual ResultStatus Load(Kernel::Process& process) = 0; /** * Loads the system mode that this application needs. diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp index 5d4380684..073fb9d2f 100644 --- a/src/core/loader/nax.cpp +++ b/src/core/loader/nax.cpp @@ -41,7 +41,7 @@ FileType AppLoader_NAX::GetFileType() { return IdentifyTypeImpl(*nax); } -ResultStatus AppLoader_NAX::Load(Kernel::SharedPtr<Kernel::Process>& process) { +ResultStatus AppLoader_NAX::Load(Kernel::Process& process) { if (is_loaded) { return ResultStatus::ErrorAlreadyLoaded; } diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h index 56605fe45..fc3c01876 100644 --- a/src/core/loader/nax.h +++ b/src/core/loader/nax.h @@ -33,7 +33,7 @@ public: FileType GetFileType() override; - ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; + ResultStatus Load(Kernel::Process& process) override; ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; ResultStatus ReadProgramId(u64& out_program_id) override; diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index 6aaffae59..7e1b0d84f 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp @@ -30,7 +30,7 @@ FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) { return FileType::Error; } -ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { +ResultStatus AppLoader_NCA::Load(Kernel::Process& process) { if (is_loaded) { return ResultStatus::ErrorAlreadyLoaded; } diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h index 10be197c4..95d9b73a1 100644 --- a/src/core/loader/nca.h +++ b/src/core/loader/nca.h @@ -6,7 +6,6 @@ #include "common/common_types.h" #include "core/file_sys/vfs.h" -#include "core/hle/kernel/object.h" #include "core/loader/loader.h" namespace FileSys { @@ -34,7 +33,7 @@ public: return IdentifyType(file); } - ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; + ResultStatus Load(Kernel::Process& process) override; ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; u64 ReadRomFSIVFCOffset() const override; diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index c49ec34ab..c10f826a4 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" @@ -175,23 +175,19 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) { return true; } -ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) { +ResultStatus AppLoader_NRO::Load(Kernel::Process& process) { if (is_loaded) { return ResultStatus::ErrorAlreadyLoaded; } // Load NRO - static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR}; + const VAddr base_address = process.VMManager().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/nro.h b/src/core/loader/nro.h index 96d2de305..04b46119a 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h @@ -6,7 +6,6 @@ #include <string> #include "common/common_types.h" -#include "core/hle/kernel/object.h" #include "core/loader/linker.h" #include "core/loader/loader.h" @@ -33,7 +32,7 @@ public: return IdentifyType(file); } - ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; + ResultStatus Load(Kernel::Process& process) override; ResultStatus ReadIcon(std::vector<u8>& buffer) override; ResultStatus ReadProgramId(u64& out_program_id) override; diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 78a4438c4..cbe2a3e53 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" @@ -153,21 +153,17 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base) { return load_base + image_size; } -ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) { +ResultStatus AppLoader_NSO::Load(Kernel::Process& process) { if (is_loaded) { return ResultStatus::ErrorAlreadyLoaded; } // 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.VMManager().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/loader/nso.h b/src/core/loader/nso.h index aaeb1f2a9..7f142405b 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h @@ -4,9 +4,7 @@ #pragma once -#include <string> #include "common/common_types.h" -#include "core/hle/kernel/object.h" #include "core/loader/linker.h" #include "core/loader/loader.h" @@ -30,7 +28,7 @@ public: static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base); - ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; + ResultStatus Load(Kernel::Process& process) override; }; } // namespace Loader diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index 291a9876d..b7ba77ef4 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp @@ -10,8 +10,6 @@ #include "core/file_sys/control_metadata.h" #include "core/file_sys/nca_metadata.h" #include "core/file_sys/patch_manager.h" -#include "core/file_sys/registered_cache.h" -#include "core/file_sys/romfs.h" #include "core/file_sys/submission_package.h" #include "core/hle/kernel/process.h" #include "core/loader/deconstructed_rom_directory.h" @@ -62,7 +60,7 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) { return FileType::Error; } -ResultStatus AppLoader_NSP::Load(Kernel::SharedPtr<Kernel::Process>& process) { +ResultStatus AppLoader_NSP::Load(Kernel::Process& process) { if (is_loaded) { return ResultStatus::ErrorAlreadyLoaded; } diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h index 7ef810499..eac9b819a 100644 --- a/src/core/loader/nsp.h +++ b/src/core/loader/nsp.h @@ -35,7 +35,7 @@ public: return IdentifyType(file); } - ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; + ResultStatus Load(Kernel::Process& process) override; ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; ResultStatus ReadProgramId(u64& out_program_id) override; diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index 16509229f..eda67a8c8 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp @@ -9,8 +9,6 @@ #include "core/file_sys/content_archive.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" -#include "core/file_sys/romfs.h" -#include "core/file_sys/submission_package.h" #include "core/hle/kernel/process.h" #include "core/loader/nca.h" #include "core/loader/xci.h" @@ -46,7 +44,7 @@ FileType AppLoader_XCI::IdentifyType(const FileSys::VirtualFile& file) { return FileType::Error; } -ResultStatus AppLoader_XCI::Load(Kernel::SharedPtr<Kernel::Process>& process) { +ResultStatus AppLoader_XCI::Load(Kernel::Process& process) { if (is_loaded) { return ResultStatus::ErrorAlreadyLoaded; } diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h index cc4287e17..17e47b658 100644 --- a/src/core/loader/xci.h +++ b/src/core/loader/xci.h @@ -35,7 +35,7 @@ public: return IdentifyType(file); } - ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; + ResultStatus Load(Kernel::Process& process) override; ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; ResultStatus ReadProgramId(u64& out_program_id) override; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 316b46820..014298ed6 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; @@ -105,7 +119,7 @@ void RemoveDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPoin static u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) { u8* direct_pointer = nullptr; - auto& vm_manager = process.vm_manager; + auto& vm_manager = process.VMManager(); auto it = vm_manager.FindVMA(vaddr); ASSERT(it != vm_manager.vma_map.end()); @@ -200,7 +214,7 @@ void Write(const VAddr vaddr, const T data) { } bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) { - auto& page_table = process.vm_manager.page_table; + const auto& page_table = process.VMManager().page_table; const u8* page_pointer = page_table.pointers[vaddr >> PAGE_BITS]; if (page_pointer) @@ -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()->VMManager(); + + CheckRegion(vm_manager.GetCodeRegionBaseAddress(), vm_manager.GetCodeRegionEndAddress()); + CheckRegion(vm_manager.GetHeapRegionBaseAddress(), vm_manager.GetHeapRegionEndAddress()); } u8 Read8(const VAddr addr) { @@ -371,7 +387,7 @@ u64 Read64(const VAddr addr) { void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, const std::size_t size) { - auto& page_table = process.vm_manager.page_table; + const auto& page_table = process.VMManager().page_table; std::size_t remaining_size = size; std::size_t page_index = src_addr >> PAGE_BITS; @@ -436,7 +452,7 @@ void Write64(const VAddr addr, const u64 data) { void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer, const std::size_t size) { - auto& page_table = process.vm_manager.page_table; + const auto& page_table = process.VMManager().page_table; std::size_t remaining_size = size; std::size_t page_index = dest_addr >> PAGE_BITS; std::size_t page_offset = dest_addr & PAGE_MASK; @@ -482,7 +498,7 @@ void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t } void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) { - auto& page_table = process.vm_manager.page_table; + const auto& page_table = process.VMManager().page_table; std::size_t remaining_size = size; std::size_t page_index = dest_addr >> PAGE_BITS; std::size_t page_offset = dest_addr & PAGE_MASK; @@ -524,7 +540,7 @@ void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std: void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, const std::size_t size) { - auto& page_table = process.vm_manager.page_table; + const auto& page_table = process.VMManager().page_table; std::size_t remaining_size = size; std::size_t page_index = src_addr >> PAGE_BITS; std::size_t page_offset = src_addr & PAGE_MASK; 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, diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp index 7c69fc26e..c0a57e71f 100644 --- a/src/tests/core/arm/arm_test_common.cpp +++ b/src/tests/core/arm/arm_test_common.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> + #include "core/core.h" #include "core/hle/kernel/process.h" #include "core/memory.h" @@ -14,11 +16,12 @@ TestEnvironment::TestEnvironment(bool mutable_memory_) : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) { Core::CurrentProcess() = Kernel::Process::Create(kernel, ""); - page_table = &Core::CurrentProcess()->vm_manager.page_table; + page_table = &Core::CurrentProcess()->VMManager().page_table; - page_table->pointers.fill(nullptr); + std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr); page_table->special_regions.clear(); - page_table->attributes.fill(Memory::PageType::Unmapped); + std::fill(page_table->attributes.begin(), page_table->attributes.end(), + Memory::PageType::Unmapped); Memory::MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory); Memory::MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory); diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index b81b0723d..9f5581045 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -41,6 +41,7 @@ public: static constexpr std::size_t NumCBData = 16; static constexpr std::size_t NumVertexArrays = 32; static constexpr std::size_t NumVertexAttributes = 32; + static constexpr std::size_t NumTextureSamplers = 32; static constexpr std::size_t MaxShaderProgram = 6; static constexpr std::size_t MaxShaderStage = 5; // Maximum number of const buffers per shader stage. @@ -461,7 +462,11 @@ public: u32 entry; } macros; - INSERT_PADDING_WORDS(0x1B8); + INSERT_PADDING_WORDS(0x189); + + u32 tfb_enabled; + + INSERT_PADDING_WORDS(0x2E); RenderTargetConfig rt[NumRenderTargets]; @@ -594,7 +599,9 @@ public: u32 depth_write_enabled; - INSERT_PADDING_WORDS(0x7); + u32 alpha_test_enabled; + + INSERT_PADDING_WORDS(0x6); u32 d3d_cull_mode; @@ -635,7 +642,11 @@ public: u32 vb_element_base; - INSERT_PADDING_WORDS(0x40); + INSERT_PADDING_WORDS(0x38); + + float point_size; + + INSERT_PADDING_WORDS(0x7); u32 zeta_enable; @@ -977,6 +988,7 @@ private: "Field " #field_name " has invalid position") ASSERT_REG_POSITION(macros, 0x45); +ASSERT_REG_POSITION(tfb_enabled, 0x1D1); ASSERT_REG_POSITION(rt, 0x200); ASSERT_REG_POSITION(viewport_transform[0], 0x280); ASSERT_REG_POSITION(viewport, 0x300); @@ -996,6 +1008,7 @@ ASSERT_REG_POSITION(zeta_height, 0x48b); ASSERT_REG_POSITION(depth_test_enable, 0x4B3); ASSERT_REG_POSITION(independent_blend_enable, 0x4B9); ASSERT_REG_POSITION(depth_write_enabled, 0x4BA); +ASSERT_REG_POSITION(alpha_test_enabled, 0x4BB); ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2); ASSERT_REG_POSITION(depth_test_func, 0x4C3); ASSERT_REG_POSITION(blend, 0x4CF); @@ -1009,6 +1022,7 @@ ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6); ASSERT_REG_POSITION(stencil_front_mask, 0x4E7); ASSERT_REG_POSITION(screen_y_control, 0x4EB); ASSERT_REG_POSITION(vb_element_base, 0x50D); +ASSERT_REG_POSITION(point_size, 0x546); ASSERT_REG_POSITION(zeta_enable, 0x54E); ASSERT_REG_POSITION(tsc, 0x557); ASSERT_REG_POSITION(tic, 0x55D); diff --git a/src/video_core/engines/maxwell_compute.cpp b/src/video_core/engines/maxwell_compute.cpp index e4e5f9e5e..59e28b22d 100644 --- a/src/video_core/engines/maxwell_compute.cpp +++ b/src/video_core/engines/maxwell_compute.cpp @@ -2,12 +2,29 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" +#include "core/core.h" #include "video_core/engines/maxwell_compute.h" namespace Tegra { namespace Engines { -void MaxwellCompute::WriteReg(u32 method, u32 value) {} +void MaxwellCompute::WriteReg(u32 method, u32 value) { + ASSERT_MSG(method < Regs::NUM_REGS, + "Invalid MaxwellCompute register, increase the size of the Regs structure"); + + regs.reg_array[method] = value; + + switch (method) { + case MAXWELL_COMPUTE_REG_INDEX(compute): { + LOG_CRITICAL(HW_GPU, "Compute shaders are not implemented"); + UNREACHABLE(); + break; + } + default: + break; + } +} } // namespace Engines } // namespace Tegra diff --git a/src/video_core/engines/maxwell_compute.h b/src/video_core/engines/maxwell_compute.h index 2b3e4ced6..6ea934fb9 100644 --- a/src/video_core/engines/maxwell_compute.h +++ b/src/video_core/engines/maxwell_compute.h @@ -4,17 +4,53 @@ #pragma once +#include <array> +#include "common/assert.h" +#include "common/bit_field.h" +#include "common/common_funcs.h" #include "common/common_types.h" namespace Tegra::Engines { +#define MAXWELL_COMPUTE_REG_INDEX(field_name) \ + (offsetof(Tegra::Engines::MaxwellCompute::Regs, field_name) / sizeof(u32)) + class MaxwellCompute final { public: MaxwellCompute() = default; ~MaxwellCompute() = default; + struct Regs { + static constexpr std::size_t NUM_REGS = 0xCF8; + + union { + struct { + INSERT_PADDING_WORDS(0x281); + + union { + u32 compute_end; + BitField<0, 1, u32> unknown; + } compute; + + INSERT_PADDING_WORDS(0xA76); + }; + std::array<u32, NUM_REGS> reg_array; + }; + } regs{}; + + static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), + "MaxwellCompute Regs has wrong size"); + /// Write the value to the register identified by method. void WriteReg(u32 method, u32 value); }; +#define ASSERT_REG_POSITION(field_name, position) \ + static_assert(offsetof(MaxwellCompute::Regs, field_name) == position * 4, \ + "Field " #field_name " has invalid position") + +ASSERT_REG_POSITION(compute, 0x281); + +#undef ASSERT_REG_POSITION + } // namespace Tegra::Engines diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 70fb54507..1fcd13f04 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -450,6 +450,9 @@ void RasterizerOpenGL::DrawArrays() { SyncBlendState(); SyncLogicOpState(); SyncCullMode(); + SyncAlphaTest(); + SyncTransformFeedback(); + SyncPointState(); // TODO(bunnei): Sync framebuffer_scale uniform here // TODO(bunnei): Sync scissorbox uniform(s) here @@ -883,4 +886,30 @@ void RasterizerOpenGL::SyncLogicOpState() { state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation); } +void RasterizerOpenGL::SyncAlphaTest() { + const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; + + // TODO(Rodrigo): Alpha testing is a legacy OpenGL feature, but it can be + // implemented with a test+discard in fragment shaders. + if (regs.alpha_test_enabled != 0) { + LOG_CRITICAL(Render_OpenGL, "Alpha testing is not implemented"); + UNREACHABLE(); + } +} + +void RasterizerOpenGL::SyncTransformFeedback() { + const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; + + if (regs.tfb_enabled != 0) { + LOG_CRITICAL(Render_OpenGL, "Transform feedbacks are not implemented"); + UNREACHABLE(); + } +} + +void RasterizerOpenGL::SyncPointState() { + const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; + + state.point.size = regs.point_size; +} + } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index bf9560bdc..4c8ecbd1c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -158,6 +158,15 @@ private: /// Syncs the LogicOp state to match the guest state void SyncLogicOpState(); + /// Syncs the alpha test state to match the guest state + void SyncAlphaTest(); + + /// Syncs the transform feedback state to match the guest state + void SyncTransformFeedback(); + + /// Syncs the point state to match the guest state + void SyncPointState(); + bool has_ARB_direct_state_access = false; bool has_ARB_multi_bind = false; bool has_ARB_separate_shader_objects = false; @@ -178,7 +187,7 @@ private: OGLVertexArray> vertex_array_cache; - std::array<SamplerInfo, GLShader::NumTextureSamplers> texture_samplers; + std::array<SamplerInfo, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> texture_samplers; static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; OGLBufferCache buffer_cache; diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h index b86cd96e8..3de15ba9b 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.h +++ b/src/video_core/renderer_opengl/gl_shader_manager.h @@ -11,9 +11,6 @@ namespace OpenGL::GLShader { -/// Number of OpenGL texture samplers that can be used in the fragment shader -static constexpr std::size_t NumTextureSamplers = 32; - using Tegra::Engines::Maxwell3D; /// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index e5173e20a..1fe26a2a9 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -79,6 +79,8 @@ OpenGLState::OpenGLState() { viewport.height = 0; clip_distance = {}; + + point.size = 1; } void OpenGLState::Apply() const { @@ -205,9 +207,6 @@ void OpenGLState::Apply() const { glActiveTexture(TextureUnits::MaxwellTexture(static_cast<int>(i)).Enum()); glBindTexture(texture_unit.target, texture_unit.texture); } - if (texture_unit.sampler != cur_state_texture_unit.sampler) { - glBindSampler(static_cast<GLuint>(i), texture_unit.sampler); - } // Update the texture swizzle if (texture_unit.swizzle.r != cur_state_texture_unit.swizzle.r || texture_unit.swizzle.g != cur_state_texture_unit.swizzle.g || @@ -219,6 +218,27 @@ void OpenGLState::Apply() const { } } + // Samplers + { + bool has_delta{}; + std::size_t first{}, last{}; + std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> samplers; + for (std::size_t i = 0; i < std::size(samplers); ++i) { + samplers[i] = texture_units[i].sampler; + if (samplers[i] != cur_state.texture_units[i].sampler) { + if (!has_delta) { + first = i; + has_delta = true; + } + last = i; + } + } + if (has_delta) { + glBindSamplers(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1), + samplers.data()); + } + } + // Framebuffer if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); @@ -283,6 +303,11 @@ void OpenGLState::Apply() const { } } + // Point + if (point.size != cur_state.point.size) { + glPointSize(point.size); + } + cur_state = *this; } diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 9a93029d8..dc21a2ee3 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -6,6 +6,7 @@ #include <array> #include <glad/glad.h> +#include "video_core/engines/maxwell_3d.h" namespace OpenGL { @@ -114,7 +115,7 @@ public: target = GL_TEXTURE_2D; } }; - std::array<TextureUnit, 32> texture_units; + std::array<TextureUnit, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> texture_units; struct { GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING @@ -141,6 +142,10 @@ public: GLsizei height; } viewport; + struct { + float size; // GL_POINT_SIZE + } point; + std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE OpenGLState(); diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 20ba6d4f6..3d5476e5d 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -13,47 +13,20 @@ namespace Tegra::Texture { /** + * This table represents the internal swizzle of a gob, + * in format 16 bytes x 2 sector packing. * Calculates the offset of an (x, y) position within a swizzled texture. - * Taken from the Tegra X1 TRM. + * Taken from the Tegra X1 Technical Reference Manual. pages 1187-1188 */ -static u32 GetSwizzleOffset(u32 x, u32 y, u32 image_width, u32 bytes_per_pixel, u32 block_height) { - // Round up to the next gob - const u32 image_width_in_gobs{(image_width * bytes_per_pixel + 63) / 64}; - - u32 GOB_address = 0 + (y / (8 * block_height)) * 512 * block_height * image_width_in_gobs + - (x * bytes_per_pixel / 64) * 512 * block_height + - (y % (8 * block_height) / 8) * 512; - x *= bytes_per_pixel; - u32 address = GOB_address + ((x % 64) / 32) * 256 + ((y % 8) / 2) * 64 + ((x % 32) / 16) * 32 + - (y % 2) * 16 + (x % 16); - - return address; -} - -void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, - u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height) { - u8* data_ptrs[2]; - for (unsigned y = 0; y < height; ++y) { - for (unsigned x = 0; x < width; ++x) { - u32 swizzle_offset = GetSwizzleOffset(x, y, width, bytes_per_pixel, block_height); - u32 pixel_index = (x + y * width) * out_bytes_per_pixel; - - data_ptrs[unswizzle] = swizzled_data + swizzle_offset; - data_ptrs[!unswizzle] = &unswizzled_data[pixel_index]; - - std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel); - } - } -} - -template <std::size_t N, std::size_t M> +template <std::size_t N, std::size_t M, u32 Align> struct alignas(64) SwizzleTable { + static_assert(M * Align == 64, "Swizzle Table does not align to GOB"); constexpr SwizzleTable() { for (u32 y = 0; y < N; ++y) { for (u32 x = 0; x < M; ++x) { - const u32 x2 = x * 16; + const u32 x2 = x * Align; values[y][x] = static_cast<u16>(((x2 % 64) / 32) * 256 + ((y % 8) / 2) * 64 + - ((x2 % 32) / 16) * 32 + (y % 2) * 16); + ((x2 % 32) / 16) * 32 + (y % 2) * 16 + (x2 % 16)); } } } @@ -63,24 +36,60 @@ struct alignas(64) SwizzleTable { std::array<std::array<u16, M>, N> values{}; }; -constexpr auto swizzle_table = SwizzleTable<8, 4>(); +constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>(); +constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>(); -void FastSwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u8* swizzled_data, - u8* unswizzled_data, bool unswizzle, u32 block_height) { +static void LegacySwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, + u8* swizzled_data, u8* unswizzled_data, bool unswizzle, + u32 block_height) { + std::array<u8*, 2> data_ptrs; + const std::size_t stride = width * bytes_per_pixel; + const std::size_t gobs_in_x = 64; + const std::size_t gobs_in_y = 8; + const std::size_t gobs_size = gobs_in_x * gobs_in_y; + const std::size_t image_width_in_gobs{(stride + gobs_in_x - 1) / gobs_in_x}; + for (std::size_t y = 0; y < height; ++y) { + const std::size_t gob_y_address = + (y / (gobs_in_y * block_height)) * gobs_size * block_height * image_width_in_gobs + + (y % (gobs_in_y * block_height) / gobs_in_y) * gobs_size; + const auto& table = legacy_swizzle_table[y % gobs_in_y]; + for (std::size_t x = 0; x < width; ++x) { + const std::size_t gob_address = + gob_y_address + (x * bytes_per_pixel / gobs_in_x) * gobs_size * block_height; + const std::size_t x2 = x * bytes_per_pixel; + const std::size_t swizzle_offset = gob_address + table[x2 % gobs_in_x]; + const std::size_t pixel_index = (x + y * width) * out_bytes_per_pixel; + + data_ptrs[unswizzle] = swizzled_data + swizzle_offset; + data_ptrs[!unswizzle] = unswizzled_data + pixel_index; + + std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel); + } + } +} + +static void FastSwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, + u8* swizzled_data, u8* unswizzled_data, bool unswizzle, + u32 block_height) { std::array<u8*, 2> data_ptrs; const std::size_t stride{width * bytes_per_pixel}; - const std::size_t image_width_in_gobs{(stride + 63) / 64}; + const std::size_t gobs_in_x = 64; + const std::size_t gobs_in_y = 8; + const std::size_t gobs_size = gobs_in_x * gobs_in_y; + const std::size_t image_width_in_gobs{(stride + gobs_in_x - 1) / gobs_in_x}; const std::size_t copy_size{16}; for (std::size_t y = 0; y < height; ++y) { const std::size_t initial_gob = - (y / (8 * block_height)) * 512 * block_height * image_width_in_gobs + - (y % (8 * block_height) / 8) * 512; - const std::size_t pixel_base{y * width * bytes_per_pixel}; - const auto& table = swizzle_table[y % 8]; + (y / (gobs_in_y * block_height)) * gobs_size * block_height * image_width_in_gobs + + (y % (gobs_in_y * block_height) / gobs_in_y) * gobs_size; + const std::size_t pixel_base{y * width * out_bytes_per_pixel}; + const auto& table = fast_swizzle_table[y % gobs_in_y]; for (std::size_t xb = 0; xb < stride; xb += copy_size) { - const std::size_t gob_address{initial_gob + (xb / 64) * 512 * block_height}; + const std::size_t gob_address{initial_gob + + (xb / gobs_in_x) * gobs_size * block_height}; const std::size_t swizzle_offset{gob_address + table[(xb / 16) % 4]}; - const std::size_t pixel_index{xb + pixel_base}; + const std::size_t out_x = xb * out_bytes_per_pixel / bytes_per_pixel; + const std::size_t pixel_index{out_x + pixel_base}; data_ptrs[unswizzle] = swizzled_data + swizzle_offset; data_ptrs[!unswizzle] = unswizzled_data + pixel_index; std::memcpy(data_ptrs[0], data_ptrs[1], copy_size); @@ -88,6 +97,17 @@ void FastSwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u8* swizzled_da } } +void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, + u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height) { + if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) { + FastSwizzleData(width, height, bytes_per_pixel, out_bytes_per_pixel, swizzled_data, + unswizzled_data, unswizzle, block_height); + } else { + LegacySwizzleData(width, height, bytes_per_pixel, out_bytes_per_pixel, swizzled_data, + unswizzled_data, unswizzle, block_height); + } +} + u32 BytesPerPixel(TextureFormat format) { switch (format) { case TextureFormat::DXT1: @@ -134,13 +154,8 @@ u32 BytesPerPixel(TextureFormat format) { std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, u32 height, u32 block_height) { std::vector<u8> unswizzled_data(width * height * bytes_per_pixel); - if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) { - FastSwizzleData(width / tile_size, height / tile_size, bytes_per_pixel, - Memory::GetPointer(address), unswizzled_data.data(), true, block_height); - } else { - CopySwizzledData(width / tile_size, height / tile_size, bytes_per_pixel, bytes_per_pixel, - Memory::GetPointer(address), unswizzled_data.data(), true, block_height); - } + CopySwizzledData(width / tile_size, height / tile_size, bytes_per_pixel, bytes_per_pixel, + Memory::GetPointer(address), unswizzled_data.data(), true, block_height); return unswizzled_data; } diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 991ae10cd..67890455a 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -26,10 +26,10 @@ #include "yuzu/main.h" #include "yuzu/ui_settings.h" -GameList::SearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist) : gamelist{gamelist} {} +GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist) : gamelist{gamelist} {} // EventFilter in order to process systemkeys while editing the searchfield -bool GameList::SearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* event) { +bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* event) { // If it isn't a KeyRelease event then continue with standard event processing if (event->type() != QEvent::KeyRelease) return QObject::eventFilter(obj, event); @@ -88,21 +88,21 @@ bool GameList::SearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* e return QObject::eventFilter(obj, event); } -void GameList::SearchField::setFilterResult(int visible, int total) { +void GameListSearchField::setFilterResult(int visible, int total) { label_filter_result->setText(tr("%1 of %n result(s)", "", total).arg(visible)); } -void GameList::SearchField::clear() { +void GameListSearchField::clear() { edit_filter->setText(""); } -void GameList::SearchField::setFocus() { +void GameListSearchField::setFocus() { if (edit_filter->isVisible()) { edit_filter->setFocus(); } } -GameList::SearchField::SearchField(GameList* parent) : QWidget{parent} { +GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} { KeyReleaseEater* keyReleaseEater = new KeyReleaseEater(parent); layout_filter = new QHBoxLayout; layout_filter->setMargin(8); @@ -202,7 +202,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent) this->main_window = parent; layout = new QVBoxLayout; tree_view = new QTreeView; - search_field = new SearchField(this); + search_field = new GameListSearchField(this); item_model = new QStandardItemModel(tree_view); tree_view->setModel(item_model); diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 3bf51870e..05e115e19 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -22,6 +22,7 @@ #include "yuzu/compatibility_list.h" class GameListWorker; +class GameListSearchField; class GMainWindow; namespace FileSys { @@ -46,33 +47,6 @@ public: COLUMN_COUNT, // Number of columns }; - class SearchField : public QWidget { - public: - void setFilterResult(int visible, int total); - void clear(); - void setFocus(); - explicit SearchField(GameList* parent = nullptr); - - private: - class KeyReleaseEater : public QObject { - public: - explicit KeyReleaseEater(GameList* gamelist); - - private: - GameList* gamelist = nullptr; - QString edit_filter_text_old; - - protected: - bool eventFilter(QObject* obj, QEvent* event) override; - }; - QHBoxLayout* layout_filter = nullptr; - QTreeView* tree_view = nullptr; - QLabel* label_filter = nullptr; - QLineEdit* edit_filter = nullptr; - QLabel* label_filter_result = nullptr; - QToolButton* button_filter_close = nullptr; - }; - explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs, GMainWindow* parent = nullptr); ~GameList() override; @@ -110,7 +84,7 @@ private: void RefreshGameDirectory(); std::shared_ptr<FileSys::VfsFilesystem> vfs; - SearchField* search_field; + GameListSearchField* search_field; GMainWindow* main_window = nullptr; QVBoxLayout* layout = nullptr; QTreeView* tree_view = nullptr; @@ -118,6 +92,8 @@ private: GameListWorker* current_worker = nullptr; QFileSystemWatcher* watcher = nullptr; CompatibilityList compatibility_list; + + friend class GameListSearchField; }; Q_DECLARE_METATYPE(GameListOpenTarget); diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index cee109730..3db0e90da 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -16,6 +16,7 @@ #include <QObject> #include <QStandardItem> #include <QString> +#include <QWidget> #include "common/common_types.h" #include "common/logging/log.h" @@ -176,3 +177,42 @@ public: return data(SizeRole).toULongLong() < other.data(SizeRole).toULongLong(); } }; + +class GameList; +class QHBoxLayout; +class QTreeView; +class QLabel; +class QLineEdit; +class QToolButton; + +class GameListSearchField : public QWidget { + Q_OBJECT + +public: + explicit GameListSearchField(GameList* parent = nullptr); + + void setFilterResult(int visible, int total); + + void clear(); + void setFocus(); + +private: + class KeyReleaseEater : public QObject { + public: + explicit KeyReleaseEater(GameList* gamelist); + + private: + GameList* gamelist = nullptr; + QString edit_filter_text_old; + + protected: + // EventFilter in order to process systemkeys while editing the searchfield + bool eventFilter(QObject* obj, QEvent* event) override; + }; + QHBoxLayout* layout_filter = nullptr; + QTreeView* tree_view = nullptr; + QLabel* label_filter = nullptr; + QLineEdit* edit_filter = nullptr; + QLabel* label_filter_result = nullptr; + QToolButton* button_filter_close = nullptr; +}; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index cb37796fa..27015d02c 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -622,9 +622,9 @@ void GMainWindow::BootGame(const QString& filename) { std::string title_name; const auto res = Core::System::GetInstance().GetGameName(title_name); if (res != Loader::ResultStatus::Success) { - const u64 program_id = Core::System::GetInstance().CurrentProcess()->program_id; + const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); - const auto [nacp, icon_file] = FileSys::PatchManager(program_id).GetControlMetadata(); + const auto [nacp, icon_file] = FileSys::PatchManager(title_id).GetControlMetadata(); if (nacp != nullptr) title_name = nacp->GetApplicationName(); @@ -756,11 +756,51 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); } +static std::size_t CalculateRomFSEntrySize(const FileSys::VirtualDir& dir, bool full) { + std::size_t out = 0; + + for (const auto& subdir : dir->GetSubdirectories()) { + out += 1 + CalculateRomFSEntrySize(subdir, full); + } + + return out + (full ? dir->GetFiles().size() : 0); +} + +static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src, + const FileSys::VirtualDir& dest, std::size_t block_size, bool full) { + if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) + return false; + if (dialog.wasCanceled()) + return false; + + if (full) { + for (const auto& file : src->GetFiles()) { + const auto out = VfsDirectoryCreateFileWrapper(dest, file->GetName()); + if (!FileSys::VfsRawCopy(file, out, block_size)) + return false; + dialog.setValue(dialog.value() + 1); + if (dialog.wasCanceled()) + return false; + } + } + + for (const auto& dir : src->GetSubdirectories()) { + const auto out = dest->CreateSubdirectory(dir->GetName()); + if (!RomFSRawCopy(dialog, dir, out, block_size, full)) + return false; + dialog.setValue(dialog.value() + 1); + if (dialog.wasCanceled()) + return false; + } + + return true; +} + void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { const auto path = fmt::format("{}{:016X}/romfs", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), program_id); - auto failed = [this, &path]() { + const auto failed = [this, &path] { QMessageBox::warning(this, tr("RomFS Extraction Failed!"), tr("There was an error copying the RomFS files or the user " "cancelled the operation.")); @@ -808,53 +848,13 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa failed(); const auto full = res == "Full"; - - const static std::function<size_t(const FileSys::VirtualDir&, bool)> calculate_entry_size = - [](const FileSys::VirtualDir& dir, bool full) { - size_t out = 0; - for (const auto& subdir : dir->GetSubdirectories()) - out += 1 + calculate_entry_size(subdir, full); - return out + full ? dir->GetFiles().size() : 0; - }; - const auto entry_size = calculate_entry_size(extracted, full); + const auto entry_size = CalculateRomFSEntrySize(extracted, full); QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, entry_size, this); progress.setWindowModality(Qt::WindowModal); progress.setMinimumDuration(100); - const static std::function<bool(QProgressDialog&, const FileSys::VirtualDir&, - const FileSys::VirtualDir&, size_t, bool)> - qt_raw_copy = [](QProgressDialog& dialog, const FileSys::VirtualDir& src, - const FileSys::VirtualDir& dest, size_t block_size, bool full) { - if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) - return false; - if (dialog.wasCanceled()) - return false; - - if (full) { - for (const auto& file : src->GetFiles()) { - const auto out = VfsDirectoryCreateFileWrapper(dest, file->GetName()); - if (!FileSys::VfsRawCopy(file, out, block_size)) - return false; - dialog.setValue(dialog.value() + 1); - if (dialog.wasCanceled()) - return false; - } - } - - for (const auto& dir : src->GetSubdirectories()) { - const auto out = dest->CreateSubdirectory(dir->GetName()); - if (!qt_raw_copy(dialog, dir, out, block_size, full)) - return false; - dialog.setValue(dialog.value() + 1); - if (dialog.wasCanceled()) - return false; - } - - return true; - }; - - if (qt_raw_copy(progress, extracted, out, 0x400000, full)) { + if (RomFSRawCopy(progress, extracted, out, 0x400000, full)) { progress.close(); QMessageBox::information(this, tr("RomFS Extraction Succeeded!"), tr("The operation completed successfully.")); @@ -931,7 +931,7 @@ void GMainWindow::OnMenuInstallToNAND() { } const auto qt_raw_copy = [this](const FileSys::VirtualFile& src, - const FileSys::VirtualFile& dest, size_t block_size) { + const FileSys::VirtualFile& dest, std::size_t block_size) { if (src == nullptr || dest == nullptr) return false; if (!dest->Resize(src->GetSize())) |