diff options
Diffstat (limited to 'src/core')
106 files changed, 2741 insertions, 981 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index fc6cb67c7..faaa50e4d 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -7,15 +7,21 @@ add_library(core STATIC core_timing.cpp core_timing.h file_sys/directory.h + file_sys/disk_filesystem.cpp + file_sys/disk_filesystem.h file_sys/errors.h file_sys/filesystem.cpp file_sys/filesystem.h file_sys/path_parser.cpp file_sys/path_parser.h + file_sys/program_metadata.cpp + file_sys/program_metadata.h file_sys/romfs_factory.cpp file_sys/romfs_factory.h file_sys/romfs_filesystem.cpp file_sys/romfs_filesystem.h + file_sys/savedata_factory.cpp + file_sys/savedata_factory.h file_sys/storage.h frontend/emu_window.cpp frontend/emu_window.h @@ -28,8 +34,6 @@ add_library(core STATIC hle/config_mem.h hle/ipc.h hle/ipc_helpers.h - hle/kernel/address_arbiter.cpp - hle/kernel/address_arbiter.h hle/kernel/client_port.cpp hle/kernel/client_port.h hle/kernel/client_session.cpp @@ -55,6 +59,8 @@ add_library(core STATIC hle/kernel/process.h hle/kernel/resource_limit.cpp hle/kernel/resource_limit.h + hle/kernel/scheduler.cpp + hle/kernel/scheduler.h hle/kernel/server_port.cpp hle/kernel/server_port.h hle/kernel/server_session.cpp @@ -112,6 +118,10 @@ add_library(core STATIC hle/service/filesystem/filesystem.h hle/service/filesystem/fsp_srv.cpp hle/service/filesystem/fsp_srv.h + hle/service/friend/friend.cpp + hle/service/friend/friend.h + hle/service/friend/friend_a.cpp + hle/service/friend/friend_a.h hle/service/hid/hid.cpp hle/service/hid/hid.h hle/service/lm/lm.cpp @@ -124,6 +134,10 @@ add_library(core STATIC hle/service/nifm/nifm_s.h hle/service/nifm/nifm_u.cpp hle/service/nifm/nifm_u.h + hle/service/ns/ns.cpp + hle/service/ns/ns.h + hle/service/ns/pl_u.cpp + hle/service/ns/pl_u.h hle/service/nvdrv/devices/nvdevice.h hle/service/nvdrv/devices/nvdisp_disp0.cpp hle/service/nvdrv/devices/nvdisp_disp0.h @@ -155,6 +169,14 @@ add_library(core STATIC hle/service/service.h hle/service/set/set.cpp hle/service/set/set.h + hle/service/set/set_cal.cpp + hle/service/set/set_cal.h + hle/service/set/set_fd.cpp + hle/service/set/set_fd.h + hle/service/set/set_sys.cpp + hle/service/set/set_sys.h + hle/service/set/settings.cpp + hle/service/set/settings.h hle/service/sm/controller.cpp hle/service/sm/controller.h hle/service/sm/sm.cpp diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 9f2224b78..32ff3c345 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -31,8 +31,12 @@ public: /// Step CPU by one instruction virtual void Step() = 0; + /// Maps a backing memory region for the CPU virtual void MapBackingMemory(VAddr address, size_t size, u8* memory, - Kernel::VMAPermission perms) {} + Kernel::VMAPermission perms) = 0; + + /// Unmaps a region of memory that was previously mapped using MapBackingMemory + virtual void UnmapMemory(VAddr address, size_t size) = 0; /// Clear all instruction cache virtual void ClearInstructionCache() = 0; diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index a64ce9551..7d83f9717 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -6,7 +6,9 @@ #include <memory> #include <dynarmic/A64/a64.h> #include <dynarmic/A64/config.h> +#include "common/logging/log.h" #include "core/arm/dynarmic/arm_dynarmic.h" +#include "core/core.h" #include "core/core_timing.h" #include "core/hle/kernel/memory.h" #include "core/hle/kernel/svc.h" @@ -53,6 +55,9 @@ public: } void InterpreterFallback(u64 pc, size_t num_instructions) override { + LOG_INFO(Core_ARM, "Unicorn fallback @ 0x%" PRIx64 " for %zu instructions (instr = %08x)", + pc, num_instructions, MemoryReadCode(pc)); + ARM_Interface::ThreadContext ctx; parent.SaveContext(ctx); parent.inner_unicorn.LoadContext(ctx); @@ -63,8 +68,17 @@ public: } void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override { - ASSERT_MSG(false, "ExceptionRaised(exception = %zu, pc = %" PRIx64 ")", - static_cast<size_t>(exception), pc); + switch (exception) { + case Dynarmic::A64::Exception::WaitForInterrupt: + case Dynarmic::A64::Exception::WaitForEvent: + case Dynarmic::A64::Exception::SendEvent: + case Dynarmic::A64::Exception::SendEventLocal: + case Dynarmic::A64::Exception::Yield: + return; + default: + ASSERT_MSG(false, "ExceptionRaised(exception = %zu, pc = %" PRIx64 ")", + static_cast<size_t>(exception), pc); + } } void CallSVC(u32 swi) override { @@ -81,23 +95,30 @@ public: u64 GetTicksRemaining() override { return ticks_remaining; } + u64 GetCNTPCT() override { + return CoreTiming::GetTicks(); + } ARM_Dynarmic& parent; size_t ticks_remaining = 0; size_t num_interpreted_instructions = 0; u64 tpidrro_el0 = 0; + u64 tpidr_el0 = 0; }; std::unique_ptr<Dynarmic::A64::Jit> MakeJit(const std::unique_ptr<ARM_Dynarmic_Callbacks>& cb) { - const auto page_table = Kernel::g_current_process->vm_manager.page_table.pointers.data(); + const auto page_table = Core::CurrentProcess()->vm_manager.page_table.pointers.data(); Dynarmic::A64::UserConfig config; config.callbacks = cb.get(); config.tpidrro_el0 = &cb->tpidrro_el0; + config.tpidr_el0 = &cb->tpidr_el0; config.dczid_el0 = 4; + config.ctr_el0 = 0x8444c004; config.page_table = reinterpret_cast<void**>(page_table); config.page_table_address_space_bits = Memory::ADDRESS_SPACE_BITS; config.silently_mirror_page_table = false; + return std::make_unique<Dynarmic::A64::Jit>(config); } @@ -126,6 +147,10 @@ void ARM_Dynarmic::MapBackingMemory(u64 address, size_t size, u8* memory, inner_unicorn.MapBackingMemory(address, size, memory, perms); } +void ARM_Dynarmic::UnmapMemory(u64 address, size_t size) { + inner_unicorn.UnmapMemory(address, size); +} + void ARM_Dynarmic::SetPC(u64 pc) { jit->SetPC(pc); } diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index a436ddd0f..128669d01 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h @@ -19,7 +19,7 @@ public: void MapBackingMemory(VAddr address, size_t size, u8* memory, Kernel::VMAPermission perms) override; - + void UnmapMemory(u64 address, size_t size) override; void SetPC(u64 pc) override; u64 GetPC() const override; u64 GetReg(int index) const override; diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index 7542ed46a..bd98cb160 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -53,7 +53,8 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si void* user_data) { ARM_Interface::ThreadContext ctx{}; Core::CPU().SaveContext(ctx); - ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x%llx", addr); + ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x%llx, pc=0x%llx, lr=0x%llx", addr, + ctx.pc, ctx.cpu_registers[30]); return {}; } @@ -77,6 +78,10 @@ void ARM_Unicorn::MapBackingMemory(VAddr address, size_t size, u8* memory, CHECKED(uc_mem_map_ptr(uc, address, size, static_cast<u32>(perms), memory)); } +void ARM_Unicorn::UnmapMemory(VAddr address, size_t size) { + CHECKED(uc_mem_unmap(uc, address, size)); +} + void ARM_Unicorn::SetPC(u64 pc) { CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &pc)); } diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index a2841c564..b99b58e4c 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h @@ -14,6 +14,7 @@ public: ~ARM_Unicorn(); void MapBackingMemory(VAddr address, size_t size, u8* memory, Kernel::VMAPermission perms) override; + void UnmapMemory(VAddr address, size_t size) override; void SetPC(u64 pc) override; u64 GetPC() const override; u64 GetReg(int index) const override; diff --git a/src/core/core.cpp b/src/core/core.cpp index a42cb48eb..183c5109c 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -104,7 +104,7 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file return init_result; } - const Loader::ResultStatus load_result{app_loader->Load(Kernel::g_current_process)}; + const Loader::ResultStatus load_result{app_loader->Load(current_process)}; if (Loader::ResultStatus::Success != load_result) { LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", load_result); System::Shutdown(); @@ -137,22 +137,26 @@ void System::Reschedule() { } reschedule_pending = false; - Kernel::Reschedule(); + Core::System::GetInstance().Scheduler().Reschedule(); } System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { LOG_DEBUG(HW_Memory, "initialized OK"); + CoreTiming::Init(); + + current_process = Kernel::Process::Create("main"); + switch (Settings::values.cpu_core) { case Settings::CpuCore::Unicorn: - cpu_core = std::make_unique<ARM_Unicorn>(); + cpu_core = std::make_shared<ARM_Unicorn>(); break; case Settings::CpuCore::Dynarmic: default: #ifdef ARCHITECTURE_x86_64 - cpu_core = std::make_unique<ARM_Dynarmic>(); + cpu_core = std::make_shared<ARM_Dynarmic>(); #else - cpu_core = std::make_unique<ARM_Unicorn>(); + cpu_core = std::make_shared<ARM_Unicorn>(); LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); #endif break; @@ -162,9 +166,9 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { telemetry_session = std::make_unique<Core::TelemetrySession>(); - CoreTiming::Init(); HW::Init(); Kernel::Init(system_mode); + scheduler = std::make_unique<Kernel::Scheduler>(cpu_core.get()); Service::Init(); GDBStub::Init(); @@ -192,15 +196,18 @@ void System::Shutdown() { perf_results.frametime * 1000.0); // Shutdown emulation session - GDBStub::Shutdown(); VideoCore::Shutdown(); + GDBStub::Shutdown(); Service::Shutdown(); + scheduler = nullptr; Kernel::Shutdown(); HW::Shutdown(); - CoreTiming::Shutdown(); + telemetry_session = nullptr; + gpu_core = nullptr; cpu_core = nullptr; + CoreTiming::Shutdown(); + app_loader = nullptr; - telemetry_session = nullptr; LOG_DEBUG(Core, "Shutdown OK"); } diff --git a/src/core/core.h b/src/core/core.h index 2d15ebe34..552c8f5ee 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -7,6 +7,8 @@ #include <memory> #include <string> #include "common/common_types.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/scheduler.h" #include "core/loader/loader.h" #include "core/memory.h" #include "core/perf_stats.h" @@ -107,6 +109,14 @@ public: return *gpu_core; } + Kernel::Scheduler& Scheduler() { + return *scheduler; + } + + Kernel::SharedPtr<Kernel::Process>& CurrentProcess() { + return current_process; + } + PerfStats perf_stats; FrameLimiter frame_limiter; @@ -140,11 +150,12 @@ private: /// AppLoader used to load the current executing application std::unique_ptr<Loader::AppLoader> app_loader; - ///< ARM11 CPU core - std::unique_ptr<ARM_Interface> cpu_core; - + std::shared_ptr<ARM_Interface> cpu_core; + std::unique_ptr<Kernel::Scheduler> scheduler; std::unique_ptr<Tegra::GPU> gpu_core; + Kernel::SharedPtr<Kernel::Process> current_process; + /// When true, signals that a reschedule should happen bool reschedule_pending{}; @@ -165,4 +176,8 @@ inline TelemetrySession& Telemetry() { return System::GetInstance().TelemetrySession(); } +inline Kernel::SharedPtr<Kernel::Process>& CurrentProcess() { + return System::GetInstance().CurrentProcess(); +} + } // namespace Core diff --git a/src/core/file_sys/disk_filesystem.cpp b/src/core/file_sys/disk_filesystem.cpp new file mode 100644 index 000000000..22b17ba04 --- /dev/null +++ b/src/core/file_sys/disk_filesystem.cpp @@ -0,0 +1,146 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstring> +#include <memory> +#include "common/common_types.h" +#include "common/logging/log.h" +#include "core/file_sys/disk_filesystem.h" +#include "core/file_sys/errors.h" + +namespace FileSys { + +std::string Disk_FileSystem::GetName() const { + return "Disk"; +} + +ResultVal<std::unique_ptr<StorageBackend>> Disk_FileSystem::OpenFile(const std::string& path, + Mode mode) const { + ASSERT_MSG(mode == Mode::Read || mode == Mode::Write, "Other file modes are not supported"); + + std::string full_path = base_directory + path; + auto file = std::make_shared<FileUtil::IOFile>(full_path, mode == Mode::Read ? "rb" : "wb"); + + if (!file->IsOpen()) { + return ERROR_PATH_NOT_FOUND; + } + + return MakeResult<std::unique_ptr<StorageBackend>>( + std::make_unique<Disk_Storage>(std::move(file))); +} + +ResultCode Disk_FileSystem::DeleteFile(const Path& path) const { + LOG_WARNING(Service_FS, "(STUBBED) called"); + // TODO(bunnei): Use correct error code + return ResultCode(-1); +} + +ResultCode Disk_FileSystem::RenameFile(const Path& src_path, const Path& dest_path) const { + LOG_WARNING(Service_FS, "(STUBBED) called"); + // TODO(wwylele): Use correct error code + return ResultCode(-1); +} + +ResultCode Disk_FileSystem::DeleteDirectory(const Path& path) const { + LOG_WARNING(Service_FS, "(STUBBED) called"); + // TODO(wwylele): Use correct error code + return ResultCode(-1); +} + +ResultCode Disk_FileSystem::DeleteDirectoryRecursively(const Path& path) const { + LOG_WARNING(Service_FS, "(STUBBED) called"); + // TODO(wwylele): Use correct error code + return ResultCode(-1); +} + +ResultCode Disk_FileSystem::CreateFile(const std::string& path, u64 size) const { + LOG_WARNING(Service_FS, "(STUBBED) called"); + + std::string full_path = base_directory + path; + if (size == 0) { + FileUtil::CreateEmptyFile(full_path); + return RESULT_SUCCESS; + } + + FileUtil::IOFile file(full_path, "wb"); + // Creates a sparse file (or a normal file on filesystems without the concept of sparse files) + // We do this by seeking to the right size, then writing a single null byte. + if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) { + return RESULT_SUCCESS; + } + + LOG_ERROR(Service_FS, "Too large file"); + // TODO(Subv): Find out the correct error code + return ResultCode(-1); +} + +ResultCode Disk_FileSystem::CreateDirectory(const Path& path) const { + LOG_WARNING(Service_FS, "(STUBBED) called"); + // TODO(wwylele): Use correct error code + return ResultCode(-1); +} + +ResultCode Disk_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const { + LOG_WARNING(Service_FS, "(STUBBED) called"); + // TODO(wwylele): Use correct error code + return ResultCode(-1); +} + +ResultVal<std::unique_ptr<DirectoryBackend>> Disk_FileSystem::OpenDirectory( + const Path& path) const { + return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<Disk_Directory>()); +} + +u64 Disk_FileSystem::GetFreeSpaceSize() const { + LOG_WARNING(Service_FS, "(STUBBED) called"); + return 0; +} + +ResultVal<FileSys::EntryType> Disk_FileSystem::GetEntryType(const std::string& path) const { + std::string full_path = base_directory + path; + if (!FileUtil::Exists(full_path)) { + return ERROR_PATH_NOT_FOUND; + } + + // TODO(Subv): Find out the EntryType values + UNIMPLEMENTED_MSG("Unimplemented GetEntryType"); +} + +ResultVal<size_t> Disk_Storage::Read(const u64 offset, const size_t length, u8* buffer) const { + LOG_TRACE(Service_FS, "called offset=%llu, length=%zu", offset, length); + file->Seek(offset, SEEK_SET); + return MakeResult<size_t>(file->ReadBytes(buffer, length)); +} + +ResultVal<size_t> Disk_Storage::Write(const u64 offset, const size_t length, const bool flush, + const u8* buffer) const { + LOG_WARNING(Service_FS, "(STUBBED) called"); + file->Seek(offset, SEEK_SET); + size_t written = file->WriteBytes(buffer, length); + if (flush) { + file->Flush(); + } + return MakeResult<size_t>(written); +} + +u64 Disk_Storage::GetSize() const { + return file->GetSize(); +} + +bool Disk_Storage::SetSize(const u64 size) const { + LOG_WARNING(Service_FS, "(STUBBED) called"); + return false; +} + +u32 Disk_Directory::Read(const u32 count, Entry* entries) { + LOG_WARNING(Service_FS, "(STUBBED) called"); + return 0; +} + +bool Disk_Directory::Close() const { + LOG_WARNING(Service_FS, "(STUBBED) called"); + return true; +} + +} // namespace FileSys diff --git a/src/core/file_sys/disk_filesystem.h b/src/core/file_sys/disk_filesystem.h new file mode 100644 index 000000000..53767b949 --- /dev/null +++ b/src/core/file_sys/disk_filesystem.h @@ -0,0 +1,66 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <cstddef> +#include <memory> +#include <string> +#include "common/common_types.h" +#include "common/file_util.h" +#include "core/file_sys/directory.h" +#include "core/file_sys/filesystem.h" +#include "core/file_sys/storage.h" +#include "core/hle/result.h" + +namespace FileSys { + +class Disk_FileSystem : public FileSystemBackend { +public: + explicit Disk_FileSystem(std::string base_directory) + : base_directory(std::move(base_directory)) {} + + std::string GetName() const override; + + ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path, + Mode mode) const override; + ResultCode DeleteFile(const Path& path) const override; + ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override; + ResultCode DeleteDirectory(const Path& path) const override; + ResultCode DeleteDirectoryRecursively(const Path& path) const override; + ResultCode CreateFile(const std::string& path, u64 size) const override; + ResultCode CreateDirectory(const Path& path) const override; + ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override; + ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override; + u64 GetFreeSpaceSize() const override; + ResultVal<EntryType> GetEntryType(const std::string& path) const override; + +protected: + std::string base_directory; +}; + +class Disk_Storage : public StorageBackend { +public: + Disk_Storage(std::shared_ptr<FileUtil::IOFile> file) : file(std::move(file)) {} + + ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override; + ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; + u64 GetSize() const override; + bool SetSize(u64 size) const override; + bool Close() const override { + return false; + } + void Flush() const override {} + +private: + std::shared_ptr<FileUtil::IOFile> file; +}; + +class Disk_Directory : public DirectoryBackend { +public: + u32 Read(const u32 count, Entry* entries) override; + bool Close() const override; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h index be3224ef8..0ed7d2a0c 100644 --- a/src/core/file_sys/errors.h +++ b/src/core/file_sys/errors.h @@ -10,36 +10,17 @@ namespace FileSys { namespace ErrCodes { enum { - RomFSNotFound = 100, - ArchiveNotMounted = 101, - FileNotFound = 112, - PathNotFound = 113, - GameCardNotInserted = 141, - NotFound = 120, - FileAlreadyExists = 180, - DirectoryAlreadyExists = 185, - AlreadyExists = 190, - InvalidOpenFlags = 230, - DirectoryNotEmpty = 240, - NotAFile = 250, - NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive - ExeFSSectionNotFound = 567, - CommandNotAllowed = 630, - InvalidReadFlag = 700, - InvalidPath = 702, - WriteBeyondEnd = 705, - UnsupportedOpenFlags = 760, - IncorrectExeFSReadSize = 761, - UnexpectedFileOrDirectory = 770, + NotFound = 1, }; } +constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrorModule::FS, ErrCodes::NotFound); + // TODO(bunnei): Replace these with correct errors for Switch OS constexpr ResultCode ERROR_INVALID_PATH(ResultCode(-1)); constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(ResultCode(-1)); constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(ResultCode(-1)); constexpr ResultCode ERROR_FILE_NOT_FOUND(ResultCode(-1)); -constexpr ResultCode ERROR_PATH_NOT_FOUND(ResultCode(-1)); constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(ResultCode(-1)); constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(ResultCode(-1)); constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(ResultCode(-1)); diff --git a/src/core/file_sys/filesystem.h b/src/core/file_sys/filesystem.h index 02705506b..94ad2abf2 100644 --- a/src/core/file_sys/filesystem.h +++ b/src/core/file_sys/filesystem.h @@ -27,11 +27,14 @@ enum LowPathType : u32 { Wchar = 4, }; -union Mode { - u32 hex; - BitField<0, 1, u32> read_flag; - BitField<1, 1, u32> write_flag; - BitField<2, 1, u32> create_flag; +enum EntryType : u32 { + Directory = 0, + File = 1, +}; + +enum class Mode : u32 { + Read = 1, + Write = 2, }; class Path { @@ -86,7 +89,7 @@ public: * @param size The size of the new file, filled with zeroes * @return Result of the operation */ - virtual ResultCode CreateFile(const Path& path, u64 size) const = 0; + virtual ResultCode CreateFile(const std::string& path, u64 size) const = 0; /** * Delete a file specified by its path @@ -138,8 +141,8 @@ public: * @param mode Mode to open the file with * @return Opened file, or error code */ - virtual ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const Path& path, - const Mode& mode) const = 0; + virtual ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path, + Mode mode) const = 0; /** * Open a directory specified by its path @@ -153,6 +156,12 @@ public: * @return The number of free bytes in the archive */ virtual u64 GetFreeSpaceSize() const = 0; + + /** + * Get the type of the specified path + * @return The type of the specified path or error code + */ + virtual ResultVal<EntryType> GetEntryType(const std::string& path) const = 0; }; class FileSystemFactory : NonCopyable { @@ -174,10 +183,9 @@ public: /** * Deletes the archive contents and then re-creates the base folder * @param path Path to the archive - * @param format_info Format information for the new archive * @return ResultCode of the operation, 0 on success */ - virtual ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) = 0; + virtual ResultCode Format(const Path& path) = 0; /** * Retrieves the format info about the archive with the specified path diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp new file mode 100644 index 000000000..a6dcebcc3 --- /dev/null +++ b/src/core/file_sys/program_metadata.cpp @@ -0,0 +1,114 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cinttypes> +#include "common/file_util.h" +#include "common/logging/log.h" +#include "core/file_sys/program_metadata.h" +#include "core/loader/loader.h" + +namespace FileSys { + +Loader::ResultStatus ProgramMetadata::Load(const std::string& file_path) { + FileUtil::IOFile file(file_path, "rb"); + if (!file.IsOpen()) + return Loader::ResultStatus::Error; + + std::vector<u8> file_data(file.GetSize()); + + if (!file.ReadBytes(file_data.data(), file_data.size())) + return Loader::ResultStatus::Error; + + Loader::ResultStatus result = Load(file_data); + if (result != Loader::ResultStatus::Success) + LOG_ERROR(Service_FS, "Failed to load NPDM from file %s!", file_path.c_str()); + + return result; +} + +Loader::ResultStatus ProgramMetadata::Load(const std::vector<u8> file_data, size_t offset) { + size_t total_size = static_cast<size_t>(file_data.size() - offset); + if (total_size < sizeof(Header)) + return Loader::ResultStatus::Error; + + size_t header_offset = offset; + memcpy(&npdm_header, &file_data[offset], sizeof(Header)); + + size_t aci_offset = header_offset + npdm_header.aci_offset; + size_t acid_offset = header_offset + npdm_header.acid_offset; + memcpy(&aci_header, &file_data[aci_offset], sizeof(AciHeader)); + memcpy(&acid_header, &file_data[acid_offset], sizeof(AcidHeader)); + + size_t fac_offset = acid_offset + acid_header.fac_offset; + size_t fah_offset = aci_offset + aci_header.fah_offset; + memcpy(&acid_file_access, &file_data[fac_offset], sizeof(FileAccessControl)); + memcpy(&aci_file_access, &file_data[fah_offset], sizeof(FileAccessHeader)); + + return Loader::ResultStatus::Success; +} + +bool ProgramMetadata::Is64BitProgram() const { + return npdm_header.has_64_bit_instructions; +} + +ProgramAddressSpaceType ProgramMetadata::GetAddressSpaceType() const { + return npdm_header.address_space_type; +} + +u8 ProgramMetadata::GetMainThreadPriority() const { + return npdm_header.main_thread_priority; +} + +u8 ProgramMetadata::GetMainThreadCore() const { + return npdm_header.main_thread_cpu; +} + +u32 ProgramMetadata::GetMainThreadStackSize() const { + return npdm_header.main_stack_size; +} + +u64 ProgramMetadata::GetTitleID() const { + return aci_header.title_id; +} + +u64 ProgramMetadata::GetFilesystemPermissions() const { + return aci_file_access.permissions; +} + +void ProgramMetadata::Print() const { + LOG_DEBUG(Service_FS, "Magic: %.4s", npdm_header.magic.data()); + LOG_DEBUG(Service_FS, "Main thread priority: 0x%02x", npdm_header.main_thread_priority); + LOG_DEBUG(Service_FS, "Main thread core: %u", npdm_header.main_thread_cpu); + LOG_DEBUG(Service_FS, "Main thread stack size: 0x%x bytes", npdm_header.main_stack_size); + LOG_DEBUG(Service_FS, "Process category: %u", npdm_header.process_category); + LOG_DEBUG(Service_FS, "Flags: %02x", npdm_header.flags); + LOG_DEBUG(Service_FS, " > 64-bit instructions: %s", + npdm_header.has_64_bit_instructions ? "YES" : "NO"); + + auto address_space = "Unknown"; + switch (npdm_header.address_space_type) { + case ProgramAddressSpaceType::Is64Bit: + address_space = "64-bit"; + break; + case ProgramAddressSpaceType::Is32Bit: + address_space = "32-bit"; + break; + } + + LOG_DEBUG(Service_FS, " > Address space: %s\n", address_space); + + // Begin ACID printing (potential perms, signed) + LOG_DEBUG(Service_FS, "Magic: %.4s", acid_header.magic.data()); + LOG_DEBUG(Service_FS, "Flags: %02x", acid_header.flags); + LOG_DEBUG(Service_FS, " > Is Retail: %s", acid_header.is_retail ? "YES" : "NO"); + LOG_DEBUG(Service_FS, "Title ID Min: %016" PRIX64, acid_header.title_id_min); + LOG_DEBUG(Service_FS, "Title ID Max: %016" PRIX64, acid_header.title_id_max); + LOG_DEBUG(Service_FS, "Filesystem Access: %016" PRIX64 "\n", acid_file_access.permissions); + + // Begin ACI0 printing (actual perms, unsigned) + LOG_DEBUG(Service_FS, "Magic: %.4s", aci_header.magic.data()); + LOG_DEBUG(Service_FS, "Title ID: %016" PRIX64, aci_header.title_id); + LOG_DEBUG(Service_FS, "Filesystem Access: %016" PRIX64 "\n", aci_file_access.permissions); +} +} // namespace FileSys diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h new file mode 100644 index 000000000..b80a08485 --- /dev/null +++ b/src/core/file_sys/program_metadata.h @@ -0,0 +1,154 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <string> +#include <vector> +#include "common/bit_field.h" +#include "common/common_types.h" +#include "common/swap.h" + +namespace Loader { +enum class ResultStatus; +} + +namespace FileSys { + +enum class ProgramAddressSpaceType : u8 { + Is64Bit = 1, + Is32Bit = 2, +}; + +enum class ProgramFilePermission : u64 { + MountContent = 1ULL << 0, + SaveDataBackup = 1ULL << 5, + SdCard = 1ULL << 21, + Calibration = 1ULL << 34, + Bit62 = 1ULL << 62, + Everything = 1ULL << 63, +}; + +/** + * Helper which implements an interface to parse Program Description Metadata (NPDM) + * Data can either be loaded from a file path or with data and an offset into it. + */ +class ProgramMetadata { +public: + Loader::ResultStatus Load(const std::string& file_path); + Loader::ResultStatus Load(const std::vector<u8> file_data, size_t offset = 0); + + bool Is64BitProgram() const; + ProgramAddressSpaceType GetAddressSpaceType() const; + u8 GetMainThreadPriority() const; + u8 GetMainThreadCore() const; + u32 GetMainThreadStackSize() const; + u64 GetTitleID() const; + u64 GetFilesystemPermissions() const; + + void Print() const; + +private: + struct Header { + std::array<char, 4> magic; + std::array<u8, 8> reserved; + union { + u8 flags; + + BitField<0, 1, u8> has_64_bit_instructions; + BitField<1, 3, ProgramAddressSpaceType> address_space_type; + BitField<4, 4, u8> reserved_2; + }; + u8 reserved_3; + u8 main_thread_priority; + u8 main_thread_cpu; + std::array<u8, 8> reserved_4; + u32_le process_category; + u32_le main_stack_size; + std::array<u8, 0x10> application_name; + std::array<u8, 0x40> reserved_5; + u32_le aci_offset; + u32_le aci_size; + u32_le acid_offset; + u32_le acid_size; + }; + + static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong"); + + struct AcidHeader { + std::array<u8, 0x100> signature; + std::array<u8, 0x100> nca_modulus; + std::array<char, 4> magic; + u32_le nca_size; + std::array<u8, 0x4> reserved; + union { + u32 flags; + + BitField<0, 1, u32> is_retail; + BitField<1, 31, u32> flags_unk; + }; + u64_le title_id_min; + u64_le title_id_max; + u32_le fac_offset; + u32_le fac_size; + u32_le sac_offset; + u32_le sac_size; + u32_le kac_offset; + u32_le kac_size; + INSERT_PADDING_BYTES(0x8); + }; + + static_assert(sizeof(AcidHeader) == 0x240, "ACID header structure size is wrong"); + + struct AciHeader { + std::array<char, 4> magic; + std::array<u8, 0xC> reserved; + u64_le title_id; + INSERT_PADDING_BYTES(0x8); + u32_le fah_offset; + u32_le fah_size; + u32_le sac_offset; + u32_le sac_size; + u32_le kac_offset; + u32_le kac_size; + INSERT_PADDING_BYTES(0x8); + }; + + static_assert(sizeof(AciHeader) == 0x40, "ACI0 header structure size is wrong"); + +#pragma pack(push, 1) + + struct FileAccessControl { + u8 version; + INSERT_PADDING_BYTES(3); + u64_le permissions; + std::array<u8, 0x20> unknown; + }; + + static_assert(sizeof(FileAccessControl) == 0x2C, "FS access control structure size is wrong"); + + struct FileAccessHeader { + u8 version; + INSERT_PADDING_BYTES(3); + u64_le permissions; + u32_le unk_offset; + u32_le unk_size; + u32_le unk_offset_2; + u32_le unk_size_2; + }; + + static_assert(sizeof(FileAccessHeader) == 0x1C, "FS access header structure size is wrong"); + +#pragma pack(pop) + + Header npdm_header; + AciHeader aci_header; + AcidHeader acid_header; + + FileAccessControl acid_file_access; + FileAccessHeader aci_file_access; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp index e0de49f05..b21427948 100644 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp @@ -23,7 +23,7 @@ ResultVal<std::unique_ptr<FileSystemBackend>> RomFS_Factory::Open(const Path& pa return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive)); } -ResultCode RomFS_Factory::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) { +ResultCode RomFS_Factory::Format(const Path& path) { LOG_ERROR(Service_FS, "Unimplemented Format archive %s", GetName().c_str()); // TODO(bunnei): Find the right error code for this return ResultCode(-1); diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h index 10ea13966..e0698e642 100644 --- a/src/core/file_sys/romfs_factory.h +++ b/src/core/file_sys/romfs_factory.h @@ -23,7 +23,7 @@ public: return "ArchiveFactory_RomFS"; } ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override; - ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override; + ResultCode Format(const Path& path) override; ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; private: diff --git a/src/core/file_sys/romfs_filesystem.cpp b/src/core/file_sys/romfs_filesystem.cpp index ca1463d7c..f1f9b4d04 100644 --- a/src/core/file_sys/romfs_filesystem.cpp +++ b/src/core/file_sys/romfs_filesystem.cpp @@ -14,8 +14,8 @@ std::string RomFS_FileSystem::GetName() const { return "RomFS"; } -ResultVal<std::unique_ptr<StorageBackend>> RomFS_FileSystem::OpenFile(const Path& path, - const Mode& mode) const { +ResultVal<std::unique_ptr<StorageBackend>> RomFS_FileSystem::OpenFile(const std::string& path, + Mode mode) const { return MakeResult<std::unique_ptr<StorageBackend>>( std::make_unique<RomFS_Storage>(romfs_file, data_offset, data_size)); } @@ -48,7 +48,7 @@ ResultCode RomFS_FileSystem::DeleteDirectoryRecursively(const Path& path) const return ResultCode(-1); } -ResultCode RomFS_FileSystem::CreateFile(const Path& path, u64 size) const { +ResultCode RomFS_FileSystem::CreateFile(const std::string& path, u64 size) const { LOG_CRITICAL(Service_FS, "Attempted to create a file in an ROMFS archive (%s).", GetName().c_str()); // TODO(bunnei): Use correct error code @@ -79,6 +79,12 @@ u64 RomFS_FileSystem::GetFreeSpaceSize() const { return 0; } +ResultVal<FileSys::EntryType> RomFS_FileSystem::GetEntryType(const std::string& path) const { + LOG_CRITICAL(Service_FS, "Called within an ROMFS archive (path %s).", path.c_str()); + // TODO(wwylele): Use correct error code + return ResultCode(-1); +} + ResultVal<size_t> RomFS_Storage::Read(const u64 offset, const size_t length, u8* buffer) const { LOG_TRACE(Service_FS, "called offset=%llu, length=%zu", offset, length); romfs_file->Seek(data_offset + offset, SEEK_SET); diff --git a/src/core/file_sys/romfs_filesystem.h b/src/core/file_sys/romfs_filesystem.h index 900ea567a..cedd70645 100644 --- a/src/core/file_sys/romfs_filesystem.h +++ b/src/core/file_sys/romfs_filesystem.h @@ -29,17 +29,18 @@ public: std::string GetName() const override; - ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const Path& path, - const Mode& mode) const override; + ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path, + Mode mode) const override; ResultCode DeleteFile(const Path& path) const override; ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override; ResultCode DeleteDirectory(const Path& path) const override; ResultCode DeleteDirectoryRecursively(const Path& path) const override; - ResultCode CreateFile(const Path& path, u64 size) const override; + ResultCode CreateFile(const std::string& path, u64 size) const override; ResultCode CreateDirectory(const Path& path) const override; ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override; ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override; u64 GetFreeSpaceSize() const override; + ResultVal<EntryType> GetEntryType(const std::string& path) const override; protected: std::shared_ptr<FileUtil::IOFile> romfs_file; diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp new file mode 100644 index 000000000..14868fed2 --- /dev/null +++ b/src/core/file_sys/savedata_factory.cpp @@ -0,0 +1,57 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cinttypes> +#include <memory> +#include "common/common_types.h" +#include "common/logging/log.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/file_sys/disk_filesystem.h" +#include "core/file_sys/savedata_factory.h" +#include "core/hle/kernel/process.h" + +namespace FileSys { + +SaveData_Factory::SaveData_Factory(std::string nand_directory) + : nand_directory(std::move(nand_directory)) {} + +ResultVal<std::unique_ptr<FileSystemBackend>> SaveData_Factory::Open(const Path& path) { + std::string save_directory = GetFullPath(); + // Return an error if the save data doesn't actually exist. + if (!FileUtil::IsDirectory(save_directory)) { + // TODO(Subv): Find out correct error code. + return ResultCode(-1); + } + + auto archive = std::make_unique<Disk_FileSystem>(save_directory); + return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive)); +} + +ResultCode SaveData_Factory::Format(const Path& path) { + LOG_WARNING(Service_FS, "Format archive %s", GetName().c_str()); + // Create the save data directory. + if (!FileUtil::CreateFullPath(GetFullPath())) { + // TODO(Subv): Find the correct error code. + return ResultCode(-1); + } + + return RESULT_SUCCESS; +} + +ResultVal<ArchiveFormatInfo> SaveData_Factory::GetFormatInfo(const Path& path) const { + LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str()); + // TODO(bunnei): Find the right error code for this + return ResultCode(-1); +} + +std::string SaveData_Factory::GetFullPath() const { + u64 title_id = Core::CurrentProcess()->program_id; + // TODO(Subv): Somehow obtain this value. + u32 user = 0; + return Common::StringFromFormat("%ssave/%016" PRIX64 "/%08X/", nand_directory.c_str(), title_id, + user); +} + +} // namespace FileSys diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h new file mode 100644 index 000000000..73a42aab6 --- /dev/null +++ b/src/core/file_sys/savedata_factory.h @@ -0,0 +1,33 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <string> +#include "common/common_types.h" +#include "core/file_sys/filesystem.h" +#include "core/hle/result.h" + +namespace FileSys { + +/// File system interface to the SaveData archive +class SaveData_Factory final : public FileSystemFactory { +public: + explicit SaveData_Factory(std::string nand_directory); + + std::string GetName() const override { + return "SaveData_Factory"; + } + ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override; + ResultCode Format(const Path& path) override; + ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; + +private: + std::string nand_directory; + + std::string GetFullPath() const; +}; + +} // namespace FileSys diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index 0dcaede67..a6602e12c 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h @@ -91,6 +91,10 @@ struct BufferDescriptorX { address |= static_cast<VAddr>(address_bits_36_38) << 36; return address; } + + u64 Size() const { + return static_cast<u64>(size); + } }; static_assert(sizeof(BufferDescriptorX) == 8, "BufferDescriptorX size is incorrect"); diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 6066d8a18..3f87c4297 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -118,7 +118,8 @@ public: AlignWithPadding(); - if (context.Session()->IsDomain()) { + const bool request_has_domain_header{context.GetDomainMessageHeader() != nullptr}; + if (context.Session()->IsDomain() && request_has_domain_header) { IPC::DomainMessageHeader domain_header{}; domain_header.num_objects = num_domain_objects; PushRaw(domain_header); diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp deleted file mode 100644 index 776d342f0..000000000 --- a/src/core/hle/kernel/address_arbiter.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/common_types.h" -#include "common/logging/log.h" -#include "core/hle/kernel/address_arbiter.h" -#include "core/hle/kernel/errors.h" -#include "core/hle/kernel/thread.h" -#include "core/memory.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Kernel namespace - -namespace Kernel { - -AddressArbiter::AddressArbiter() {} -AddressArbiter::~AddressArbiter() {} - -SharedPtr<AddressArbiter> AddressArbiter::Create(std::string name) { - SharedPtr<AddressArbiter> address_arbiter(new AddressArbiter); - - address_arbiter->name = std::move(name); - - return address_arbiter; -} - -ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, s32 value, - u64 nanoseconds) { - switch (type) { - - // Signal thread(s) waiting for arbitrate address... - case ArbitrationType::Signal: - // Negative value means resume all threads - if (value < 0) { - ArbitrateAllThreads(address); - } else { - // Resume first N threads - for (int i = 0; i < value; i++) - ArbitrateHighestPriorityThread(address); - } - break; - - // Wait current thread (acquire the arbiter)... - case ArbitrationType::WaitIfLessThan: - if ((s32)Memory::Read32(address) < value) { - Kernel::WaitCurrentThread_ArbitrateAddress(address); - } - break; - case ArbitrationType::WaitIfLessThanWithTimeout: - if ((s32)Memory::Read32(address) < value) { - Kernel::WaitCurrentThread_ArbitrateAddress(address); - GetCurrentThread()->WakeAfterDelay(nanoseconds); - } - break; - case ArbitrationType::DecrementAndWaitIfLessThan: { - s32 memory_value = Memory::Read32(address); - if (memory_value < value) { - // Only change the memory value if the thread should wait - Memory::Write32(address, (s32)memory_value - 1); - Kernel::WaitCurrentThread_ArbitrateAddress(address); - } - break; - } - case ArbitrationType::DecrementAndWaitIfLessThanWithTimeout: { - s32 memory_value = Memory::Read32(address); - if (memory_value < value) { - // Only change the memory value if the thread should wait - Memory::Write32(address, (s32)memory_value - 1); - Kernel::WaitCurrentThread_ArbitrateAddress(address); - GetCurrentThread()->WakeAfterDelay(nanoseconds); - } - break; - } - - default: - LOG_ERROR(Kernel, "unknown type=%d", type); - return ERR_INVALID_ENUM_VALUE_FND; - } - - // The calls that use a timeout seem to always return a Timeout error even if they did not put - // the thread to sleep - if (type == ArbitrationType::WaitIfLessThanWithTimeout || - type == ArbitrationType::DecrementAndWaitIfLessThanWithTimeout) { - - return RESULT_TIMEOUT; - } - return RESULT_SUCCESS; -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h deleted file mode 100644 index f902ddf2d..000000000 --- a/src/core/hle/kernel/address_arbiter.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "common/common_types.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/result.h" - -// Address arbiters are an underlying kernel synchronization object that can be created/used via -// supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR -// applications use them as an underlying mechanism to implement thread-safe barriers, events, and -// semphores. - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Kernel namespace - -namespace Kernel { - -enum class ArbitrationType : u32 { - Signal, - WaitIfLessThan, - DecrementAndWaitIfLessThan, - WaitIfLessThanWithTimeout, - DecrementAndWaitIfLessThanWithTimeout, -}; - -class AddressArbiter final : public Object { -public: - /** - * Creates an address arbiter. - * - * @param name Optional name used for debugging. - * @returns The created AddressArbiter. - */ - static SharedPtr<AddressArbiter> Create(std::string name = "Unknown"); - - std::string GetTypeName() const override { - return "Arbiter"; - } - std::string GetName() const override { - return name; - } - - static const HandleType HANDLE_TYPE = HandleType::AddressArbiter; - HandleType GetHandleType() const override { - return HANDLE_TYPE; - } - - std::string name; ///< Name of address arbiter object (optional) - - ResultCode ArbitrateAddress(ArbitrationType type, VAddr address, s32 value, u64 nanoseconds); - -private: - AddressArbiter(); - ~AddressArbiter() override; -}; - -} // namespace Kernel diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp index 3beb55753..822449cd5 100644 --- a/src/core/hle/kernel/handle_table.cpp +++ b/src/core/hle/kernel/handle_table.cpp @@ -5,6 +5,7 @@ #include <utility> #include "common/assert.h" #include "common/logging/log.h" +#include "core/core.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" @@ -77,7 +78,7 @@ SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const { if (handle == CurrentThread) { return GetCurrentThread(); } else if (handle == CurrentProcess) { - return g_current_process; + return Core::CurrentProcess(); } if (!IsValid(handle)) { diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index db104e8a2..293756790 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -7,11 +7,13 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/event.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/server_session.h" +#include "core/memory.h" namespace Kernel { @@ -25,6 +27,32 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s boost::range::remove_erase(connected_sessions, server_session); } +SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread, + const std::string& reason, u64 timeout, + WakeupCallback&& callback) { + + // Put the client thread to sleep until the wait event is signaled or the timeout expires. + thread->wakeup_callback = + [context = *this, callback](ThreadWakeupReason reason, SharedPtr<Thread> thread, + SharedPtr<WaitObject> object, size_t index) mutable -> bool { + ASSERT(thread->status == THREADSTATUS_WAIT_HLE_EVENT); + callback(thread, context, reason); + context.WriteToOutgoingCommandBuffer(*thread); + return true; + }; + + auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason); + thread->status = THREADSTATUS_WAIT_HLE_EVENT; + thread->wait_objects = {event}; + event->AddWaitingThread(thread); + + if (timeout > 0) { + thread->WakeAfterDelay(timeout); + } + + return event; +} + HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session) : server_session(std::move(server_session)) { cmd_buf[0] = 0; @@ -34,7 +62,7 @@ HLERequestContext::~HLERequestContext() = default; void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { IPC::RequestParser rp(src_cmdbuf); - command_header = std::make_unique<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>()); + command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>()); if (command_header->type == IPC::CommandType::Close) { // Close does not populate the rest of the IPC header @@ -44,7 +72,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { // If handle descriptor is present, add size of it if (command_header->enable_handle_descriptor) { handle_descriptor_header = - std::make_unique<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>()); + std::make_shared<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>()); if (handle_descriptor_header->send_current_pid) { rp.Skip(2, false); } @@ -84,13 +112,18 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { if (Session()->IsDomain() && (command_header->type == IPC::CommandType::Request || !incoming)) { // If this is an incoming message, only CommandType "Request" has a domain header - // All outgoing domain messages have the domain header - domain_message_header = - std::make_unique<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>()); + // All outgoing domain messages have the domain header, if only incoming has it + if (incoming || domain_message_header) { + domain_message_header = + std::make_shared<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>()); + } else { + if (Session()->IsDomain()) + LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!"); + } } data_payload_header = - std::make_unique<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>()); + std::make_shared<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>()); data_payload_offset = rp.GetCurrentOffset(); @@ -153,8 +186,11 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb return RESULT_SUCCESS; } -ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, - HandleTable& dst_table) { +ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { + std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf; + Memory::ReadBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), + dst_cmdbuf.size() * sizeof(u32)); + // The header was already built in the internal command buffer. Attempt to parse it to verify // the integrity and then copy it over to the target command buffer. ParseCommandBuffer(cmd_buf.data(), false); @@ -165,7 +201,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P if (domain_message_header) size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32); - std::copy_n(cmd_buf.begin(), size, dst_cmdbuf); + std::copy_n(cmd_buf.begin(), size, dst_cmdbuf.data()); if (command_header->enable_handle_descriptor) { ASSERT_MSG(!move_objects.empty() || !copy_objects.empty(), @@ -195,7 +231,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P // TODO(Subv): Translate the X/A/B/W buffers. - if (Session()->IsDomain()) { + if (Session()->IsDomain() && domain_message_header) { ASSERT(domain_message_header->num_objects == domain_objects.size()); // Write the domain objects to the command buffer, these go after the raw untranslated data. // TODO(Subv): This completely ignores C buffers. @@ -207,7 +243,106 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P dst_cmdbuf[domain_offset++] = static_cast<u32_le>(request_handlers.size()); } } + + // Copy the translated command buffer back into the thread's command buffer area. + Memory::WriteBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), + dst_cmdbuf.size() * sizeof(u32)); + return RESULT_SUCCESS; } +std::vector<u8> HLERequestContext::ReadBuffer() const { + std::vector<u8> buffer; + const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[0].Size()}; + + if (is_buffer_a) { + buffer.resize(BufferDescriptorA()[0].Size()); + Memory::ReadBlock(BufferDescriptorA()[0].Address(), buffer.data(), buffer.size()); + } else { + buffer.resize(BufferDescriptorX()[0].Size()); + Memory::ReadBlock(BufferDescriptorX()[0].Address(), buffer.data(), buffer.size()); + } + + return buffer; +} + +size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size) const { + const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[0].Size()}; + + ASSERT_MSG(size <= GetWriteBufferSize(), "Size %d is too big", size); + + if (is_buffer_b) { + Memory::WriteBlock(BufferDescriptorB()[0].Address(), buffer, size); + } else { + Memory::WriteBlock(BufferDescriptorC()[0].Address(), buffer, size); + } + + return size; +} + +size_t HLERequestContext::WriteBuffer(const std::vector<u8>& buffer) const { + return WriteBuffer(buffer.data(), buffer.size()); +} + +size_t HLERequestContext::GetReadBufferSize() const { + const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[0].Size()}; + return is_buffer_a ? BufferDescriptorA()[0].Size() : BufferDescriptorX()[0].Size(); +} + +size_t HLERequestContext::GetWriteBufferSize() const { + const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[0].Size()}; + return is_buffer_b ? BufferDescriptorB()[0].Size() : BufferDescriptorC()[0].Size(); +} + +std::string HLERequestContext::Description() const { + if (!command_header) { + return "No command header available"; + } + std::ostringstream s; + s << "IPC::CommandHeader: Type:" << static_cast<u32>(command_header->type.Value()); + s << ", X(Pointer):" << command_header->num_buf_x_descriptors; + if (command_header->num_buf_x_descriptors) { + s << '['; + for (u64 i = 0; i < command_header->num_buf_x_descriptors; ++i) { + s << "0x" << std::hex << BufferDescriptorX()[i].Size(); + if (i < command_header->num_buf_x_descriptors - 1) + s << ", "; + } + s << ']'; + } + s << ", A(Send):" << command_header->num_buf_a_descriptors; + if (command_header->num_buf_a_descriptors) { + s << '['; + for (u64 i = 0; i < command_header->num_buf_a_descriptors; ++i) { + s << "0x" << std::hex << BufferDescriptorA()[i].Size(); + if (i < command_header->num_buf_a_descriptors - 1) + s << ", "; + } + s << ']'; + } + s << ", B(Receive):" << command_header->num_buf_b_descriptors; + if (command_header->num_buf_b_descriptors) { + s << '['; + for (u64 i = 0; i < command_header->num_buf_b_descriptors; ++i) { + s << "0x" << std::hex << BufferDescriptorB()[i].Size(); + if (i < command_header->num_buf_b_descriptors - 1) + s << ", "; + } + s << ']'; + } + s << ", C(ReceiveList):" << BufferDescriptorC().size(); + if (!BufferDescriptorC().empty()) { + s << '['; + for (u64 i = 0; i < BufferDescriptorC().size(); ++i) { + s << "0x" << std::hex << BufferDescriptorC()[i].Size(); + if (i < BufferDescriptorC().size() - 1) + s << ", "; + } + s << ']'; + } + s << ", data_size:" << command_header->data_size.Value(); + + return s.str(); +} + } // namespace Kernel diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index da8335b35..8b35da4c9 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -6,6 +6,7 @@ #include <array> #include <memory> +#include <string> #include <vector> #include <boost/container/small_vector.hpp> #include "common/common_types.h" @@ -13,6 +14,7 @@ #include "core/hle/ipc.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/server_session.h" +#include "core/hle/kernel/thread.h" namespace Service { class ServiceFrameworkBase; @@ -24,6 +26,7 @@ class Domain; class HandleTable; class HLERequestContext; class Process; +class Event; /** * Interface implemented by HLE Session handlers. @@ -102,14 +105,31 @@ public: return server_session; } + using WakeupCallback = std::function<void(SharedPtr<Thread> thread, HLERequestContext& context, + ThreadWakeupReason reason)>; + + /** + * Puts the specified guest thread to sleep until the returned event is signaled or until the + * specified timeout expires. + * @param thread Thread to be put to sleep. + * @param reason Reason for pausing the thread, to be used for debugging purposes. + * @param timeout Timeout in nanoseconds after which the thread will be awoken and the callback + * invoked with a Timeout reason. + * @param callback Callback to be invoked when the thread is resumed. This callback must write + * the entire command response once again, regardless of the state of it before this function + * was called. + * @returns Event that when signaled will resume the thread and call the callback function. + */ + SharedPtr<Event> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason, + u64 timeout, WakeupCallback&& callback); + void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming); /// Populates this context with data from the requesting process/thread. ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process, HandleTable& src_table); /// Writes data from this context back to the requesting process/thread. - ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, - HandleTable& dst_table); + ResultCode WriteToOutgoingCommandBuffer(Thread& thread); u32_le GetCommand() const { return command; @@ -139,10 +159,25 @@ public: return buffer_c_desciptors; } - const std::unique_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const { + const std::shared_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const { return domain_message_header; } + /// Helper function to read a buffer using the appropriate buffer descriptor + std::vector<u8> ReadBuffer() const; + + /// Helper function to write a buffer using the appropriate buffer descriptor + size_t WriteBuffer(const void* buffer, size_t size) const; + + /// Helper function to write a buffer using the appropriate buffer descriptor + size_t WriteBuffer(const std::vector<u8>& buffer) const; + + /// Helper function to get the size of the input buffer + size_t GetReadBufferSize() const; + + /// Helper function to get the size of the output buffer + size_t GetWriteBufferSize() const; + template <typename T> SharedPtr<T> GetCopyObject(size_t index) { ASSERT(index < copy_objects.size()); @@ -187,6 +222,8 @@ public: return domain_objects.size(); } + std::string Description() const; + private: std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; SharedPtr<Kernel::ServerSession> server_session; @@ -195,10 +232,10 @@ private: boost::container::small_vector<SharedPtr<Object>, 8> copy_objects; boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects; - std::unique_ptr<IPC::CommandHeader> command_header; - std::unique_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header; - std::unique_ptr<IPC::DataPayloadHeader> data_payload_header; - std::unique_ptr<IPC::DomainMessageHeader> domain_message_header; + std::shared_ptr<IPC::CommandHeader> command_header; + std::shared_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header; + std::shared_ptr<IPC::DataPayloadHeader> data_payload_header; + std::shared_ptr<IPC::DomainMessageHeader> domain_message_header; std::vector<IPC::BufferDescriptorX> buffer_x_desciptors; std::vector<IPC::BufferDescriptorABW> buffer_a_desciptors; std::vector<IPC::BufferDescriptorABW> buffer_b_desciptors; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index b0c3f4ae1..b325b879b 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -41,7 +41,6 @@ void Shutdown() { g_object_address_table.Clear(); Kernel::ThreadingShutdown(); - g_current_process = nullptr; Kernel::TimersShutdown(); Kernel::ResourceLimitsShutdown(); diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index c77e58f3c..053bf4e17 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -33,10 +33,6 @@ enum class HandleType : u32 { ServerSession, }; -enum { - DEFAULT_STACK_SIZE = 0x10000, -}; - enum class ResetType { OneShot, Sticky, diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 8e74059ea..3694afc60 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -20,12 +20,9 @@ namespace Kernel { // Lists all processes that exist in the current session. static std::vector<SharedPtr<Process>> process_list; -SharedPtr<CodeSet> CodeSet::Create(std::string name, u64 program_id) { +SharedPtr<CodeSet> CodeSet::Create(std::string name) { SharedPtr<CodeSet> codeset(new CodeSet); - codeset->name = std::move(name); - codeset->program_id = program_id; - return codeset; } @@ -41,6 +38,7 @@ SharedPtr<Process> Process::Create(std::string&& name) { process->flags.raw = 0; process->flags.memory_region.Assign(MemoryRegion::APPLICATION); process->status = ProcessStatus::Created; + process->program_id = 0; process_list.push_back(process); return process; @@ -119,11 +117,12 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { } void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { - // Allocate and map stack + // Allocate and map the main thread stack + // 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::HEAP_VADDR_END - stack_size, - std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, - MemoryState::Heap) + .MapMemoryBlock(Memory::STACK_VADDR, std::make_shared<std::vector<u8>>(stack_size, 0), 0, + stack_size, MemoryState::Mapped) .Unwrap(); misc_memory_used += stack_size; memory_region->used += stack_size; @@ -155,9 +154,9 @@ void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { }; // Map CodeSet segments - MapSegment(module_->code, VMAPermission::ReadExecute, MemoryState::Code); - MapSegment(module_->rodata, VMAPermission::Read, MemoryState::Static); - MapSegment(module_->data, VMAPermission::ReadWrite, MemoryState::Static); + MapSegment(module_->code, VMAPermission::ReadExecute, MemoryState::CodeStatic); + MapSegment(module_->rodata, VMAPermission::Read, MemoryState::CodeMutable); + MapSegment(module_->data, VMAPermission::ReadWrite, MemoryState::CodeMutable); } VAddr Process::GetLinearHeapAreaAddress() const { @@ -184,6 +183,8 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per // Initialize heap heap_memory = std::make_shared<std::vector<u8>>(); heap_start = heap_end = target; + } else { + vm_manager.UnmapRange(heap_start, heap_end - heap_start); } // If necessary, expand backing vector to cover new heap extents. @@ -203,7 +204,7 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per size, MemoryState::Heap)); vm_manager.Reprotect(vma, perms); - heap_used += size; + heap_used = size; memory_region->used += size; return MakeResult<VAddr>(heap_end - size); @@ -290,7 +291,7 @@ ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) { CASCADE_RESULT(auto new_vma, vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, - vma->second.meminfo_state)); + MemoryState::Mapped)); // Protect mirror with permissions from old region vm_manager.Reprotect(new_vma, vma->second.permissions); // Remove permissions from old region @@ -321,5 +322,4 @@ SharedPtr<Process> GetProcessById(u32 process_id) { return *itr; } -SharedPtr<Process> g_current_process; } // namespace Kernel diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index add98472f..68e77a4d1 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -56,7 +56,7 @@ class ResourceLimit; struct MemoryRegionInfo; struct CodeSet final : public Object { - static SharedPtr<CodeSet> Create(std::string name, u64 program_id); + static SharedPtr<CodeSet> Create(std::string name); std::string GetTypeName() const override { return "CodeSet"; @@ -72,8 +72,6 @@ struct CodeSet final : public Object { /// Name of the process std::string name; - /// Title ID corresponding to the process - u64 program_id; std::shared_ptr<std::vector<u8>> memory; @@ -113,6 +111,9 @@ public: static u32 next_process_id; + /// Title ID corresponding to the process + u64 program_id; + /// Resource limit descriptor for this process SharedPtr<ResourceLimit> resource_limit; @@ -202,5 +203,4 @@ void ClearProcessList(); /// Retrieves a process from the current list of processes. SharedPtr<Process> GetProcessById(u32 process_id); -extern SharedPtr<Process> g_current_process; } // namespace Kernel diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp new file mode 100644 index 000000000..921f27efb --- /dev/null +++ b/src/core/hle/kernel/scheduler.cpp @@ -0,0 +1,135 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/scheduler.h" + +namespace Kernel { + +Scheduler::Scheduler(ARM_Interface* cpu_core) : cpu_core(cpu_core) {} + +Scheduler::~Scheduler() { + for (auto& thread : thread_list) { + thread->Stop(); + } +} + +bool Scheduler::HaveReadyThreads() { + return ready_queue.get_first() != nullptr; +} + +Thread* Scheduler::GetCurrentThread() const { + return current_thread.get(); +} + +Thread* Scheduler::PopNextReadyThread() { + Thread* next = nullptr; + Thread* thread = GetCurrentThread(); + + if (thread && thread->status == THREADSTATUS_RUNNING) { + // We have to do better than the current thread. + // This call returns null when that's not possible. + next = ready_queue.pop_first_better(thread->current_priority); + if (!next) { + // Otherwise just keep going with the current thread + next = thread; + } + } else { + next = ready_queue.pop_first(); + } + + return next; +} + +void Scheduler::SwitchContext(Thread* new_thread) { + Thread* previous_thread = GetCurrentThread(); + + // Save context for previous thread + if (previous_thread) { + previous_thread->last_running_ticks = CoreTiming::GetTicks(); + cpu_core->SaveContext(previous_thread->context); + + if (previous_thread->status == THREADSTATUS_RUNNING) { + // This is only the case when a reschedule is triggered without the current thread + // yielding execution (i.e. an event triggered, system core time-sliced, etc) + ready_queue.push_front(previous_thread->current_priority, previous_thread); + previous_thread->status = THREADSTATUS_READY; + } + } + + // Load context of new thread + if (new_thread) { + ASSERT_MSG(new_thread->status == THREADSTATUS_READY, + "Thread must be ready to become running."); + + // Cancel any outstanding wakeup events for this thread + new_thread->CancelWakeupTimer(); + + auto previous_process = Core::CurrentProcess(); + + current_thread = new_thread; + + ready_queue.remove(new_thread->current_priority, new_thread); + new_thread->status = THREADSTATUS_RUNNING; + + if (previous_process != current_thread->owner_process) { + Core::CurrentProcess() = current_thread->owner_process; + SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table); + } + + cpu_core->LoadContext(new_thread->context); + cpu_core->SetTlsAddress(new_thread->GetTLSAddress()); + } else { + current_thread = nullptr; + // Note: We do not reset the current process and current page table when idling because + // technically we haven't changed processes, our threads are just paused. + } +} + +void Scheduler::Reschedule() { + Thread* cur = GetCurrentThread(); + Thread* next = PopNextReadyThread(); + + if (cur && next) { + LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId()); + } else if (cur) { + LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId()); + } else if (next) { + LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId()); + } + + SwitchContext(next); +} + +void Scheduler::AddThread(SharedPtr<Thread> thread, u32 priority) { + thread_list.push_back(thread); + ready_queue.prepare(priority); +} + +void Scheduler::RemoveThread(Thread* thread) { + thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), + thread_list.end()); +} + +void Scheduler::ScheduleThread(Thread* thread, u32 priority) { + ASSERT(thread->status == THREADSTATUS_READY); + ready_queue.push_back(priority, thread); +} + +void Scheduler::UnscheduleThread(Thread* thread, u32 priority) { + ASSERT(thread->status == THREADSTATUS_READY); + ready_queue.remove(priority, thread); +} + +void Scheduler::SetThreadPriority(Thread* thread, u32 priority) { + // If thread was ready, adjust queues + if (thread->status == THREADSTATUS_READY) + ready_queue.move(thread, thread->current_priority, priority); + else + ready_queue.prepare(priority); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h new file mode 100644 index 000000000..27d0247d6 --- /dev/null +++ b/src/core/hle/kernel/scheduler.h @@ -0,0 +1,73 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <vector> +#include "common/common_types.h" +#include "common/thread_queue_list.h" +#include "core/arm/arm_interface.h" +#include "core/hle/kernel/thread.h" + +namespace Kernel { + +class Scheduler final { +public: + explicit Scheduler(ARM_Interface* cpu_core); + ~Scheduler(); + + /// Returns whether there are any threads that are ready to run. + bool HaveReadyThreads(); + + /// Reschedules to the next available thread (call after current thread is suspended) + void Reschedule(); + + /// Gets the current running thread + Thread* GetCurrentThread() const; + + /// Adds a new thread to the scheduler + void AddThread(SharedPtr<Thread> thread, u32 priority); + + /// Removes a thread from the scheduler + void RemoveThread(Thread* thread); + + /// Schedules a thread that has become "ready" + void ScheduleThread(Thread* thread, u32 priority); + + /// Unschedules a thread that was already scheduled + void UnscheduleThread(Thread* thread, u32 priority); + + /// Sets the priority of a thread in the scheduler + void SetThreadPriority(Thread* thread, u32 priority); + + /// Returns a list of all threads managed by the scheduler + const std::vector<SharedPtr<Thread>>& GetThreadList() const { + return thread_list; + } + +private: + /** + * Pops and returns the next thread from the thread queue + * @return A pointer to the next ready thread + */ + Thread* PopNextReadyThread(); + + /** + * Switches the CPU's active thread context to that of the specified thread + * @param new_thread The thread to switch to + */ + void SwitchContext(Thread* new_thread); + + /// Lists all thread ids that aren't deleted/etc. + std::vector<SharedPtr<Thread>> thread_list; + + /// Lists only ready thread ids. + Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue; + + SharedPtr<Thread> current_thread = nullptr; + + ARM_Interface* cpu_core; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 54481f7f1..9b4a0ef0a 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -4,6 +4,7 @@ #include <tuple> +#include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" @@ -57,6 +58,33 @@ void ServerSession::Acquire(Thread* thread) { pending_requesting_threads.pop_back(); } +ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) { + auto& domain_message_header = context.GetDomainMessageHeader(); + if (domain_message_header) { + // If there is a DomainMessageHeader, then this is CommandType "Request" + const u32 object_id{context.GetDomainMessageHeader()->object_id}; + switch (domain_message_header->command) { + case IPC::DomainMessageHeader::CommandType::SendMessage: + return domain_request_handlers[object_id - 1]->HandleSyncRequest(context); + + case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { + LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x%08X", object_id); + + domain_request_handlers[object_id - 1] = nullptr; + + IPC::ResponseBuilder rb{context, 2}; + rb.Push(RESULT_SUCCESS); + return RESULT_SUCCESS; + } + } + + LOG_CRITICAL(IPC, "Unknown domain command=%d", domain_message_header->command.Value()); + ASSERT(false); + } + + return RESULT_SUCCESS; +} + ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) { // The ServerSession received a sync request, this means that there's new data available // from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or @@ -64,49 +92,42 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) { Kernel::HLERequestContext context(this); u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress()); - context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process, + context.PopulateFromIncomingCommandBuffer(cmd_buf, *Core::CurrentProcess(), Kernel::g_handle_table); - // If the session has been converted to a domain, handle the doomain request - if (IsDomain()) { - auto& domain_message_header = context.GetDomainMessageHeader(); - if (domain_message_header) { - // If there is a DomainMessageHeader, then this is CommandType "Request" - const u32 object_id{context.GetDomainMessageHeader()->object_id}; - switch (domain_message_header->command) { - case IPC::DomainMessageHeader::CommandType::SendMessage: - return domain_request_handlers[object_id - 1]->HandleSyncRequest(context); - - case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { - LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x%08X", object_id); - - domain_request_handlers[object_id - 1] = nullptr; - - IPC::ResponseBuilder rb{context, 2}; - rb.Push(RESULT_SUCCESS); - return RESULT_SUCCESS; - } - } - - LOG_CRITICAL(IPC, "Unknown domain command=%d", domain_message_header->command.Value()); - ASSERT(false); - } + ResultCode result = RESULT_SUCCESS; + // If the session has been converted to a domain, handle the domain request + if (IsDomain() && context.GetDomainMessageHeader()) { + result = HandleDomainSyncRequest(context); // If there is no domain header, the regular session handler is used + } else if (hle_handler != nullptr) { + // If this ServerSession has an associated HLE handler, forward the request to it. + result = hle_handler->HandleSyncRequest(context); } - // If this ServerSession has an associated HLE handler, forward the request to it. - ResultCode result{RESULT_SUCCESS}; - if (hle_handler != nullptr) { - // Attempt to translate the incoming request's command buffer. - ResultCode translate_result = TranslateHLERequest(this); - if (translate_result.IsError()) - return translate_result; - - result = hle_handler->HandleSyncRequest(context); - } else { - // Add the thread to the list of threads that have issued a sync request with this - // server. - pending_requesting_threads.push_back(std::move(thread)); + if (thread->status == THREADSTATUS_RUNNING) { + // Put the thread to sleep until the server replies, it will be awoken in + // svcReplyAndReceive for LLE servers. + thread->status = THREADSTATUS_WAIT_IPC; + + if (hle_handler != nullptr) { + // For HLE services, we put the request threads to sleep for a short duration to + // simulate IPC overhead, but only if the HLE handler didn't put the thread to sleep for + // other reasons like an async callback. The IPC overhead is needed to prevent + // starvation when a thread only does sync requests to HLE services while a + // lower-priority thread is waiting to run. + + // This delay was approximated in a homebrew application by measuring the average time + // it takes for svcSendSyncRequest to return when performing the SetLcdForceBlack IPC + // request to the GSP:GPU service in a n3DS with firmware 11.6. The measured values have + // a high variance and vary between models. + static constexpr u64 IPCDelayNanoseconds = 39000; + thread->WakeAfterDelay(IPCDelayNanoseconds); + } else { + // Add the thread to the list of threads that have issued a sync request with this + // server. + pending_requesting_threads.push_back(std::move(thread)); + } } // If this ServerSession does not have an HLE implementation, just wake up the threads waiting @@ -140,9 +161,4 @@ ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& n return std::make_tuple(std::move(server_session), std::move(client_session)); } - -ResultCode TranslateHLERequest(ServerSession* server_session) { - // TODO(Subv): Implement this function once multiple concurrent processes are supported. - return RESULT_SUCCESS; -} } // namespace Kernel diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index 144692106..2da807042 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -21,6 +21,7 @@ class ServerSession; class Session; class SessionRequestHandler; class Thread; +class HLERequestContext; /** * Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS @@ -116,17 +117,12 @@ private: */ static ResultVal<SharedPtr<ServerSession>> Create(std::string name = "Unknown"); + /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an + /// object handle. + ResultCode HandleDomainSyncRequest(Kernel::HLERequestContext& context); + /// When set to True, converts the session to a domain at the end of the command bool convert_to_domain{}; }; -/** - * Performs command buffer translation for an HLE IPC request. - * The command buffer from the ServerSession thread's TLS is copied into a - * buffer and all descriptors in the buffer are processed. - * TODO(Subv): Implement this function, currently we do not support multiple processes running at - * once, but once that is implemented we'll need to properly translate all descriptors - * in the command buffer. - */ -ResultCode TranslateHLERequest(ServerSession* server_session); } // namespace Kernel diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 835fc710b..4d6cd7462 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -4,6 +4,7 @@ #include <cstring> #include "common/logging/log.h" +#include "core/core.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/memory.h" #include "core/hle/kernel/shared_memory.h" @@ -51,8 +52,8 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u } // Refresh the address mappings for the current process. - if (Kernel::g_current_process != nullptr) { - Kernel::g_current_process->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); + if (Core::CurrentProcess() != nullptr) { + Core::CurrentProcess()->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); } } else { auto& vm_manager = shared_memory->owner_process->vm_manager; @@ -111,13 +112,6 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi return ERR_INVALID_COMBINATION; } - // Heap-backed memory blocks can not be mapped with other_permissions = DontCare - if (base_address != 0 && other_permissions == MemoryPermission::DontCare) { - LOG_ERROR(Kernel, "cannot map id=%u, address=0x%llx name=%s, permissions don't match", - GetObjectId(), address, name.c_str()); - return ERR_INVALID_COMBINATION; - } - // Error out if the provided permissions are not compatible with what the creator process needs. if (other_permissions != MemoryPermission::DontCare && static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) { @@ -126,12 +120,6 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi return ERR_WRONG_PERMISSION; } - // TODO(Subv): Check for the Shared Device Mem flag in the creator process. - /*if (was_created_with_shared_device_mem && address != 0) { - return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); - }*/ - // TODO(Subv): The same process that created a SharedMemory object // can not map it in its own address space unless it was created with addr=0, result 0xD900182C. diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 4d20ef134..118ce3ee5 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -3,10 +3,12 @@ // Refer to the license.txt file included. #include <algorithm> +#include <cinttypes> #include "common/logging/log.h" #include "common/microprofile.h" #include "common/string_util.h" +#include "core/core.h" #include "core/core_timing.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" @@ -30,7 +32,7 @@ namespace Kernel { /// Set the process heap to a given Size. It can both extend and shrink the heap. static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { LOG_TRACE(Kernel_SVC, "called, heap_size=0x%llx", heap_size); - auto& process = *g_current_process; + auto& process = *Core::CurrentProcess(); CASCADE_RESULT(*heap_addr, process.HeapAllocate(Memory::HEAP_VADDR, heap_size, VMAPermission::ReadWrite)); return RESULT_SUCCESS; @@ -45,14 +47,14 @@ static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { LOG_TRACE(Kernel_SVC, "called, dst_addr=0x%llx, src_addr=0x%llx, size=0x%llx", dst_addr, src_addr, size); - return g_current_process->MirrorMemory(dst_addr, src_addr, size); + return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size); } /// Unmaps a region that was previously mapped with svcMapMemory static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { LOG_TRACE(Kernel_SVC, "called, dst_addr=0x%llx, src_addr=0x%llx, size=0x%llx", dst_addr, src_addr, size); - return g_current_process->UnmapMemory(dst_addr, src_addr, size); + return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size); } /// Connect to an OS service given the port name, returns the handle to the port to out @@ -305,23 +307,23 @@ 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); - auto& vm_manager = g_current_process->vm_manager; + auto& vm_manager = Core::CurrentProcess()->vm_manager; switch (static_cast<GetInfoType>(info_id)) { case GetInfoType::AllowedCpuIdBitmask: - *result = g_current_process->allowed_processor_mask; + *result = Core::CurrentProcess()->allowed_processor_mask; break; case GetInfoType::AllowedThreadPrioBitmask: - *result = g_current_process->allowed_thread_priority_mask; + *result = Core::CurrentProcess()->allowed_thread_priority_mask; break; case GetInfoType::MapRegionBaseAddr: - *result = vm_manager.GetMapRegionBaseAddr(); + *result = Memory::MAP_REGION_VADDR; break; case GetInfoType::MapRegionSize: - *result = vm_manager.GetAddressSpaceSize(); + *result = Memory::MAP_REGION_SIZE; break; case GetInfoType::HeapRegionBaseAddr: - *result = vm_manager.GetNewMapRegionBaseAddr() + vm_manager.GetNewMapRegionSize(); + *result = Memory::HEAP_VADDR; break; case GetInfoType::HeapRegionSize: *result = Memory::HEAP_SIZE; @@ -345,13 +347,13 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) *result = vm_manager.GetAddressSpaceSize(); break; case GetInfoType::NewMapRegionBaseAddr: - *result = vm_manager.GetNewMapRegionBaseAddr(); + *result = Memory::NEW_MAP_REGION_VADDR; break; case GetInfoType::NewMapRegionSize: - *result = vm_manager.GetNewMapRegionSize(); + *result = Memory::NEW_MAP_REGION_SIZE; break; case GetInfoType::IsVirtualAddressMemoryEnabled: - *result = g_current_process->is_virtual_address_memory_enabled; + *result = Core::CurrentProcess()->is_virtual_address_memory_enabled; break; case GetInfoType::TitleId: LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query titleid, returned 0"); @@ -391,7 +393,7 @@ 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 = g_current_process->resource_limit; + SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit; if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) { return ERR_NOT_AUTHORIZED; } @@ -434,7 +436,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s case MemoryPermission::WriteExecute: case MemoryPermission::ReadWriteExecute: case MemoryPermission::DontCare: - return shared_memory->Map(g_current_process.get(), addr, permissions_type, + return shared_memory->Map(Core::CurrentProcess().get(), addr, permissions_type, MemoryPermission::DontCare); default: LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions); @@ -443,6 +445,16 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s return RESULT_SUCCESS; } +static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { + LOG_WARNING(Kernel_SVC, + "called, shared_memory_handle=0x%08X, addr=0x%" PRIx64 ", size=0x%" PRIx64 "", + shared_memory_handle, addr, size); + + SharedPtr<SharedMemory> shared_memory = g_handle_table.Get<SharedMemory>(shared_memory_handle); + + return shared_memory->Unmap(Core::CurrentProcess().get(), addr); +} + /// Query process memory static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/, Handle process_handle, u64 addr) { @@ -452,11 +464,11 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_i } auto vma = process->vm_manager.FindVMA(addr); memory_info->attributes = 0; - if (vma == g_current_process->vm_manager.vma_map.end()) { + if (vma == Core::CurrentProcess()->vm_manager.vma_map.end()) { memory_info->base_address = 0; memory_info->permission = static_cast<u32>(VMAPermission::None); memory_info->size = 0; - memory_info->type = static_cast<u32>(MemoryState::Free); + memory_info->type = static_cast<u32>(MemoryState::Unmapped); } else { memory_info->base_address = vma->second.base; memory_info->permission = static_cast<u32>(vma->second.permissions); @@ -476,16 +488,17 @@ static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAdd /// Exits the current process static void ExitProcess() { - LOG_INFO(Kernel_SVC, "Process %u exiting", g_current_process->process_id); + LOG_INFO(Kernel_SVC, "Process %u exiting", Core::CurrentProcess()->process_id); - ASSERT_MSG(g_current_process->status == ProcessStatus::Running, "Process has already exited"); + ASSERT_MSG(Core::CurrentProcess()->status == ProcessStatus::Running, + "Process has already exited"); - g_current_process->status = ProcessStatus::Exited; + Core::CurrentProcess()->status = ProcessStatus::Exited; // Stop all the process threads that are currently waiting for objects. - auto& thread_list = GetThreadList(); + auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList(); for (auto& thread : thread_list) { - if (thread->owner_process != g_current_process) + if (thread->owner_process != Core::CurrentProcess()) continue; if (thread == GetCurrentThread()) @@ -514,14 +527,14 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V return ERR_OUT_OF_RANGE; } - SharedPtr<ResourceLimit>& resource_limit = g_current_process->resource_limit; + SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit; if (resource_limit->GetMaxResourceValue(ResourceTypes::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 = g_current_process->ideal_processor; + processor_id = Core::CurrentProcess()->ideal_processor; ASSERT(processor_id != THREADPROCESSORID_DEFAULT); } @@ -543,7 +556,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V CASCADE_RESULT(SharedPtr<Thread> thread, Thread::Create(name, entry_point, priority, arg, processor_id, stack_top, - g_current_process)); + Core::CurrentProcess())); CASCADE_RESULT(thread->guest_handle, g_handle_table.Create(thread)); *out_handle = thread->guest_handle; @@ -585,7 +598,7 @@ static void SleepThread(s64 nanoseconds) { // Don't attempt to yield execution if there are no available threads to run, // this way we avoid a useless reschedule to the idle thread. - if (nanoseconds == 0 && !HaveReadyThreads()) + if (nanoseconds == 0 && !Core::System::GetInstance().Scheduler().HaveReadyThreads()) return; // Sleep current thread and check for next thread to schedule @@ -761,6 +774,16 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss return RESULT_SUCCESS; } +static ResultCode ClearEvent(Handle handle) { + LOG_TRACE(Kernel_SVC, "called, event=0xX", handle); + + SharedPtr<Event> evt = g_handle_table.Get<Event>(handle); + if (evt == nullptr) + return ERR_INVALID_HANDLE; + evt->Clear(); + return RESULT_SUCCESS; +} + namespace { struct FunctionDef { using Func = void(); @@ -790,9 +813,9 @@ static const FunctionDef SVC_Table[] = { {0x0F, SvcWrap<SetThreadCoreMask>, "SetThreadCoreMask"}, {0x10, SvcWrap<GetCurrentProcessorNumber>, "GetCurrentProcessorNumber"}, {0x11, nullptr, "SignalEvent"}, - {0x12, nullptr, "ClearEvent"}, + {0x12, SvcWrap<ClearEvent>, "ClearEvent"}, {0x13, SvcWrap<MapSharedMemory>, "MapSharedMemory"}, - {0x14, nullptr, "UnmapSharedMemory"}, + {0x14, SvcWrap<UnmapSharedMemory>, "UnmapSharedMemory"}, {0x15, SvcWrap<CreateTransferMemory>, "CreateTransferMemory"}, {0x16, SvcWrap<CloseHandle>, "CloseHandle"}, {0x17, SvcWrap<ResetSignal>, "ResetSignal"}, diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 7a165d8dc..b224f5e67 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -91,6 +91,11 @@ void SvcWrap() { FuncReturn(func((u32)PARAM(0), PARAM(1), PARAM(2), (u32)PARAM(3)).raw); } +template <ResultCode func(u32, u64, u64)> +void SvcWrap() { + FuncReturn(func((u32)PARAM(0), PARAM(1), PARAM(2)).raw); +} + template <ResultCode func(u32*, u64, u64, s64)> void SvcWrap() { u32 param_1 = 0; diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 1a33cc6cb..145f50887 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -41,14 +41,6 @@ void Thread::Acquire(Thread* thread) { // us to simply use a pool index or similar. static Kernel::HandleTable wakeup_callback_handle_table; -// Lists all thread ids that aren't deleted/etc. -static std::vector<SharedPtr<Thread>> thread_list; - -// Lists only ready thread ids. -static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue; - -static SharedPtr<Thread> current_thread; - // The first available thread id at startup static u32 next_thread_id; @@ -63,20 +55,6 @@ inline static u32 const NewThreadId() { Thread::Thread() {} Thread::~Thread() {} -Thread* GetCurrentThread() { - return current_thread.get(); -} - -/** - * Check if the specified thread is waiting on the specified address to be arbitrated - * @param thread The thread to test - * @param wait_address The address to test against - * @return True if the thread is waiting, false otherwise - */ -static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) { - return thread->status == THREADSTATUS_WAIT_ARB && wait_address == thread->wait_address; -} - void Thread::Stop() { // Cancel any outstanding wakeup events for this thread CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); @@ -86,7 +64,7 @@ void Thread::Stop() { // Clean up thread from ready queue // This is only needed when the thread is termintated forcefully (SVC TerminateProcess) if (status == THREADSTATUS_READY) { - ready_queue.remove(current_priority, this); + Core::System::GetInstance().Scheduler().UnscheduleThread(this, current_priority); } status = THREADSTATUS_DEAD; @@ -106,113 +84,7 @@ void Thread::Stop() { u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE; u64 tls_slot = ((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE; - Kernel::g_current_process->tls_slots[tls_page].reset(tls_slot); -} - -Thread* ArbitrateHighestPriorityThread(u32 address) { - Thread* highest_priority_thread = nullptr; - u32 priority = THREADPRIO_LOWEST; - - // Iterate through threads, find highest priority thread that is waiting to be arbitrated... - for (auto& thread : thread_list) { - if (!CheckWait_AddressArbiter(thread.get(), address)) - continue; - - if (thread == nullptr) - continue; - - if (thread->current_priority <= priority) { - highest_priority_thread = thread.get(); - priority = thread->current_priority; - } - } - - // If a thread was arbitrated, resume it - if (nullptr != highest_priority_thread) { - highest_priority_thread->ResumeFromWait(); - } - - return highest_priority_thread; -} - -void ArbitrateAllThreads(u32 address) { - // Resume all threads found to be waiting on the address - for (auto& thread : thread_list) { - if (CheckWait_AddressArbiter(thread.get(), address)) - thread->ResumeFromWait(); - } -} - -/** - * Switches the CPU's active thread context to that of the specified thread - * @param new_thread The thread to switch to - */ -static void SwitchContext(Thread* new_thread) { - Thread* previous_thread = GetCurrentThread(); - - // Save context for previous thread - if (previous_thread) { - previous_thread->last_running_ticks = CoreTiming::GetTicks(); - Core::CPU().SaveContext(previous_thread->context); - - if (previous_thread->status == THREADSTATUS_RUNNING) { - // This is only the case when a reschedule is triggered without the current thread - // yielding execution (i.e. an event triggered, system core time-sliced, etc) - ready_queue.push_front(previous_thread->current_priority, previous_thread); - previous_thread->status = THREADSTATUS_READY; - } - } - - // Load context of new thread - if (new_thread) { - ASSERT_MSG(new_thread->status == THREADSTATUS_READY, - "Thread must be ready to become running."); - - // Cancel any outstanding wakeup events for this thread - CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle); - - auto previous_process = Kernel::g_current_process; - - current_thread = new_thread; - - ready_queue.remove(new_thread->current_priority, new_thread); - new_thread->status = THREADSTATUS_RUNNING; - - if (previous_process != current_thread->owner_process) { - Kernel::g_current_process = current_thread->owner_process; - SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); - } - - Core::CPU().LoadContext(new_thread->context); - Core::CPU().SetTlsAddress(new_thread->GetTLSAddress()); - } else { - current_thread = nullptr; - // Note: We do not reset the current process and current page table when idling because - // technically we haven't changed processes, our threads are just paused. - } -} - -/** - * Pops and returns the next thread from the thread queue - * @return A pointer to the next ready thread - */ -static Thread* PopNextReadyThread() { - Thread* next; - Thread* thread = GetCurrentThread(); - - if (thread && thread->status == THREADSTATUS_RUNNING) { - // We have to do better than the current thread. - // This call returns null when that's not possible. - next = ready_queue.pop_first_better(thread->current_priority); - if (!next) { - // Otherwise just keep going with the current thread - next = thread; - } - } else { - next = ready_queue.pop_first(); - } - - return next; + Core::CurrentProcess()->tls_slots[tls_page].reset(tls_slot); } void WaitCurrentThread_Sleep() { @@ -220,17 +92,10 @@ void WaitCurrentThread_Sleep() { thread->status = THREADSTATUS_WAIT_SLEEP; } -void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { - Thread* thread = GetCurrentThread(); - thread->wait_address = wait_address; - thread->status = THREADSTATUS_WAIT_ARB; -} - void ExitCurrentThread() { Thread* thread = GetCurrentThread(); thread->Stop(); - thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), - thread_list.end()); + Core::System::GetInstance().Scheduler().RemoveThread(thread); } /** @@ -248,7 +113,8 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { bool resume = true; if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY || - thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) { + thread->status == THREADSTATUS_WAIT_SYNCH_ALL || + thread->status == THREADSTATUS_WAIT_HLE_EVENT) { // Remove the thread from each of its waiting objects' waitlists for (auto& object : thread->wait_objects) @@ -282,8 +148,9 @@ void Thread::ResumeFromWait() { switch (status) { case THREADSTATUS_WAIT_SYNCH_ALL: case THREADSTATUS_WAIT_SYNCH_ANY: - case THREADSTATUS_WAIT_ARB: + case THREADSTATUS_WAIT_HLE_EVENT: case THREADSTATUS_WAIT_SLEEP: + case THREADSTATUS_WAIT_IPC: break; case THREADSTATUS_READY: @@ -307,32 +174,12 @@ void Thread::ResumeFromWait() { wakeup_callback = nullptr; - ready_queue.push_back(current_priority, this); status = THREADSTATUS_READY; + Core::System::GetInstance().Scheduler().ScheduleThread(this, current_priority); Core::System::GetInstance().PrepareReschedule(); } /** - * Prints the thread queue for debugging purposes - */ -static void DebugThreadQueue() { - Thread* thread = GetCurrentThread(); - if (!thread) { - LOG_DEBUG(Kernel, "Current: NO CURRENT THREAD"); - } else { - LOG_DEBUG(Kernel, "0x%02X %u (current)", thread->current_priority, - GetCurrentThread()->GetObjectId()); - } - - for (auto& t : thread_list) { - u32 priority = ready_queue.contains(t.get()); - if (priority != -1) { - LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId()); - } - } -} - -/** * Finds a free location for the TLS section of a thread. * @param tls_slots The TLS page array of the thread's owner process. * Returns a tuple of (page, slot, alloc_needed) where: @@ -399,8 +246,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, SharedPtr<Thread> thread(new Thread); - thread_list.push_back(thread); - ready_queue.prepare(priority); + Core::System::GetInstance().Scheduler().AddThread(thread, priority); thread->thread_id = NewThreadId(); thread->status = THREADSTATUS_DORMANT; @@ -453,7 +299,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, // TODO(Subv): Find the correct MemoryState for this region. vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE, linheap_memory, offset, Memory::PAGE_SIZE, - MemoryState::ThreadLocalStorage); + MemoryState::ThreadLocal); } // Mark the slot as used @@ -471,12 +317,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, void Thread::SetPriority(u32 priority) { ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, "Invalid priority value."); - // If thread was ready, adjust queues - if (status == THREADSTATUS_READY) - ready_queue.move(this, current_priority, priority); - else - ready_queue.prepare(priority); - + Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority); nominal_priority = current_priority = priority; } @@ -490,22 +331,18 @@ void Thread::UpdatePriority() { } void Thread::BoostPriority(u32 priority) { - // If thread was ready, adjust queues - if (status == THREADSTATUS_READY) - ready_queue.move(this, current_priority, priority); - else - ready_queue.prepare(priority); + Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority); current_priority = priority; } SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority, SharedPtr<Process> owner_process) { // Setup page table so we can write to memory - SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); + SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table); // Initialize new "main" thread auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0, - Memory::HEAP_VADDR_END, owner_process); + Memory::STACK_VADDR_END, owner_process); SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); @@ -520,25 +357,6 @@ SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority, return thread; } -bool HaveReadyThreads() { - return ready_queue.get_first() != nullptr; -} - -void Reschedule() { - Thread* cur = GetCurrentThread(); - Thread* next = PopNextReadyThread(); - - if (cur && next) { - LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId()); - } else if (cur) { - LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId()); - } else if (next) { - LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId()); - } - - SwitchContext(next); -} - void Thread::SetWaitSynchronizationResult(ResultCode result) { context.cpu_registers[0] = result.raw; } @@ -561,25 +379,20 @@ VAddr Thread::GetCommandBufferAddress() const { //////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Gets the current thread + */ +Thread* GetCurrentThread() { + return Core::System::GetInstance().Scheduler().GetCurrentThread(); +} + void ThreadingInit() { ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); - - current_thread = nullptr; next_thread_id = 1; } void ThreadingShutdown() { - current_thread = nullptr; - - for (auto& t : thread_list) { - t->Stop(); - } - thread_list.clear(); - ready_queue.clear(); -} - -const std::vector<SharedPtr<Thread>>& GetThreadList() { - return thread_list; + Kernel::ClearProcessList(); } } // namespace Kernel diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 0a1ada27d..dbf47e269 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -38,8 +38,9 @@ enum ThreadProcessorId : s32 { enum ThreadStatus { THREADSTATUS_RUNNING, ///< Currently running THREADSTATUS_READY, ///< Ready to run - THREADSTATUS_WAIT_ARB, ///< Waiting on an address arbiter + THREADSTATUS_WAIT_HLE_EVENT, ///< Waiting for hle event to finish THREADSTATUS_WAIT_SLEEP, ///< Waiting due to a SleepThread SVC + THREADSTATUS_WAIT_IPC, ///< Waiting for the reply from an IPC request THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true THREADSTATUS_DORMANT, ///< Created but not yet made ready @@ -249,28 +250,6 @@ SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority, SharedPtr<Process> owner_process); /** - * Returns whether there are any threads that are ready to run. - */ -bool HaveReadyThreads(); - -/** - * Reschedules to the next available thread (call after current thread is suspended) - */ -void Reschedule(); - -/** - * Arbitrate the highest priority thread that is waiting - * @param address The address for which waiting threads should be arbitrated - */ -Thread* ArbitrateHighestPriorityThread(VAddr address); - -/** - * Arbitrate all threads currently waiting. - * @param address The address for which waiting threads should be arbitrated - */ -void ArbitrateAllThreads(VAddr address); - -/** * Gets the current thread */ Thread* GetCurrentThread(); @@ -301,9 +280,4 @@ void ThreadingInit(); */ void ThreadingShutdown(); -/** - * Get a const reference to the thread list for debug use - */ -const std::vector<SharedPtr<Thread>>& GetThreadList(); - } // namespace Kernel diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index d5b36d71a..1c2f873aa 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -18,8 +18,26 @@ namespace Kernel { static const char* GetMemoryStateName(MemoryState state) { static const char* names[] = { - "Free", "Reserved", "IO", "Static", "Code", "Private", - "Shared", "Continuous", "Aliased", "Alias", "AliasCode", "Locked", + "Unmapped", + "Io", + "Normal", + "CodeStatic", + "CodeMutable", + "Heap", + "Shared", + "Unknown1" + "ModuleCodeStatic", + "ModuleCodeMutable", + "IpcBuffer0", + "Mapped", + "ThreadLocal", + "TransferMemoryIsolated", + "TransferMemory", + "ProcessMemory", + "Unknown2" + "IpcBuffer1", + "IpcBuffer3", + "KernelStack", }; return names[(int)state]; @@ -142,7 +160,7 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) { VirtualMemoryArea& vma = vma_handle->second; vma.type = VMAType::Free; vma.permissions = VMAPermission::None; - vma.meminfo_state = MemoryState::Free; + vma.meminfo_state = MemoryState::Unmapped; vma.backing_block = nullptr; vma.offset = 0; @@ -166,6 +184,9 @@ ResultCode VMManager::UnmapRange(VAddr target, u64 size) { } ASSERT(FindVMA(target)->second.size >= size); + + Core::CPU().UnmapMemory(target, size); + return RESULT_SUCCESS; } @@ -377,19 +398,4 @@ u64 VMManager::GetAddressSpaceSize() { return MAX_ADDRESS; } -VAddr VMManager::GetMapRegionBaseAddr() { - LOG_WARNING(Kernel, "(STUBBED) called"); - return Memory::HEAP_VADDR; -} - -VAddr VMManager::GetNewMapRegionBaseAddr() { - LOG_WARNING(Kernel, "(STUBBED) called"); - return 0x8000000; -} - -u64 VMManager::GetNewMapRegionSize() { - LOG_WARNING(Kernel, "(STUBBED) called"); - return 0x8000000; -} - } // namespace Kernel diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 8de704a60..4d66146f6 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -41,15 +41,24 @@ enum class VMAPermission : u8 { /// Set of values returned in MemoryInfo.state by svcQueryMemory. enum class MemoryState : u32 { - Free = 0, - IO = 1, - Normal = 2, - Code = 3, - Static = 4, - Heap = 5, - Shared = 6, - Mapped = 6, - ThreadLocalStorage = 12, + Unmapped = 0x0, + Io = 0x1, + Normal = 0x2, + CodeStatic = 0x3, + CodeMutable = 0x4, + Heap = 0x5, + Shared = 0x6, + ModuleCodeStatic = 0x8, + ModuleCodeMutable = 0x9, + IpcBuffer0 = 0xA, + Mapped = 0xB, + ThreadLocal = 0xC, + TransferMemoryIsolated = 0xD, + TransferMemory = 0xE, + ProcessMemory = 0xF, + IpcBuffer1 = 0x11, + IpcBuffer3 = 0x12, + KernelStack = 0x13, }; /** @@ -66,7 +75,7 @@ struct VirtualMemoryArea { VMAType type = VMAType::Free; VMAPermission permissions = VMAPermission::None; /// Tag returned by svcQueryMemory. Not otherwise used. - MemoryState meminfo_state = MemoryState::Free; + MemoryState meminfo_state = MemoryState::Unmapped; // Settings for type = AllocatedMemoryBlock /// Memory block backing this VMA. @@ -192,15 +201,6 @@ public: /// Gets the total address space address size, used by svcGetInfo u64 GetAddressSpaceSize(); - /// Gets the map region base address, used by svcGetInfo - VAddr GetMapRegionBaseAddr(); - - /// Gets the base address for a new memory region, used by svcGetInfo - VAddr GetNewMapRegionBaseAddr(); - - /// Gets the size for a new memory region, used by svcGetInfo - u64 GetNewMapRegionSize(); - /// Each VMManager has its own page table, which is set as the main one when the owning process /// is scheduled. Memory::PageTable page_table; diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp index ec147b84c..b08ac72c1 100644 --- a/src/core/hle/kernel/wait_object.cpp +++ b/src/core/hle/kernel/wait_object.cpp @@ -39,7 +39,8 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { for (const auto& thread : waiting_threads) { // The list of waiting threads must not contain threads that are not waiting to be awakened. ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY || - thread->status == THREADSTATUS_WAIT_SYNCH_ALL, + thread->status == THREADSTATUS_WAIT_SYNCH_ALL || + thread->status == THREADSTATUS_WAIT_HLE_EVENT, "Inconsistent thread statuses in waiting_threads"); if (thread->current_priority >= candidate_priority) diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 656e1b4a7..97fef7a48 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -108,11 +108,11 @@ union ResultCode { } constexpr bool IsSuccess() const { - return is_error.ExtractValue(raw) == 0; + return raw == 0; } constexpr bool IsError() const { - return is_error.ExtractValue(raw) == 1; + return raw != 0; } }; diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index ee7d07aa7..52c3491d5 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp @@ -65,12 +65,19 @@ void ACC_U0::GetUserExistence(Kernel::HLERequestContext& ctx) { } void ACC_U0::ListAllUsers(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); constexpr std::array<u128, 10> user_ids{DEFAULT_USER_ID}; - const auto& output_buffer = ctx.BufferDescriptorC()[0]; - Memory::WriteBlock(output_buffer.Address(), user_ids.data(), user_ids.size()); + ctx.WriteBuffer(user_ids.data(), user_ids.size()); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void ACC_U0::ListOpenUsers(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + constexpr std::array<u128, 10> user_ids{DEFAULT_USER_ID}; + ctx.WriteBuffer(user_ids.data(), user_ids.size()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_ACC, "called"); } void ACC_U0::GetProfile(Kernel::HLERequestContext& ctx) { @@ -104,6 +111,7 @@ ACC_U0::ACC_U0() : ServiceFramework("acc:u0") { static const FunctionInfo functions[] = { {1, &ACC_U0::GetUserExistence, "GetUserExistence"}, {2, &ACC_U0::ListAllUsers, "ListAllUsers"}, + {3, &ACC_U0::ListOpenUsers, "ListOpenUsers"}, {4, &ACC_U0::GetLastOpenedUser, "GetLastOpenedUser"}, {5, &ACC_U0::GetProfile, "GetProfile"}, {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, diff --git a/src/core/hle/service/acc/acc_u0.h b/src/core/hle/service/acc/acc_u0.h index d7732e75b..222f37282 100644 --- a/src/core/hle/service/acc/acc_u0.h +++ b/src/core/hle/service/acc/acc_u0.h @@ -29,6 +29,7 @@ public: private: void GetUserExistence(Kernel::HLERequestContext& ctx); void ListAllUsers(Kernel::HLERequestContext& ctx); + void ListOpenUsers(Kernel::HLERequestContext& ctx); void GetLastOpenedUser(Kernel::HLERequestContext& ctx); void GetProfile(Kernel::HLERequestContext& ctx); void InitializeApplicationInfo(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 07cea8717..d9f003ed4 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -2,12 +2,15 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cinttypes> +#include "core/file_sys/filesystem.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" #include "core/hle/service/apm/apm.h" +#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/nvflinger/nvflinger.h" namespace Service { @@ -34,7 +37,38 @@ void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx) rb.Push(RESULT_SUCCESS); } -IAudioController::IAudioController() : ServiceFramework("IAudioController") {} +IAudioController::IAudioController() : ServiceFramework("IAudioController") { + static const FunctionInfo functions[] = { + {0, &IAudioController::SetExpectedMasterVolume, "SetExpectedMasterVolume"}, + {1, &IAudioController::GetMainAppletExpectedMasterVolume, + "GetMainAppletExpectedMasterVolume"}, + {2, &IAudioController::GetLibraryAppletExpectedMasterVolume, + "GetLibraryAppletExpectedMasterVolume"}, + {3, nullptr, "ChangeMainAppletMasterVolume"}, + {4, nullptr, "SetTransparentVolumeRate"}, + }; + RegisterHandlers(functions); +} + +void IAudioController::SetExpectedMasterVolume(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void IAudioController::GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(volume); +} + +void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(volume); +} IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {} @@ -46,6 +80,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger {1, &ISelfController::LockExit, "LockExit"}, {2, &ISelfController::UnlockExit, "UnlockExit"}, {9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"}, + {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"}, {11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"}, {12, &ISelfController::SetPerformanceModeChangedNotification, @@ -98,6 +133,13 @@ void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestCo LOG_WARNING(Service_AM, "(STUBBED) called flag=%u", static_cast<u32>(flag)); } +void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; @@ -306,11 +348,11 @@ private: u64 offset = rp.Pop<u64>(); - const auto& output_buffer = ctx.BufferDescriptorC()[0]; + const size_t size{ctx.GetWriteBufferSize()}; - ASSERT(offset + output_buffer.Size() <= buffer.size()); + ASSERT(offset + size <= buffer.size()); - Memory::WriteBlock(output_buffer.Address(), buffer.data() + offset, output_buffer.Size()); + ctx.WriteBuffer(buffer.data() + offset, size); IPC::ResponseBuilder rb{ctx, 2}; @@ -377,9 +419,25 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { } void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); + IPC::RequestParser rp{ctx}; + u128 uid = rp.PopRaw<u128>(); + + LOG_WARNING(Service, "(STUBBED) called uid = %016" PRIX64 "%016" PRIX64, uid[1], uid[0]); + + IPC::ResponseBuilder rb{ctx, 4}; + + FileSys::Path unused; + auto savedata = FileSystem::OpenFileSystem(FileSystem::Type::SaveData, unused); + if (savedata.Failed()) { + // Create the save data and return an error indicating that the operation was performed. + FileSystem::FormatFileSystem(FileSystem::Type::SaveData); + // TODO(Subv): Find out the correct error code for this. + rb.Push(ResultCode(ErrorModule::FS, 40)); + } else { + rb.Push(RESULT_SUCCESS); + } + + rb.Push<u64>(0); } void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 793ac6555..27dbd8c95 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -36,6 +36,13 @@ private: class IAudioController final : public ServiceFramework<IAudioController> { public: IAudioController(); + +private: + void SetExpectedMasterVolume(Kernel::HLERequestContext& ctx); + void GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx); + void GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx); + + u32 volume{100}; }; class IDisplayController final : public ServiceFramework<IDisplayController> { @@ -62,6 +69,7 @@ private: void UnlockExit(Kernel::HLERequestContext& ctx); void GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx); void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx); + void SetScreenShotPermission(Kernel::HLERequestContext& ctx); std::shared_ptr<NVFlinger::NVFlinger> nvflinger; Kernel::SharedPtr<Kernel::Event> launchable_event; diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 260683201..8b55d2fcb 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -2,16 +2,44 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/service/aoc/aoc_u.h" namespace Service { namespace AOC { +AOC_U::AOC_U() : ServiceFramework("aoc:u") { + static const FunctionInfo functions[] = { + {0, nullptr, "CountAddOnContentByApplicationId"}, + {1, nullptr, "ListAddOnContentByApplicationId"}, + {2, &AOC_U::CountAddOnContent, "CountAddOnContent"}, + {3, &AOC_U::ListAddOnContent, "ListAddOnContent"}, + {4, nullptr, "GetAddOnContentBaseIdByApplicationId"}, + {5, nullptr, "GetAddOnContentBaseId"}, + {6, nullptr, "PrepareAddOnContentByApplicationId"}, + {7, nullptr, "PrepareAddOnContent"}, + }; + RegisterHandlers(functions); +} + +void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(0); + LOG_WARNING(Service_AOC, "(STUBBED) called"); +} + +void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(0); + LOG_WARNING(Service_AOC, "(STUBBED) called"); +} + void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<AOC_U>()->InstallAsService(service_manager); } -AOC_U::AOC_U() : ServiceFramework("aoc:u") {} - } // namespace AOC } // namespace Service diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h index 0cbbf1e5d..6e0ba15a5 100644 --- a/src/core/hle/service/aoc/aoc_u.h +++ b/src/core/hle/service/aoc/aoc_u.h @@ -13,6 +13,10 @@ class AOC_U final : public ServiceFramework<AOC_U> { public: AOC_U(); ~AOC_U() = default; + +private: + void CountAddOnContent(Kernel::HLERequestContext& ctx); + void ListAddOnContent(Kernel::HLERequestContext& ctx); }; /// Registers all AOC services with the specified service manager. diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index f56ba2ea1..e873d768f 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -52,7 +52,9 @@ public: CoreTiming::ScheduleEvent(audio_ticks, audio_event); } - ~IAudioOut() = default; + ~IAudioOut() { + CoreTiming::UnscheduleEvent(audio_event, 0); + } private: void StartAudioOut(Kernel::HLERequestContext& ctx) { @@ -99,8 +101,6 @@ private: void GetReleasedAudioOutBuffer_1(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_Audio, "(STUBBED) called"); - const auto& buffer = ctx.BufferDescriptorB()[0]; - // TODO(st4rk): This is how libtransistor currently implements the // GetReleasedAudioOutBuffer, it should return the key (a VAddr) to the app and this address // is used to know which buffer should be filled with data and send again to the service @@ -112,7 +112,7 @@ private: queue_keys.pop_back(); } - Memory::WriteBlock(buffer.Address(), &key, sizeof(u64)); + ctx.WriteBuffer(&key, sizeof(u64)); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); @@ -158,10 +158,8 @@ void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_Audio, "(STUBBED) called"); IPC::RequestParser rp{ctx}; - auto& buffer = ctx.BufferDescriptorB()[0]; const std::string audio_interface = "AudioInterface"; - - Memory::WriteBlock(buffer.Address(), &audio_interface[0], audio_interface.size()); + ctx.WriteBuffer(audio_interface.c_str(), audio_interface.size()); IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0); diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index c8d8ba748..1cbca6c4b 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -45,7 +45,9 @@ public: // Start the audio event CoreTiming::ScheduleEvent(audio_ticks, audio_event); } - ~IAudioRenderer() = default; + ~IAudioRenderer() { + CoreTiming::UnscheduleEvent(audio_event, 0); + } private: void UpdateAudioCallback() { @@ -53,7 +55,8 @@ private: } void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) { - AudioRendererResponseData response_data = {0}; + LOG_DEBUG(Service_Audio, "%s", ctx.Description().c_str()); + AudioRendererResponseData response_data{}; response_data.section_0_size = response_data.state_entries.size() * sizeof(AudioRendererStateEntry); @@ -69,9 +72,7 @@ private: response_data.state_entries[i].state = 5; } - auto& buffer = ctx.BufferDescriptorB()[0]; - - Memory::WriteBlock(buffer.Address(), &response_data, response_data.total_size); + ctx.WriteBuffer(&response_data, response_data.total_size); IPC::ResponseBuilder rb{ctx, 2}; @@ -179,10 +180,10 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { } void AudRenU::GetAudioRenderersProcessMasterVolume(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 2}; + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - + rb.Push<u32>(100); LOG_WARNING(Service_Audio, "(STUBBED) called"); } diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 4b47548fd..ef05955b9 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -3,7 +3,9 @@ // Refer to the license.txt file included. #include <boost/container/flat_map.hpp> +#include "common/file_util.h" #include "core/file_sys/filesystem.h" +#include "core/file_sys/savedata_factory.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/fsp_srv.h" @@ -41,12 +43,30 @@ ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, return itr->second->Open(path); } -void UnregisterFileSystems() { +ResultCode FormatFileSystem(Type type) { + LOG_TRACE(Service_FS, "Formatting FileSystem with type=%d", type); + + auto itr = filesystem_map.find(type); + if (itr == filesystem_map.end()) { + // TODO(bunnei): Find a better error code for this + return ResultCode(-1); + } + + FileSys::Path unused; + return itr->second->Format(unused); +} + +void RegisterFileSystems() { filesystem_map.clear(); + + std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX); + + auto savedata = std::make_unique<FileSys::SaveData_Factory>(std::move(nand_directory)); + RegisterFileSystem(std::move(savedata), Type::SaveData); } void InstallInterfaces(SM::ServiceManager& service_manager) { - UnregisterFileSystems(); + RegisterFileSystems(); std::make_shared<FSP_SRV>()->InstallAsService(service_manager); } diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index a674c9493..8d30e94a1 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -25,6 +25,7 @@ namespace FileSystem { /// Supported FileSystem types enum class Type { RomFS = 1, + SaveData = 2, }; /** @@ -43,6 +44,13 @@ ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& fact ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, FileSys::Path& path); +/** + * Formats a file system + * @param type Type of the file system to format + * @return ResultCode of the operation + */ +ResultCode FormatFileSystem(Type type); + /// Registers all Filesystem services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager); diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 34d4fd035..97b3fa290 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cinttypes> #include "common/logging/log.h" #include "core/core.h" #include "core/file_sys/filesystem.h" @@ -33,12 +34,10 @@ private: IPC::RequestParser rp{ctx}; const s64 offset = rp.Pop<s64>(); const s64 length = rp.Pop<s64>(); - const auto& descriptor = ctx.BufferDescriptorB()[0]; LOG_DEBUG(Service_FS, "called, offset=0x%llx, length=0x%llx", offset, length); // Error checking - ASSERT_MSG(length == descriptor.Size(), "unexpected size difference"); if (length < 0) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); @@ -60,17 +59,194 @@ private: } // Write the data to memory - Memory::WriteBlock(descriptor.Address(), output.data(), descriptor.Size()); + ctx.WriteBuffer(output); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } }; +class IFile final : public ServiceFramework<IFile> { +public: + explicit IFile(std::unique_ptr<FileSys::StorageBackend>&& backend) + : ServiceFramework("IFile"), backend(std::move(backend)) { + static const FunctionInfo functions[] = { + {0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"}, {2, nullptr, "Flush"}, + {3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, + }; + RegisterHandlers(functions); + } + +private: + std::unique_ptr<FileSys::StorageBackend> backend; + + void Read(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 unk = rp.Pop<u64>(); + const s64 offset = rp.Pop<s64>(); + const s64 length = rp.Pop<s64>(); + + LOG_DEBUG(Service_FS, "called, offset=0x%llx, length=0x%llx", offset, length); + + // Error checking + if (length < 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); + return; + } + if (offset < 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); + return; + } + + // Read the data from the Storage backend + std::vector<u8> output(length); + ResultVal<size_t> res = backend->Read(offset, length, output.data()); + if (res.Failed()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res.Code()); + return; + } + + // Write the data to memory + ctx.WriteBuffer(output); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast<u64>(*res)); + } + + void Write(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 unk = rp.Pop<u64>(); + const s64 offset = rp.Pop<s64>(); + const s64 length = rp.Pop<s64>(); + + LOG_DEBUG(Service_FS, "called, offset=0x%llx, length=0x%llx", offset, length); + + // Error checking + if (length < 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); + return; + } + if (offset < 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); + return; + } + + // Write the data to the Storage backend + std::vector<u8> data = ctx.ReadBuffer(); + ResultVal<size_t> res = backend->Write(offset, length, true, data.data()); + if (res.Failed()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res.Code()); + return; + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } +}; + +class IFileSystem final : public ServiceFramework<IFileSystem> { +public: + explicit IFileSystem(std::unique_ptr<FileSys::FileSystemBackend>&& backend) + : ServiceFramework("IFileSystem"), backend(std::move(backend)) { + static const FunctionInfo functions[] = { + {0, &IFileSystem::CreateFile, "CreateFile"}, + {7, &IFileSystem::GetEntryType, "GetEntryType"}, + {8, &IFileSystem::OpenFile, "OpenFile"}, + {10, &IFileSystem::Commit, "Commit"}, + }; + RegisterHandlers(functions); + } + + void CreateFile(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto file_buffer = ctx.ReadBuffer(); + auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); + + std::string name(file_buffer.begin(), end); + + u64 mode = rp.Pop<u64>(); + u32 size = rp.Pop<u32>(); + + LOG_DEBUG(Service_FS, "called file %s mode 0x%" PRIX64 " size 0x%08X", name.c_str(), mode, + size); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend->CreateFile(name, size)); + } + + void OpenFile(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto file_buffer = ctx.ReadBuffer(); + auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); + + std::string name(file_buffer.begin(), end); + + auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>()); + + LOG_DEBUG(Service_FS, "called file %s mode %u", name.c_str(), static_cast<u32>(mode)); + + auto result = backend->OpenFile(name, mode); + if (result.Failed()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result.Code()); + return; + } + + auto file = std::move(result.Unwrap()); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IFile>(std::move(file)); + } + + void GetEntryType(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto file_buffer = ctx.ReadBuffer(); + auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); + + std::string name(file_buffer.begin(), end); + + LOG_DEBUG(Service_FS, "called file %s", name.c_str()); + + auto result = backend->GetEntryType(name); + if (result.Failed()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result.Code()); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(static_cast<u32>(*result)); + } + + void Commit(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + +private: + std::unique_ptr<FileSys::FileSystemBackend> backend; +}; + FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { static const FunctionInfo functions[] = { {1, &FSP_SRV::Initalize, "Initalize"}, {18, &FSP_SRV::MountSdCard, "MountSdCard"}, + {22, &FSP_SRV::CreateSaveData, "CreateSaveData"}, + {51, &FSP_SRV::MountSaveData, "MountSaveData"}, {200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"}, {202, nullptr, "OpenDataStorageByDataId"}, {203, &FSP_SRV::OpenRomStorage, "OpenRomStorage"}, @@ -104,6 +280,30 @@ void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } +void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto save_struct = rp.PopRaw<std::array<u8, 0x40>>(); + auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); + u128 uid = rp.PopRaw<u128>(); + + LOG_WARNING(Service_FS, "(STUBBED) called uid = %016" PRIX64 "%016" PRIX64, uid[1], uid[0]); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called"); + + FileSys::Path unused; + auto filesystem = OpenFileSystem(Type::SaveData, unused).Unwrap(); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IFileSystem>(std::move(filesystem)); +} + void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_FS, "(STUBBED) called"); diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index 56afc4b90..e15ba4375 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h @@ -24,6 +24,8 @@ private: void Initalize(Kernel::HLERequestContext& ctx); void MountSdCard(Kernel::HLERequestContext& ctx); + void CreateSaveData(Kernel::HLERequestContext& ctx); + void MountSaveData(Kernel::HLERequestContext& ctx); void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); void OpenRomStorage(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp new file mode 100644 index 000000000..26593bb0c --- /dev/null +++ b/src/core/hle/service/friend/friend.cpp @@ -0,0 +1,28 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/friend/friend.h" +#include "core/hle/service/friend/friend_a.h" + +namespace Service { +namespace Friend { + +void Module::Interface::Unknown(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_Friend, "(STUBBED) called"); +} + +Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) + : ServiceFramework(name), module(std::move(module)) {} + +void InstallInterfaces(SM::ServiceManager& service_manager) { + auto module = std::make_shared<Module>(); + std::make_shared<Friend_A>(module)->InstallAsService(service_manager); +} + +} // namespace Friend +} // namespace Service diff --git a/src/core/hle/service/friend/friend.h b/src/core/hle/service/friend/friend.h new file mode 100644 index 000000000..ffa498397 --- /dev/null +++ b/src/core/hle/service/friend/friend.h @@ -0,0 +1,29 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Friend { + +class Module final { +public: + class Interface : public ServiceFramework<Interface> { + public: + Interface(std::shared_ptr<Module> module, const char* name); + + void Unknown(Kernel::HLERequestContext& ctx); + + protected: + std::shared_ptr<Module> module; + }; +}; + +/// Registers all Friend services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace Friend +} // namespace Service diff --git a/src/core/hle/service/friend/friend_a.cpp b/src/core/hle/service/friend/friend_a.cpp new file mode 100644 index 000000000..e1f2397c2 --- /dev/null +++ b/src/core/hle/service/friend/friend_a.cpp @@ -0,0 +1,19 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/friend/friend_a.h" + +namespace Service { +namespace Friend { + +Friend_A::Friend_A(std::shared_ptr<Module> module) + : Module::Interface(std::move(module), "friend:a") { + static const FunctionInfo functions[] = { + {0, &Friend_A::Unknown, "Unknown"}, + }; + RegisterHandlers(functions); +} + +} // namespace Friend +} // namespace Service diff --git a/src/core/hle/service/friend/friend_a.h b/src/core/hle/service/friend/friend_a.h new file mode 100644 index 000000000..68fa58297 --- /dev/null +++ b/src/core/hle/service/friend/friend_a.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/friend/friend.h" + +namespace Service { +namespace Friend { + +class Friend_A final : public Module::Interface { +public: + explicit Friend_A(std::shared_ptr<Module> module); +}; + +} // namespace Friend +} // namespace Service diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index d757d2eae..7e04ad8d4 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -45,6 +45,10 @@ public: CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event); } + ~IAppletResource() { + CoreTiming::UnscheduleEvent(pad_update_event, 0); + } + private: void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 1}; @@ -176,7 +180,10 @@ public: {0, &Hid::CreateAppletResource, "CreateAppletResource"}, {1, &Hid::ActivateDebugPad, "ActivateDebugPad"}, {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"}, + {21, &Hid::ActivateMouse, "ActivateMouse"}, + {31, &Hid::ActivateKeyboard, "ActivateKeyboard"}, {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, + {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"}, {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"}, {103, &Hid::ActivateNpad, "ActivateNpad"}, @@ -184,9 +191,15 @@ public: "AcquireNpadStyleSetUpdateEventHandle"}, {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"}, {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"}, + {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, + "SetNpadJoyAssignmentModeSingleByDefault"}, {124, nullptr, "SetNpadJoyAssignmentModeDual"}, {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"}, + {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"}, + {201, &Hid::SendVibrationValue, "SendVibrationValue"}, + {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"}, {203, &Hid::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"}, + {206, &Hid::SendVibrationValues, "SendVibrationValues"}, }; RegisterHandlers(functions); @@ -222,12 +235,30 @@ private: LOG_WARNING(Service_HID, "(STUBBED) called"); } + void ActivateMouse(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void ActivateKeyboard(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + void StartSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_HID, "(STUBBED) called"); } + void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -266,18 +297,49 @@ private: LOG_WARNING(Service_HID, "(STUBBED) called"); } + void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void SendVibrationValue(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void GetActualVibrationValue(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_HID, "(STUBBED) called"); } + void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(0); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IActiveVibrationDeviceList>(); LOG_DEBUG(Service_HID, "called"); } + + void SendVibrationValues(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } }; void ReloadInputDevices() {} diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 290a2ee74..dd2d5fe63 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/event.h" #include "core/hle/service/nifm/nifm.h" #include "core/hle/service/nifm/nifm_a.h" #include "core/hle/service/nifm/nifm_s.h" @@ -28,10 +29,10 @@ class IRequest final : public ServiceFramework<IRequest> { public: explicit IRequest() : ServiceFramework("IRequest") { static const FunctionInfo functions[] = { - {0, nullptr, "GetRequestState"}, - {1, nullptr, "GetResult"}, - {2, nullptr, "GetSystemEventReadableHandles"}, - {3, nullptr, "Cancel"}, + {0, &IRequest::GetRequestState, "GetRequestState"}, + {1, &IRequest::GetResult, "GetResult"}, + {2, &IRequest::GetSystemEventReadableHandles, "GetSystemEventReadableHandles"}, + {3, &IRequest::Cancel, "Cancel"}, {4, nullptr, "Submit"}, {5, nullptr, "SetRequirement"}, {6, nullptr, "SetRequirementPreset"}, @@ -55,7 +56,37 @@ public: {25, nullptr, "UnregisterSocketDescriptor"}, }; RegisterHandlers(functions); + + event1 = Kernel::Event::Create(Kernel::ResetType::OneShot, "IRequest:Event1"); + event2 = Kernel::Event::Create(Kernel::ResetType::OneShot, "IRequest:Event2"); + } + +private: + void GetRequestState(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); + } + void GetResult(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); + } + void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2, 2}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(event1, event2); } + void Cancel(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + Kernel::SharedPtr<Kernel::Event> event1, event2; }; class INetworkProfile final : public ServiceFramework<INetworkProfile> { @@ -70,13 +101,56 @@ public: } }; +class IGeneralService final : public ServiceFramework<IGeneralService> { +public: + IGeneralService(); + +private: + void GetClientId(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(0); + } + void CreateScanRequest(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IScanRequest>(); + + LOG_DEBUG(Service_NIFM, "called"); + } + void CreateRequest(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IRequest>(); + + LOG_DEBUG(Service_NIFM, "called"); + } + void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<INetworkProfile>(); + + LOG_DEBUG(Service_NIFM, "called"); + } +}; + IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") { static const FunctionInfo functions[] = { {1, &IGeneralService::GetClientId, "GetClientId"}, {2, &IGeneralService::CreateScanRequest, "CreateScanRequest"}, {4, &IGeneralService::CreateRequest, "CreateRequest"}, - {6, nullptr, "GetCurrentNetworkProfile"}, - {7, nullptr, "EnumerateNetworkInterfaces"}, + {5, nullptr, "GetCurrentNetworkProfile"}, + {6, nullptr, "EnumerateNetworkInterfaces"}, + {7, nullptr, "EnumerateNetworkProfiles"}, {8, nullptr, "GetNetworkProfile"}, {9, nullptr, "SetNetworkProfile"}, {10, &IGeneralService::RemoveNetworkProfile, "RemoveNetworkProfile"}, @@ -111,50 +185,28 @@ IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") { RegisterHandlers(functions); } -void IGeneralService::GetClientId(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(RESULT_SUCCESS); - rb.Push<u64>(0); -} - -void IGeneralService::CreateScanRequest(Kernel::HLERequestContext& ctx) { +void Module::Interface::CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IScanRequest>(); - + rb.PushIpcInterface<IGeneralService>(); LOG_DEBUG(Service_NIFM, "called"); } -void IGeneralService::CreateRequest(Kernel::HLERequestContext& ctx) { +void Module::Interface::CreateGeneralService(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IRequest>(); - + rb.PushIpcInterface<IGeneralService>(); LOG_DEBUG(Service_NIFM, "called"); } -void IGeneralService::RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); -} - -void IGeneralService::CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<INetworkProfile>(); - - LOG_DEBUG(Service_NIFM, "called"); -} +Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) + : ServiceFramework(name), module(std::move(module)) {} void InstallInterfaces(SM::ServiceManager& service_manager) { - std::make_shared<NIFM_A>()->InstallAsService(service_manager); - std::make_shared<NIFM_S>()->InstallAsService(service_manager); - std::make_shared<NIFM_U>()->InstallAsService(service_manager); + auto module = std::make_shared<Module>(); + std::make_shared<NIFM_A>(module)->InstallAsService(service_manager); + std::make_shared<NIFM_S>(module)->InstallAsService(service_manager); + std::make_shared<NIFM_U>(module)->InstallAsService(service_manager); } } // namespace NIFM diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h index 6edbfe4a4..11d263b12 100644 --- a/src/core/hle/service/nifm/nifm.h +++ b/src/core/hle/service/nifm/nifm.h @@ -9,16 +9,18 @@ namespace Service { namespace NIFM { -class IGeneralService final : public ServiceFramework<IGeneralService> { +class Module final { public: - IGeneralService(); - -private: - void GetClientId(Kernel::HLERequestContext& ctx); - void CreateScanRequest(Kernel::HLERequestContext& ctx); - void CreateRequest(Kernel::HLERequestContext& ctx); - void RemoveNetworkProfile(Kernel::HLERequestContext& ctx); - void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx); + class Interface : public ServiceFramework<Interface> { + public: + Interface(std::shared_ptr<Module> module, const char* name); + + void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx); + void CreateGeneralService(Kernel::HLERequestContext& ctx); + + protected: + std::shared_ptr<Module> module; + }; }; void InstallInterfaces(SM::ServiceManager& service_manager); diff --git a/src/core/hle/service/nifm/nifm_a.cpp b/src/core/hle/service/nifm/nifm_a.cpp index ee61d8ff4..f75df8c04 100644 --- a/src/core/hle/service/nifm/nifm_a.cpp +++ b/src/core/hle/service/nifm/nifm_a.cpp @@ -2,29 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/logging/log.h" -#include "core/hle/ipc_helpers.h" -#include "core/hle/service/nifm/nifm.h" #include "core/hle/service/nifm/nifm_a.h" namespace Service { namespace NIFM { -void NIFM_A::CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IGeneralService>(); - LOG_DEBUG(Service_NIFM, "called"); -} - -void NIFM_A::CreateGeneralService(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IGeneralService>(); - LOG_DEBUG(Service_NIFM, "called"); -} - -NIFM_A::NIFM_A() : ServiceFramework("nifm:a") { +NIFM_A::NIFM_A(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:a") { static const FunctionInfo functions[] = { {4, &NIFM_A::CreateGeneralServiceOld, "CreateGeneralServiceOld"}, {5, &NIFM_A::CreateGeneralService, "CreateGeneralService"}, diff --git a/src/core/hle/service/nifm/nifm_a.h b/src/core/hle/service/nifm/nifm_a.h index 06a92a93c..eaea14e29 100644 --- a/src/core/hle/service/nifm/nifm_a.h +++ b/src/core/hle/service/nifm/nifm_a.h @@ -4,20 +4,14 @@ #pragma once -#include "core/hle/kernel/hle_ipc.h" -#include "core/hle/service/service.h" +#include "core/hle/service/nifm/nifm.h" namespace Service { namespace NIFM { -class NIFM_A final : public ServiceFramework<NIFM_A> { +class NIFM_A final : public Module::Interface { public: - NIFM_A(); - ~NIFM_A() = default; - -private: - void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx); - void CreateGeneralService(Kernel::HLERequestContext& ctx); + explicit NIFM_A(std::shared_ptr<Module> module); }; } // namespace NIFM diff --git a/src/core/hle/service/nifm/nifm_s.cpp b/src/core/hle/service/nifm/nifm_s.cpp index c38b2a4c7..9c0b300e4 100644 --- a/src/core/hle/service/nifm/nifm_s.cpp +++ b/src/core/hle/service/nifm/nifm_s.cpp @@ -2,29 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/logging/log.h" -#include "core/hle/ipc_helpers.h" -#include "core/hle/service/nifm/nifm.h" #include "core/hle/service/nifm/nifm_s.h" namespace Service { namespace NIFM { -void NIFM_S::CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IGeneralService>(); - LOG_DEBUG(Service_NIFM, "called"); -} - -void NIFM_S::CreateGeneralService(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IGeneralService>(); - LOG_DEBUG(Service_NIFM, "called"); -} - -NIFM_S::NIFM_S() : ServiceFramework("nifm:s") { +NIFM_S::NIFM_S(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:s") { static const FunctionInfo functions[] = { {4, &NIFM_S::CreateGeneralServiceOld, "CreateGeneralServiceOld"}, {5, &NIFM_S::CreateGeneralService, "CreateGeneralService"}, diff --git a/src/core/hle/service/nifm/nifm_s.h b/src/core/hle/service/nifm/nifm_s.h index d11a1ec29..f9e2d8039 100644 --- a/src/core/hle/service/nifm/nifm_s.h +++ b/src/core/hle/service/nifm/nifm_s.h @@ -4,20 +4,14 @@ #pragma once -#include "core/hle/kernel/hle_ipc.h" -#include "core/hle/service/service.h" +#include "core/hle/service/nifm/nifm.h" namespace Service { namespace NIFM { -class NIFM_S final : public ServiceFramework<NIFM_S> { +class NIFM_S final : public Module::Interface { public: - NIFM_S(); - ~NIFM_S() = default; - -private: - void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx); - void CreateGeneralService(Kernel::HLERequestContext& ctx); + explicit NIFM_S(std::shared_ptr<Module> module); }; } // namespace NIFM diff --git a/src/core/hle/service/nifm/nifm_u.cpp b/src/core/hle/service/nifm/nifm_u.cpp index a5895c13c..44e6f483d 100644 --- a/src/core/hle/service/nifm/nifm_u.cpp +++ b/src/core/hle/service/nifm/nifm_u.cpp @@ -2,29 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/logging/log.h" -#include "core/hle/ipc_helpers.h" -#include "core/hle/service/nifm/nifm.h" #include "core/hle/service/nifm/nifm_u.h" namespace Service { namespace NIFM { -void NIFM_U::CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IGeneralService>(); - LOG_DEBUG(Service_NIFM, "called"); -} - -void NIFM_U::CreateGeneralService(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IGeneralService>(); - LOG_DEBUG(Service_NIFM, "called"); -} - -NIFM_U::NIFM_U() : ServiceFramework("nifm:u") { +NIFM_U::NIFM_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:u") { static const FunctionInfo functions[] = { {4, &NIFM_U::CreateGeneralServiceOld, "CreateGeneralServiceOld"}, {5, &NIFM_U::CreateGeneralService, "CreateGeneralService"}, diff --git a/src/core/hle/service/nifm/nifm_u.h b/src/core/hle/service/nifm/nifm_u.h index da40b604f..912006775 100644 --- a/src/core/hle/service/nifm/nifm_u.h +++ b/src/core/hle/service/nifm/nifm_u.h @@ -4,20 +4,14 @@ #pragma once -#include "core/hle/kernel/hle_ipc.h" -#include "core/hle/service/service.h" +#include "core/hle/service/nifm/nifm.h" namespace Service { namespace NIFM { -class NIFM_U final : public ServiceFramework<NIFM_U> { +class NIFM_U final : public Module::Interface { public: - NIFM_U(); - ~NIFM_U() = default; - -private: - void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx); - void CreateGeneralService(Kernel::HLERequestContext& ctx); + explicit NIFM_U(std::shared_ptr<Module> module); }; } // namespace NIFM diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp new file mode 100644 index 000000000..45681c50f --- /dev/null +++ b/src/core/hle/service/ns/ns.cpp @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/ns/ns.h" +#include "core/hle/service/ns/pl_u.h" + +namespace Service { +namespace NS { + +void InstallInterfaces(SM::ServiceManager& service_manager) { + std::make_shared<PL_U>()->InstallAsService(service_manager); +} + +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h new file mode 100644 index 000000000..a4b7e3ded --- /dev/null +++ b/src/core/hle/service/ns/ns.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace NS { + +/// Registers all NS services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp new file mode 100644 index 000000000..695e295ca --- /dev/null +++ b/src/core/hle/service/ns/pl_u.cpp @@ -0,0 +1,112 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/common_paths.h" +#include "common/file_util.h" +#include "core/core.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/ns/pl_u.h" + +namespace Service { +namespace NS { + +struct FontRegion { + u32 offset; + u32 size; +}; + +// The below data is specific to shared font data dumped from Switch on f/w 2.2 +// Virtual address and offsets/sizes likely will vary by dump +static constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL}; +static constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000}; +static constexpr std::array<FontRegion, 6> SHARED_FONT_REGIONS{ + FontRegion{0x00000008, 0x001fe764}, FontRegion{0x001fe774, 0x00773e58}, + FontRegion{0x009725d4, 0x0001aca8}, FontRegion{0x0098d284, 0x00369cec}, + FontRegion{0x00cf6f78, 0x0039b858}, FontRegion{0x010927d8, 0x00019e80}, +}; + +enum class LoadState : u32 { + Loading = 0, + Done = 1, +}; + +PL_U::PL_U() : ServiceFramework("pl:u") { + static const FunctionInfo functions[] = { + {1, &PL_U::GetLoadState, "GetLoadState"}, + {2, &PL_U::GetSize, "GetSize"}, + {3, &PL_U::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"}, + {4, &PL_U::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}}; + RegisterHandlers(functions); + + // Attempt to load shared font data from disk + const std::string filepath{FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT}; + FileUtil::CreateFullPath(filepath); // Create path if not already created + FileUtil::IOFile file(filepath, "rb"); + + if (file.IsOpen()) { + // Read shared font data + ASSERT(file.GetSize() == SHARED_FONT_MEM_SIZE); + shared_font = std::make_shared<std::vector<u8>>(static_cast<size_t>(file.GetSize())); + file.ReadBytes(shared_font->data(), shared_font->size()); + } else { + LOG_WARNING(Service_NS, "Unable to load shared font: %s", filepath.c_str()); + } +} + +void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u32 font_id{rp.Pop<u32>()}; + + LOG_DEBUG(Service_NS, "called, font_id=%d", font_id); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(static_cast<u32>(LoadState::Done)); +} + +void PL_U::GetSize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u32 font_id{rp.Pop<u32>()}; + + LOG_DEBUG(Service_NS, "called, font_id=%d", font_id); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(SHARED_FONT_REGIONS[font_id].size); +} + +void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u32 font_id{rp.Pop<u32>()}; + + LOG_DEBUG(Service_NS, "called, font_id=%d", font_id); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(SHARED_FONT_REGIONS[font_id].offset); +} + +void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { + if (shared_font != nullptr) { + // TODO(bunnei): This is a less-than-ideal solution to load a RAM dump of the Switch shared + // font data. This (likely) relies on exact address, size, and offsets from the original + // dump. In the future, we need to replace this with a more robust solution. + + // Map backing memory for the font data + Core::CurrentProcess()->vm_manager.MapMemoryBlock(SHARED_FONT_MEM_VADDR, shared_font, 0, + SHARED_FONT_MEM_SIZE, + Kernel::MemoryState::Shared); + + // Create shared font memory object + shared_font_mem = Kernel::SharedMemory::Create( + Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite, + Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE, + "PL_U:shared_font_mem"); + } + + LOG_DEBUG(Service_NS, "called"); + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(shared_font_mem); +} + +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/ns/pl_u.h b/src/core/hle/service/ns/pl_u.h new file mode 100644 index 000000000..7a4766338 --- /dev/null +++ b/src/core/hle/service/ns/pl_u.h @@ -0,0 +1,33 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include "core/hle/kernel/shared_memory.h" +#include "core/hle/service/service.h" + +namespace Service { +namespace NS { + +class PL_U final : public ServiceFramework<PL_U> { +public: + PL_U(); + ~PL_U() = default; + +private: + void GetLoadState(Kernel::HLERequestContext& ctx); + void GetSize(Kernel::HLERequestContext& ctx); + void GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx); + void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx); + + /// Handle to shared memory region designated for a shared font + Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem; + + /// Backing memory for the shared font data + std::shared_ptr<std::vector<u8>> shared_font; +}; + +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index ee99ab280..45711d686 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -17,6 +17,8 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector< switch (static_cast<IoctlCommand>(command.raw)) { case IoctlCommand::IocGetConfigCommand: return NvOsGetConfigU32(input, output); + case IoctlCommand::IocCtrlEventWaitCommand: + return IocCtrlEventWait(input, output); } UNIMPLEMENTED(); return 0; @@ -45,6 +47,18 @@ u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& return 0; } +u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output) { + IocCtrlEventWaitParams params{}; + std::memcpy(¶ms, input.data(), sizeof(params)); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, syncpt_id=%u threshold=%u timeout=%d", + params.syncpt_id, params.threshold, params.timeout); + + // TODO(Subv): Implement actual syncpt waiting. + params.value = 0; + std::memcpy(output.data(), ¶ms, sizeof(params)); + return 0; +} + } // namespace Devices } // namespace Nvidia } // namespace Service diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index fd02a5e45..0ca01aa6d 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -31,6 +31,7 @@ private: IocModuleRegRDWRCommand = 0xC008010E, IocSyncptWaitexCommand = 0xC0100019, IocSyncptReadMaxCommand = 0xC008001A, + IocCtrlEventWaitCommand = 0xC010001D, IocGetConfigCommand = 0xC183001B, }; @@ -41,7 +42,17 @@ private: }; static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size"); + struct IocCtrlEventWaitParams { + u32_le syncpt_id; + u32_le threshold; + s32_le timeout; + u32_le value; + }; + static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size"); + u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); + + u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output); }; } // namespace Devices diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index cd8c0c605..b3842eb4c 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -103,11 +103,8 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) { [&](const auto& entry) { return entry.second->id == params.id; }); ASSERT(itr != handles.end()); - // Make a new handle for the object - u32 handle = next_handle++; - handles[handle] = itr->second; - - params.handle = handle; + // Return the existing handle instead of creating a new one. + params.handle = itr->first; std::memcpy(output.data(), ¶ms, sizeof(params)); return 0; diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp index 1a5efaeaf..c70370f1f 100644 --- a/src/core/hle/service/nvdrv/interface.cpp +++ b/src/core/hle/service/nvdrv/interface.cpp @@ -15,9 +15,8 @@ namespace Nvidia { void NVDRV::Open(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NVDRV, "called"); - auto buffer = ctx.BufferDescriptorA()[0]; - - std::string device_name = Memory::ReadCString(buffer.Address(), buffer.Size()); + const auto& buffer = ctx.ReadBuffer(); + std::string device_name(buffer.begin(), buffer.end()); u32 fd = nvdrv->Open(device_name); IPC::ResponseBuilder rb{ctx, 4}; @@ -33,25 +32,13 @@ void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) { u32 fd = rp.Pop<u32>(); u32 command = rp.Pop<u32>(); + std::vector<u8> output(ctx.GetWriteBufferSize()); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - if (ctx.BufferDescriptorA()[0].Size() != 0) { - auto input_buffer = ctx.BufferDescriptorA()[0]; - auto output_buffer = ctx.BufferDescriptorB()[0]; - std::vector<u8> input(input_buffer.Size()); - std::vector<u8> output(output_buffer.Size()); - Memory::ReadBlock(input_buffer.Address(), input.data(), input_buffer.Size()); - rb.Push(nvdrv->Ioctl(fd, command, input, output)); - Memory::WriteBlock(output_buffer.Address(), output.data(), output_buffer.Size()); - } else { - auto input_buffer = ctx.BufferDescriptorX()[0]; - auto output_buffer = ctx.BufferDescriptorC()[0]; - std::vector<u8> input(input_buffer.size); - std::vector<u8> output(output_buffer.size); - Memory::ReadBlock(input_buffer.Address(), input.data(), input_buffer.size); - rb.Push(nvdrv->Ioctl(fd, command, input, output)); - Memory::WriteBlock(output_buffer.Address(), output.data(), output_buffer.size); - } + rb.Push(nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output)); + + ctx.WriteBuffer(output); } void NVDRV::Close(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index e44644624..6a55ff96d 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -17,6 +17,13 @@ namespace Devices { class nvdevice; } +struct IoctlFence { + u32 id; + u32 value; +}; + +static_assert(sizeof(IoctlFence) == 8, "IoctlFence has wrong size"); + class Module final { public: Module(); diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index ff7b6b039..e4ff2e267 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp @@ -26,26 +26,30 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) { LOG_WARNING(Service, "Adding graphics buffer %u", slot); queue.emplace_back(buffer); + + if (buffer_wait_event) { + buffer_wait_event->Signal(); + } } -u32 BufferQueue::DequeueBuffer(u32 pixel_format, u32 width, u32 height) { +boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) { auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { // Only consider free buffers. Buffers become free once again after they've been Acquired // and Released by the compositor, see the NVFlinger::Compose method. - if (buffer.status != Buffer::Status::Free) + if (buffer.status != Buffer::Status::Free) { return false; + } // Make sure that the parameters match. - auto& igbp_buffer = buffer.igbp_buffer; - return igbp_buffer.format == pixel_format && igbp_buffer.width == width && - igbp_buffer.height == height; + return buffer.igbp_buffer.width == width && buffer.igbp_buffer.height == height; }); + if (itr == queue.end()) { - LOG_CRITICAL(Service_NVDRV, "no free buffers for pixel_format=%d, width=%d, height=%d", - pixel_format, width, height); - itr = queue.begin(); + return boost::none; } + buffer_wait_event = nullptr; + itr->status = Buffer::Status::Dequeued; return itr->slot; } @@ -83,6 +87,10 @@ void BufferQueue::ReleaseBuffer(u32 slot) { ASSERT(itr != queue.end()); ASSERT(itr->status == Buffer::Status::Acquired); itr->status = Buffer::Status::Free; + + if (buffer_wait_event) { + buffer_wait_event->Signal(); + } } u32 BufferQueue::Query(QueryType type) { @@ -98,5 +106,10 @@ u32 BufferQueue::Query(QueryType type) { return 0; } +void BufferQueue::SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event) { + ASSERT_MSG(!buffer_wait_event, "buffer_wait_event only supports a single waiting thread!"); + buffer_wait_event = std::move(wait_event); +} + } // namespace NVFlinger } // namespace Service diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h index ef9732769..686eadca7 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ b/src/core/hle/service/nvflinger/buffer_queue.h @@ -69,12 +69,13 @@ public: }; void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer); - u32 DequeueBuffer(u32 pixel_format, u32 width, u32 height); + boost::optional<u32> DequeueBuffer(u32 width, u32 height); const IGBPBuffer& RequestBuffer(u32 slot) const; void QueueBuffer(u32 slot, BufferTransformFlags transform); boost::optional<const Buffer&> AcquireBuffer(); void ReleaseBuffer(u32 slot); u32 Query(QueryType type); + void SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event); u32 GetId() const { return id; @@ -90,6 +91,9 @@ private: std::vector<Buffer> queue; Kernel::SharedPtr<Kernel::Event> native_handle; + + /// Used to signal waiting thread when no buffers are available + Kernel::SharedPtr<Kernel::Event> buffer_wait_event; }; } // namespace NVFlinger diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 2089462b7..0d30f54dc 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -6,6 +6,7 @@ #include "common/alignment.h" #include "common/scope_exit.h" +#include "core/core.h" #include "core/core_timing.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvdrv/nvdrv.h" @@ -129,6 +130,7 @@ void NVFlinger::Compose() { if (buffer == boost::none) { // There was no queued buffer to draw, render previous frame + Core::System::GetInstance().perf_stats.EndGameFrame(); VideoCore::g_renderer->SwapBuffers({}); continue; } @@ -148,6 +150,9 @@ void NVFlinger::Compose() { igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->transform); buffer_queue->ReleaseBuffer(buffer->slot); + + // TODO(Subv): Figure out when we should actually signal this event. + buffer_queue->GetNativeHandle()->Signal(); } } diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 1dd04a12f..a1ca8a033 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -7,6 +7,7 @@ #include "common/assert.h" #include "common/logging/log.h" #include "common/string_util.h" +#include "core/core.h" #include "core/hle/ipc.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/client_port.h" @@ -20,13 +21,15 @@ #include "core/hle/service/apm/apm.h" #include "core/hle/service/audio/audio.h" #include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/friend/friend.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/lm/lm.h" #include "core/hle/service/nifm/nifm.h" +#include "core/hle/service/ns/ns.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/pctl/pctl.h" #include "core/hle/service/service.h" -#include "core/hle/service/set/set.h" +#include "core/hle/service/set/settings.h" #include "core/hle/service/sm/controller.h" #include "core/hle/service/sm/sm.h" #include "core/hle/service/sockets/sockets.h" @@ -149,9 +152,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co UNIMPLEMENTED_MSG("command_type=%d", context.GetCommandType()); } - u32* cmd_buf = (u32*)Memory::GetPointer(Kernel::GetCurrentThread()->GetTLSAddress()); - context.WriteToOutgoingCommandBuffer(cmd_buf, *Kernel::g_current_process, - Kernel::g_handle_table); + context.WriteToOutgoingCommandBuffer(*Kernel::GetCurrentThread()); return RESULT_SUCCESS; } @@ -179,9 +180,11 @@ void Init() { APM::InstallInterfaces(*SM::g_service_manager); Audio::InstallInterfaces(*SM::g_service_manager); FileSystem::InstallInterfaces(*SM::g_service_manager); + Friend::InstallInterfaces(*SM::g_service_manager); HID::InstallInterfaces(*SM::g_service_manager); LM::InstallInterfaces(*SM::g_service_manager); NIFM::InstallInterfaces(*SM::g_service_manager); + NS::InstallInterfaces(*SM::g_service_manager); Nvidia::InstallInterfaces(*SM::g_service_manager); PCTL::InstallInterfaces(*SM::g_service_manager); Sockets::InstallInterfaces(*SM::g_service_manager); diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index 1062ba8b3..aa7c924e7 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp @@ -17,9 +17,7 @@ void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) { u32 id = rp.Pop<u32>(); constexpr std::array<u8, 13> lang_codes{}; - const auto& output_buffer = ctx.BufferDescriptorC()[0]; - - Memory::WriteBlock(output_buffer.Address(), lang_codes.data(), lang_codes.size()); + ctx.WriteBuffer(lang_codes.data(), lang_codes.size()); IPC::ResponseBuilder rb{ctx, 2}; @@ -28,16 +26,19 @@ void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_SET, "(STUBBED) called"); } -SET::SET(const char* name) : ServiceFramework(name) { +SET::SET() : ServiceFramework("set") { static const FunctionInfo functions[] = { + {0, nullptr, "GetLanguageCode"}, {1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"}, + {2, nullptr, "MakeLanguageCode"}, + {3, nullptr, "GetAvailableLanguageCodeCount"}, + {4, nullptr, "GetRegionCode"}, + {5, nullptr, "GetAvailableLanguageCodes2"}, + {6, nullptr, "GetAvailableLanguageCodeCount2"}, + {7, nullptr, "GetKeyCodeMap"}, }; RegisterHandlers(functions); } -void InstallInterfaces(SM::ServiceManager& service_manager) { - std::make_shared<SET>("set")->InstallAsService(service_manager); -} - } // namespace Set } // namespace Service diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h index 61e957946..7b7814ed1 100644 --- a/src/core/hle/service/set/set.h +++ b/src/core/hle/service/set/set.h @@ -11,15 +11,12 @@ namespace Set { class SET final : public ServiceFramework<SET> { public: - explicit SET(const char* name); + explicit SET(); ~SET() = default; private: void GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx); }; -/// Registers all Set services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); - } // namespace Set } // namespace Service diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp new file mode 100644 index 000000000..6231acd96 --- /dev/null +++ b/src/core/hle/service/set/set_cal.cpp @@ -0,0 +1,40 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/set/set_cal.h" + +namespace Service { +namespace Set { + +SET_CAL::SET_CAL() : ServiceFramework("set:cal") { + static const FunctionInfo functions[] = { + {0, nullptr, "GetBluetoothBdAddress"}, + {1, nullptr, "GetConfigurationId1"}, + {2, nullptr, "GetAccelerometerOffset"}, + {3, nullptr, "GetAccelerometerScale"}, + {4, nullptr, "GetGyroscopeOffset"}, + {5, nullptr, "GetGyroscopeScale"}, + {6, nullptr, "GetWirelessLanMacAddress"}, + {7, nullptr, "GetWirelessLanCountryCodeCount"}, + {8, nullptr, "GetWirelessLanCountryCodes"}, + {9, nullptr, "GetSerialNumber"}, + {10, nullptr, "SetInitialSystemAppletProgramId"}, + {11, nullptr, "SetOverlayDispProgramId"}, + {12, nullptr, "GetBatteryLot"}, + {14, nullptr, "GetEciDeviceCertificate"}, + {15, nullptr, "GetEticketDeviceCertificate"}, + {16, nullptr, "GetSslKey"}, + {17, nullptr, "GetSslCertificate"}, + {18, nullptr, "GetGameCardKey"}, + {19, nullptr, "GetGameCardCertificate"}, + {20, nullptr, "GetEciDeviceKey"}, + {21, nullptr, "GetEticketDeviceKey"}, + {22, nullptr, "GetSpeakerParameter"}, + {23, nullptr, "GetLcdVendorId"}, + }; + RegisterHandlers(functions); +} + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/set_cal.h b/src/core/hle/service/set/set_cal.h new file mode 100644 index 000000000..9c0b851d0 --- /dev/null +++ b/src/core/hle/service/set/set_cal.h @@ -0,0 +1,19 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Set { + +class SET_CAL final : public ServiceFramework<SET_CAL> { +public: + explicit SET_CAL(); + ~SET_CAL() = default; +}; + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/set_fd.cpp b/src/core/hle/service/set/set_fd.cpp new file mode 100644 index 000000000..8320d4250 --- /dev/null +++ b/src/core/hle/service/set/set_fd.cpp @@ -0,0 +1,25 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/set/set_fd.h" + +namespace Service { +namespace Set { + +SET_FD::SET_FD() : ServiceFramework("set:fd") { + static const FunctionInfo functions[] = { + {2, nullptr, "SetSettingsItemValue"}, + {3, nullptr, "ResetSettingsItemValue"}, + {4, nullptr, "CreateSettingsItemKeyIterator"}, + {10, nullptr, "ReadSettings"}, + {11, nullptr, "ResetSettings"}, + {20, nullptr, "SetWebInspectorFlag"}, + {21, nullptr, "SetAllowedSslHosts"}, + {22, nullptr, "SetHostFsMountPoint"}, + }; + RegisterHandlers(functions); +} + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/set_fd.h b/src/core/hle/service/set/set_fd.h new file mode 100644 index 000000000..65b36bcb3 --- /dev/null +++ b/src/core/hle/service/set/set_fd.h @@ -0,0 +1,19 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Set { + +class SET_FD final : public ServiceFramework<SET_FD> { +public: + explicit SET_FD(); + ~SET_FD() = default; +}; + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp new file mode 100644 index 000000000..363abd10a --- /dev/null +++ b/src/core/hle/service/set/set_sys.cpp @@ -0,0 +1,167 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/client_port.h" +#include "core/hle/service/set/set_sys.h" + +namespace Service { +namespace Set { + +void SET_SYS::GetColorSetId(Kernel::HLERequestContext& ctx) { + + IPC::ResponseBuilder rb{ctx, 3}; + + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); + + LOG_WARNING(Service_SET, "(STUBBED) called"); +} + +SET_SYS::SET_SYS() : ServiceFramework("set:sys") { + static const FunctionInfo functions[] = { + {0, nullptr, "SetLanguageCode"}, + {1, nullptr, "SetNetworkSettings"}, + {2, nullptr, "GetNetworkSettings"}, + {3, nullptr, "GetFirmwareVersion"}, + {4, nullptr, "GetFirmwareVersion2"}, + {7, nullptr, "GetLockScreenFlag"}, + {8, nullptr, "SetLockScreenFlag"}, + {9, nullptr, "GetBacklightSettings"}, + {10, nullptr, "SetBacklightSettings"}, + {11, nullptr, "SetBluetoothDevicesSettings"}, + {12, nullptr, "GetBluetoothDevicesSettings"}, + {13, nullptr, "GetExternalSteadyClockSourceId"}, + {14, nullptr, "SetExternalSteadyClockSourceId"}, + {15, nullptr, "GetUserSystemClockContext"}, + {16, nullptr, "SetUserSystemClockContext"}, + {17, nullptr, "GetAccountSettings"}, + {18, nullptr, "SetAccountSettings"}, + {19, nullptr, "GetAudioVolume"}, + {20, nullptr, "SetAudioVolume"}, + {21, nullptr, "GetEulaVersions"}, + {22, nullptr, "SetEulaVersions"}, + {23, &SET_SYS::GetColorSetId, "GetColorSetId"}, + {24, nullptr, "SetColorSetId"}, + {25, nullptr, "GetConsoleInformationUploadFlag"}, + {26, nullptr, "SetConsoleInformationUploadFlag"}, + {27, nullptr, "GetAutomaticApplicationDownloadFlag"}, + {28, nullptr, "SetAutomaticApplicationDownloadFlag"}, + {29, nullptr, "GetNotificationSettings"}, + {30, nullptr, "SetNotificationSettings"}, + {31, nullptr, "GetAccountNotificationSettings"}, + {32, nullptr, "SetAccountNotificationSettings"}, + {35, nullptr, "GetVibrationMasterVolume"}, + {36, nullptr, "SetVibrationMasterVolume"}, + {37, nullptr, "GetSettingsItemValueSize"}, + {38, nullptr, "GetSettingsItemValue"}, + {39, nullptr, "GetTvSettings"}, + {40, nullptr, "SetTvSettings"}, + {41, nullptr, "GetEdid"}, + {42, nullptr, "SetEdid"}, + {43, nullptr, "GetAudioOutputMode"}, + {44, nullptr, "SetAudioOutputMode"}, + {45, nullptr, "IsForceMuteOnHeadphoneRemoved"}, + {46, nullptr, "SetForceMuteOnHeadphoneRemoved"}, + {47, nullptr, "GetQuestFlag"}, + {48, nullptr, "SetQuestFlag"}, + {49, nullptr, "GetDataDeletionSettings"}, + {50, nullptr, "SetDataDeletionSettings"}, + {51, nullptr, "GetInitialSystemAppletProgramId"}, + {52, nullptr, "GetOverlayDispProgramId"}, + {53, nullptr, "GetDeviceTimeZoneLocationName"}, + {54, nullptr, "SetDeviceTimeZoneLocationName"}, + {55, nullptr, "GetWirelessCertificationFileSize"}, + {56, nullptr, "GetWirelessCertificationFile"}, + {57, nullptr, "SetRegionCode"}, + {58, nullptr, "GetNetworkSystemClockContext"}, + {59, nullptr, "SetNetworkSystemClockContext"}, + {60, nullptr, "IsUserSystemClockAutomaticCorrectionEnabled"}, + {61, nullptr, "SetUserSystemClockAutomaticCorrectionEnabled"}, + {62, nullptr, "GetDebugModeFlag"}, + {63, nullptr, "GetPrimaryAlbumStorage"}, + {64, nullptr, "SetPrimaryAlbumStorage"}, + {65, nullptr, "GetUsb30EnableFlag"}, + {66, nullptr, "SetUsb30EnableFlag"}, + {67, nullptr, "GetBatteryLot"}, + {68, nullptr, "GetSerialNumber"}, + {69, nullptr, "GetNfcEnableFlag"}, + {70, nullptr, "SetNfcEnableFlag"}, + {71, nullptr, "GetSleepSettings"}, + {72, nullptr, "SetSleepSettings"}, + {73, nullptr, "GetWirelessLanEnableFlag"}, + {74, nullptr, "SetWirelessLanEnableFlag"}, + {75, nullptr, "GetInitialLaunchSettings"}, + {76, nullptr, "SetInitialLaunchSettings"}, + {77, nullptr, "GetDeviceNickName"}, + {78, nullptr, "SetDeviceNickName"}, + {79, nullptr, "GetProductModel"}, + {80, nullptr, "GetLdnChannel"}, + {81, nullptr, "SetLdnChannel"}, + {82, nullptr, "AcquireTelemetryDirtyFlagEventHandle"}, + {83, nullptr, "GetTelemetryDirtyFlags"}, + {84, nullptr, "GetPtmBatteryLot"}, + {85, nullptr, "SetPtmBatteryLot"}, + {86, nullptr, "GetPtmFuelGaugeParameter"}, + {87, nullptr, "SetPtmFuelGaugeParameter"}, + {88, nullptr, "GetBluetoothEnableFlag"}, + {89, nullptr, "SetBluetoothEnableFlag"}, + {90, nullptr, "GetMiiAuthorId"}, + {91, nullptr, "SetShutdownRtcValue"}, + {92, nullptr, "GetShutdownRtcValue"}, + {93, nullptr, "AcquireFatalDirtyFlagEventHandle"}, + {94, nullptr, "GetFatalDirtyFlags"}, + {95, nullptr, "GetAutoUpdateEnableFlag"}, + {96, nullptr, "SetAutoUpdateEnableFlag"}, + {97, nullptr, "GetNxControllerSettings"}, + {98, nullptr, "SetNxControllerSettings"}, + {99, nullptr, "GetBatteryPercentageFlag"}, + {100, nullptr, "SetBatteryPercentageFlag"}, + {101, nullptr, "GetExternalRtcResetFlag"}, + {102, nullptr, "SetExternalRtcResetFlag"}, + {103, nullptr, "GetUsbFullKeyEnableFlag"}, + {104, nullptr, "SetUsbFullKeyEnableFlag"}, + {105, nullptr, "SetExternalSteadyClockInternalOffset"}, + {106, nullptr, "GetExternalSteadyClockInternalOffset"}, + {107, nullptr, "GetBacklightSettingsEx"}, + {108, nullptr, "SetBacklightSettingsEx"}, + {109, nullptr, "GetHeadphoneVolumeWarningCount"}, + {110, nullptr, "SetHeadphoneVolumeWarningCount"}, + {111, nullptr, "GetBluetoothAfhEnableFlag"}, + {112, nullptr, "SetBluetoothAfhEnableFlag"}, + {113, nullptr, "GetBluetoothBoostEnableFlag"}, + {114, nullptr, "SetBluetoothBoostEnableFlag"}, + {115, nullptr, "GetInRepairProcessEnableFlag"}, + {116, nullptr, "SetInRepairProcessEnableFlag"}, + {117, nullptr, "GetHeadphoneVolumeUpdateFlag"}, + {118, nullptr, "SetHeadphoneVolumeUpdateFlag"}, + {119, nullptr, "NeedsToUpdateHeadphoneVolume"}, + {120, nullptr, "GetPushNotificationActivityModeOnSleep"}, + {121, nullptr, "SetPushNotificationActivityModeOnSleep"}, + {122, nullptr, "GetServiceDiscoveryControlSettings"}, + {123, nullptr, "SetServiceDiscoveryControlSettings"}, + {124, nullptr, "GetErrorReportSharePermission"}, + {125, nullptr, "SetErrorReportSharePermission"}, + {126, nullptr, "GetAppletLaunchFlags"}, + {127, nullptr, "SetAppletLaunchFlags"}, + {128, nullptr, "GetConsoleSixAxisSensorAccelerationBias"}, + {129, nullptr, "SetConsoleSixAxisSensorAccelerationBias"}, + {130, nullptr, "GetConsoleSixAxisSensorAngularVelocityBias"}, + {131, nullptr, "SetConsoleSixAxisSensorAngularVelocityBias"}, + {132, nullptr, "GetConsoleSixAxisSensorAccelerationGain"}, + {133, nullptr, "SetConsoleSixAxisSensorAccelerationGain"}, + {134, nullptr, "GetConsoleSixAxisSensorAngularVelocityGain"}, + {135, nullptr, "SetConsoleSixAxisSensorAngularVelocityGain"}, + {136, nullptr, "GetKeyboardLayout"}, + {137, nullptr, "SetKeyboardLayout"}, + {138, nullptr, "GetWebInspectorFlag"}, + {139, nullptr, "GetAllowedSslHosts"}, + {140, nullptr, "GetHostFsMountPoint"}, + }; + RegisterHandlers(functions); +} + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h new file mode 100644 index 000000000..105f1a3c7 --- /dev/null +++ b/src/core/hle/service/set/set_sys.h @@ -0,0 +1,22 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Set { + +class SET_SYS final : public ServiceFramework<SET_SYS> { +public: + explicit SET_SYS(); + ~SET_SYS() = default; + +private: + void GetColorSetId(Kernel::HLERequestContext& ctx); +}; + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/settings.cpp b/src/core/hle/service/set/settings.cpp new file mode 100644 index 000000000..c6bc9e240 --- /dev/null +++ b/src/core/hle/service/set/settings.cpp @@ -0,0 +1,22 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/set/set.h" +#include "core/hle/service/set/set_cal.h" +#include "core/hle/service/set/set_fd.h" +#include "core/hle/service/set/set_sys.h" +#include "core/hle/service/set/settings.h" + +namespace Service { +namespace Set { + +void InstallInterfaces(SM::ServiceManager& service_manager) { + std::make_shared<SET>()->InstallAsService(service_manager); + std::make_shared<SET_CAL>()->InstallAsService(service_manager); + std::make_shared<SET_FD>()->InstallAsService(service_manager); + std::make_shared<SET_SYS>()->InstallAsService(service_manager); +} + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/settings.h b/src/core/hle/service/set/settings.h new file mode 100644 index 000000000..6c8d5a58c --- /dev/null +++ b/src/core/hle/service/set/settings.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Set { + +/// Registers all Settings services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/sockets/bsd_u.cpp b/src/core/hle/service/sockets/bsd_u.cpp index 629ffb040..2ca1000ca 100644 --- a/src/core/hle/service/sockets/bsd_u.cpp +++ b/src/core/hle/service/sockets/bsd_u.cpp @@ -17,6 +17,15 @@ void BSD_U::RegisterClient(Kernel::HLERequestContext& ctx) { rb.Push<u32>(0); // bsd errno } +void BSD_U::StartMonitoring(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); // bsd errno +} + void BSD_U::Socket(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; @@ -67,6 +76,7 @@ void BSD_U::Close(Kernel::HLERequestContext& ctx) { BSD_U::BSD_U() : ServiceFramework("bsd:u") { static const FunctionInfo functions[] = {{0, &BSD_U::RegisterClient, "RegisterClient"}, + {1, &BSD_U::StartMonitoring, "StartMonitoring"}, {2, &BSD_U::Socket, "Socket"}, {11, &BSD_U::SendTo, "SendTo"}, {14, &BSD_U::Connect, "Connect"}, diff --git a/src/core/hle/service/sockets/bsd_u.h b/src/core/hle/service/sockets/bsd_u.h index fde08a22b..4e1252e9d 100644 --- a/src/core/hle/service/sockets/bsd_u.h +++ b/src/core/hle/service/sockets/bsd_u.h @@ -17,6 +17,7 @@ public: private: void RegisterClient(Kernel::HLERequestContext& ctx); + void StartMonitoring(Kernel::HLERequestContext& ctx); void Socket(Kernel::HLERequestContext& ctx); void Connect(Kernel::HLERequestContext& ctx); void SendTo(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 364ddcea2..ad49f4265 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -146,6 +146,13 @@ void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called"); } +void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISystemClock>(); + LOG_DEBUG(Service_Time, "called"); +} + Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) : ServiceFramework(name), time(std::move(time)) {} diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h index 1cbbadb21..197029e7a 100644 --- a/src/core/hle/service/time/time.h +++ b/src/core/hle/service/time/time.h @@ -56,6 +56,7 @@ public: void GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx); void GetStandardSteadyClock(Kernel::HLERequestContext& ctx); void GetTimeZoneService(Kernel::HLERequestContext& ctx); + void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx); protected: std::shared_ptr<Module> time; diff --git a/src/core/hle/service/time/time_s.cpp b/src/core/hle/service/time/time_s.cpp index 1634d3300..b172b2bd6 100644 --- a/src/core/hle/service/time/time_s.cpp +++ b/src/core/hle/service/time/time_s.cpp @@ -10,6 +10,10 @@ namespace Time { TIME_S::TIME_S(std::shared_ptr<Module> time) : Module::Interface(std::move(time), "time:s") { static const FunctionInfo functions[] = { {0, &TIME_S::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, + {1, &TIME_S::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, + {2, &TIME_S::GetStandardSteadyClock, "GetStandardSteadyClock"}, + {3, &TIME_S::GetTimeZoneService, "GetTimeZoneService"}, + {4, &TIME_S::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/time/time_u.cpp b/src/core/hle/service/time/time_u.cpp index ae4f78adf..fc1ace325 100644 --- a/src/core/hle/service/time/time_u.cpp +++ b/src/core/hle/service/time/time_u.cpp @@ -13,6 +13,7 @@ TIME_U::TIME_U(std::shared_ptr<Module> time) : Module::Interface(std::move(time) {1, &TIME_U::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, {2, &TIME_U::GetStandardSteadyClock, "GetStandardSteadyClock"}, {3, &TIME_U::GetTimeZoneService, "GetTimeZoneService"}, + {4, &TIME_U::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 8b4ed30d2..7b6453447 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -8,6 +8,7 @@ #include "common/scope_exit.h" #include "core/core_timing.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvflinger/buffer_queue.h" #include "core/hle/service/vi/vi.h" #include "core/hle/service/vi/vi_m.h" @@ -38,6 +39,7 @@ public: template <typename T> T Read() { + ASSERT(read_index + sizeof(T) <= buffer.size()); T val; std::memcpy(&val, buffer.data() + read_index, sizeof(T)); read_index += sizeof(T); @@ -47,6 +49,7 @@ public: template <typename T> T ReadUnaligned() { + ASSERT(read_index + sizeof(T) <= buffer.size()); T val; std::memcpy(&val, buffer.data() + read_index, sizeof(T)); read_index += sizeof(T); @@ -54,6 +57,7 @@ public: } std::vector<u8> ReadBlock(size_t length) { + ASSERT(read_index + length <= buffer.size()); const u8* const begin = buffer.data() + read_index; const u8* const end = begin + length; std::vector<u8> data(begin, end); @@ -86,7 +90,18 @@ public: write_index = Common::AlignUp(write_index, 4); } + template <typename T> + void WriteObject(const T& val) { + u32_le size = static_cast<u32>(sizeof(val)); + Write(size); + // TODO(Subv): Support file descriptors. + Write<u32_le>(0); // Fd count. + Write(val); + } + void Deserialize() { + ASSERT(buffer.size() > sizeof(Header)); + Header header{}; std::memcpy(&header, buffer.data(), sizeof(Header)); @@ -262,10 +277,11 @@ public: Data data; }; -// TODO(bunnei): Remove this. When set to 1, games will think a fence is valid and boot further. -// This will break libnx and potentially other apps that more stringently check this. This is here -// purely as a convenience, and should go away once we implement fences. -static constexpr u32 FENCE_HACK = 0; +struct BufferProducerFence { + u32 is_valid; + std::array<Nvidia::IoctlFence, 4> fences; +}; +static_assert(sizeof(BufferProducerFence) == 36, "BufferProducerFence has wrong size"); class IGBPDequeueBufferResponseParcel : public Parcel { public: @@ -274,20 +290,16 @@ public: protected: void SerializeData() override { - // TODO(bunnei): Find out what this all means. Writing anything non-zero here breaks libnx. - Write<u32>(0); - Write<u32>(FENCE_HACK); - Write<u32>(0); - Write<u32>(0); - Write<u32>(0); - Write<u32>(0); - Write<u32>(0); - Write<u32>(0); - Write<u32>(0); - Write<u32>(0); - Write<u32>(0); - Write<u32>(0); - Write<u32>(0); + // TODO(Subv): Find out how this Fence is used. + BufferProducerFence fence = {}; + fence.is_valid = 1; + for (auto& fence_ : fence.fences) + fence_.id = -1; + + Write(slot); + Write<u32_le>(1); + WriteObject(fence); + Write<u32_le>(0); } u32_le slot; @@ -316,11 +328,10 @@ public: protected: void SerializeData() override { - // TODO(bunnei): Find out what this all means. Writing anything non-zero here breaks libnx. - Write<u32_le>(0); - Write<u32_le>(FENCE_HACK); - Write<u32_le>(0); - Write(buffer); + // TODO(Subv): Figure out what this value means, writing non-zero here will make libnx try + // to read an IGBPBuffer object from the parcel. + Write<u32_le>(1); + WriteObject(buffer); Write<u32_le>(0); } @@ -429,7 +440,7 @@ public: {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"}, {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"}, {2, &IHOSBinderDriver::GetNativeHandle, "GetNativeHandle"}, - {3, &IHOSBinderDriver::TransactParcelAuto, "TransactParcelAuto"}, + {3, &IHOSBinderDriver::TransactParcel, "TransactParcelAuto"}, }; RegisterHandlers(functions); } @@ -453,95 +464,79 @@ private: SetPreallocatedBuffer = 14 }; - void TransactParcel(u32 id, TransactionId transaction, const std::vector<u8>& input_data, - VAddr output_addr, u64 output_size) { + void TransactParcel(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + u32 id = rp.Pop<u32>(); + auto transaction = static_cast<TransactionId>(rp.Pop<u32>()); + u32 flags = rp.Pop<u32>(); auto buffer_queue = nv_flinger->GetBufferQueue(id); + LOG_DEBUG(Service_VI, "called, transaction=%x", transaction); + if (transaction == TransactionId::Connect) { - IGBPConnectRequestParcel request{input_data}; + IGBPConnectRequestParcel request{ctx.ReadBuffer()}; IGBPConnectResponseParcel response{1280, 720}; - std::vector<u8> response_buffer = response.Serialize(); - Memory::WriteBlock(output_addr, response_buffer.data(), response_buffer.size()); + ctx.WriteBuffer(response.Serialize()); } else if (transaction == TransactionId::SetPreallocatedBuffer) { - IGBPSetPreallocatedBufferRequestParcel request{input_data}; + IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()}; buffer_queue->SetPreallocatedBuffer(request.data.slot, request.buffer); IGBPSetPreallocatedBufferResponseParcel response{}; - std::vector<u8> response_buffer = response.Serialize(); - Memory::WriteBlock(output_addr, response_buffer.data(), response_buffer.size()); + ctx.WriteBuffer(response.Serialize()); } else if (transaction == TransactionId::DequeueBuffer) { - IGBPDequeueBufferRequestParcel request{input_data}; - - u32 slot = buffer_queue->DequeueBuffer(request.data.pixel_format, request.data.width, - request.data.height); - - IGBPDequeueBufferResponseParcel response{slot}; - std::vector<u8> response_buffer = response.Serialize(); - Memory::WriteBlock(output_addr, response_buffer.data(), response_buffer.size()); + IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()}; + const u32 width{request.data.width}; + const u32 height{request.data.height}; + boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); + + if (slot != boost::none) { + // Buffer is available + IGBPDequeueBufferResponseParcel response{*slot}; + ctx.WriteBuffer(response.Serialize()); + } else { + // Wait the current thread until a buffer becomes available + auto wait_event = ctx.SleepClientThread( + Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1, + [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, + ThreadWakeupReason reason) { + // Repeat TransactParcel DequeueBuffer when a buffer is available + auto buffer_queue = nv_flinger->GetBufferQueue(id); + boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); + IGBPDequeueBufferResponseParcel response{*slot}; + ctx.WriteBuffer(response.Serialize()); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + }); + buffer_queue->SetBufferWaitEvent(std::move(wait_event)); + } } else if (transaction == TransactionId::RequestBuffer) { - IGBPRequestBufferRequestParcel request{input_data}; + IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()}; auto& buffer = buffer_queue->RequestBuffer(request.slot); IGBPRequestBufferResponseParcel response{buffer}; - std::vector<u8> response_buffer = response.Serialize(); - Memory::WriteBlock(output_addr, response_buffer.data(), response_buffer.size()); + ctx.WriteBuffer(response.Serialize()); } else if (transaction == TransactionId::QueueBuffer) { - IGBPQueueBufferRequestParcel request{input_data}; + IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()}; buffer_queue->QueueBuffer(request.data.slot, request.data.transform); IGBPQueueBufferResponseParcel response{1280, 720}; - std::vector<u8> response_buffer = response.Serialize(); - Memory::WriteBlock(output_addr, response_buffer.data(), response_buffer.size()); + ctx.WriteBuffer(response.Serialize()); } else if (transaction == TransactionId::Query) { - IGBPQueryRequestParcel request{input_data}; + IGBPQueryRequestParcel request{ctx.ReadBuffer()}; u32 value = buffer_queue->Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type)); IGBPQueryResponseParcel response{value}; - std::vector<u8> response_buffer = response.Serialize(); - Memory::WriteBlock(output_addr, response_buffer.data(), response_buffer.size()); + ctx.WriteBuffer(response.Serialize()); } else if (transaction == TransactionId::CancelBuffer) { LOG_WARNING(Service_VI, "(STUBBED) called, transaction=CancelBuffer"); } else { ASSERT_MSG(false, "Unimplemented"); } - } - - void TransactParcel(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - u32 id = rp.Pop<u32>(); - auto transaction = static_cast<TransactionId>(rp.Pop<u32>()); - u32 flags = rp.Pop<u32>(); - LOG_DEBUG(Service_VI, "called, transaction=%x", transaction); - - auto& input_buffer = ctx.BufferDescriptorA()[0]; - auto& output_buffer = ctx.BufferDescriptorB()[0]; - std::vector<u8> input_data(input_buffer.Size()); - Memory::ReadBlock(input_buffer.Address(), input_data.data(), input_buffer.Size()); - - TransactParcel(id, transaction, input_data, output_buffer.Address(), output_buffer.Size()); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - } - - void TransactParcelAuto(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - u32 id = rp.Pop<u32>(); - auto transaction = static_cast<TransactionId>(rp.Pop<u32>()); - u32 flags = rp.Pop<u32>(); - LOG_DEBUG(Service_VI, "called, transaction=%x", transaction); - - auto& input_buffer = ctx.BufferDescriptorX()[0]; - auto& output_buffer = ctx.BufferDescriptorC()[0]; - std::vector<u8> input_data(input_buffer.size); - Memory::ReadBlock(input_buffer.Address(), input_data.data(), input_buffer.size); - - TransactParcel(id, transaction, input_data, output_buffer.Address(), output_buffer.Size()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -719,18 +714,13 @@ void IApplicationDisplayService::OpenLayer(Kernel::HLERequestContext& ctx) { u64 layer_id = rp.Pop<u64>(); u64 aruid = rp.Pop<u64>(); - auto& buffer = ctx.BufferDescriptorB()[0]; - u64 display_id = nv_flinger->OpenDisplay(display_name); u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); NativeWindow native_window{buffer_queue_id}; - auto data = native_window.Serialize(); - Memory::WriteBlock(buffer.Address(), data.data(), data.size()); - IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); rb.Push(RESULT_SUCCESS); - rb.Push<u64>(data.size()); + rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize())); } void IApplicationDisplayService::CreateStrayLayer(Kernel::HLERequestContext& ctx) { @@ -741,21 +731,16 @@ void IApplicationDisplayService::CreateStrayLayer(Kernel::HLERequestContext& ctx rp.Pop<u32>(); // padding u64 display_id = rp.Pop<u64>(); - auto& buffer = ctx.BufferDescriptorB()[0]; - // TODO(Subv): What's the difference between a Stray and a Managed layer? u64 layer_id = nv_flinger->CreateLayer(display_id); u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); NativeWindow native_window{buffer_queue_id}; - auto data = native_window.Serialize(); - Memory::WriteBlock(buffer.Address(), data.data(), data.size()); - IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0); rb.Push(RESULT_SUCCESS); rb.Push(layer_id); - rb.Push<u64>(data.size()); + rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize())); } void IApplicationDisplayService::DestroyStrayLayer(Kernel::HLERequestContext& ctx) { @@ -781,8 +766,7 @@ void IApplicationDisplayService::SetLayerScalingMode(Kernel::HLERequestContext& void IApplicationDisplayService::ListDisplays(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; DisplayInfo display_info; - auto& buffer = ctx.BufferDescriptorB()[0]; - Memory::WriteBlock(buffer.Address(), &display_info, sizeof(DisplayInfo)); + ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); rb.Push(RESULT_SUCCESS); rb.Push<u64>(1); diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 661803b5f..aa09ed323 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -53,6 +53,7 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileUti FileType AppLoader_DeconstructedRomDirectory::IdentifyType(FileUtil::IOFile& file, const std::string& filepath) { bool is_main_found{}; + bool is_npdm_found{}; bool is_rtld_found{}; bool is_sdk_found{}; @@ -67,6 +68,9 @@ FileType AppLoader_DeconstructedRomDirectory::IdentifyType(FileUtil::IOFile& fil // Verify filename if (Common::ToLower(virtual_name) == "main") { is_main_found = true; + } else if (Common::ToLower(virtual_name) == "main.npdm") { + is_npdm_found = true; + return true; } else if (Common::ToLower(virtual_name) == "rtld") { is_rtld_found = true; } else if (Common::ToLower(virtual_name) == "sdk") { @@ -83,14 +87,14 @@ FileType AppLoader_DeconstructedRomDirectory::IdentifyType(FileUtil::IOFile& fil } // We are done if we've found and verified all required NSOs - return !(is_main_found && is_rtld_found && is_sdk_found); + return !(is_main_found && is_npdm_found && is_rtld_found && is_sdk_found); }; // Search the directory recursively, looking for the required modules const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP; FileUtil::ForeachDirectoryEntry(nullptr, directory, callback); - if (is_main_found && is_rtld_found && is_sdk_found) { + if (is_main_found && is_npdm_found && is_rtld_found && is_sdk_found) { return FileType::DeconstructedRomDirectory; } @@ -106,11 +110,17 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( return ResultStatus::Error; } - process = Kernel::Process::Create("main"); + const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP; + const std::string npdm_path = directory + DIR_SEP + "main.npdm"; + + ResultStatus result = metadata.Load(npdm_path); + if (result != ResultStatus::Success) { + return result; + } + metadata.Print(); // Load NSO modules VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR}; - const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP; for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { const std::string path = directory + DIR_SEP + module; @@ -123,11 +133,13 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( } } + process->program_id = metadata.GetTitleID(); process->svc_access_mask.set(); process->address_mappings = default_address_mappings; process->resource_limit = Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); - process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Kernel::DEFAULT_STACK_SIZE); + process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(), + metadata.GetMainThreadStackSize()); // Find the RomFS by searching for a ".romfs" file in this directory filepath_romfs = FindRomFS(directory); diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index 536a2dab7..23295d911 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h @@ -6,6 +6,7 @@ #include <string> #include "common/common_types.h" +#include "core/file_sys/program_metadata.h" #include "core/hle/kernel/kernel.h" #include "core/loader/loader.h" @@ -41,6 +42,7 @@ public: private: std::string filepath_romfs; std::string filepath; + FileSys::ProgramMetadata metadata; }; } // namespace Loader diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index b87320656..0ba8c6fd2 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -300,7 +300,7 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) { std::vector<u8> program_image(total_image_size); size_t current_image_position = 0; - SharedPtr<CodeSet> codeset = CodeSet::Create("", 0); + SharedPtr<CodeSet> codeset = CodeSet::Create(""); for (unsigned int i = 0; i < header->e_phnum; ++i) { Elf32_Phdr* p = &segments[i]; @@ -406,7 +406,6 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) { SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); codeset->name = filename; - process = Kernel::Process::Create("main"); process->LoadModule(codeset, codeset->entrypoint); process->svc_access_mask.set(); process->address_mappings = default_address_mappings; @@ -415,7 +414,7 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) { process->resource_limit = Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); - process->Run(codeset->entrypoint, 48, Kernel::DEFAULT_STACK_SIZE); + process->Run(codeset->entrypoint, 48, Memory::STACK_SIZE); is_loaded = true; return ResultStatus::Success; diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 6f8a2f21e..6dcd1ce48 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -8,6 +8,7 @@ #include "common/file_util.h" #include "common/logging/log.h" #include "common/swap.h" +#include "core/core.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/loader/nro.h" @@ -83,7 +84,7 @@ bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) { } // Build program image - Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("", 0); + Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(""); std::vector<u8> program_image; program_image.resize(PageAlignSize(nro_header.file_size)); file.Seek(0, SEEK_SET); @@ -112,7 +113,7 @@ bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) { // Load codeset for current process codeset->name = path; codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); - Kernel::g_current_process->LoadModule(codeset, load_base); + Core::CurrentProcess()->LoadModule(codeset, load_base); return true; } @@ -125,8 +126,6 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) { return ResultStatus::Error; } - process = Kernel::Process::Create("main"); - // Load NRO static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR}; @@ -138,7 +137,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) { process->address_mappings = default_address_mappings; process->resource_limit = Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); - process->Run(base_addr, 48, Kernel::DEFAULT_STACK_SIZE); + process->Run(base_addr, 48, Memory::STACK_SIZE); is_loaded = true; return ResultStatus::Success; diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 407025da0..100aa022e 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -9,6 +9,7 @@ #include "common/file_util.h" #include "common/logging/log.h" #include "common/swap.h" +#include "core/core.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/loader/nso.h" @@ -109,7 +110,7 @@ VAddr AppLoader_NSO::LoadModule(const std::string& path, VAddr load_base) { } // Build program image - Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("", 0); + Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(""); std::vector<u8> program_image; for (int i = 0; i < nso_header.segments.size(); ++i) { std::vector<u8> data = @@ -142,7 +143,7 @@ VAddr AppLoader_NSO::LoadModule(const std::string& path, VAddr load_base) { // Load codeset for current process codeset->name = path; codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); - Kernel::g_current_process->LoadModule(codeset, load_base); + Core::CurrentProcess()->LoadModule(codeset, load_base); return load_base + image_size; } @@ -155,8 +156,6 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) { return ResultStatus::Error; } - process = Kernel::Process::Create("main"); - // Load module LoadModule(filepath, Memory::PROCESS_IMAGE_VADDR); LOG_DEBUG(Loader, "loaded module %s @ 0x%" PRIx64, filepath.c_str(), @@ -166,7 +165,7 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) { process->address_mappings = default_address_mappings; process->resource_limit = Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); - process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Kernel::DEFAULT_STACK_SIZE); + process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Memory::STACK_SIZE); is_loaded = true; return ResultStatus::Success; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index cc1ed16b6..4e34d8334 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -109,7 +109,7 @@ static std::set<MemoryHookPointer> GetSpecialHandlers(const PageTable& page_tabl } static std::set<MemoryHookPointer> GetSpecialHandlers(VAddr vaddr, u64 size) { - const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table; + const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table; return GetSpecialHandlers(page_table, vaddr, size); } @@ -118,6 +118,11 @@ boost::optional<T> ReadSpecial(VAddr addr); template <typename T> T Read(const VAddr vaddr) { + if ((vaddr >> PAGE_BITS) >= PAGE_TABLE_NUM_ENTRIES) { + LOG_ERROR(HW_Memory, "Read%lu after page table @ 0x%016" PRIX64, sizeof(T) * 8, vaddr); + return 0; + } + const PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; switch (type) { case PageType::Unmapped: @@ -146,6 +151,12 @@ bool WriteSpecial(VAddr addr, const T data); template <typename T> void Write(const VAddr vaddr, const T data) { + if ((vaddr >> PAGE_BITS) >= PAGE_TABLE_NUM_ENTRIES) { + LOG_ERROR(HW_Memory, "Write%lu after page table 0x%08X @ 0x%016" PRIX64, sizeof(data) * 8, + (u32)data, vaddr); + return; + } + const PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; switch (type) { case PageType::Unmapped: @@ -191,7 +202,7 @@ bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) { } bool IsValidVirtualAddress(const VAddr vaddr) { - return IsValidVirtualAddress(*Kernel::g_current_process, vaddr); + return IsValidVirtualAddress(*Core::CurrentProcess(), vaddr); } bool IsValidPhysicalAddress(const PAddr paddr) { @@ -353,7 +364,7 @@ void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_ } void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) { - ReadBlock(*Kernel::g_current_process, src_addr, dest_buffer, size); + ReadBlock(*Core::CurrentProcess(), src_addr, dest_buffer, size); } void Write8(const VAddr addr, const u8 data) { @@ -424,11 +435,11 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi } void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size) { - WriteBlock(*Kernel::g_current_process, dest_addr, src_buffer, size); + WriteBlock(*Core::CurrentProcess(), dest_addr, src_buffer, size); } void ZeroBlock(const VAddr dest_addr, const size_t size) { - const auto& process = *Kernel::g_current_process; + const auto& process = *Core::CurrentProcess(); size_t remaining_size = size; size_t page_index = dest_addr >> PAGE_BITS; @@ -469,7 +480,7 @@ void ZeroBlock(const VAddr dest_addr, const size_t size) { } void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) { - const auto& process = *Kernel::g_current_process; + const auto& process = *Core::CurrentProcess(); size_t remaining_size = size; size_t page_index = src_addr >> PAGE_BITS; @@ -515,7 +526,7 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) { template <> boost::optional<u8> ReadSpecial<u8>(VAddr addr) { - const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table; + const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table; for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u8))) if (auto result = handler->Read8(addr)) return *result; @@ -524,7 +535,7 @@ boost::optional<u8> ReadSpecial<u8>(VAddr addr) { template <> boost::optional<u16> ReadSpecial<u16>(VAddr addr) { - const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table; + const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table; for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u16))) if (auto result = handler->Read16(addr)) return *result; @@ -533,7 +544,7 @@ boost::optional<u16> ReadSpecial<u16>(VAddr addr) { template <> boost::optional<u32> ReadSpecial<u32>(VAddr addr) { - const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table; + const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table; for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u32))) if (auto result = handler->Read32(addr)) return *result; @@ -542,7 +553,7 @@ boost::optional<u32> ReadSpecial<u32>(VAddr addr) { template <> boost::optional<u64> ReadSpecial<u64>(VAddr addr) { - const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table; + const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table; for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u64))) if (auto result = handler->Read64(addr)) return *result; @@ -551,7 +562,7 @@ boost::optional<u64> ReadSpecial<u64>(VAddr addr) { template <> bool WriteSpecial<u8>(VAddr addr, const u8 data) { - const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table; + const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table; for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u8))) if (handler->Write8(addr, data)) return true; @@ -560,7 +571,7 @@ bool WriteSpecial<u8>(VAddr addr, const u8 data) { template <> bool WriteSpecial<u16>(VAddr addr, const u16 data) { - const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table; + const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table; for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u16))) if (handler->Write16(addr, data)) return true; @@ -569,7 +580,7 @@ bool WriteSpecial<u16>(VAddr addr, const u16 data) { template <> bool WriteSpecial<u32>(VAddr addr, const u32 data) { - const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table; + const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table; for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u32))) if (handler->Write32(addr, data)) return true; @@ -578,7 +589,7 @@ bool WriteSpecial<u32>(VAddr addr, const u32 data) { template <> bool WriteSpecial<u64>(VAddr addr, const u64 data) { - const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table; + const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table; for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u64))) if (handler->Write64(addr, data)) return true; @@ -621,7 +632,7 @@ boost::optional<VAddr> PhysicalToVirtualAddress(const PAddr addr) { } else if (addr >= VRAM_PADDR && addr < VRAM_PADDR_END) { return addr - VRAM_PADDR + VRAM_VADDR; } else if (addr >= FCRAM_PADDR && addr < FCRAM_PADDR_END) { - return addr - FCRAM_PADDR + Kernel::g_current_process->GetLinearHeapAreaAddress(); + return addr - FCRAM_PADDR + Core::CurrentProcess()->GetLinearHeapAreaAddress(); } else if (addr >= DSP_RAM_PADDR && addr < DSP_RAM_PADDR_END) { return addr - DSP_RAM_PADDR + DSP_RAM_VADDR; } else if (addr >= IO_AREA_PADDR && addr < IO_AREA_PADDR_END) { diff --git a/src/core/memory.h b/src/core/memory.h index f3ace7a98..f406cc848 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -129,21 +129,6 @@ enum : VAddr { PROCESS_IMAGE_MAX_SIZE = 0x08000000, PROCESS_IMAGE_VADDR_END = PROCESS_IMAGE_VADDR + PROCESS_IMAGE_MAX_SIZE, - /// Area where IPC buffers are mapped onto. - IPC_MAPPING_VADDR = 0x04000000, - IPC_MAPPING_SIZE = 0x04000000, - IPC_MAPPING_VADDR_END = IPC_MAPPING_VADDR + IPC_MAPPING_SIZE, - - /// Application heap (includes stack). - HEAP_VADDR = 0x108000000, - HEAP_SIZE = 0xF0000000, - HEAP_VADDR_END = HEAP_VADDR + HEAP_SIZE, - - /// Area where shared memory buffers are mapped onto. - SHARED_MEMORY_VADDR = 0x10000000, - SHARED_MEMORY_SIZE = 0x04000000, - SHARED_MEMORY_VADDR_END = SHARED_MEMORY_VADDR + SHARED_MEMORY_SIZE, - /// Maps 1:1 to an offset in FCRAM. Used for HW allocations that need to be linear in physical /// memory. LINEAR_HEAP_VADDR = 0x14000000, @@ -176,14 +161,39 @@ 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 = 0x228000000, - TLS_ENTRY_SIZE = 0x200, - /// Equivalent to LINEAR_HEAP_VADDR, but expanded to cover the extra memory in the New 3DS. NEW_LINEAR_HEAP_VADDR = 0x30000000, NEW_LINEAR_HEAP_SIZE = 0x10000000, NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE, + + /// Area where TLS (Thread-Local Storage) buffers are allocated. + TLS_AREA_VADDR = NEW_LINEAR_HEAP_VADDR_END, + TLS_ENTRY_SIZE = 0x200, + TLS_AREA_SIZE = 0x10000000, + TLS_ADREA_VADDR_END = TLS_AREA_VADDR + TLS_AREA_SIZE, + + /// Application stack + STACK_VADDR = TLS_ADREA_VADDR_END, + STACK_SIZE = 0x10000, + STACK_VADDR_END = STACK_VADDR + STACK_SIZE, + + /// 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, }; /// Currently active page table |