diff options
Diffstat (limited to 'src/core')
136 files changed, 3690 insertions, 899 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index cb77b99ee..877a9e353 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -70,6 +70,8 @@ add_library(core STATIC file_sys/sdmc_factory.h file_sys/submission_package.cpp file_sys/submission_package.h + file_sys/system_archive/mii_model.cpp + file_sys/system_archive/mii_model.h file_sys/system_archive/ng_word.cpp file_sys/system_archive/ng_word.h file_sys/system_archive/system_archive.cpp @@ -111,6 +113,8 @@ add_library(core STATIC frontend/scope_acquire_window_context.h gdbstub/gdbstub.cpp gdbstub/gdbstub.h + hardware_interrupt_manager.cpp + hardware_interrupt_manager.h hle/ipc.h hle/ipc_helpers.h hle/kernel/address_arbiter.cpp @@ -175,6 +179,7 @@ add_library(core STATIC hle/service/acc/acc_u0.h hle/service/acc/acc_u1.cpp hle/service/acc/acc_u1.h + hle/service/acc/errors.h hle/service/acc/profile_manager.cpp hle/service/acc/profile_manager.h hle/service/am/am.cpp @@ -207,6 +212,8 @@ add_library(core STATIC hle/service/aoc/aoc_u.h hle/service/apm/apm.cpp hle/service/apm/apm.h + hle/service/apm/controller.cpp + hle/service/apm/controller.h hle/service/apm/interface.cpp hle/service/apm/interface.h hle/service/audio/audctl.cpp @@ -270,6 +277,7 @@ add_library(core STATIC hle/service/filesystem/fsp_srv.h hle/service/fgm/fgm.cpp hle/service/fgm/fgm.h + hle/service/friend/errors.h hle/service/friend/friend.cpp hle/service/friend/friend.h hle/service/friend/interface.cpp @@ -291,6 +299,7 @@ add_library(core STATIC hle/service/hid/irs.h hle/service/hid/xcd.cpp hle/service/hid/xcd.h + hle/service/hid/errors.h hle/service/hid/controllers/controller_base.cpp hle/service/hid/controllers/controller_base.h hle/service/hid/controllers/debug_pad.cpp @@ -367,6 +376,7 @@ add_library(core STATIC hle/service/nvdrv/devices/nvmap.h hle/service/nvdrv/interface.cpp hle/service/nvdrv/interface.h + hle/service/nvdrv/nvdata.h hle/service/nvdrv/nvdrv.cpp hle/service/nvdrv/nvdrv.h hle/service/nvdrv/nvmemp.cpp @@ -429,6 +439,8 @@ add_library(core STATIC hle/service/time/interface.h hle/service/time/time.cpp hle/service/time/time.h + hle/service/time/time_sharedmemory.cpp + hle/service/time/time_sharedmemory.h hle/service/usb/usb.cpp hle/service/usb/usb.h hle/service/vi/display/vi_display.cpp diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index c6691a8e1..45e94e625 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -44,13 +44,6 @@ public: /// Step CPU by one instruction virtual void Step() = 0; - /// Maps a backing memory region for the CPU - virtual void MapBackingMemory(VAddr address, std::size_t size, u8* memory, - Kernel::VMAPermission perms) = 0; - - /// Unmaps a region of memory that was previously mapped using MapBackingMemory - virtual void UnmapMemory(VAddr address, std::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 44307fa19..f1506b372 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -177,15 +177,6 @@ ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, ARM_Dynarmic::~ARM_Dynarmic() = default; -void ARM_Dynarmic::MapBackingMemory(u64 address, std::size_t size, u8* memory, - Kernel::VMAPermission perms) { - inner_unicorn.MapBackingMemory(address, size, memory, perms); -} - -void ARM_Dynarmic::UnmapMemory(u64 address, std::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 b701e97a3..504d46c68 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h @@ -23,9 +23,6 @@ public: ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); ~ARM_Dynarmic() override; - void MapBackingMemory(VAddr address, std::size_t size, u8* memory, - Kernel::VMAPermission perms) override; - void UnmapMemory(u64 address, std::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 4e07fe8b5..97d5c2a8a 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -50,11 +50,14 @@ static void CodeHook(uc_engine* uc, uint64_t address, uint32_t size, void* user_ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value, void* user_data) { + auto* const system = static_cast<System*>(user_data); + ARM_Interface::ThreadContext ctx{}; - Core::CurrentArmInterface().SaveContext(ctx); + system->CurrentArmInterface().SaveContext(ctx); ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x{:X}, pc=0x{:X}, lr=0x{:X}", addr, ctx.pc, ctx.cpu_registers[30]); - return {}; + + return false; } ARM_Unicorn::ARM_Unicorn(System& system) : system{system} { @@ -65,7 +68,7 @@ ARM_Unicorn::ARM_Unicorn(System& system) : system{system} { uc_hook hook{}; CHECKED(uc_hook_add(uc, &hook, UC_HOOK_INTR, (void*)InterruptHook, this, 0, -1)); - CHECKED(uc_hook_add(uc, &hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, this, 0, -1)); + CHECKED(uc_hook_add(uc, &hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, &system, 0, -1)); if (GDBStub::IsServerEnabled()) { CHECKED(uc_hook_add(uc, &hook, UC_HOOK_CODE, (void*)CodeHook, this, 0, -1)); last_bkpt_hit = false; @@ -76,15 +79,6 @@ ARM_Unicorn::~ARM_Unicorn() { CHECKED(uc_close(uc)); } -void ARM_Unicorn::MapBackingMemory(VAddr address, std::size_t size, u8* memory, - Kernel::VMAPermission perms) { - CHECKED(uc_mem_map_ptr(uc, address, size, static_cast<u32>(perms), memory)); -} - -void ARM_Unicorn::UnmapMemory(VAddr address, std::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 34e974b4d..fe2ffd70c 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h @@ -18,9 +18,6 @@ public: explicit ARM_Unicorn(System& system); ~ARM_Unicorn() override; - void MapBackingMemory(VAddr address, std::size_t size, u8* memory, - Kernel::VMAPermission perms) override; - void UnmapMemory(VAddr address, std::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 262411db8..3d0978cbf 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -19,12 +19,14 @@ #include "core/file_sys/vfs_concat.h" #include "core/file_sys/vfs_real.h" #include "core/gdbstub/gdbstub.h" +#include "core/hardware_interrupt_manager.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/thread.h" #include "core/hle/service/am/applets/applets.h" +#include "core/hle/service/apm/controller.h" #include "core/hle/service/glue/manager.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" @@ -102,7 +104,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, return vfs->OpenFile(path, FileSys::Mode::Read); } struct System::Impl { - explicit Impl(System& system) : kernel{system}, cpu_core_manager{system}, reporter{system} {} + explicit Impl(System& system) + : kernel{system}, cpu_core_manager{system}, applet_manager{system}, reporter{system} {} Cpu& CurrentCpuCore() { return cpu_core_manager.GetCurrentCore(); @@ -143,14 +146,14 @@ struct System::Impl { telemetry_session = std::make_unique<Core::TelemetrySession>(); service_manager = std::make_shared<Service::SM::ServiceManager>(); - Service::Init(service_manager, system, *virtual_filesystem); + Service::Init(service_manager, system); GDBStub::Init(); renderer = VideoCore::CreateRenderer(emu_window, system); if (!renderer->Init()) { return ResultStatus::ErrorVideoCore; } - + interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system); gpu_core = VideoCore::CreateGPU(system); is_powered_on = true; @@ -297,6 +300,7 @@ struct System::Impl { std::unique_ptr<VideoCore::RendererBase> renderer; std::unique_ptr<Tegra::GPU> gpu_core; std::shared_ptr<Tegra::DebugContext> debug_context; + std::unique_ptr<Core::Hardware::InterruptManager> interrupt_manager; CpuCoreManager cpu_core_manager; bool is_powered_on = false; @@ -306,6 +310,9 @@ struct System::Impl { /// Frontend applets Service::AM::Applets::AppletManager applet_manager; + /// APM (Performance) services + Service::APM::Controller apm_controller{core_timing}; + /// Glue services Service::Glue::ARPManager arp_manager; @@ -440,6 +447,14 @@ const Tegra::GPU& System::GPU() const { return *impl->gpu_core; } +Core::Hardware::InterruptManager& System::InterruptManager() { + return *impl->interrupt_manager; +} + +const Core::Hardware::InterruptManager& System::InterruptManager() const { + return *impl->interrupt_manager; +} + VideoCore::RendererBase& System::Renderer() { return *impl->renderer; } @@ -568,6 +583,14 @@ const Service::Glue::ARPManager& System::GetARPManager() const { return impl->arp_manager; } +Service::APM::Controller& System::GetAPMController() { + return impl->apm_controller; +} + +const Service::APM::Controller& System::GetAPMController() const { + return impl->apm_controller; +} + System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { return impl->Init(*this, emu_window); } diff --git a/src/core/core.h b/src/core/core.h index 70adb7af9..0138d93b0 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -43,6 +43,10 @@ struct AppletFrontendSet; class AppletManager; } // namespace AM::Applets +namespace APM { +class Controller; +} + namespace Glue { class ARPManager; } @@ -66,6 +70,10 @@ namespace Core::Timing { class CoreTiming; } +namespace Core::Hardware { +class InterruptManager; +} + namespace Core { class ARM_Interface; @@ -230,6 +238,12 @@ public: /// Provides a constant reference to the core timing instance. const Timing::CoreTiming& CoreTiming() const; + /// Provides a reference to the interrupt manager instance. + Core::Hardware::InterruptManager& InterruptManager(); + + /// Provides a constant reference to the interrupt manager instance. + const Core::Hardware::InterruptManager& InterruptManager() const; + /// Provides a reference to the kernel instance. Kernel::KernelCore& Kernel(); @@ -296,6 +310,10 @@ public: const Service::Glue::ARPManager& GetARPManager() const; + Service::APM::Controller& GetAPMController(); + + const Service::APM::Controller& GetAPMController() const; + private: System(); @@ -319,10 +337,6 @@ private: static System s_instance; }; -inline ARM_Interface& CurrentArmInterface() { - return System::GetInstance().CurrentArmInterface(); -} - inline Kernel::Process* CurrentProcess() { return System::GetInstance().CurrentProcess(); } diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp index ba63c3e61..21c410e34 100644 --- a/src/core/core_cpu.cpp +++ b/src/core/core_cpu.cpp @@ -53,16 +53,12 @@ bool CpuBarrier::Rendezvous() { Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index) : cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} { - if (Settings::values.use_cpu_jit) { #ifdef ARCHITECTURE_x86_64 - arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index); + arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index); #else - arm_interface = std::make_unique<ARM_Unicorn>(system); - LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); + arm_interface = std::make_unique<ARM_Unicorn>(system); + LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); #endif - } else { - arm_interface = std::make_unique<ARM_Unicorn>(system); - } scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface); } @@ -70,15 +66,12 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba Cpu::~Cpu() = default; std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) { - if (Settings::values.use_cpu_jit) { #ifdef ARCHITECTURE_x86_64 - return std::make_unique<DynarmicExclusiveMonitor>(num_cores); + return std::make_unique<DynarmicExclusiveMonitor>(num_cores); #else - return nullptr; // TODO(merry): Passthrough exclusive monitor + // TODO(merry): Passthrough exclusive monitor + return nullptr; #endif - } else { - return nullptr; // TODO(merry): Passthrough exclusive monitor - } } void Cpu::RunLoop(bool tight_loop) { diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index 6dd633363..46aceec3d 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -37,6 +37,7 @@ namespace Core::Crypto { constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; +constexpr u64 FULL_TICKET_SIZE = 0x400; using namespace Common; @@ -55,6 +56,99 @@ const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{ {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"}, }; +namespace { +template <std::size_t Size> +bool IsAllZeroArray(const std::array<u8, Size>& array) { + return std::all_of(array.begin(), array.end(), [](const auto& elem) { return elem == 0; }); +} +} // namespace + +u64 GetSignatureTypeDataSize(SignatureType type) { + switch (type) { + case SignatureType::RSA_4096_SHA1: + case SignatureType::RSA_4096_SHA256: + return 0x200; + case SignatureType::RSA_2048_SHA1: + case SignatureType::RSA_2048_SHA256: + return 0x100; + case SignatureType::ECDSA_SHA1: + case SignatureType::ECDSA_SHA256: + return 0x3C; + } + UNREACHABLE(); +} + +u64 GetSignatureTypePaddingSize(SignatureType type) { + switch (type) { + case SignatureType::RSA_4096_SHA1: + case SignatureType::RSA_4096_SHA256: + case SignatureType::RSA_2048_SHA1: + case SignatureType::RSA_2048_SHA256: + return 0x3C; + case SignatureType::ECDSA_SHA1: + case SignatureType::ECDSA_SHA256: + return 0x40; + } + UNREACHABLE(); +} + +SignatureType Ticket::GetSignatureType() const { + if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { + return ticket->sig_type; + } + if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { + return ticket->sig_type; + } + if (auto ticket = std::get_if<ECDSATicket>(&data)) { + return ticket->sig_type; + } + + UNREACHABLE(); +} + +TicketData& Ticket::GetData() { + if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { + return ticket->data; + } + if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { + return ticket->data; + } + if (auto ticket = std::get_if<ECDSATicket>(&data)) { + return ticket->data; + } + + UNREACHABLE(); +} + +const TicketData& Ticket::GetData() const { + if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { + return ticket->data; + } + if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { + return ticket->data; + } + if (auto ticket = std::get_if<ECDSATicket>(&data)) { + return ticket->data; + } + + UNREACHABLE(); +} + +u64 Ticket::GetSize() const { + const auto sig_type = GetSignatureType(); + + return sizeof(SignatureType) + GetSignatureTypeDataSize(sig_type) + + GetSignatureTypePaddingSize(sig_type) + sizeof(TicketData); +} + +Ticket Ticket::SynthesizeCommon(Key128 title_key, const std::array<u8, 16>& rights_id) { + RSA2048Ticket out{}; + out.sig_type = SignatureType::RSA_2048_SHA256; + out.data.rights_id = rights_id; + out.data.title_key_common = title_key; + return Ticket{out}; +} + Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) { Key128 out{}; @@ -135,6 +229,27 @@ void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) { } } +RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const { + if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek)) + return {}; + + const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek); + + std::vector<u8> extended_iv(eticket_extended_kek.begin(), eticket_extended_kek.begin() + 0x10); + std::array<u8, 0x230> extended_dec{}; + AESCipher<Key128> rsa_1(eticket_final, Mode::CTR); + rsa_1.SetIV(extended_iv); + rsa_1.Transcode(eticket_extended_kek.data() + 0x10, eticket_extended_kek.size() - 0x10, + extended_dec.data(), Op::Decrypt); + + RSAKeyPair<2048> rsa_key{}; + std::memcpy(rsa_key.decryption_key.data(), extended_dec.data(), rsa_key.decryption_key.size()); + std::memcpy(rsa_key.modulus.data(), extended_dec.data() + 0x100, rsa_key.modulus.size()); + std::memcpy(rsa_key.exponent.data(), extended_dec.data() + 0x200, rsa_key.exponent.size()); + + return rsa_key; +} + Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source) { AESCipher<Key128> mac_cipher(keyblob_key, Mode::ECB); Key128 mac_key{}; @@ -237,7 +352,7 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke return Loader::ResultStatus::Success; } -std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) { +std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save) { if (!ticket_save.IsOpen()) return {}; @@ -246,14 +361,14 @@ std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) { return {}; } - std::vector<TicketRaw> out; + std::vector<Ticket> out; for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) { if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 && buffer[offset + 3] == 0x0) { out.emplace_back(); auto& next = out.back(); - std::memcpy(&next, buffer.data() + offset, sizeof(TicketRaw)); - offset += next.size(); + std::memcpy(&next, buffer.data() + offset, sizeof(Ticket)); + offset += FULL_TICKET_SIZE; } } @@ -305,29 +420,23 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) { return offset; } -std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket, +std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, const RSAKeyPair<2048>& key) { - u32 cert_authority; - std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority)); - if (cert_authority == 0) + const auto issuer = ticket.GetData().issuer; + if (issuer == std::array<u8, 0x40>{}) return {}; - if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) { - LOG_INFO(Crypto, - "Attempting to parse ticket with non-standard certificate authority {:08X}.", - cert_authority); + if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') { + LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority."); } - Key128 rights_id; - std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128)); + Key128 rights_id = ticket.GetData().rights_id; if (rights_id == Key128{}) return {}; - Key128 key_temp{}; - - if (!std::any_of(ticket.begin() + 0x190, ticket.begin() + 0x280, [](u8 b) { return b != 0; })) { - std::memcpy(key_temp.data(), ticket.data() + 0x180, key_temp.size()); - return std::make_pair(rights_id, key_temp); + if (!std::any_of(ticket.GetData().title_key_common_pad.begin(), + ticket.GetData().title_key_common_pad.end(), [](u8 b) { return b != 0; })) { + return std::make_pair(rights_id, ticket.GetData().title_key_common); } mbedtls_mpi D; // RSA Private Exponent @@ -342,7 +451,7 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket, mbedtls_mpi_read_binary(&D, key.decryption_key.data(), key.decryption_key.size()); mbedtls_mpi_read_binary(&N, key.modulus.data(), key.modulus.size()); - mbedtls_mpi_read_binary(&S, ticket.data() + 0x180, 0x100); + mbedtls_mpi_read_binary(&S, ticket.GetData().title_key_block.data(), 0x100); mbedtls_mpi_exp_mod(&M, &S, &D, &N, nullptr); @@ -366,6 +475,7 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket, return {}; ASSERT(*offset > 0); + Key128 key_temp{}; std::memcpy(key_temp.data(), m_2.data() + *offset, key_temp.size()); return std::make_pair(rights_id, key_temp); @@ -450,6 +560,8 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16); encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]); + } else if (out[0].compare(0, 20, "eticket_extended_kek") == 0) { + eticket_extended_kek = Common::HexStringToArray<576>(out[1]); } else { for (const auto& kv : KEYS_VARIABLE_LENGTH) { if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) @@ -862,20 +974,19 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) { // Titlekeys data.DecryptProdInfo(GetBISKey(0)); - const auto eticket_extended_kek = data.GetETicketExtendedKek(); + eticket_extended_kek = data.GetETicketExtendedKek(); + WriteKeyToFile(KeyCategory::Console, "eticket_extended_kek", eticket_extended_kek); + PopulateTickets(); +} - std::vector<u8> extended_iv(0x10); - std::memcpy(extended_iv.data(), eticket_extended_kek.data(), extended_iv.size()); - std::array<u8, 0x230> extended_dec{}; - AESCipher<Key128> rsa_1(eticket_final, Mode::CTR); - rsa_1.SetIV(extended_iv); - rsa_1.Transcode(eticket_extended_kek.data() + 0x10, eticket_extended_kek.size() - 0x10, - extended_dec.data(), Op::Decrypt); +void KeyManager::PopulateTickets() { + const auto rsa_key = GetETicketRSAKey(); - RSAKeyPair<2048> rsa_key{}; - std::memcpy(rsa_key.decryption_key.data(), extended_dec.data(), rsa_key.decryption_key.size()); - std::memcpy(rsa_key.modulus.data(), extended_dec.data() + 0x100, rsa_key.modulus.size()); - std::memcpy(rsa_key.exponent.data(), extended_dec.data() + 0x200, rsa_key.exponent.size()); + if (rsa_key == RSAKeyPair<2048>{}) + return; + + if (!common_tickets.empty() && !personal_tickets.empty()) + return; const FileUtil::IOFile save1(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/80000000000000e1", @@ -886,19 +997,41 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) { const auto blob2 = GetTicketblob(save2); auto res = GetTicketblob(save1); + const auto idx = res.size(); res.insert(res.end(), blob2.begin(), blob2.end()); - for (const auto& raw : res) { - const auto pair = ParseTicket(raw, rsa_key); + for (std::size_t i = 0; i < res.size(); ++i) { + const auto common = i < idx; + const auto pair = ParseTicket(res[i], rsa_key); if (!pair) continue; const auto& [rid, key] = *pair; u128 rights_id; std::memcpy(rights_id.data(), rid.data(), rid.size()); + + if (common) { + common_tickets[rights_id] = res[i]; + } else { + personal_tickets[rights_id] = res[i]; + } + SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); } } +void KeyManager::SynthesizeTickets() { + for (const auto& key : s128_keys) { + if (key.first.type != S128KeyType::Titlekey) { + continue; + } + u128 rights_id{key.first.field1, key.first.field2}; + Key128 rights_id_2; + std::memcpy(rights_id_2.data(), rights_id.data(), rights_id_2.size()); + const auto ticket = Ticket::SynthesizeCommon(key.second, rights_id_2); + common_tickets.insert_or_assign(rights_id, ticket); + } +} + void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) { if (key == Key128{}) return; @@ -997,6 +1130,46 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) { DeriveBase(); } +const std::map<u128, Ticket>& KeyManager::GetCommonTickets() const { + return common_tickets; +} + +const std::map<u128, Ticket>& KeyManager::GetPersonalizedTickets() const { + return personal_tickets; +} + +bool KeyManager::AddTicketCommon(Ticket raw) { + const auto rsa_key = GetETicketRSAKey(); + if (rsa_key == RSAKeyPair<2048>{}) + return false; + + const auto pair = ParseTicket(raw, rsa_key); + if (!pair) + return false; + const auto& [rid, key] = *pair; + u128 rights_id; + std::memcpy(rights_id.data(), rid.data(), rid.size()); + common_tickets[rights_id] = raw; + SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); + return true; +} + +bool KeyManager::AddTicketPersonalized(Ticket raw) { + const auto rsa_key = GetETicketRSAKey(); + if (rsa_key == RSAKeyPair<2048>{}) + return false; + + const auto pair = ParseTicket(raw, rsa_key); + if (!pair) + return false; + const auto& [rid, key] = *pair; + u128 rights_id; + std::memcpy(rights_id.data(), rid.data(), rid.size()); + common_tickets[rights_id] = raw; + SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); + return true; +} + const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = { {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}}, {"eticket_rsa_kek_source", diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index 22f268c65..7265c4171 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h @@ -9,8 +9,10 @@ #include <optional> #include <string> +#include <variant> #include <boost/container/flat_map.hpp> #include <fmt/format.h> +#include "common/common_funcs.h" #include "common/common_types.h" #include "core/crypto/partition_data_manager.h" #include "core/file_sys/vfs_types.h" @@ -30,7 +32,79 @@ constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180; using Key128 = std::array<u8, 0x10>; using Key256 = std::array<u8, 0x20>; using SHA256Hash = std::array<u8, 0x20>; -using TicketRaw = std::array<u8, 0x400>; + +enum class SignatureType { + RSA_4096_SHA1 = 0x10000, + RSA_2048_SHA1 = 0x10001, + ECDSA_SHA1 = 0x10002, + RSA_4096_SHA256 = 0x10003, + RSA_2048_SHA256 = 0x10004, + ECDSA_SHA256 = 0x10005, +}; + +u64 GetSignatureTypeDataSize(SignatureType type); +u64 GetSignatureTypePaddingSize(SignatureType type); + +enum class TitleKeyType : u8 { + Common = 0, + Personalized = 1, +}; + +struct TicketData { + std::array<u8, 0x40> issuer; + union { + std::array<u8, 0x100> title_key_block; + + struct { + Key128 title_key_common; + std::array<u8, 0xF0> title_key_common_pad; + }; + }; + + INSERT_PADDING_BYTES(0x1); + TitleKeyType type; + INSERT_PADDING_BYTES(0x3); + u8 revision; + INSERT_PADDING_BYTES(0xA); + u64 ticket_id; + u64 device_id; + std::array<u8, 0x10> rights_id; + u32 account_id; + INSERT_PADDING_BYTES(0x14C); +}; +static_assert(sizeof(TicketData) == 0x2C0, "TicketData has incorrect size."); + +struct RSA4096Ticket { + SignatureType sig_type; + std::array<u8, 0x200> sig_data; + INSERT_PADDING_BYTES(0x3C); + TicketData data; +}; + +struct RSA2048Ticket { + SignatureType sig_type; + std::array<u8, 0x100> sig_data; + INSERT_PADDING_BYTES(0x3C); + TicketData data; +}; + +struct ECDSATicket { + SignatureType sig_type; + std::array<u8, 0x3C> sig_data; + INSERT_PADDING_BYTES(0x40); + TicketData data; +}; + +struct Ticket { + std::variant<RSA4096Ticket, RSA2048Ticket, ECDSATicket> data; + + SignatureType GetSignatureType() const; + TicketData& GetData(); + const TicketData& GetData() const; + u64 GetSize() const; + + static Ticket SynthesizeCommon(Key128 title_key, const std::array<u8, 0x10>& rights_id); +}; static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big."); static_assert(sizeof(Key256) == 32, "Key256 must be 256 bytes big."); @@ -43,6 +117,19 @@ struct RSAKeyPair { std::array<u8, 4> exponent; }; +template <size_t bit_size, size_t byte_size> +bool operator==(const RSAKeyPair<bit_size, byte_size>& lhs, + const RSAKeyPair<bit_size, byte_size>& rhs) { + return std::tie(lhs.encryption_key, lhs.decryption_key, lhs.modulus, lhs.exponent) == + std::tie(rhs.encryption_key, rhs.decryption_key, rhs.modulus, rhs.exponent); +} + +template <size_t bit_size, size_t byte_size> +bool operator!=(const RSAKeyPair<bit_size, byte_size>& lhs, + const RSAKeyPair<bit_size, byte_size>& rhs) { + return !(lhs == rhs); +} + enum class KeyCategory : u8 { Standard, Title, @@ -151,22 +238,35 @@ public: static bool KeyFileExists(bool title); - // Call before using the sd seed to attempt to derive it if it dosen't exist. Needs system save - // 8*43 and the private file to exist. + // Call before using the sd seed to attempt to derive it if it dosen't exist. Needs system + // save 8*43 and the private file to exist. void DeriveSDSeedLazy(); bool BaseDeriveNecessary() const; void DeriveBase(); void DeriveETicket(PartitionDataManager& data); + void PopulateTickets(); + void SynthesizeTickets(); void PopulateFromPartitionData(PartitionDataManager& data); + const std::map<u128, Ticket>& GetCommonTickets() const; + const std::map<u128, Ticket>& GetPersonalizedTickets() const; + + bool AddTicketCommon(Ticket raw); + bool AddTicketPersonalized(Ticket raw); + private: std::map<KeyIndex<S128KeyType>, Key128> s128_keys; std::map<KeyIndex<S256KeyType>, Key256> s256_keys; + // Map from rights ID to ticket + std::map<u128, Ticket> common_tickets; + std::map<u128, Ticket> personal_tickets; + std::array<std::array<u8, 0xB0>, 0x20> encrypted_keyblobs{}; std::array<std::array<u8, 0x90>, 0x20> keyblobs{}; + std::array<u8, 576> eticket_extended_kek{}; bool dev_mode; void LoadFromFile(const std::string& filename, bool is_title_keys); @@ -178,6 +278,8 @@ private: void DeriveGeneralPurposeKeys(std::size_t crypto_revision); + RSAKeyPair<2048> GetETicketRSAKey() const; + void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); @@ -195,11 +297,11 @@ std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblo std::optional<Key128> DeriveSDSeed(); Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys); -std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save); +std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save); -// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority (offset -// 0x140-0x144 is zero) -std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket, +// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority +// (offset 0x140-0x144 is zero) +std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, const RSAKeyPair<2048>& eticket_extended_key); } // namespace Core::Crypto diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index eb76174c5..7310b3602 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -94,6 +94,10 @@ u64 ProgramMetadata::GetFilesystemPermissions() const { return aci_file_access.permissions; } +u32 ProgramMetadata::GetSystemResourceSize() const { + return npdm_header.system_resource_size; +} + const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const { return aci_kernel_capabilities; } diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index 43bf2820a..88ec97d85 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h @@ -58,6 +58,7 @@ public: u32 GetMainThreadStackSize() const; u64 GetTitleID() const; u64 GetFilesystemPermissions() const; + u32 GetSystemResourceSize() const; const KernelCapabilityDescriptors& GetKernelCapabilities() const; void Print() const; @@ -76,7 +77,8 @@ private: u8 reserved_3; u8 main_thread_priority; u8 main_thread_cpu; - std::array<u8, 8> reserved_4; + std::array<u8, 4> reserved_4; + u32_le system_resource_size; u32_le process_category; u32_le main_stack_size; std::array<u8, 0x10> application_name; diff --git a/src/core/file_sys/system_archive/mii_model.cpp b/src/core/file_sys/system_archive/mii_model.cpp new file mode 100644 index 000000000..6a9add87c --- /dev/null +++ b/src/core/file_sys/system_archive/mii_model.cpp @@ -0,0 +1,46 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/file_sys/system_archive/mii_model.h" +#include "core/file_sys/vfs_vector.h" + +namespace FileSys::SystemArchive { + +namespace MiiModelData { + +constexpr std::array<u8, 0x10> NFTR_STANDARD{'N', 'F', 'T', 'R', 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +constexpr std::array<u8, 0x10> NFSR_STANDARD{'N', 'F', 'S', 'R', 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +constexpr auto TEXTURE_LOW_LINEAR = NFTR_STANDARD; +constexpr auto TEXTURE_LOW_SRGB = NFTR_STANDARD; +constexpr auto TEXTURE_MID_LINEAR = NFTR_STANDARD; +constexpr auto TEXTURE_MID_SRGB = NFTR_STANDARD; +constexpr auto SHAPE_HIGH = NFSR_STANDARD; +constexpr auto SHAPE_MID = NFSR_STANDARD; + +} // namespace MiiModelData + +VirtualDir MiiModel() { + auto out = std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, + std::vector<VirtualDir>{}, "data"); + + out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_LOW_LINEAR.size()>>( + MiiModelData::TEXTURE_LOW_LINEAR, "NXTextureLowLinear.dat")); + out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_LOW_SRGB.size()>>( + MiiModelData::TEXTURE_LOW_SRGB, "NXTextureLowSRGB.dat")); + out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_MID_LINEAR.size()>>( + MiiModelData::TEXTURE_MID_LINEAR, "NXTextureMidLinear.dat")); + out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_MID_SRGB.size()>>( + MiiModelData::TEXTURE_MID_SRGB, "NXTextureMidSRGB.dat")); + out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_HIGH.size()>>( + MiiModelData::SHAPE_HIGH, "ShapeHigh.dat")); + out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_MID.size()>>( + MiiModelData::SHAPE_MID, "ShapeMid.dat")); + + return std::move(out); +} + +} // namespace FileSys::SystemArchive diff --git a/src/core/file_sys/system_archive/mii_model.h b/src/core/file_sys/system_archive/mii_model.h new file mode 100644 index 000000000..6c2d9398b --- /dev/null +++ b/src/core/file_sys/system_archive/mii_model.h @@ -0,0 +1,13 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/file_sys/vfs_types.h" + +namespace FileSys::SystemArchive { + +VirtualDir MiiModel(); + +} // namespace FileSys::SystemArchive diff --git a/src/core/file_sys/system_archive/system_archive.cpp b/src/core/file_sys/system_archive/system_archive.cpp index c9722ed77..6d8445383 100644 --- a/src/core/file_sys/system_archive/system_archive.cpp +++ b/src/core/file_sys/system_archive/system_archive.cpp @@ -4,6 +4,7 @@ #include "common/logging/log.h" #include "core/file_sys/romfs.h" +#include "core/file_sys/system_archive/mii_model.h" #include "core/file_sys/system_archive/ng_word.h" #include "core/file_sys/system_archive/system_archive.h" #include "core/file_sys/system_archive/system_version.h" @@ -24,7 +25,7 @@ struct SystemArchiveDescriptor { constexpr std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHIVES{{ {0x0100000000000800, "CertStore", nullptr}, {0x0100000000000801, "ErrorMessage", nullptr}, - {0x0100000000000802, "MiiModel", nullptr}, + {0x0100000000000802, "MiiModel", &MiiModel}, {0x0100000000000803, "BrowserDll", nullptr}, {0x0100000000000804, "Help", nullptr}, {0x0100000000000805, "SharedFont", nullptr}, diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp new file mode 100644 index 000000000..c2115db2d --- /dev/null +++ b/src/core/hardware_interrupt_manager.cpp @@ -0,0 +1,30 @@ +// Copyright 2019 Yuzu Emulator Project +// 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/hardware_interrupt_manager.h" +#include "core/hle/service/nvdrv/interface.h" +#include "core/hle/service/sm/sm.h" + +namespace Core::Hardware { + +InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { + gpu_interrupt_event = + system.CoreTiming().RegisterEvent("GPUInterrupt", [this](u64 message, s64) { + auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv"); + const u32 syncpt = static_cast<u32>(message >> 32); + const u32 value = static_cast<u32>(message); + nvdrv->SignalGPUInterruptSyncpt(syncpt, value); + }); +} + +InterruptManager::~InterruptManager() = default; + +void InterruptManager::GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) { + const u64 msg = (static_cast<u64>(syncpoint_id) << 32ULL) | value; + system.CoreTiming().ScheduleEvent(10, gpu_interrupt_event, msg); +} + +} // namespace Core::Hardware diff --git a/src/core/hardware_interrupt_manager.h b/src/core/hardware_interrupt_manager.h new file mode 100644 index 000000000..494db883a --- /dev/null +++ b/src/core/hardware_interrupt_manager.h @@ -0,0 +1,31 @@ +// Copyright 2019 Yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +namespace Core { +class System; +} + +namespace Core::Timing { +struct EventType; +} + +namespace Core::Hardware { + +class InterruptManager { +public: + explicit InterruptManager(Core::System& system); + ~InterruptManager(); + + void GPUInterruptSyncpt(u32 syncpoint_id, u32 value); + +private: + Core::System& system; + Core::Timing::EventType* gpu_interrupt_event{}; +}; + +} // namespace Core::Hardware diff --git a/src/core/hle/kernel/code_set.h b/src/core/hle/kernel/code_set.h index 879957dcb..d8ad54030 100644 --- a/src/core/hle/kernel/code_set.h +++ b/src/core/hle/kernel/code_set.h @@ -8,6 +8,7 @@ #include <vector> #include "common/common_types.h" +#include "core/hle/kernel/physical_memory.h" namespace Kernel { @@ -77,7 +78,7 @@ struct CodeSet final { } /// The overall data that backs this code set. - std::vector<u8> memory; + Kernel::PhysicalMemory memory; /// The segments that comprise this code set. std::array<Segment, 3> segments; diff --git a/src/core/hle/kernel/physical_memory.h b/src/core/hle/kernel/physical_memory.h new file mode 100644 index 000000000..090565310 --- /dev/null +++ b/src/core/hle/kernel/physical_memory.h @@ -0,0 +1,19 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/alignment.h" + +namespace Kernel { + +// This encapsulation serves 2 purposes: +// - First, to encapsulate host physical memory under a single type and set an +// standard for managing it. +// - Second to ensure all host backing memory used is aligned to 256 bytes due +// to strict alignment restrictions on GPU memory. + +using PhysicalMemory = std::vector<u8, Common::AlignmentAllocator<u8, 256>>; + +} // namespace Kernel diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 7cfc513a1..e80a12ac3 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include <algorithm> +#include <bitset> #include <memory> #include <random> #include "common/alignment.h" @@ -48,8 +49,58 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) { } } // Anonymous namespace -SharedPtr<Process> Process::Create(Core::System& system, std::string name, - Process::ProcessType type) { +// Represents a page used for thread-local storage. +// +// Each TLS page contains slots that may be used by processes and threads. +// Every process and thread is created with a slot in some arbitrary page +// (whichever page happens to have an available slot). +class TLSPage { +public: + static constexpr std::size_t num_slot_entries = Memory::PAGE_SIZE / Memory::TLS_ENTRY_SIZE; + + explicit TLSPage(VAddr address) : base_address{address} {} + + bool HasAvailableSlots() const { + return !is_slot_used.all(); + } + + VAddr GetBaseAddress() const { + return base_address; + } + + std::optional<VAddr> ReserveSlot() { + for (std::size_t i = 0; i < is_slot_used.size(); i++) { + if (is_slot_used[i]) { + continue; + } + + is_slot_used[i] = true; + return base_address + (i * Memory::TLS_ENTRY_SIZE); + } + + return std::nullopt; + } + + void ReleaseSlot(VAddr address) { + // Ensure that all given addresses are consistent with how TLS pages + // are intended to be used when releasing slots. + ASSERT(IsWithinPage(address)); + ASSERT((address % Memory::TLS_ENTRY_SIZE) == 0); + + const std::size_t index = (address - base_address) / Memory::TLS_ENTRY_SIZE; + is_slot_used[index] = false; + } + +private: + bool IsWithinPage(VAddr address) const { + return base_address <= address && address < base_address + Memory::PAGE_SIZE; + } + + VAddr base_address; + std::bitset<num_slot_entries> is_slot_used; +}; + +SharedPtr<Process> Process::Create(Core::System& system, std::string name, ProcessType type) { auto& kernel = system.Kernel(); SharedPtr<Process> process(new Process(system)); @@ -78,20 +129,17 @@ u64 Process::GetTotalPhysicalMemoryAvailable() const { return vm_manager.GetTotalPhysicalMemoryAvailable(); } -u64 Process::GetTotalPhysicalMemoryAvailableWithoutMmHeap() const { - // TODO: Subtract the personal heap size from this when the - // personal heap is implemented. - return GetTotalPhysicalMemoryAvailable(); +u64 Process::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const { + return GetTotalPhysicalMemoryAvailable() - GetSystemResourceSize(); } u64 Process::GetTotalPhysicalMemoryUsed() const { - return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size; + return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size + + GetSystemResourceUsage(); } -u64 Process::GetTotalPhysicalMemoryUsedWithoutMmHeap() const { - // TODO: Subtract the personal heap size from this when the - // personal heap is implemented. - return GetTotalPhysicalMemoryUsed(); +u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const { + return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage(); } void Process::RegisterThread(const Thread* thread) { @@ -121,6 +169,7 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { program_id = metadata.GetTitleID(); ideal_core = metadata.GetMainThreadCore(); is_64bit_process = metadata.Is64BitProgram(); + system_resource_size = metadata.GetSystemResourceSize(); vm_manager.Reset(metadata.GetAddressSpaceType()); @@ -135,19 +184,11 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { } void Process::Run(s32 main_thread_priority, u64 stack_size) { - // The kernel always ensures that the given stack size is page aligned. - main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE); - - // 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. - const VAddr mapping_address = vm_manager.GetTLSIORegionEndAddress() - main_thread_stack_size; - vm_manager - .MapMemoryBlock(mapping_address, std::make_shared<std::vector<u8>>(main_thread_stack_size), - 0, main_thread_stack_size, MemoryState::Stack) - .Unwrap(); + AllocateMainThreadStack(stack_size); + tls_region_address = CreateTLSRegion(); vm_manager.LogLayout(); + ChangeStatus(ProcessStatus::Running); SetupMainThread(*this, kernel, main_thread_priority); @@ -177,69 +218,66 @@ void Process::PrepareForTermination() { stop_threads(system.Scheduler(2).GetThreadList()); stop_threads(system.Scheduler(3).GetThreadList()); + FreeTLSRegion(tls_region_address); + tls_region_address = 0; + ChangeStatus(ProcessStatus::Exited); } /** - * Finds a free location for the TLS section of a thread. - * @param tls_slots The TLS page array of the thread's owner process. - * Returns a tuple of (page, slot, alloc_needed) where: - * page: The index of the first allocated TLS page that has free slots. - * slot: The index of the first free slot in the indicated page. - * alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full). + * Attempts to find a TLS page that contains a free slot for + * use by a thread. + * + * @returns If a page with an available slot is found, then an iterator + * pointing to the page is returned. Otherwise the end iterator + * is returned instead. */ -static std::tuple<std::size_t, std::size_t, bool> FindFreeThreadLocalSlot( - const std::vector<std::bitset<8>>& tls_slots) { - // Iterate over all the allocated pages, and try to find one where not all slots are used. - for (std::size_t page = 0; page < tls_slots.size(); ++page) { - const auto& page_tls_slots = tls_slots[page]; - if (!page_tls_slots.all()) { - // We found a page with at least one free slot, find which slot it is - for (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) { - if (!page_tls_slots.test(slot)) { - return std::make_tuple(page, slot, false); - } - } - } - } - - return std::make_tuple(0, 0, true); +static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) { + return std::find_if(tls_pages.begin(), tls_pages.end(), + [](const auto& page) { return page.HasAvailableSlots(); }); } -VAddr Process::MarkNextAvailableTLSSlotAsUsed(Thread& thread) { - auto [available_page, available_slot, needs_allocation] = FindFreeThreadLocalSlot(tls_slots); - const VAddr tls_begin = vm_manager.GetTLSIORegionBaseAddress(); +VAddr Process::CreateTLSRegion() { + auto tls_page_iter = FindTLSPageWithAvailableSlots(tls_pages); - if (needs_allocation) { - tls_slots.emplace_back(0); // The page is completely available at the start - available_page = tls_slots.size() - 1; - available_slot = 0; // Use the first slot in the new page + if (tls_page_iter == tls_pages.cend()) { + const auto region_address = + vm_manager.FindFreeRegion(vm_manager.GetTLSIORegionBaseAddress(), + vm_manager.GetTLSIORegionEndAddress(), Memory::PAGE_SIZE); + ASSERT(region_address.Succeeded()); - // Allocate some memory from the end of the linear heap for this region. - auto& tls_memory = thread.GetTLSMemory(); - tls_memory->insert(tls_memory->end(), Memory::PAGE_SIZE, 0); + const auto map_result = vm_manager.MapMemoryBlock( + *region_address, std::make_shared<PhysicalMemory>(Memory::PAGE_SIZE), 0, + Memory::PAGE_SIZE, MemoryState::ThreadLocal); + ASSERT(map_result.Succeeded()); - vm_manager.RefreshMemoryBlockMappings(tls_memory.get()); + tls_pages.emplace_back(*region_address); - vm_manager.MapMemoryBlock(tls_begin + available_page * Memory::PAGE_SIZE, tls_memory, 0, - Memory::PAGE_SIZE, MemoryState::ThreadLocal); - } + const auto reserve_result = tls_pages.back().ReserveSlot(); + ASSERT(reserve_result.has_value()); - tls_slots[available_page].set(available_slot); + return *reserve_result; + } - return tls_begin + available_page * Memory::PAGE_SIZE + available_slot * Memory::TLS_ENTRY_SIZE; + return *tls_page_iter->ReserveSlot(); } -void Process::FreeTLSSlot(VAddr tls_address) { - const VAddr tls_base = tls_address - vm_manager.GetTLSIORegionBaseAddress(); - const VAddr tls_page = tls_base / Memory::PAGE_SIZE; - const VAddr tls_slot = (tls_base % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE; +void Process::FreeTLSRegion(VAddr tls_address) { + const VAddr aligned_address = Common::AlignDown(tls_address, Memory::PAGE_SIZE); + auto iter = + std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) { + return page.GetBaseAddress() == aligned_address; + }); - tls_slots[tls_page].reset(tls_slot); + // Something has gone very wrong if we're freeing a region + // with no actual page available. + ASSERT(iter != tls_pages.cend()); + + iter->ReleaseSlot(tls_address); } void Process::LoadModule(CodeSet module_, VAddr base_addr) { - const auto memory = std::make_shared<std::vector<u8>>(std::move(module_.memory)); + const auto memory = std::make_shared<PhysicalMemory>(std::move(module_.memory)); const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions, MemoryState memory_state) { @@ -282,4 +320,16 @@ void Process::ChangeStatus(ProcessStatus new_status) { WakeupAllWaitingThreads(); } +void Process::AllocateMainThreadStack(u64 stack_size) { + // The kernel always ensures that the given stack size is page aligned. + main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE); + + // Allocate and map the main thread stack + const VAddr mapping_address = vm_manager.GetTLSIORegionEndAddress() - main_thread_stack_size; + vm_manager + .MapMemoryBlock(mapping_address, std::make_shared<PhysicalMemory>(main_thread_stack_size), + 0, main_thread_stack_size, MemoryState::Stack) + .Unwrap(); +} + } // namespace Kernel diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 248fd3840..c2df451f3 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -5,7 +5,6 @@ #pragma once #include <array> -#include <bitset> #include <cstddef> #include <list> #include <string> @@ -32,6 +31,7 @@ namespace Kernel { class KernelCore; class ResourceLimit; class Thread; +class TLSPage; struct CodeSet; @@ -135,6 +135,11 @@ public: return mutex; } + /// Gets the address to the process' dedicated TLS region. + VAddr GetTLSRegionAddress() const { + return tls_region_address; + } + /// Gets the current status of the process ProcessStatus GetStatus() const { return status; @@ -168,8 +173,24 @@ public: return capabilities.GetPriorityMask(); } - u32 IsVirtualMemoryEnabled() const { - return is_virtual_address_memory_enabled; + /// Gets the amount of secure memory to allocate for memory management. + u32 GetSystemResourceSize() const { + return system_resource_size; + } + + /// Gets the amount of secure memory currently in use for memory management. + u32 GetSystemResourceUsage() const { + // On hardware, this returns the amount of system resource memory that has + // been used by the kernel. This is problematic for Yuzu to emulate, because + // system resource memory is used for page tables -- and yuzu doesn't really + // have a way to calculate how much memory is required for page tables for + // the current process at any given time. + // TODO: Is this even worth implementing? Games may retrieve this value via + // an SDK function that gets used + available system resource size for debug + // or diagnostic purposes. However, it seems unlikely that a game would make + // decisions based on how much system memory is dedicated to its page tables. + // Is returning a value other than zero wise? + return 0; } /// Whether this process is an AArch64 or AArch32 process. @@ -196,15 +217,15 @@ public: u64 GetTotalPhysicalMemoryAvailable() const; /// Retrieves the total physical memory available to this process in bytes, - /// without the size of the personal heap added to it. - u64 GetTotalPhysicalMemoryAvailableWithoutMmHeap() const; + /// without the size of the personal system resource heap added to it. + u64 GetTotalPhysicalMemoryAvailableWithoutSystemResource() const; /// Retrieves the total physical memory used by this process in bytes. u64 GetTotalPhysicalMemoryUsed() const; /// Retrieves the total physical memory used by this process in bytes, - /// without the size of the personal heap added to it. - u64 GetTotalPhysicalMemoryUsedWithoutMmHeap() const; + /// without the size of the personal system resource heap added to it. + u64 GetTotalPhysicalMemoryUsedWithoutSystemResource() const; /// Gets the list of all threads created with this process as their owner. const std::list<const Thread*>& GetThreadList() const { @@ -260,10 +281,10 @@ public: // Thread-local storage management // Marks the next available region as used and returns the address of the slot. - VAddr MarkNextAvailableTLSSlotAsUsed(Thread& thread); + [[nodiscard]] VAddr CreateTLSRegion(); // Frees a used TLS slot identified by the given address - void FreeTLSSlot(VAddr tls_address); + void FreeTLSRegion(VAddr tls_address); private: explicit Process(Core::System& system); @@ -280,6 +301,9 @@ private: /// a process signal. void ChangeStatus(ProcessStatus new_status); + /// Allocates the main thread stack for the process, given the stack size in bytes. + void AllocateMainThreadStack(u64 stack_size); + /// Memory manager for this process. Kernel::VMManager vm_manager; @@ -290,7 +314,7 @@ private: u64 code_memory_size = 0; /// Current status of the process - ProcessStatus status; + ProcessStatus status{}; /// The ID of this process u64 process_id = 0; @@ -298,19 +322,23 @@ private: /// Title ID corresponding to the process u64 program_id = 0; + /// Specifies additional memory to be reserved for the process's memory management by the + /// system. When this is non-zero, secure memory is allocated and used for page table allocation + /// instead of using the normal global page tables/memory block management. + u32 system_resource_size = 0; + /// Resource limit descriptor for this process SharedPtr<ResourceLimit> resource_limit; /// The ideal CPU core for this process, threads are scheduled on this core by default. u8 ideal_core = 0; - u32 is_virtual_address_memory_enabled = 0; /// The Thread Local Storage area is allocated as processes create threads, /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part /// holds the TLS for a specific thread. This vector contains which parts are in use for each /// page as a bitmask. /// This vector will grow as more pages are allocated for new threads. - std::vector<std::bitset<8>> tls_slots; + std::vector<TLSPage> tls_pages; /// Contains the parsed process capability descriptors. ProcessCapabilities capabilities; @@ -338,8 +366,11 @@ private: /// variable related facilities. Mutex mutex; + /// Address indicating the location of the process' dedicated TLS region. + VAddr tls_region_address = 0; + /// Random values for svcGetInfo RandomEntropy - std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy; + std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{}; /// List of threads that are running with this process as their owner. std::list<const Thread*> thread_list; diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index f15c5ee36..a815c4eea 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -28,7 +28,7 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process* owner_ shared_memory->other_permissions = other_permissions; if (address == 0) { - shared_memory->backing_block = std::make_shared<std::vector<u8>>(size); + shared_memory->backing_block = std::make_shared<Kernel::PhysicalMemory>(size); shared_memory->backing_block_offset = 0; // Refresh the address mappings for the current process. @@ -59,8 +59,8 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process* owner_ } SharedPtr<SharedMemory> SharedMemory::CreateForApplet( - KernelCore& kernel, std::shared_ptr<std::vector<u8>> heap_block, std::size_t offset, u64 size, - MemoryPermission permissions, MemoryPermission other_permissions, std::string name) { + KernelCore& kernel, std::shared_ptr<Kernel::PhysicalMemory> heap_block, std::size_t offset, + u64 size, MemoryPermission permissions, MemoryPermission other_permissions, std::string name) { SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel)); shared_memory->owner_process = nullptr; diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index c2b6155e1..01ca6dcd2 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -10,6 +10,7 @@ #include "common/common_types.h" #include "core/hle/kernel/object.h" +#include "core/hle/kernel/physical_memory.h" #include "core/hle/kernel/process.h" #include "core/hle/result.h" @@ -62,12 +63,10 @@ public: * block. * @param name Optional object name, used for debugging purposes. */ - static SharedPtr<SharedMemory> CreateForApplet(KernelCore& kernel, - std::shared_ptr<std::vector<u8>> heap_block, - std::size_t offset, u64 size, - MemoryPermission permissions, - MemoryPermission other_permissions, - std::string name = "Unknown Applet"); + static SharedPtr<SharedMemory> CreateForApplet( + KernelCore& kernel, std::shared_ptr<Kernel::PhysicalMemory> heap_block, std::size_t offset, + u64 size, MemoryPermission permissions, MemoryPermission other_permissions, + std::string name = "Unknown Applet"); std::string GetTypeName() const override { return "SharedMemory"; @@ -135,7 +134,7 @@ private: ~SharedMemory() override; /// Backing memory for this shared memory block. - std::shared_ptr<std::vector<u8>> backing_block; + std::shared_ptr<PhysicalMemory> backing_block; /// Offset into the backing block for this shared memory. std::size_t backing_block_offset = 0; /// Size of the memory block. Page-aligned. diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index de6363ff2..1fd1a732a 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -98,9 +98,9 @@ ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_add return ERR_INVALID_ADDRESS_STATE; } - if (!vm_manager.IsWithinNewMapRegion(dst_addr, size)) { + if (!vm_manager.IsWithinStackRegion(dst_addr, size)) { LOG_ERROR(Kernel_SVC, - "Destination is not within the new map region, addr=0x{:016X}, size=0x{:016X}", + "Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}", dst_addr, size); return ERR_INVALID_MEMORY_RANGE; } @@ -318,7 +318,14 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad return result; } - return vm_manager.UnmapRange(dst_addr, size); + const auto unmap_res = vm_manager.UnmapRange(dst_addr, size); + + // Reprotect the source mapping on success + if (unmap_res.IsSuccess()) { + ASSERT(vm_manager.ReprotectRange(src_addr, size, VMAPermission::ReadWrite).IsSuccess()); + } + + return unmap_res; } /// Connect to an OS service given the port name, returns the handle to the port to out @@ -726,19 +733,19 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha // 2.0.0+ ASLRRegionBaseAddr = 12, ASLRRegionSize = 13, - NewMapRegionBaseAddr = 14, - NewMapRegionSize = 15, + StackRegionBaseAddr = 14, + StackRegionSize = 15, // 3.0.0+ - IsVirtualAddressMemoryEnabled = 16, - PersonalMmHeapUsage = 17, + SystemResourceSize = 16, + SystemResourceUsage = 17, TitleId = 18, // 4.0.0+ PrivilegedProcessId = 19, // 5.0.0+ UserExceptionContextAddr = 20, // 6.0.0+ - TotalPhysicalMemoryAvailableWithoutMmHeap = 21, - TotalPhysicalMemoryUsedWithoutMmHeap = 22, + TotalPhysicalMemoryAvailableWithoutSystemResource = 21, + TotalPhysicalMemoryUsedWithoutSystemResource = 22, }; const auto info_id_type = static_cast<GetInfoType>(info_id); @@ -752,16 +759,16 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha case GetInfoType::HeapRegionSize: case GetInfoType::ASLRRegionBaseAddr: case GetInfoType::ASLRRegionSize: - case GetInfoType::NewMapRegionBaseAddr: - case GetInfoType::NewMapRegionSize: + case GetInfoType::StackRegionBaseAddr: + case GetInfoType::StackRegionSize: case GetInfoType::TotalPhysicalMemoryAvailable: case GetInfoType::TotalPhysicalMemoryUsed: - case GetInfoType::IsVirtualAddressMemoryEnabled: - case GetInfoType::PersonalMmHeapUsage: + case GetInfoType::SystemResourceSize: + case GetInfoType::SystemResourceUsage: case GetInfoType::TitleId: case GetInfoType::UserExceptionContextAddr: - case GetInfoType::TotalPhysicalMemoryAvailableWithoutMmHeap: - case GetInfoType::TotalPhysicalMemoryUsedWithoutMmHeap: { + case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource: + case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource: { if (info_sub_id != 0) { return ERR_INVALID_ENUM_VALUE; } @@ -806,12 +813,12 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha *result = process->VMManager().GetASLRRegionSize(); return RESULT_SUCCESS; - case GetInfoType::NewMapRegionBaseAddr: - *result = process->VMManager().GetNewMapRegionBaseAddress(); + case GetInfoType::StackRegionBaseAddr: + *result = process->VMManager().GetStackRegionBaseAddress(); return RESULT_SUCCESS; - case GetInfoType::NewMapRegionSize: - *result = process->VMManager().GetNewMapRegionSize(); + case GetInfoType::StackRegionSize: + *result = process->VMManager().GetStackRegionSize(); return RESULT_SUCCESS; case GetInfoType::TotalPhysicalMemoryAvailable: @@ -822,8 +829,13 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha *result = process->GetTotalPhysicalMemoryUsed(); return RESULT_SUCCESS; - case GetInfoType::IsVirtualAddressMemoryEnabled: - *result = process->IsVirtualMemoryEnabled(); + case GetInfoType::SystemResourceSize: + *result = process->GetSystemResourceSize(); + return RESULT_SUCCESS; + + case GetInfoType::SystemResourceUsage: + LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage"); + *result = process->GetSystemResourceUsage(); return RESULT_SUCCESS; case GetInfoType::TitleId: @@ -831,17 +843,15 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha return RESULT_SUCCESS; case GetInfoType::UserExceptionContextAddr: - LOG_WARNING(Kernel_SVC, - "(STUBBED) Attempted to query user exception context address, returned 0"); - *result = 0; + *result = process->GetTLSRegionAddress(); return RESULT_SUCCESS; - case GetInfoType::TotalPhysicalMemoryAvailableWithoutMmHeap: - *result = process->GetTotalPhysicalMemoryAvailable(); + case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource: + *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource(); return RESULT_SUCCESS; - case GetInfoType::TotalPhysicalMemoryUsedWithoutMmHeap: - *result = process->GetTotalPhysicalMemoryUsedWithoutMmHeap(); + case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource: + *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource(); return RESULT_SUCCESS; default: @@ -946,6 +956,86 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha } } +/// Maps memory at a desired address +static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { + LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); + + if (!Common::Is4KBAligned(addr)) { + LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr); + return ERR_INVALID_ADDRESS; + } + + if (!Common::Is4KBAligned(size)) { + LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size); + return ERR_INVALID_SIZE; + } + + if (size == 0) { + LOG_ERROR(Kernel_SVC, "Size is zero"); + return ERR_INVALID_SIZE; + } + + if (!(addr < addr + size)) { + LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address"); + return ERR_INVALID_MEMORY_RANGE; + } + + Process* const current_process = system.Kernel().CurrentProcess(); + auto& vm_manager = current_process->VMManager(); + + if (current_process->GetSystemResourceSize() == 0) { + LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); + return ERR_INVALID_STATE; + } + + if (!vm_manager.IsWithinMapRegion(addr, size)) { + LOG_ERROR(Kernel_SVC, "Range not within map region"); + return ERR_INVALID_MEMORY_RANGE; + } + + return vm_manager.MapPhysicalMemory(addr, size); +} + +/// Unmaps memory previously mapped via MapPhysicalMemory +static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { + LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); + + if (!Common::Is4KBAligned(addr)) { + LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr); + return ERR_INVALID_ADDRESS; + } + + if (!Common::Is4KBAligned(size)) { + LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size); + return ERR_INVALID_SIZE; + } + + if (size == 0) { + LOG_ERROR(Kernel_SVC, "Size is zero"); + return ERR_INVALID_SIZE; + } + + if (!(addr < addr + size)) { + LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address"); + return ERR_INVALID_MEMORY_RANGE; + } + + Process* const current_process = system.Kernel().CurrentProcess(); + auto& vm_manager = current_process->VMManager(); + + if (current_process->GetSystemResourceSize() == 0) { + LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); + return ERR_INVALID_STATE; + } + + if (!vm_manager.IsWithinMapRegion(addr, size)) { + LOG_ERROR(Kernel_SVC, "Range not within map region"); + return ERR_INVALID_MEMORY_RANGE; + } + + return vm_manager.UnmapPhysicalMemory(addr, size); +} + /// Sets the thread activity static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) { LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); @@ -1647,8 +1737,8 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var // Wait for an address (via Address Arbiter) static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value, s64 timeout) { - LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", - address, type, value, timeout); + LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", address, + type, value, timeout); // If the passed address is a kernel virtual address, return invalid memory state. if (Memory::IsKernelVirtualAddress(address)) { @@ -1670,8 +1760,8 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, // Signals to an address (via Address Arbiter) static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value, s32 num_to_wake) { - LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", - address, type, value, num_to_wake); + LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", + address, type, value, num_to_wake); // If the passed address is a kernel virtual address, return invalid memory state. if (Memory::IsKernelVirtualAddress(address)) { @@ -2303,8 +2393,8 @@ static const FunctionDef SVC_Table[] = { {0x29, SvcWrap<GetInfo>, "GetInfo"}, {0x2A, nullptr, "FlushEntireDataCache"}, {0x2B, nullptr, "FlushDataCache"}, - {0x2C, nullptr, "MapPhysicalMemory"}, - {0x2D, nullptr, "UnmapPhysicalMemory"}, + {0x2C, SvcWrap<MapPhysicalMemory>, "MapPhysicalMemory"}, + {0x2D, SvcWrap<UnmapPhysicalMemory>, "UnmapPhysicalMemory"}, {0x2E, nullptr, "GetFutureThreadInfo"}, {0x2F, nullptr, "GetLastThreadInfo"}, {0x30, SvcWrap<GetResourceLimitLimitValue>, "GetResourceLimitLimitValue"}, diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 865473c6f..c2d8d0dc3 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -32,6 +32,11 @@ void SvcWrap(Core::System& system) { FuncReturn(system, func(system, Param(system, 0)).raw); } +template <ResultCode func(Core::System&, u64, u64)> +void SvcWrap(Core::System& system) { + FuncReturn(system, func(system, Param(system, 0), Param(system, 1)).raw); +} + template <ResultCode func(Core::System&, u32)> void SvcWrap(Core::System& system) { FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw); diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index a055a5002..ec529e7f2 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -65,7 +65,7 @@ void Thread::Stop() { owner_process->UnregisterThread(this); // Mark the TLS slot in the thread's page as free. - owner_process->FreeTLSSlot(tls_address); + owner_process->FreeTLSRegion(tls_address); } void Thread::WakeAfterDelay(s64 nanoseconds) { @@ -205,9 +205,9 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name thread->name = std::move(name); thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); thread->owner_process = &owner_process; + thread->tls_address = thread->owner_process->CreateTLSRegion(); thread->scheduler = &system.Scheduler(processor_id); thread->scheduler->AddThread(thread); - thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread); thread->owner_process->RegisterThread(thread.get()); diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index b4b9cda7c..07e989637 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -5,7 +5,6 @@ #pragma once #include <functional> -#include <memory> #include <string> #include <vector> @@ -78,9 +77,6 @@ enum class ThreadActivity : u32 { class Thread final : public WaitObject { public: - using TLSMemory = std::vector<u8>; - using TLSMemoryPtr = std::shared_ptr<TLSMemory>; - using MutexWaitingThreads = std::vector<SharedPtr<Thread>>; using ThreadContext = Core::ARM_Interface::ThreadContext; @@ -169,14 +165,6 @@ public: return thread_id; } - TLSMemoryPtr& GetTLSMemory() { - return tls_memory; - } - - const TLSMemoryPtr& GetTLSMemory() const { - return tls_memory; - } - /// Resumes a thread from waiting void ResumeFromWait(); @@ -463,11 +451,9 @@ private: u32 ideal_core{0xFFFFFFFF}; u64 affinity_mask{0x1}; - TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>(); + ThreadActivity activity = ThreadActivity::Normal; std::string name; - - ThreadActivity activity = ThreadActivity::Normal; }; /** diff --git a/src/core/hle/kernel/transfer_memory.cpp b/src/core/hle/kernel/transfer_memory.cpp index 26c4e5e67..1113c815e 100644 --- a/src/core/hle/kernel/transfer_memory.cpp +++ b/src/core/hle/kernel/transfer_memory.cpp @@ -47,7 +47,7 @@ ResultCode TransferMemory::MapMemory(VAddr address, u64 size, MemoryPermission p return ERR_INVALID_STATE; } - backing_block = std::make_shared<std::vector<u8>>(size); + backing_block = std::make_shared<PhysicalMemory>(size); const auto map_state = owner_permissions == MemoryPermission::None ? MemoryState::TransferMemoryIsolated diff --git a/src/core/hle/kernel/transfer_memory.h b/src/core/hle/kernel/transfer_memory.h index a140b1e2b..6be9dc094 100644 --- a/src/core/hle/kernel/transfer_memory.h +++ b/src/core/hle/kernel/transfer_memory.h @@ -8,6 +8,7 @@ #include <vector> #include "core/hle/kernel/object.h" +#include "core/hle/kernel/physical_memory.h" union ResultCode; @@ -82,7 +83,7 @@ private: ~TransferMemory() override; /// Memory block backing this instance. - std::shared_ptr<std::vector<u8>> backing_block; + std::shared_ptr<PhysicalMemory> backing_block; /// The base address for the memory managed by this instance. VAddr base_address = 0; diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index c929c2a52..c7af87073 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -5,13 +5,15 @@ #include <algorithm> #include <iterator> #include <utility> +#include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" #include "common/memory_hook.h" -#include "core/arm/arm_interface.h" #include "core/core.h" #include "core/file_sys/program_metadata.h" #include "core/hle/kernel/errors.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/vm_manager.h" #include "core/memory.h" #include "core/memory_setup.h" @@ -49,10 +51,14 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { type != next.type) { return false; } - if (type == VMAType::AllocatedMemoryBlock && - (backing_block != next.backing_block || offset + size != next.offset)) { + if ((attribute & MemoryAttribute::DeviceMapped) == MemoryAttribute::DeviceMapped) { + // TODO: Can device mapped memory be merged sanely? + // Not merging it may cause inaccuracies versus hardware when memory layout is queried. return false; } + if (type == VMAType::AllocatedMemoryBlock) { + return true; + } if (type == VMAType::BackingMemory && backing_memory + size != next.backing_memory) { return false; } @@ -98,9 +104,9 @@ bool VMManager::IsValidHandle(VMAHandle handle) const { } ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, - std::shared_ptr<std::vector<u8>> block, + std::shared_ptr<PhysicalMemory> block, std::size_t offset, u64 size, - MemoryState state) { + MemoryState state, VMAPermission perm) { ASSERT(block != nullptr); ASSERT(offset + size <= block->size()); @@ -109,17 +115,8 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, VirtualMemoryArea& final_vma = vma_handle->second; ASSERT(final_vma.size == size); - system.ArmInterface(0).MapBackingMemory(target, size, block->data() + offset, - VMAPermission::ReadWriteExecute); - system.ArmInterface(1).MapBackingMemory(target, size, block->data() + offset, - VMAPermission::ReadWriteExecute); - system.ArmInterface(2).MapBackingMemory(target, size, block->data() + offset, - VMAPermission::ReadWriteExecute); - system.ArmInterface(3).MapBackingMemory(target, size, block->data() + offset, - VMAPermission::ReadWriteExecute); - final_vma.type = VMAType::AllocatedMemoryBlock; - final_vma.permissions = VMAPermission::ReadWrite; + final_vma.permissions = perm; final_vma.state = state; final_vma.backing_block = std::move(block); final_vma.offset = offset; @@ -137,11 +134,6 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me VirtualMemoryArea& final_vma = vma_handle->second; ASSERT(final_vma.size == size); - system.ArmInterface(0).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); - system.ArmInterface(1).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); - system.ArmInterface(2).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); - system.ArmInterface(3).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); - final_vma.type = VMAType::BackingMemory; final_vma.permissions = VMAPermission::ReadWrite; final_vma.state = state; @@ -152,22 +144,33 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me } ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const { - // Find the first Free VMA. - const VAddr base = GetASLRRegionBaseAddress(); - const VMAHandle vma_handle = std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) { - if (vma.second.type != VMAType::Free) - return false; + return FindFreeRegion(GetASLRRegionBaseAddress(), GetASLRRegionEndAddress(), size); +} - const VAddr vma_end = vma.second.base + vma.second.size; - return vma_end > base && vma_end >= base + size; - }); +ResultVal<VAddr> VMManager::FindFreeRegion(VAddr begin, VAddr end, u64 size) const { + ASSERT(begin < end); + ASSERT(size <= end - begin); - if (vma_handle == vma_map.end()) { + const VMAHandle vma_handle = + std::find_if(vma_map.begin(), vma_map.end(), [begin, end, size](const auto& vma) { + if (vma.second.type != VMAType::Free) { + return false; + } + const VAddr vma_base = vma.second.base; + const VAddr vma_end = vma_base + vma.second.size; + const VAddr assumed_base = (begin < vma_base) ? vma_base : begin; + const VAddr used_range = assumed_base + size; + + return vma_base <= assumed_base && assumed_base < used_range && used_range < end && + used_range <= vma_end; + }); + + if (vma_handle == vma_map.cend()) { // TODO(Subv): Find the correct error code here. return ResultCode(-1); } - const VAddr target = std::max(base, vma_handle->second.base); + const VAddr target = std::max(begin, vma_handle->second.base); return MakeResult<VAddr>(target); } @@ -219,11 +222,6 @@ ResultCode VMManager::UnmapRange(VAddr target, u64 size) { ASSERT(FindVMA(target)->second.size >= size); - system.ArmInterface(0).UnmapMemory(target, size); - system.ArmInterface(1).UnmapMemory(target, size); - system.ArmInterface(2).UnmapMemory(target, size); - system.ArmInterface(3).UnmapMemory(target, size); - return RESULT_SUCCESS; } @@ -263,7 +261,7 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) { if (heap_memory == nullptr) { // Initialize heap - heap_memory = std::make_shared<std::vector<u8>>(size); + heap_memory = std::make_shared<PhysicalMemory>(size); heap_end = heap_region_base + size; } else { UnmapRange(heap_region_base, GetCurrentHeapSize()); @@ -297,6 +295,162 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) { return MakeResult<VAddr>(heap_region_base); } +ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) { + // Check how much memory we've already mapped. + const auto mapped_size_result = SizeOfAllocatedVMAsInRange(target, size); + if (mapped_size_result.Failed()) { + return mapped_size_result.Code(); + } + + // If we've already mapped the desired amount, return early. + const std::size_t mapped_size = *mapped_size_result; + if (mapped_size == size) { + return RESULT_SUCCESS; + } + + // Check that we can map the memory we want. + const auto res_limit = system.CurrentProcess()->GetResourceLimit(); + const u64 physmem_remaining = res_limit->GetMaxResourceValue(ResourceType::PhysicalMemory) - + res_limit->GetCurrentResourceValue(ResourceType::PhysicalMemory); + if (physmem_remaining < (size - mapped_size)) { + return ERR_RESOURCE_LIMIT_EXCEEDED; + } + + // Keep track of the memory regions we unmap. + std::vector<std::pair<u64, u64>> mapped_regions; + ResultCode result = RESULT_SUCCESS; + + // Iterate, trying to map memory. + { + const auto end_addr = target + size; + const auto last_addr = end_addr - 1; + VAddr cur_addr = target; + + auto iter = FindVMA(target); + ASSERT(iter != vma_map.end()); + + while (true) { + const auto& vma = iter->second; + const auto vma_start = vma.base; + const auto vma_end = vma_start + vma.size; + const auto vma_last = vma_end - 1; + + // Map the memory block + const auto map_size = std::min(end_addr - cur_addr, vma_end - cur_addr); + if (vma.state == MemoryState::Unmapped) { + const auto map_res = + MapMemoryBlock(cur_addr, std::make_shared<PhysicalMemory>(map_size), 0, + map_size, MemoryState::Heap, VMAPermission::ReadWrite); + result = map_res.Code(); + if (result.IsError()) { + break; + } + + mapped_regions.emplace_back(cur_addr, map_size); + } + + // Break once we hit the end of the range. + if (last_addr <= vma_last) { + break; + } + + // Advance to the next block. + cur_addr = vma_end; + iter = FindVMA(cur_addr); + ASSERT(iter != vma_map.end()); + } + } + + // If we failed, unmap memory. + if (result.IsError()) { + for (const auto [unmap_address, unmap_size] : mapped_regions) { + ASSERT_MSG(UnmapRange(unmap_address, unmap_size).IsSuccess(), + "Failed to unmap memory range."); + } + + return result; + } + + // Update amount of mapped physical memory. + physical_memory_mapped += size - mapped_size; + + return RESULT_SUCCESS; +} + +ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) { + // Check how much memory is currently mapped. + const auto mapped_size_result = SizeOfUnmappablePhysicalMemoryInRange(target, size); + if (mapped_size_result.Failed()) { + return mapped_size_result.Code(); + } + + // If we've already unmapped all the memory, return early. + const std::size_t mapped_size = *mapped_size_result; + if (mapped_size == 0) { + return RESULT_SUCCESS; + } + + // Keep track of the memory regions we unmap. + std::vector<std::pair<u64, u64>> unmapped_regions; + ResultCode result = RESULT_SUCCESS; + + // Try to unmap regions. + { + const auto end_addr = target + size; + const auto last_addr = end_addr - 1; + VAddr cur_addr = target; + + auto iter = FindVMA(target); + ASSERT(iter != vma_map.end()); + + while (true) { + const auto& vma = iter->second; + const auto vma_start = vma.base; + const auto vma_end = vma_start + vma.size; + const auto vma_last = vma_end - 1; + + // Unmap the memory block + const auto unmap_size = std::min(end_addr - cur_addr, vma_end - cur_addr); + if (vma.state == MemoryState::Heap) { + result = UnmapRange(cur_addr, unmap_size); + if (result.IsError()) { + break; + } + + unmapped_regions.emplace_back(cur_addr, unmap_size); + } + + // Break once we hit the end of the range. + if (last_addr <= vma_last) { + break; + } + + // Advance to the next block. + cur_addr = vma_end; + iter = FindVMA(cur_addr); + ASSERT(iter != vma_map.end()); + } + } + + // If we failed, re-map regions. + // TODO: Preserve memory contents? + if (result.IsError()) { + for (const auto [map_address, map_size] : unmapped_regions) { + const auto remap_res = + MapMemoryBlock(map_address, std::make_shared<PhysicalMemory>(map_size), 0, map_size, + MemoryState::Heap, VMAPermission::None); + ASSERT_MSG(remap_res.Succeeded(), "Failed to remap a memory block."); + } + + return result; + } + + // Update mapped amount + physical_memory_mapped -= mapped_size; + + return RESULT_SUCCESS; +} + ResultCode VMManager::MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) { constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped; const auto src_check_result = CheckRangeState( @@ -436,7 +590,7 @@ ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, Mem ASSERT_MSG(vma_offset + size <= vma->second.size, "Shared memory exceeds bounds of mapped block"); - const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block; + const std::shared_ptr<PhysicalMemory>& backing_block = vma->second.backing_block; const std::size_t backing_block_offset = vma->second.offset + vma_offset; CASCADE_RESULT(auto new_vma, @@ -444,12 +598,12 @@ ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, Mem // Protect mirror with permissions from old region Reprotect(new_vma, vma->second.permissions); // Remove permissions from old region - Reprotect(vma, VMAPermission::None); + ReprotectRange(src_addr, size, VMAPermission::None); return RESULT_SUCCESS; } -void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) { +void VMManager::RefreshMemoryBlockMappings(const PhysicalMemory* block) { // If this ever proves to have a noticeable performance impact, allow users of the function to // specify a specific range of addresses to limit the scan to. for (const auto& p : vma_map) { @@ -577,14 +731,14 @@ VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u64 offset_in_vma) { VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) { const VMAIter next_vma = std::next(iter); if (next_vma != vma_map.end() && iter->second.CanBeMergedWith(next_vma->second)) { - iter->second.size += next_vma->second.size; + MergeAdjacentVMA(iter->second, next_vma->second); vma_map.erase(next_vma); } if (iter != vma_map.begin()) { VMAIter prev_vma = std::prev(iter); if (prev_vma->second.CanBeMergedWith(iter->second)) { - prev_vma->second.size += iter->second.size; + MergeAdjacentVMA(prev_vma->second, iter->second); vma_map.erase(iter); iter = prev_vma; } @@ -593,6 +747,44 @@ VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) { return iter; } +void VMManager::MergeAdjacentVMA(VirtualMemoryArea& left, const VirtualMemoryArea& right) { + ASSERT(left.CanBeMergedWith(right)); + + // Always merge allocated memory blocks, even when they don't share the same backing block. + if (left.type == VMAType::AllocatedMemoryBlock && + (left.backing_block != right.backing_block || left.offset + left.size != right.offset)) { + const auto right_begin = right.backing_block->begin() + right.offset; + const auto right_end = right_begin + right.size; + + // Check if we can save work. + if (left.offset == 0 && left.size == left.backing_block->size()) { + // Fast case: left is an entire backing block. + left.backing_block->insert(left.backing_block->end(), right_begin, right_end); + } else { + // Slow case: make a new memory block for left and right. + const auto left_begin = left.backing_block->begin() + left.offset; + const auto left_end = left_begin + left.size; + const auto left_size = static_cast<std::size_t>(std::distance(left_begin, left_end)); + const auto right_size = static_cast<std::size_t>(std::distance(right_begin, right_end)); + + auto new_memory = std::make_shared<PhysicalMemory>(); + new_memory->reserve(left_size + right_size); + new_memory->insert(new_memory->end(), left_begin, left_end); + new_memory->insert(new_memory->end(), right_begin, right_end); + + left.backing_block = std::move(new_memory); + left.offset = 0; + } + + // Page table update is needed, because backing memory changed. + left.size += right.size; + UpdatePageTableForVMA(left); + } else { + // Just update the size. + left.size += right.size; + } +} + void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { switch (vma.type) { case VMAType::Free: @@ -614,9 +806,11 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType type) { u64 map_region_size = 0; u64 heap_region_size = 0; - u64 new_map_region_size = 0; + u64 stack_region_size = 0; u64 tls_io_region_size = 0; + u64 stack_and_tls_io_end = 0; + switch (type) { case FileSys::ProgramAddressSpaceType::Is32Bit: case FileSys::ProgramAddressSpaceType::Is32BitNoMap: @@ -632,6 +826,7 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty map_region_size = 0; heap_region_size = 0x80000000; } + stack_and_tls_io_end = 0x40000000; break; case FileSys::ProgramAddressSpaceType::Is36Bit: address_space_width = 36; @@ -641,6 +836,7 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty aslr_region_end = aslr_region_base + 0xFF8000000; map_region_size = 0x180000000; heap_region_size = 0x180000000; + stack_and_tls_io_end = 0x80000000; break; case FileSys::ProgramAddressSpaceType::Is39Bit: address_space_width = 39; @@ -650,7 +846,7 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty aslr_region_end = aslr_region_base + 0x7FF8000000; map_region_size = 0x1000000000; heap_region_size = 0x180000000; - new_map_region_size = 0x80000000; + stack_region_size = 0x80000000; tls_io_region_size = 0x1000000000; break; default: @@ -658,6 +854,8 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty return; } + const u64 stack_and_tls_io_begin = aslr_region_base; + address_space_base = 0; address_space_end = 1ULL << address_space_width; @@ -668,15 +866,20 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty heap_region_end = heap_region_base + heap_region_size; heap_end = heap_region_base; - new_map_region_base = heap_region_end; - new_map_region_end = new_map_region_base + new_map_region_size; + stack_region_base = heap_region_end; + stack_region_end = stack_region_base + stack_region_size; - tls_io_region_base = new_map_region_end; + tls_io_region_base = stack_region_end; tls_io_region_end = tls_io_region_base + tls_io_region_size; - if (new_map_region_size == 0) { - new_map_region_base = address_space_base; - new_map_region_end = address_space_end; + if (stack_region_size == 0) { + stack_region_base = stack_and_tls_io_begin; + stack_region_end = stack_and_tls_io_end; + } + + if (tls_io_region_size == 0) { + tls_io_region_base = stack_and_tls_io_begin; + tls_io_region_end = stack_and_tls_io_end; } } @@ -756,6 +959,84 @@ VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, Memo std::make_tuple(initial_state, initial_permissions, initial_attributes & ~ignore_mask)); } +ResultVal<std::size_t> VMManager::SizeOfAllocatedVMAsInRange(VAddr address, + std::size_t size) const { + const VAddr end_addr = address + size; + const VAddr last_addr = end_addr - 1; + std::size_t mapped_size = 0; + + VAddr cur_addr = address; + auto iter = FindVMA(cur_addr); + ASSERT(iter != vma_map.end()); + + while (true) { + const auto& vma = iter->second; + const VAddr vma_start = vma.base; + const VAddr vma_end = vma_start + vma.size; + const VAddr vma_last = vma_end - 1; + + // Add size if relevant. + if (vma.state != MemoryState::Unmapped) { + mapped_size += std::min(end_addr - cur_addr, vma_end - cur_addr); + } + + // Break once we hit the end of the range. + if (last_addr <= vma_last) { + break; + } + + // Advance to the next block. + cur_addr = vma_end; + iter = std::next(iter); + ASSERT(iter != vma_map.end()); + } + + return MakeResult(mapped_size); +} + +ResultVal<std::size_t> VMManager::SizeOfUnmappablePhysicalMemoryInRange(VAddr address, + std::size_t size) const { + const VAddr end_addr = address + size; + const VAddr last_addr = end_addr - 1; + std::size_t mapped_size = 0; + + VAddr cur_addr = address; + auto iter = FindVMA(cur_addr); + ASSERT(iter != vma_map.end()); + + while (true) { + const auto& vma = iter->second; + const auto vma_start = vma.base; + const auto vma_end = vma_start + vma.size; + const auto vma_last = vma_end - 1; + const auto state = vma.state; + const auto attr = vma.attribute; + + // Memory within region must be free or mapped heap. + if (!((state == MemoryState::Heap && attr == MemoryAttribute::None) || + (state == MemoryState::Unmapped))) { + return ERR_INVALID_ADDRESS_STATE; + } + + // Add size if relevant. + if (state != MemoryState::Unmapped) { + mapped_size += std::min(end_addr - cur_addr, vma_end - cur_addr); + } + + // Break once we hit the end of the range. + if (last_addr <= vma_last) { + break; + } + + // Advance to the next block. + cur_addr = vma_end; + iter = std::next(iter); + ASSERT(iter != vma_map.end()); + } + + return MakeResult(mapped_size); +} + u64 VMManager::GetTotalPhysicalMemoryAvailable() const { LOG_WARNING(Kernel, "(STUBBED) called"); return 0xF8000000; @@ -868,21 +1149,21 @@ bool VMManager::IsWithinMapRegion(VAddr address, u64 size) const { return IsInsideAddressRange(address, size, GetMapRegionBaseAddress(), GetMapRegionEndAddress()); } -VAddr VMManager::GetNewMapRegionBaseAddress() const { - return new_map_region_base; +VAddr VMManager::GetStackRegionBaseAddress() const { + return stack_region_base; } -VAddr VMManager::GetNewMapRegionEndAddress() const { - return new_map_region_end; +VAddr VMManager::GetStackRegionEndAddress() const { + return stack_region_end; } -u64 VMManager::GetNewMapRegionSize() const { - return new_map_region_end - new_map_region_base; +u64 VMManager::GetStackRegionSize() const { + return stack_region_end - stack_region_base; } -bool VMManager::IsWithinNewMapRegion(VAddr address, u64 size) const { - return IsInsideAddressRange(address, size, GetNewMapRegionBaseAddress(), - GetNewMapRegionEndAddress()); +bool VMManager::IsWithinStackRegion(VAddr address, u64 size) const { + return IsInsideAddressRange(address, size, GetStackRegionBaseAddress(), + GetStackRegionEndAddress()); } VAddr VMManager::GetTLSIORegionBaseAddress() const { diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index dfbf7a894..850a7ebc3 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -11,6 +11,7 @@ #include "common/common_types.h" #include "common/memory_hook.h" #include "common/page_table.h" +#include "core/hle/kernel/physical_memory.h" #include "core/hle/result.h" #include "core/memory.h" @@ -290,7 +291,7 @@ struct VirtualMemoryArea { // Settings for type = AllocatedMemoryBlock /// Memory block backing this VMA. - std::shared_ptr<std::vector<u8>> backing_block = nullptr; + std::shared_ptr<PhysicalMemory> backing_block = nullptr; /// Offset into the backing_memory the mapping starts from. std::size_t offset = 0; @@ -348,8 +349,9 @@ public: * @param size Size of the mapping. * @param state MemoryState tag to attach to the VMA. */ - ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block, - std::size_t offset, u64 size, MemoryState state); + ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<PhysicalMemory> block, + std::size_t offset, u64 size, MemoryState state, + VMAPermission perm = VMAPermission::ReadWrite); /** * Maps an unmanaged host memory pointer at a given address. @@ -362,14 +364,39 @@ public: ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state); /** - * Finds the first free address that can hold a region of the desired size. + * Finds the first free memory region of the given size within + * the user-addressable ASLR memory region. * - * @param size Size of the desired region. - * @return The found free address. + * @param size The size of the desired region in bytes. + * + * @returns If successful, the base address of the free region with + * the given size. */ ResultVal<VAddr> FindFreeRegion(u64 size) const; /** + * Finds the first free address range that can hold a region of the desired size + * + * @param begin The starting address of the range. + * This is treated as an inclusive beginning address. + * + * @param end The ending address of the range. + * This is treated as an exclusive ending address. + * + * @param size The size of the free region to attempt to locate, + * in bytes. + * + * @returns If successful, the base address of the free region with + * the given size. + * + * @returns If unsuccessful, a result containing an error code. + * + * @pre The starting address must be less than the ending address. + * @pre The size must not exceed the address range itself. + */ + ResultVal<VAddr> FindFreeRegion(VAddr begin, VAddr end, u64 size) const; + + /** * Maps a memory-mapped IO region at a given address. * * @param target The guest address to start the mapping at. @@ -425,6 +452,34 @@ public: /// ResultVal<VAddr> SetHeapSize(u64 size); + /// Maps memory at a given address. + /// + /// @param target The virtual address to map memory at. + /// @param size The amount of memory to map. + /// + /// @note The destination address must lie within the Map region. + /// + /// @note This function requires that SystemResourceSize be non-zero, + /// however, this is just because if it were not then the + /// resulting page tables could be exploited on hardware by + /// a malicious program. SystemResource usage does not need + /// to be explicitly checked or updated here. + ResultCode MapPhysicalMemory(VAddr target, u64 size); + + /// Unmaps memory at a given address. + /// + /// @param target The virtual address to unmap memory at. + /// @param size The amount of memory to unmap. + /// + /// @note The destination address must lie within the Map region. + /// + /// @note This function requires that SystemResourceSize be non-zero, + /// however, this is just because if it were not then the + /// resulting page tables could be exploited on hardware by + /// a malicious program. SystemResource usage does not need + /// to be explicitly checked or updated here. + ResultCode UnmapPhysicalMemory(VAddr target, u64 size); + /// Maps a region of memory as code memory. /// /// @param dst_address The base address of the region to create the aliasing memory region. @@ -493,7 +548,7 @@ public: * Scans all VMAs and updates the page table range of any that use the given vector as backing * memory. This should be called after any operation that causes reallocation of the vector. */ - void RefreshMemoryBlockMappings(const std::vector<u8>* block); + void RefreshMemoryBlockMappings(const PhysicalMemory* block); /// Dumps the address space layout to the log, for debugging void LogLayout() const; @@ -571,17 +626,17 @@ public: /// Determines whether or not the specified range is within the map region. bool IsWithinMapRegion(VAddr address, u64 size) const; - /// Gets the base address of the new map region. - VAddr GetNewMapRegionBaseAddress() const; + /// Gets the base address of the stack region. + VAddr GetStackRegionBaseAddress() const; - /// Gets the end address of the new map region. - VAddr GetNewMapRegionEndAddress() const; + /// Gets the end address of the stack region. + VAddr GetStackRegionEndAddress() const; - /// Gets the total size of the new map region in bytes. - u64 GetNewMapRegionSize() const; + /// Gets the total size of the stack region in bytes. + u64 GetStackRegionSize() const; - /// Determines whether or not the given address range is within the new map region - bool IsWithinNewMapRegion(VAddr address, u64 size) const; + /// Determines whether or not the given address range is within the stack region + bool IsWithinStackRegion(VAddr address, u64 size) const; /// Gets the base address of the TLS IO region. VAddr GetTLSIORegionBaseAddress() const; @@ -632,6 +687,11 @@ private: */ VMAIter MergeAdjacent(VMAIter vma); + /** + * Merges two adjacent VMAs. + */ + void MergeAdjacentVMA(VirtualMemoryArea& left, const VirtualMemoryArea& right); + /// Updates the pages corresponding to this VMA so they match the VMA's attributes. void UpdatePageTableForVMA(const VirtualMemoryArea& vma); @@ -676,6 +736,13 @@ private: MemoryAttribute attribute_mask, MemoryAttribute attribute, MemoryAttribute ignore_mask) const; + /// Gets the amount of memory currently mapped (state != Unmapped) in a range. + ResultVal<std::size_t> SizeOfAllocatedVMAsInRange(VAddr address, std::size_t size) const; + + /// Gets the amount of memory unmappable by UnmapPhysicalMemory in a range. + ResultVal<std::size_t> SizeOfUnmappablePhysicalMemoryInRange(VAddr address, + std::size_t size) const; + /** * A map covering the entirety of the managed address space, keyed by the `base` field of each * VMA. It must always be modified by splitting or merging VMAs, so that the invariant @@ -701,8 +768,8 @@ private: VAddr map_region_base = 0; VAddr map_region_end = 0; - VAddr new_map_region_base = 0; - VAddr new_map_region_end = 0; + VAddr stack_region_base = 0; + VAddr stack_region_end = 0; VAddr tls_io_region_base = 0; VAddr tls_io_region_end = 0; @@ -711,12 +778,17 @@ private: // the entire virtual address space extents that bound the allocations, including any holes. // This makes deallocation and reallocation of holes fast and keeps process memory contiguous // in the emulator address space, allowing Memory::GetPointer to be reasonably safe. - std::shared_ptr<std::vector<u8>> heap_memory; + std::shared_ptr<PhysicalMemory> heap_memory; // The end of the currently allocated heap. This is not an inclusive // end of the range. This is essentially 'base_address + current_size'. VAddr heap_end = 0; + // The current amount of memory mapped via MapPhysicalMemory. + // This is used here (and in Nintendo's kernel) only for debugging, and does not impact + // any behavior. + u64 physical_memory_mapped = 0; + Core::System& system; }; } // namespace Kernel diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 002efaa7a..a7c55e116 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -15,13 +15,18 @@ #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/service/acc/acc.h" #include "core/hle/service/acc/acc_aa.h" #include "core/hle/service/acc/acc_su.h" #include "core/hle/service/acc/acc_u0.h" #include "core/hle/service/acc/acc_u1.h" +#include "core/hle/service/acc/errors.h" #include "core/hle/service/acc/profile_manager.h" +#include "core/hle/service/glue/arp.h" +#include "core/hle/service/glue/manager.h" +#include "core/hle/service/sm/sm.h" #include "core/loader/loader.h" namespace Service::Account { @@ -312,10 +317,72 @@ void Module::Interface::IsUserRegistrationRequestPermitted(Kernel::HLERequestCon rb.Push(profile_manager->CanSystemRegisterUser()); } -void Module::Interface::InitializeApplicationInfoOld(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_ACC, "(STUBBED) called"); +void Module::Interface::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto pid = rp.Pop<u64>(); + + LOG_DEBUG(Service_ACC, "called, process_id={}", pid); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); + rb.Push(InitializeApplicationInfoBase(pid)); +} + +void Module::Interface::InitializeApplicationInfoRestricted(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto pid = rp.Pop<u64>(); + + LOG_WARNING(Service_ACC, "(Partial implementation) called, process_id={}", pid); + + // TODO(ogniK): We require checking if the user actually owns the title and what not. As of + // currently, we assume the user owns the title. InitializeApplicationInfoBase SHOULD be called + // first then we do extra checks if the game is a digital copy. + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InitializeApplicationInfoBase(pid)); +} + +ResultCode Module::Interface::InitializeApplicationInfoBase(u64 process_id) { + if (application_info) { + LOG_ERROR(Service_ACC, "Application already initialized"); + return ERR_ACCOUNTINFO_ALREADY_INITIALIZED; + } + + const auto& list = system.Kernel().GetProcessList(); + const auto iter = std::find_if(list.begin(), list.end(), [&process_id](const auto& process) { + return process->GetProcessID() == process_id; + }); + + if (iter == list.end()) { + LOG_ERROR(Service_ACC, "Failed to find process ID"); + application_info.application_type = ApplicationType::Unknown; + + return ERR_ACCOUNTINFO_BAD_APPLICATION; + } + + const auto launch_property = system.GetARPManager().GetLaunchProperty((*iter)->GetTitleID()); + + if (launch_property.Failed()) { + LOG_ERROR(Service_ACC, "Failed to get launch property"); + return ERR_ACCOUNTINFO_BAD_APPLICATION; + } + + switch (launch_property->base_game_storage_id) { + case FileSys::StorageId::GameCard: + application_info.application_type = ApplicationType::GameCard; + break; + case FileSys::StorageId::Host: + case FileSys::StorageId::NandUser: + case FileSys::StorageId::SdCard: + application_info.application_type = ApplicationType::Digital; + break; + default: + LOG_ERROR(Service_ACC, "Invalid game storage ID"); + return ERR_ACCOUNTINFO_BAD_APPLICATION; + } + + LOG_WARNING(Service_ACC, "ApplicationInfo init required"); + // TODO(ogniK): Actual initalization here + + return RESULT_SUCCESS; } void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index 69e4f34fc..7a7dc9ec6 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h @@ -4,6 +4,7 @@ #pragma once +#include "core/hle/service/glue/manager.h" #include "core/hle/service/service.h" namespace Service::Account { @@ -25,13 +26,34 @@ public: void ListOpenUsers(Kernel::HLERequestContext& ctx); void GetLastOpenedUser(Kernel::HLERequestContext& ctx); void GetProfile(Kernel::HLERequestContext& ctx); - void InitializeApplicationInfoOld(Kernel::HLERequestContext& ctx); + void InitializeApplicationInfo(Kernel::HLERequestContext& ctx); + void InitializeApplicationInfoRestricted(Kernel::HLERequestContext& ctx); void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx); void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx); void TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx); void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx); void GetProfileEditor(Kernel::HLERequestContext& ctx); + private: + ResultCode InitializeApplicationInfoBase(u64 process_id); + + enum class ApplicationType : u32_le { + GameCard = 0, + Digital = 1, + Unknown = 3, + }; + + struct ApplicationInfo { + Service::Glue::ApplicationLaunchProperty launch_property; + ApplicationType application_type; + + constexpr explicit operator bool() const { + return launch_property.title_id != 0x0; + } + }; + + ApplicationInfo application_info{}; + protected: std::shared_ptr<Module> module; std::shared_ptr<ProfileManager> profile_manager; diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index 2f239e8c0..0ac19f4ff 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp @@ -22,7 +22,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, {60, nullptr, "ListOpenContextStoredUsers"}, {99, nullptr, "DebugActivateOpenContextRetention"}, - {100, &ACC_U0::InitializeApplicationInfoOld, "InitializeApplicationInfoOld"}, + {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, {102, nullptr, "AuthenticateApplicationAsync"}, {103, nullptr, "CheckNetworkServiceAvailabilityAsync"}, @@ -31,7 +31,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {120, nullptr, "CreateGuestLoginRequest"}, {130, nullptr, "LoadOpenContext"}, {131, nullptr, "ListOpenContextStoredUsers"}, - {140, nullptr, "InitializeApplicationInfo"}, + {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, {141, nullptr, "ListQualifiedUsers"}, {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"}, }; diff --git a/src/core/hle/service/acc/errors.h b/src/core/hle/service/acc/errors.h new file mode 100644 index 000000000..1f0577239 --- /dev/null +++ b/src/core/hle/service/acc/errors.h @@ -0,0 +1,14 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/result.h" + +namespace Service::Account { + +constexpr ResultCode ERR_ACCOUNTINFO_BAD_APPLICATION{ErrorModule::Account, 22}; +constexpr ResultCode ERR_ACCOUNTINFO_ALREADY_INITIALIZED{ErrorModule::Account, 41}; + +} // namespace Service::Account diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 33cebb48b..aa2c83937 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -29,7 +29,8 @@ #include "core/hle/service/am/omm.h" #include "core/hle/service/am/spsm.h" #include "core/hle/service/am/tcap.h" -#include "core/hle/service/apm/apm.h" +#include "core/hle/service/apm/controller.h" +#include "core/hle/service/apm/interface.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/ns/ns.h" #include "core/hle/service/nvflinger/nvflinger.h" @@ -55,7 +56,8 @@ struct LaunchParameters { }; static_assert(sizeof(LaunchParameters) == 0x88); -IWindowController::IWindowController() : ServiceFramework("IWindowController") { +IWindowController::IWindowController(Core::System& system_) + : ServiceFramework("IWindowController"), system{system_} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "CreateWindow"}, @@ -74,7 +76,7 @@ IWindowController::IWindowController() : ServiceFramework("IWindowController") { IWindowController::~IWindowController() = default; void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) { - const u64 process_id = Core::System::GetInstance().Kernel().CurrentProcess()->GetProcessID(); + const u64 process_id = system.CurrentProcess()->GetProcessID(); LOG_DEBUG(Service_AM, "called. Process ID=0x{:016X}", process_id); @@ -230,8 +232,9 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} { IDebugFunctions::~IDebugFunctions() = default; -ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) - : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) { +ISelfController::ISelfController(Core::System& system_, + std::shared_ptr<NVFlinger::NVFlinger> nvflinger_) + : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger_)) { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "Exit"}, @@ -265,12 +268,12 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger {65, nullptr, "ReportUserIsActive"}, {66, nullptr, "GetCurrentIlluminance"}, {67, nullptr, "IsIlluminanceAvailable"}, - {68, nullptr, "SetAutoSleepDisabled"}, - {69, nullptr, "IsAutoSleepDisabled"}, + {68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"}, + {69, &ISelfController::IsAutoSleepDisabled, "IsAutoSleepDisabled"}, {70, nullptr, "ReportMultimediaError"}, {71, nullptr, "GetCurrentIlluminanceEx"}, {80, nullptr, "SetWirelessPriorityMode"}, - {90, nullptr, "GetAccumulatedSuspendedTickValue"}, + {90, &ISelfController::GetAccumulatedSuspendedTickValue, "GetAccumulatedSuspendedTickValue"}, {91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"}, {100, nullptr, "SetAlbumImageTakenNotificationEnabled"}, {1000, nullptr, "GetDebugStorageChannel"}, @@ -279,14 +282,18 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger RegisterHandlers(functions); - auto& kernel = Core::System::GetInstance().Kernel(); + auto& kernel = system_.Kernel(); launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, "ISelfController:LaunchableEvent"); - // TODO(ogniK): Figure out where, when and why this event gets signalled + // This event is created by AM on the first time GetAccumulatedSuspendedTickChangedEvent() is + // called. Yuzu can just create it unconditionally, since it doesn't need to support multiple + // ISelfControllers. The event is signaled on creation, and on transition from suspended -> not + // suspended if the event has previously been created by a call to + // GetAccumulatedSuspendedTickChangedEvent. accumulated_suspended_tick_changed_event = Kernel::WritableEvent::CreateEventPair( kernel, Kernel::ResetType::Manual, "ISelfController:AccumulatedSuspendedTickChangedEvent"); - accumulated_suspended_tick_changed_event.writable->Signal(); // Is signalled on creation + accumulated_suspended_tick_changed_event.writable->Signal(); } ISelfController::~ISelfController() = default; @@ -449,19 +456,54 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c rb.Push<u32>(idle_time_detection_extension); } +void ISelfController::SetAutoSleepDisabled(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + is_auto_sleep_disabled = rp.Pop<bool>(); + + // On the system itself, if the previous state of is_auto_sleep_disabled + // differed from the current value passed in, it'd signify the internal + // window manager to update (and also increment some statistics like update counts) + // + // It'd also indicate this change to an idle handling context. + // + // However, given we're emulating this behavior, most of this can be ignored + // and it's sufficient to simply set the member variable for querying via + // IsAutoSleepDisabled(). + + LOG_DEBUG(Service_AM, "called. is_auto_sleep_disabled={}", is_auto_sleep_disabled); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void ISelfController::IsAutoSleepDisabled(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called."); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(is_auto_sleep_disabled); +} + +void ISelfController::GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called."); + + // This command returns the total number of system ticks since ISelfController creation + // where the game was suspended. Since Yuzu doesn't implement game suspension, this command + // can just always return 0 ticks. + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(0); +} + void ISelfController::GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx) { - // The implementation of this function is fine as is, the reason we're labelling it as stubbed - // is because we're currently unsure when and where accumulated_suspended_tick_changed_event is - // actually signalled for the time being. - LOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_DEBUG(Service_AM, "called."); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(accumulated_suspended_tick_changed_event.readable); } -AppletMessageQueue::AppletMessageQueue() { - auto& kernel = Core::System::GetInstance().Kernel(); +AppletMessageQueue::AppletMessageQueue(Kernel::KernelCore& kernel) { on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, "AMMessageQueue:OnMessageRecieved"); on_operation_mode_changed = Kernel::WritableEvent::CreateEventPair( @@ -508,8 +550,9 @@ void AppletMessageQueue::OperationModeChanged() { on_operation_mode_changed.writable->Signal(); } -ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue) - : ServiceFramework("ICommonStateGetter"), msg_queue(std::move(msg_queue)) { +ICommonStateGetter::ICommonStateGetter(Core::System& system, + std::shared_ptr<AppletMessageQueue> msg_queue) + : ServiceFramework("ICommonStateGetter"), system(system), msg_queue(std::move(msg_queue)) { // clang-format off static const FunctionInfo functions[] = { {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, @@ -542,7 +585,7 @@ ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_q {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"}, {64, nullptr, "SetTvPowerStateMatchingMode"}, {65, nullptr, "GetApplicationIdByContentActionName"}, - {66, nullptr, "SetCpuBoostMode"}, + {66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"}, {80, nullptr, "PerformSystemButtonPressingIfInFocus"}, {90, nullptr, "SetPerformanceConfigurationChangedNotification"}, {91, nullptr, "GetCurrentPerformanceConfiguration"}, @@ -623,6 +666,16 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& } } +void ICommonStateGetter::SetCpuBoostMode(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called, forwarding to APM:SYS"); + + const auto& sm = system.ServiceManager(); + const auto apm_sys = sm.GetService<APM::APM_Sys>("apm:sys"); + ASSERT(apm_sys != nullptr); + + apm_sys->SetCpuBoostMode(ctx); +} + IStorage::IStorage(std::vector<u8> buffer) : ServiceFramework("IStorage"), buffer(std::move(buffer)) { // clang-format off @@ -651,13 +704,11 @@ void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { } void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) { - const bool use_docked_mode{Settings::values.use_docked_mode}; - LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode); + LOG_DEBUG(Service_AM, "called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(static_cast<u32>(use_docked_mode ? APM::PerformanceMode::Docked - : APM::PerformanceMode::Handheld)); + rb.PushEnum(system.GetAPMController().GetCurrentPerformanceMode()); } class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { @@ -887,9 +938,8 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } -ILibraryAppletCreator::ILibraryAppletCreator(u64 current_process_title_id) - : ServiceFramework("ILibraryAppletCreator"), - current_process_title_id(current_process_title_id) { +ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_) + : ServiceFramework("ILibraryAppletCreator"), system{system_} { static const FunctionInfo functions[] = { {0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"}, {1, nullptr, "TerminateAllLibraryApplets"}, @@ -911,8 +961,8 @@ void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", static_cast<u32>(applet_id), applet_mode); - const auto& applet_manager{Core::System::GetInstance().GetAppletManager()}; - const auto applet = applet_manager.GetApplet(applet_id, current_process_title_id); + const auto& applet_manager{system.GetAppletManager()}; + const auto applet = applet_manager.GetApplet(applet_id); if (applet == nullptr) { LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", static_cast<u32>(applet_id)); @@ -949,8 +999,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex const auto handle{rp.Pop<Kernel::Handle>()}; const auto transfer_mem = - Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>( - handle); + system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle); if (transfer_mem == nullptr) { LOG_ERROR(Service_AM, "shared_mem is a nullpr for handle={:08X}", handle); @@ -968,7 +1017,8 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex rb.PushIpcInterface(std::make_shared<IStorage>(std::move(memory))); } -IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { +IApplicationFunctions::IApplicationFunctions(Core::System& system_) + : ServiceFramework("IApplicationFunctions"), system{system_} { // clang-format off static const FunctionInfo functions[] = { {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"}, @@ -1007,6 +1057,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF {120, nullptr, "ExecuteProgram"}, {121, nullptr, "ClearUserChannel"}, {122, nullptr, "UnpopToUserChannel"}, + {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, {500, nullptr, "StartContinuousRecordingFlushForDebug"}, {1000, nullptr, "CreateMovieMaker"}, {1001, nullptr, "PrepareForJit"}, @@ -1014,6 +1065,10 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF // clang-format on RegisterHandlers(functions); + + auto& kernel = Core::System::GetInstance().Kernel(); + gpu_error_detected_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::Manual, "IApplicationFunctions:GpuErrorDetectedSystemEvent"); } IApplicationFunctions::~IApplicationFunctions() = default; @@ -1125,7 +1180,7 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) { // Get supported languages from NACP, if possible // Default to 0 (all languages supported) u32 supported_languages = 0; - FileSys::PatchManager pm{Core::System::GetInstance().CurrentProcess()->GetTitleID()}; + FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()}; const auto res = pm.GetControlMetadata(); if (res.first != nullptr) { @@ -1133,8 +1188,8 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) { } // Call IApplicationManagerInterface implementation. - auto& service_manager = Core::System::GetInstance().ServiceManager(); - auto ns_am2 = service_manager.GetService<Service::NS::NS>("ns:am2"); + auto& service_manager = system.ServiceManager(); + auto ns_am2 = service_manager.GetService<NS::NS>("ns:am2"); auto app_man = ns_am2->GetApplicationManagerInterface(); // Get desired application language @@ -1206,8 +1261,8 @@ void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) { "new_journal={:016X}", static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size); - FileSystem::WriteSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id, - {new_normal_size, new_journal_size}); + const auto title_id = system.CurrentProcess()->GetTitleID(); + FileSystem::WriteSaveDataSize(type, title_id, user_id, {new_normal_size, new_journal_size}); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); @@ -1226,8 +1281,8 @@ void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type), user_id[1], user_id[0]); - const auto size = - FileSystem::ReadSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id); + const auto title_id = system.CurrentProcess()->GetTitleID(); + const auto size = FileSystem::ReadSaveDataSize(type, title_id, user_id); IPC::ResponseBuilder rb{ctx, 6}; rb.Push(RESULT_SUCCESS); @@ -1235,11 +1290,19 @@ void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) { rb.Push(size.journal); } +void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(gpu_error_detected_event.readable); +} + void InstallInterfaces(SM::ServiceManager& service_manager, std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system) { - auto message_queue = std::make_shared<AppletMessageQueue>(); - message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on - // game boot + auto message_queue = std::make_shared<AppletMessageQueue>(system.Kernel()); + // Needed on game boot + message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); std::make_shared<AppletAE>(nvflinger, message_queue, system)->InstallAsService(service_manager); std::make_shared<AppletOE>(nvflinger, message_queue, system)->InstallAsService(service_manager); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 4ea609d23..28f870302 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -10,12 +10,15 @@ #include "core/hle/kernel/writable_event.h" #include "core/hle/service/service.h" -namespace Service { -namespace NVFlinger { +namespace Kernel { +class KernelCore; +} + +namespace Service::NVFlinger { class NVFlinger; } -namespace AM { +namespace Service::AM { enum SystemLanguage { Japanese = 0, @@ -47,7 +50,7 @@ public: PerformanceModeChanged = 31, }; - AppletMessageQueue(); + explicit AppletMessageQueue(Kernel::KernelCore& kernel); ~AppletMessageQueue(); const Kernel::SharedPtr<Kernel::ReadableEvent>& GetMesssageRecieveEvent() const; @@ -65,12 +68,14 @@ private: class IWindowController final : public ServiceFramework<IWindowController> { public: - IWindowController(); + explicit IWindowController(Core::System& system_); ~IWindowController() override; private: void GetAppletResourceUserId(Kernel::HLERequestContext& ctx); void AcquireForegroundRights(Kernel::HLERequestContext& ctx); + + Core::System& system; }; class IAudioController final : public ServiceFramework<IAudioController> { @@ -113,7 +118,8 @@ public: class ISelfController final : public ServiceFramework<ISelfController> { public: - explicit ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); + explicit ISelfController(Core::System& system_, + std::shared_ptr<NVFlinger::NVFlinger> nvflinger_); ~ISelfController() override; private: @@ -133,6 +139,9 @@ private: void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx); void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); + void SetAutoSleepDisabled(Kernel::HLERequestContext& ctx); + void IsAutoSleepDisabled(Kernel::HLERequestContext& ctx); + void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); std::shared_ptr<NVFlinger::NVFlinger> nvflinger; @@ -141,11 +150,13 @@ private: u32 idle_time_detection_extension = 0; u64 num_fatal_sections_entered = 0; + bool is_auto_sleep_disabled = false; }; class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { public: - explicit ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue); + explicit ICommonStateGetter(Core::System& system, + std::shared_ptr<AppletMessageQueue> msg_queue); ~ICommonStateGetter() override; private: @@ -167,7 +178,9 @@ private: void GetPerformanceMode(Kernel::HLERequestContext& ctx); void GetBootMode(Kernel::HLERequestContext& ctx); void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx); + void SetCpuBoostMode(Kernel::HLERequestContext& ctx); + Core::System& system; std::shared_ptr<AppletMessageQueue> msg_queue; }; @@ -201,7 +214,7 @@ private: class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { public: - ILibraryAppletCreator(u64 current_process_title_id); + explicit ILibraryAppletCreator(Core::System& system_); ~ILibraryAppletCreator() override; private: @@ -209,12 +222,12 @@ private: void CreateStorage(Kernel::HLERequestContext& ctx); void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx); - u64 current_process_title_id; + Core::System& system; }; class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { public: - IApplicationFunctions(); + explicit IApplicationFunctions(Core::System& system_); ~IApplicationFunctions() override; private: @@ -235,6 +248,10 @@ private: void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx); void EndBlockingHomeButton(Kernel::HLERequestContext& ctx); void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx); + void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx); + + Kernel::EventPair gpu_error_detected_event; + Core::System& system; }; class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> { @@ -268,5 +285,4 @@ public: void InstallInterfaces(SM::ServiceManager& service_manager, std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system); -} // namespace AM -} // namespace Service +} // namespace Service::AM diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp index fe5beb8f9..e454b77d8 100644 --- a/src/core/hle/service/am/applet_ae.cpp +++ b/src/core/hle/service/am/applet_ae.cpp @@ -42,7 +42,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ICommonStateGetter>(msg_queue); + rb.PushIpcInterface<ICommonStateGetter>(system, msg_queue); } void GetSelfController(Kernel::HLERequestContext& ctx) { @@ -50,7 +50,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISelfController>(nvflinger); + rb.PushIpcInterface<ISelfController>(system, nvflinger); } void GetWindowController(Kernel::HLERequestContext& ctx) { @@ -58,7 +58,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IWindowController>(); + rb.PushIpcInterface<IWindowController>(system); } void GetAudioController(Kernel::HLERequestContext& ctx) { @@ -98,7 +98,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ILibraryAppletCreator>(system.CurrentProcess()->GetTitleID()); + rb.PushIpcInterface<ILibraryAppletCreator>(system); } void GetApplicationFunctions(Kernel::HLERequestContext& ctx) { @@ -106,7 +106,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IApplicationFunctions>(); + rb.PushIpcInterface<IApplicationFunctions>(system); } std::shared_ptr<NVFlinger::NVFlinger> nvflinger; @@ -146,7 +146,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ICommonStateGetter>(msg_queue); + rb.PushIpcInterface<ICommonStateGetter>(system, msg_queue); } void GetSelfController(Kernel::HLERequestContext& ctx) { @@ -154,7 +154,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISelfController>(nvflinger); + rb.PushIpcInterface<ISelfController>(system, nvflinger); } void GetWindowController(Kernel::HLERequestContext& ctx) { @@ -162,7 +162,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IWindowController>(); + rb.PushIpcInterface<IWindowController>(system); } void GetAudioController(Kernel::HLERequestContext& ctx) { @@ -194,7 +194,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ILibraryAppletCreator>(system.CurrentProcess()->GetTitleID()); + rb.PushIpcInterface<ILibraryAppletCreator>(system); } void GetHomeMenuFunctions(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp index 6e255fe95..a2ffaa440 100644 --- a/src/core/hle/service/am/applet_oe.cpp +++ b/src/core/hle/service/am/applet_oe.cpp @@ -4,7 +4,6 @@ #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/process.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_oe.h" #include "core/hle/service/nvflinger/nvflinger.h" @@ -64,7 +63,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IWindowController>(); + rb.PushIpcInterface<IWindowController>(system); } void GetSelfController(Kernel::HLERequestContext& ctx) { @@ -72,7 +71,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISelfController>(nvflinger); + rb.PushIpcInterface<ISelfController>(system, nvflinger); } void GetCommonStateGetter(Kernel::HLERequestContext& ctx) { @@ -80,7 +79,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ICommonStateGetter>(msg_queue); + rb.PushIpcInterface<ICommonStateGetter>(system, msg_queue); } void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) { @@ -88,7 +87,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ILibraryAppletCreator>(system.CurrentProcess()->GetTitleID()); + rb.PushIpcInterface<ILibraryAppletCreator>(system); } void GetApplicationFunctions(Kernel::HLERequestContext& ctx) { @@ -96,7 +95,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IApplicationFunctions>(); + rb.PushIpcInterface<IApplicationFunctions>(system); } std::shared_ptr<NVFlinger::NVFlinger> nvflinger; diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index 6bdba2468..d2e35362f 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -23,8 +23,7 @@ namespace Service::AM::Applets { -AppletDataBroker::AppletDataBroker() { - auto& kernel = Core::System::GetInstance().Kernel(); +AppletDataBroker::AppletDataBroker(Kernel::KernelCore& kernel) { state_changed_event = Kernel::WritableEvent::CreateEventPair( kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:StateChangedEvent"); pop_out_data_event = Kernel::WritableEvent::CreateEventPair( @@ -121,7 +120,7 @@ Kernel::SharedPtr<Kernel::ReadableEvent> AppletDataBroker::GetStateChangedEvent( return state_changed_event.readable; } -Applet::Applet() = default; +Applet::Applet(Kernel::KernelCore& kernel_) : broker{kernel_} {} Applet::~Applet() = default; @@ -154,7 +153,7 @@ AppletFrontendSet::AppletFrontendSet(AppletFrontendSet&&) noexcept = default; AppletFrontendSet& AppletFrontendSet::operator=(AppletFrontendSet&&) noexcept = default; -AppletManager::AppletManager() = default; +AppletManager::AppletManager(Core::System& system_) : system{system_} {} AppletManager::~AppletManager() = default; @@ -216,28 +215,28 @@ void AppletManager::ClearAll() { frontend = {}; } -std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, u64 current_process_title_id) const { +std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const { switch (id) { case AppletId::Auth: - return std::make_shared<Auth>(*frontend.parental_controls); + return std::make_shared<Auth>(system, *frontend.parental_controls); case AppletId::Error: - return std::make_shared<Error>(*frontend.error); + return std::make_shared<Error>(system, *frontend.error); case AppletId::ProfileSelect: - return std::make_shared<ProfileSelect>(*frontend.profile_select); + return std::make_shared<ProfileSelect>(system, *frontend.profile_select); case AppletId::SoftwareKeyboard: - return std::make_shared<SoftwareKeyboard>(*frontend.software_keyboard); + return std::make_shared<SoftwareKeyboard>(system, *frontend.software_keyboard); case AppletId::PhotoViewer: - return std::make_shared<PhotoViewer>(*frontend.photo_viewer); + return std::make_shared<PhotoViewer>(system, *frontend.photo_viewer); case AppletId::LibAppletShop: - return std::make_shared<WebBrowser>(*frontend.web_browser, current_process_title_id, + return std::make_shared<WebBrowser>(system, *frontend.web_browser, frontend.e_commerce.get()); case AppletId::LibAppletOff: - return std::make_shared<WebBrowser>(*frontend.web_browser, current_process_title_id); + return std::make_shared<WebBrowser>(system, *frontend.web_browser); default: UNIMPLEMENTED_MSG( "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.", static_cast<u8>(id)); - return std::make_shared<StubApplet>(id); + return std::make_shared<StubApplet>(system, id); } } diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index adc973dad..764c3418c 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -12,6 +12,10 @@ union ResultCode; +namespace Core { +class System; +} + namespace Core::Frontend { class ECommerceApplet; class ErrorApplet; @@ -22,6 +26,10 @@ class SoftwareKeyboardApplet; class WebBrowserApplet; } // namespace Core::Frontend +namespace Kernel { +class KernelCore; +} + namespace Service::AM { class IStorage; @@ -53,7 +61,7 @@ enum class AppletId : u32 { class AppletDataBroker final { public: - AppletDataBroker(); + explicit AppletDataBroker(Kernel::KernelCore& kernel_); ~AppletDataBroker(); struct RawChannelData { @@ -108,7 +116,7 @@ private: class Applet { public: - Applet(); + explicit Applet(Kernel::KernelCore& kernel_); virtual ~Applet(); virtual void Initialize(); @@ -179,7 +187,7 @@ struct AppletFrontendSet { class AppletManager { public: - AppletManager(); + explicit AppletManager(Core::System& system_); ~AppletManager(); void SetAppletFrontendSet(AppletFrontendSet set); @@ -187,10 +195,11 @@ public: void SetDefaultAppletsIfMissing(); void ClearAll(); - std::shared_ptr<Applet> GetApplet(AppletId id, u64 current_process_title_id) const; + std::shared_ptr<Applet> GetApplet(AppletId id) const; private: AppletFrontendSet frontend; + Core::System& system; }; } // namespace Applets diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp index af3a900f8..a7db26725 100644 --- a/src/core/hle/service/am/applets/error.cpp +++ b/src/core/hle/service/am/applets/error.cpp @@ -85,7 +85,8 @@ ResultCode Decode64BitError(u64 error) { } // Anonymous namespace -Error::Error(const Core::Frontend::ErrorApplet& frontend) : frontend(frontend) {} +Error::Error(Core::System& system_, const Core::Frontend::ErrorApplet& frontend_) + : Applet{system_.Kernel()}, frontend(frontend_), system{system_} {} Error::~Error() = default; @@ -145,8 +146,8 @@ void Error::Execute() { } const auto callback = [this] { DisplayCompleted(); }; - const auto title_id = Core::CurrentProcess()->GetTitleID(); - const auto& reporter{Core::System::GetInstance().GetReporter()}; + const auto title_id = system.CurrentProcess()->GetTitleID(); + const auto& reporter{system.GetReporter()}; switch (mode) { case ErrorAppletMode::ShowError: diff --git a/src/core/hle/service/am/applets/error.h b/src/core/hle/service/am/applets/error.h index a3590d181..a105cdb0c 100644 --- a/src/core/hle/service/am/applets/error.h +++ b/src/core/hle/service/am/applets/error.h @@ -7,6 +7,10 @@ #include "core/hle/result.h" #include "core/hle/service/am/applets/applets.h" +namespace Core { +class System; +} + namespace Service::AM::Applets { enum class ErrorAppletMode : u8 { @@ -21,7 +25,7 @@ enum class ErrorAppletMode : u8 { class Error final : public Applet { public: - explicit Error(const Core::Frontend::ErrorApplet& frontend); + explicit Error(Core::System& system_, const Core::Frontend::ErrorApplet& frontend_); ~Error() override; void Initialize() override; @@ -42,6 +46,7 @@ private: std::unique_ptr<ErrorArguments> args; bool complete = false; + Core::System& system; }; } // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/general_backend.cpp b/src/core/hle/service/am/applets/general_backend.cpp index e0def8dff..328438a1d 100644 --- a/src/core/hle/service/am/applets/general_backend.cpp +++ b/src/core/hle/service/am/applets/general_backend.cpp @@ -37,7 +37,8 @@ static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) } } -Auth::Auth(Core::Frontend::ParentalControlsApplet& frontend) : frontend(frontend) {} +Auth::Auth(Core::System& system_, Core::Frontend::ParentalControlsApplet& frontend_) + : Applet{system_.Kernel()}, frontend(frontend_) {} Auth::~Auth() = default; @@ -151,7 +152,8 @@ void Auth::AuthFinished(bool successful) { broker.SignalStateChanged(); } -PhotoViewer::PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend) : frontend(frontend) {} +PhotoViewer::PhotoViewer(Core::System& system_, const Core::Frontend::PhotoViewerApplet& frontend_) + : Applet{system_.Kernel()}, frontend(frontend_), system{system_} {} PhotoViewer::~PhotoViewer() = default; @@ -185,7 +187,7 @@ void PhotoViewer::Execute() { const auto callback = [this] { ViewFinished(); }; switch (mode) { case PhotoViewerAppletMode::CurrentApp: - frontend.ShowPhotosForApplication(Core::CurrentProcess()->GetTitleID(), callback); + frontend.ShowPhotosForApplication(system.CurrentProcess()->GetTitleID(), callback); break; case PhotoViewerAppletMode::AllApps: frontend.ShowAllPhotos(callback); @@ -200,7 +202,8 @@ void PhotoViewer::ViewFinished() { broker.SignalStateChanged(); } -StubApplet::StubApplet(AppletId id) : id(id) {} +StubApplet::StubApplet(Core::System& system_, AppletId id_) + : Applet{system_.Kernel()}, id(id_), system{system_} {} StubApplet::~StubApplet() = default; @@ -209,7 +212,7 @@ void StubApplet::Initialize() { Applet::Initialize(); const auto data = broker.PeekDataToAppletForDebug(); - Core::System::GetInstance().GetReporter().SaveUnimplementedAppletReport( + system.GetReporter().SaveUnimplementedAppletReport( static_cast<u32>(id), common_args.arguments_version, common_args.library_version, common_args.theme_color, common_args.play_startup_sound, common_args.system_tick, data.normal, data.interactive); diff --git a/src/core/hle/service/am/applets/general_backend.h b/src/core/hle/service/am/applets/general_backend.h index 0da252044..cfa2df369 100644 --- a/src/core/hle/service/am/applets/general_backend.h +++ b/src/core/hle/service/am/applets/general_backend.h @@ -6,6 +6,10 @@ #include "core/hle/service/am/applets/applets.h" +namespace Core { +class System; +} + namespace Service::AM::Applets { enum class AuthAppletType : u32 { @@ -16,7 +20,7 @@ enum class AuthAppletType : u32 { class Auth final : public Applet { public: - explicit Auth(Core::Frontend::ParentalControlsApplet& frontend); + explicit Auth(Core::System& system_, Core::Frontend::ParentalControlsApplet& frontend_); ~Auth() override; void Initialize() override; @@ -45,7 +49,7 @@ enum class PhotoViewerAppletMode : u8 { class PhotoViewer final : public Applet { public: - explicit PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend); + explicit PhotoViewer(Core::System& system_, const Core::Frontend::PhotoViewerApplet& frontend_); ~PhotoViewer() override; void Initialize() override; @@ -60,11 +64,12 @@ private: const Core::Frontend::PhotoViewerApplet& frontend; bool complete = false; PhotoViewerAppletMode mode = PhotoViewerAppletMode::CurrentApp; + Core::System& system; }; class StubApplet final : public Applet { public: - explicit StubApplet(AppletId id); + explicit StubApplet(Core::System& system_, AppletId id_); ~StubApplet() override; void Initialize() override; @@ -76,6 +81,7 @@ public: private: AppletId id; + Core::System& system; }; } // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/profile_select.cpp index 57b5419e8..3eba696ca 100644 --- a/src/core/hle/service/am/applets/profile_select.cpp +++ b/src/core/hle/service/am/applets/profile_select.cpp @@ -15,8 +15,9 @@ namespace Service::AM::Applets { constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1}; -ProfileSelect::ProfileSelect(const Core::Frontend::ProfileSelectApplet& frontend) - : frontend(frontend) {} +ProfileSelect::ProfileSelect(Core::System& system_, + const Core::Frontend::ProfileSelectApplet& frontend_) + : Applet{system_.Kernel()}, frontend(frontend_) {} ProfileSelect::~ProfileSelect() = default; diff --git a/src/core/hle/service/am/applets/profile_select.h b/src/core/hle/service/am/applets/profile_select.h index 563cd744a..16364ead7 100644 --- a/src/core/hle/service/am/applets/profile_select.h +++ b/src/core/hle/service/am/applets/profile_select.h @@ -11,6 +11,10 @@ #include "core/hle/result.h" #include "core/hle/service/am/applets/applets.h" +namespace Core { +class System; +} + namespace Service::AM::Applets { struct UserSelectionConfig { @@ -29,7 +33,8 @@ static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has inco class ProfileSelect final : public Applet { public: - explicit ProfileSelect(const Core::Frontend::ProfileSelectApplet& frontend); + explicit ProfileSelect(Core::System& system_, + const Core::Frontend::ProfileSelectApplet& frontend_); ~ProfileSelect() override; void Initialize() override; diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index e197990f7..748559cd0 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -39,8 +39,9 @@ static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( return params; } -SoftwareKeyboard::SoftwareKeyboard(const Core::Frontend::SoftwareKeyboardApplet& frontend) - : frontend(frontend) {} +SoftwareKeyboard::SoftwareKeyboard(Core::System& system_, + const Core::Frontend::SoftwareKeyboardApplet& frontend_) + : Applet{system_.Kernel()}, frontend(frontend_) {} SoftwareKeyboard::~SoftwareKeyboard() = default; diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h index 0fbc43e51..ef4801fc6 100644 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ b/src/core/hle/service/am/applets/software_keyboard.h @@ -16,6 +16,10 @@ union ResultCode; +namespace Core { +class System; +} + namespace Service::AM::Applets { enum class KeysetDisable : u32 { @@ -55,7 +59,8 @@ static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect siz class SoftwareKeyboard final : public Applet { public: - explicit SoftwareKeyboard(const Core::Frontend::SoftwareKeyboardApplet& frontend); + explicit SoftwareKeyboard(Core::System& system_, + const Core::Frontend::SoftwareKeyboardApplet& frontend_); ~SoftwareKeyboard() override; void Initialize() override; diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp index f3c9fef0e..32283e819 100644 --- a/src/core/hle/service/am/applets/web_browser.cpp +++ b/src/core/hle/service/am/applets/web_browser.cpp @@ -190,8 +190,9 @@ std::map<WebArgTLVType, std::vector<u8>> GetWebArguments(const std::vector<u8>& return out; } -FileSys::VirtualFile GetApplicationRomFS(u64 title_id, FileSys::ContentRecordType type) { - const auto& installed{Core::System::GetInstance().GetContentProvider()}; +FileSys::VirtualFile GetApplicationRomFS(const Core::System& system, u64 title_id, + FileSys::ContentRecordType type) { + const auto& installed{system.GetContentProvider()}; const auto res = installed.GetEntry(title_id, type); if (res != nullptr) { @@ -207,10 +208,10 @@ FileSys::VirtualFile GetApplicationRomFS(u64 title_id, FileSys::ContentRecordTyp } // Anonymous namespace -WebBrowser::WebBrowser(Core::Frontend::WebBrowserApplet& frontend, u64 current_process_title_id, - Core::Frontend::ECommerceApplet* frontend_e_commerce) - : frontend(frontend), frontend_e_commerce(frontend_e_commerce), - current_process_title_id(current_process_title_id) {} +WebBrowser::WebBrowser(Core::System& system_, Core::Frontend::WebBrowserApplet& frontend_, + Core::Frontend::ECommerceApplet* frontend_e_commerce_) + : Applet{system_.Kernel()}, frontend(frontend_), + frontend_e_commerce(frontend_e_commerce_), system{system_} {} WebBrowser::~WebBrowser() = default; @@ -266,7 +267,7 @@ void WebBrowser::UnpackRomFS() { ASSERT(offline_romfs != nullptr); const auto dir = FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard); - const auto& vfs{Core::System::GetInstance().GetFilesystem()}; + const auto& vfs{system.GetFilesystem()}; const auto temp_dir = vfs->CreateDirectory(temporary_dir, FileSys::Mode::ReadWrite); FileSys::VfsRawCopyD(dir, temp_dir); @@ -470,10 +471,10 @@ void WebBrowser::InitializeOffline() { } if (title_id == 0) { - title_id = current_process_title_id; + title_id = system.CurrentProcess()->GetTitleID(); } - offline_romfs = GetApplicationRomFS(title_id, type); + offline_romfs = GetApplicationRomFS(system, title_id, type); if (offline_romfs == nullptr) { status = ResultCode(-1); LOG_ERROR(Service_AM, "Failed to find offline data for request!"); diff --git a/src/core/hle/service/am/applets/web_browser.h b/src/core/hle/service/am/applets/web_browser.h index 870f57b64..8d4027411 100644 --- a/src/core/hle/service/am/applets/web_browser.h +++ b/src/core/hle/service/am/applets/web_browser.h @@ -9,6 +9,10 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/applets.h" +namespace Core { +class System; +} + namespace Service::AM::Applets { enum class ShimKind : u32; @@ -17,8 +21,8 @@ enum class WebArgTLVType : u16; class WebBrowser final : public Applet { public: - WebBrowser(Core::Frontend::WebBrowserApplet& frontend, u64 current_process_title_id, - Core::Frontend::ECommerceApplet* frontend_e_commerce = nullptr); + WebBrowser(Core::System& system_, Core::Frontend::WebBrowserApplet& frontend_, + Core::Frontend::ECommerceApplet* frontend_e_commerce_ = nullptr); ~WebBrowser() override; @@ -59,8 +63,6 @@ private: bool unpacked = false; ResultCode status = RESULT_SUCCESS; - u64 current_process_title_id; - ShimKind kind; std::map<WebArgTLVType, std::vector<u8>> args; @@ -74,6 +76,8 @@ private: std::optional<u128> user_id; std::optional<bool> shop_full_display; std::string shop_extra_parameter; + + Core::System& system; }; } // namespace Service::AM::Applets diff --git a/src/core/hle/service/apm/apm.cpp b/src/core/hle/service/apm/apm.cpp index f3c09bbb1..85bbf5988 100644 --- a/src/core/hle/service/apm/apm.cpp +++ b/src/core/hle/service/apm/apm.cpp @@ -2,7 +2,6 @@ // 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/apm/apm.h" #include "core/hle/service/apm/interface.h" @@ -12,11 +11,15 @@ namespace Service::APM { Module::Module() = default; Module::~Module() = default; -void InstallInterfaces(SM::ServiceManager& service_manager) { +void InstallInterfaces(Core::System& system) { auto module_ = std::make_shared<Module>(); - std::make_shared<APM>(module_, "apm")->InstallAsService(service_manager); - std::make_shared<APM>(module_, "apm:p")->InstallAsService(service_manager); - std::make_shared<APM_Sys>()->InstallAsService(service_manager); + std::make_shared<APM>(module_, system.GetAPMController(), "apm") + ->InstallAsService(system.ServiceManager()); + std::make_shared<APM>(module_, system.GetAPMController(), "apm:p") + ->InstallAsService(system.ServiceManager()); + std::make_shared<APM>(module_, system.GetAPMController(), "apm:am") + ->InstallAsService(system.ServiceManager()); + std::make_shared<APM_Sys>(system.GetAPMController())->InstallAsService(system.ServiceManager()); } } // namespace Service::APM diff --git a/src/core/hle/service/apm/apm.h b/src/core/hle/service/apm/apm.h index 4d7d5bb7c..cf4c2bb11 100644 --- a/src/core/hle/service/apm/apm.h +++ b/src/core/hle/service/apm/apm.h @@ -8,11 +8,6 @@ namespace Service::APM { -enum class PerformanceMode : u8 { - Handheld = 0, - Docked = 1, -}; - class Module final { public: Module(); @@ -20,6 +15,6 @@ public: }; /// Registers all AM services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(Core::System& system); } // namespace Service::APM diff --git a/src/core/hle/service/apm/controller.cpp b/src/core/hle/service/apm/controller.cpp new file mode 100644 index 000000000..4376612eb --- /dev/null +++ b/src/core/hle/service/apm/controller.cpp @@ -0,0 +1,68 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/core_timing.h" +#include "core/hle/service/apm/controller.h" +#include "core/settings.h" + +namespace Service::APM { + +constexpr PerformanceConfiguration DEFAULT_PERFORMANCE_CONFIGURATION = + PerformanceConfiguration::Config7; + +Controller::Controller(Core::Timing::CoreTiming& core_timing) + : core_timing(core_timing), configs{ + {PerformanceMode::Handheld, DEFAULT_PERFORMANCE_CONFIGURATION}, + {PerformanceMode::Docked, DEFAULT_PERFORMANCE_CONFIGURATION}, + } {} + +Controller::~Controller() = default; + +void Controller::SetPerformanceConfiguration(PerformanceMode mode, + PerformanceConfiguration config) { + static const std::map<PerformanceConfiguration, u32> PCONFIG_TO_SPEED_MAP{ + {PerformanceConfiguration::Config1, 1020}, {PerformanceConfiguration::Config2, 1020}, + {PerformanceConfiguration::Config3, 1224}, {PerformanceConfiguration::Config4, 1020}, + {PerformanceConfiguration::Config5, 1020}, {PerformanceConfiguration::Config6, 1224}, + {PerformanceConfiguration::Config7, 1020}, {PerformanceConfiguration::Config8, 1020}, + {PerformanceConfiguration::Config9, 1020}, {PerformanceConfiguration::Config10, 1020}, + {PerformanceConfiguration::Config11, 1020}, {PerformanceConfiguration::Config12, 1020}, + {PerformanceConfiguration::Config13, 1785}, {PerformanceConfiguration::Config14, 1785}, + {PerformanceConfiguration::Config15, 1020}, {PerformanceConfiguration::Config16, 1020}, + }; + + SetClockSpeed(PCONFIG_TO_SPEED_MAP.find(config)->second); + configs.insert_or_assign(mode, config); +} + +void Controller::SetFromCpuBoostMode(CpuBoostMode mode) { + constexpr std::array<PerformanceConfiguration, 3> BOOST_MODE_TO_CONFIG_MAP{{ + PerformanceConfiguration::Config7, + PerformanceConfiguration::Config13, + PerformanceConfiguration::Config15, + }}; + + SetPerformanceConfiguration(PerformanceMode::Docked, + BOOST_MODE_TO_CONFIG_MAP.at(static_cast<u32>(mode))); +} + +PerformanceMode Controller::GetCurrentPerformanceMode() { + return Settings::values.use_docked_mode ? PerformanceMode::Docked : PerformanceMode::Handheld; +} + +PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) { + if (configs.find(mode) == configs.end()) { + configs.insert_or_assign(mode, DEFAULT_PERFORMANCE_CONFIGURATION); + } + + return configs[mode]; +} + +void Controller::SetClockSpeed(u32 mhz) { + LOG_INFO(Service_APM, "called, mhz={:08X}", mhz); + // TODO(DarkLordZach): Actually signal core_timing to change clock speed. +} + +} // namespace Service::APM diff --git a/src/core/hle/service/apm/controller.h b/src/core/hle/service/apm/controller.h new file mode 100644 index 000000000..8ac80eaea --- /dev/null +++ b/src/core/hle/service/apm/controller.h @@ -0,0 +1,70 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <map> +#include "common/common_types.h" + +namespace Core::Timing { +class CoreTiming; +} + +namespace Service::APM { + +enum class PerformanceConfiguration : u32 { + Config1 = 0x00010000, + Config2 = 0x00010001, + Config3 = 0x00010002, + Config4 = 0x00020000, + Config5 = 0x00020001, + Config6 = 0x00020002, + Config7 = 0x00020003, + Config8 = 0x00020004, + Config9 = 0x00020005, + Config10 = 0x00020006, + Config11 = 0x92220007, + Config12 = 0x92220008, + Config13 = 0x92220009, + Config14 = 0x9222000A, + Config15 = 0x9222000B, + Config16 = 0x9222000C, +}; + +enum class CpuBoostMode : u32 { + Disabled = 0, + Full = 1, // CPU + GPU -> Config 13, 14, 15, or 16 + Partial = 2, // GPU Only -> Config 15 or 16 +}; + +enum class PerformanceMode : u8 { + Handheld = 0, + Docked = 1, +}; + +// Class to manage the state and change of the emulated system performance. +// Specifically, this deals with PerformanceMode, which corresponds to the system being docked or +// undocked, and PerformanceConfig which specifies the exact CPU, GPU, and Memory clocks to operate +// at. Additionally, this manages 'Boost Mode', which allows games to temporarily overclock the +// system during times of high load -- this simply maps to different PerformanceConfigs to use. +class Controller { +public: + Controller(Core::Timing::CoreTiming& core_timing); + ~Controller(); + + void SetPerformanceConfiguration(PerformanceMode mode, PerformanceConfiguration config); + void SetFromCpuBoostMode(CpuBoostMode mode); + + PerformanceMode GetCurrentPerformanceMode(); + PerformanceConfiguration GetCurrentPerformanceConfiguration(PerformanceMode mode); + +private: + void SetClockSpeed(u32 mhz); + + std::map<PerformanceMode, PerformanceConfiguration> configs; + + Core::Timing::CoreTiming& core_timing; +}; + +} // namespace Service::APM diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp index d058c0245..06f0f8edd 100644 --- a/src/core/hle/service/apm/interface.cpp +++ b/src/core/hle/service/apm/interface.cpp @@ -5,43 +5,32 @@ #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/apm/apm.h" +#include "core/hle/service/apm/controller.h" #include "core/hle/service/apm/interface.h" namespace Service::APM { class ISession final : public ServiceFramework<ISession> { public: - ISession() : ServiceFramework("ISession") { + ISession(Controller& controller) : ServiceFramework("ISession"), controller(controller) { static const FunctionInfo functions[] = { {0, &ISession::SetPerformanceConfiguration, "SetPerformanceConfiguration"}, {1, &ISession::GetPerformanceConfiguration, "GetPerformanceConfiguration"}, + {2, nullptr, "SetCpuOverclockEnabled"}, }; RegisterHandlers(functions); } private: - enum class PerformanceConfiguration : u32 { - Config1 = 0x00010000, - Config2 = 0x00010001, - Config3 = 0x00010002, - Config4 = 0x00020000, - Config5 = 0x00020001, - Config6 = 0x00020002, - Config7 = 0x00020003, - Config8 = 0x00020004, - Config9 = 0x00020005, - Config10 = 0x00020006, - Config11 = 0x92220007, - Config12 = 0x92220008, - }; - void SetPerformanceConfiguration(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto mode = static_cast<PerformanceMode>(rp.Pop<u32>()); - u32 config = rp.Pop<u32>(); - LOG_WARNING(Service_APM, "(STUBBED) called mode={} config={}", static_cast<u32>(mode), - config); + const auto mode = rp.PopEnum<PerformanceMode>(); + const auto config = rp.PopEnum<PerformanceConfiguration>(); + LOG_DEBUG(Service_APM, "called mode={} config={}", static_cast<u32>(mode), + static_cast<u32>(config)); + + controller.SetPerformanceConfiguration(mode, config); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -50,20 +39,23 @@ private: void GetPerformanceConfiguration(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto mode = static_cast<PerformanceMode>(rp.Pop<u32>()); - LOG_WARNING(Service_APM, "(STUBBED) called mode={}", static_cast<u32>(mode)); + const auto mode = rp.PopEnum<PerformanceMode>(); + LOG_DEBUG(Service_APM, "called mode={}", static_cast<u32>(mode)); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(static_cast<u32>(PerformanceConfiguration::Config1)); + rb.PushEnum(controller.GetCurrentPerformanceConfiguration(mode)); } + + Controller& controller; }; -APM::APM(std::shared_ptr<Module> apm, const char* name) - : ServiceFramework(name), apm(std::move(apm)) { +APM::APM(std::shared_ptr<Module> apm, Controller& controller, const char* name) + : ServiceFramework(name), apm(std::move(apm)), controller(controller) { static const FunctionInfo functions[] = { {0, &APM::OpenSession, "OpenSession"}, - {1, nullptr, "GetPerformanceMode"}, + {1, &APM::GetPerformanceMode, "GetPerformanceMode"}, + {6, nullptr, "IsCpuOverclockEnabled"}, }; RegisterHandlers(functions); } @@ -75,10 +67,17 @@ void APM::OpenSession(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISession>(); + rb.PushIpcInterface<ISession>(controller); +} + +void APM::GetPerformanceMode(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_APM, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.PushEnum(controller.GetCurrentPerformanceMode()); } -APM_Sys::APM_Sys() : ServiceFramework{"apm:sys"} { +APM_Sys::APM_Sys(Controller& controller) : ServiceFramework{"apm:sys"}, controller(controller) { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "RequestPerformanceMode"}, @@ -87,8 +86,8 @@ APM_Sys::APM_Sys() : ServiceFramework{"apm:sys"} { {3, nullptr, "GetLastThrottlingState"}, {4, nullptr, "ClearLastThrottlingState"}, {5, nullptr, "LoadAndApplySettings"}, - {6, nullptr, "SetCpuBoostMode"}, - {7, nullptr, "GetCurrentPerformanceConfiguration"}, + {6, &APM_Sys::SetCpuBoostMode, "SetCpuBoostMode"}, + {7, &APM_Sys::GetCurrentPerformanceConfiguration, "GetCurrentPerformanceConfiguration"}, }; // clang-format on @@ -102,7 +101,28 @@ void APM_Sys::GetPerformanceEvent(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISession>(); + rb.PushIpcInterface<ISession>(controller); +} + +void APM_Sys::SetCpuBoostMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto mode = rp.PopEnum<CpuBoostMode>(); + + LOG_DEBUG(Service_APM, "called, mode={:08X}", static_cast<u32>(mode)); + + controller.SetFromCpuBoostMode(mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void APM_Sys::GetCurrentPerformanceConfiguration(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_APM, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushEnum( + controller.GetCurrentPerformanceConfiguration(controller.GetCurrentPerformanceMode())); } } // namespace Service::APM diff --git a/src/core/hle/service/apm/interface.h b/src/core/hle/service/apm/interface.h index 773541aa4..de1b89437 100644 --- a/src/core/hle/service/apm/interface.h +++ b/src/core/hle/service/apm/interface.h @@ -8,24 +8,34 @@ namespace Service::APM { +class Controller; +class Module; + class APM final : public ServiceFramework<APM> { public: - explicit APM(std::shared_ptr<Module> apm, const char* name); + explicit APM(std::shared_ptr<Module> apm, Controller& controller, const char* name); ~APM() override; private: void OpenSession(Kernel::HLERequestContext& ctx); + void GetPerformanceMode(Kernel::HLERequestContext& ctx); std::shared_ptr<Module> apm; + Controller& controller; }; class APM_Sys final : public ServiceFramework<APM_Sys> { public: - explicit APM_Sys(); + explicit APM_Sys(Controller& controller); ~APM_Sys() override; + void SetCpuBoostMode(Kernel::HLERequestContext& ctx); + private: void GetPerformanceEvent(Kernel::HLERequestContext& ctx); + void GetCurrentPerformanceConfiguration(Kernel::HLERequestContext& ctx); + + Controller& controller; }; } // namespace Service::APM diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp index 128df7db5..1781bec83 100644 --- a/src/core/hle/service/audio/audio.cpp +++ b/src/core/hle/service/audio/audio.cpp @@ -19,16 +19,16 @@ namespace Service::Audio { -void InstallInterfaces(SM::ServiceManager& service_manager) { +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { std::make_shared<AudCtl>()->InstallAsService(service_manager); std::make_shared<AudOutA>()->InstallAsService(service_manager); - std::make_shared<AudOutU>()->InstallAsService(service_manager); + std::make_shared<AudOutU>(system)->InstallAsService(service_manager); std::make_shared<AudInA>()->InstallAsService(service_manager); std::make_shared<AudInU>()->InstallAsService(service_manager); std::make_shared<AudRecA>()->InstallAsService(service_manager); std::make_shared<AudRecU>()->InstallAsService(service_manager); std::make_shared<AudRenA>()->InstallAsService(service_manager); - std::make_shared<AudRenU>()->InstallAsService(service_manager); + std::make_shared<AudRenU>(system)->InstallAsService(service_manager); std::make_shared<CodecCtl>()->InstallAsService(service_manager); std::make_shared<HwOpus>()->InstallAsService(service_manager); diff --git a/src/core/hle/service/audio/audio.h b/src/core/hle/service/audio/audio.h index f5bd3bf5f..b6d13912e 100644 --- a/src/core/hle/service/audio/audio.h +++ b/src/core/hle/service/audio/audio.h @@ -4,6 +4,10 @@ #pragma once +namespace Core { +class System; +} + namespace Service::SM { class ServiceManager; } @@ -11,6 +15,6 @@ class ServiceManager; namespace Service::Audio { /// Registers all Audio services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 7db6eb08d..fb84a8f13 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -40,8 +40,8 @@ enum class AudioState : u32 { class IAudioOut final : public ServiceFramework<IAudioOut> { public: - IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core, std::string&& device_name, - std::string&& unique_name) + IAudioOut(Core::System& system, AudoutParams audio_params, AudioCore::AudioOut& audio_core, + std::string&& device_name, std::string&& unique_name) : ServiceFramework("IAudioOut"), audio_core(audio_core), device_name(std::move(device_name)), audio_params(audio_params) { // clang-format off @@ -65,7 +65,6 @@ public: RegisterHandlers(functions); // This is the event handle used to check if the audio buffer was released - auto& system = Core::System::GetInstance(); buffer_event = Kernel::WritableEvent::CreateEventPair( system.Kernel(), Kernel::ResetType::Manual, "IAudioOutBufferReleased"); @@ -212,6 +211,22 @@ private: Kernel::EventPair buffer_event; }; +AudOutU::AudOutU(Core::System& system_) : ServiceFramework("audout:u"), system{system_} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"}, + {1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"}, + {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"}, + {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"}, + }; + // clang-format on + + RegisterHandlers(functions); + audio_core = std::make_unique<AudioCore::AudioOut>(); +} + +AudOutU::~AudOutU() = default; + void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); @@ -248,7 +263,7 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())}; auto audio_out_interface = std::make_shared<IAudioOut>( - params, *audio_core, std::move(device_name), std::move(unique_name)); + system, params, *audio_core, std::move(device_name), std::move(unique_name)); IPC::ResponseBuilder rb{ctx, 6, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -256,20 +271,9 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { rb.Push<u32>(params.channel_count); rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16)); rb.Push<u32>(static_cast<u32>(AudioState::Stopped)); - rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface); + rb.PushIpcInterface<IAudioOut>(audio_out_interface); audio_out_interfaces.push_back(std::move(audio_out_interface)); } -AudOutU::AudOutU() : ServiceFramework("audout:u") { - static const FunctionInfo functions[] = {{0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"}, - {1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"}, - {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"}, - {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"}}; - RegisterHandlers(functions); - audio_core = std::make_unique<AudioCore::AudioOut>(); -} - -AudOutU::~AudOutU() = default; - } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h index aed4c43b2..c9f532ccd 100644 --- a/src/core/hle/service/audio/audout_u.h +++ b/src/core/hle/service/audio/audout_u.h @@ -11,6 +11,10 @@ namespace AudioCore { class AudioOut; } +namespace Core { +class System; +} + namespace Kernel { class HLERequestContext; } @@ -21,15 +25,17 @@ class IAudioOut; class AudOutU final : public ServiceFramework<AudOutU> { public: - AudOutU(); + explicit AudOutU(Core::System& system_); ~AudOutU() override; private: + void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); + void OpenAudioOutImpl(Kernel::HLERequestContext& ctx); + std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces; std::unique_ptr<AudioCore::AudioOut> audio_core; - void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); - void OpenAudioOutImpl(Kernel::HLERequestContext& ctx); + Core::System& system; }; } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 75db0c2dc..f162249ed 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -5,6 +5,7 @@ #include <algorithm> #include <array> #include <memory> +#include <string_view> #include "audio_core/audio_renderer.h" #include "common/alignment.h" @@ -25,7 +26,8 @@ namespace Service::Audio { class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { public: - explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params) + explicit IAudioRenderer(Core::System& system, AudioCore::AudioRendererParameter audren_params, + const std::size_t instance_number) : ServiceFramework("IAudioRenderer") { // clang-format off static const FunctionInfo functions[] = { @@ -45,11 +47,10 @@ public: // clang-format on RegisterHandlers(functions); - auto& system = Core::System::GetInstance(); system_event = Kernel::WritableEvent::CreateEventPair( system.Kernel(), Kernel::ResetType::Manual, "IAudioRenderer:SystemEvent"); - renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), audren_params, - system_event.writable); + renderer = std::make_unique<AudioCore::AudioRenderer>( + system.CoreTiming(), audren_params, system_event.writable, instance_number); } private: @@ -159,40 +160,81 @@ private: class IAudioDevice final : public ServiceFramework<IAudioDevice> { public: - IAudioDevice() : ServiceFramework("IAudioDevice") { + explicit IAudioDevice(Core::System& system, u32_le revision_num) + : ServiceFramework("IAudioDevice"), revision{revision_num} { static const FunctionInfo functions[] = { {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"}, {1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"}, - {2, nullptr, "GetAudioDeviceOutputVolume"}, + {2, &IAudioDevice::GetAudioDeviceOutputVolume, "GetAudioDeviceOutputVolume"}, {3, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceName"}, {4, &IAudioDevice::QueryAudioDeviceSystemEvent, "QueryAudioDeviceSystemEvent"}, {5, &IAudioDevice::GetActiveChannelCount, "GetActiveChannelCount"}, - {6, &IAudioDevice::ListAudioDeviceName, - "ListAudioDeviceNameAuto"}, // TODO(ogniK): Confirm if autos are identical to non auto + {6, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceNameAuto"}, {7, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolumeAuto"}, - {8, nullptr, "GetAudioDeviceOutputVolumeAuto"}, + {8, &IAudioDevice::GetAudioDeviceOutputVolume, "GetAudioDeviceOutputVolumeAuto"}, {10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"}, - {11, nullptr, "QueryAudioDeviceInputEvent"}, - {12, nullptr, "QueryAudioDeviceOutputEvent"}, + {11, &IAudioDevice::QueryAudioDeviceInputEvent, "QueryAudioDeviceInputEvent"}, + {12, &IAudioDevice::QueryAudioDeviceOutputEvent, "QueryAudioDeviceOutputEvent"}, {13, nullptr, "GetAudioSystemMasterVolumeSetting"}, }; RegisterHandlers(functions); - auto& kernel = Core::System::GetInstance().Kernel(); + auto& kernel = system.Kernel(); buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, "IAudioOutBufferReleasedEvent"); + + // Should be similar to audio_output_device_switch_event + audio_input_device_switch_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::Automatic, "IAudioDevice:AudioInputDeviceSwitchedEvent"); + + // Should only be signalled when an audio output device has been changed, example: speaker + // to headset + audio_output_device_switch_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::Automatic, "IAudioDevice:AudioOutputDeviceSwitchedEvent"); } private: + using AudioDeviceName = std::array<char, 256>; + static constexpr std::array<std::string_view, 4> audio_device_names{{ + "AudioStereoJackOutput", + "AudioBuiltInSpeakerOutput", + "AudioTvOutput", + "AudioUsbDeviceOutput", + }}; + enum class DeviceType { + AHUBHeadphones, + AHUBSpeakers, + HDA, + USBOutput, + }; + void ListAudioDeviceName(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "called"); + + const bool usb_output_supported = + IsFeatureSupported(AudioFeatures::AudioUSBDeviceOutput, revision); + const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioDeviceName); - constexpr std::array<char, 15> audio_interface{{"AudioInterface"}}; - ctx.WriteBuffer(audio_interface); + std::vector<AudioDeviceName> name_buffer; + name_buffer.reserve(audio_device_names.size()); + + for (std::size_t i = 0; i < count && i < audio_device_names.size(); i++) { + const auto type = static_cast<DeviceType>(i); + + if (!usb_output_supported && type == DeviceType::USBOutput) { + continue; + } + + const auto& device_name = audio_device_names[i]; + auto& entry = name_buffer.emplace_back(); + device_name.copy(entry.data(), device_name.size()); + } + + ctx.WriteBuffer(name_buffer); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(1); + rb.Push(static_cast<u32>(name_buffer.size())); } void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) { @@ -208,15 +250,32 @@ private: rb.Push(RESULT_SUCCESS); } + void GetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto device_name_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(device_name_buffer); + + LOG_WARNING(Service_Audio, "(STUBBED) called. name={}", name); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(1.0f); + } + void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_Audio, "(STUBBED) called"); - constexpr std::array<char, 12> audio_interface{{"AudioDevice"}}; - ctx.WriteBuffer(audio_interface); + // Currently set to always be TV audio output. + const auto& device_name = audio_device_names[2]; - IPC::ResponseBuilder rb{ctx, 3}; + AudioDeviceName out_device_name{}; + device_name.copy(out_device_name.data(), device_name.size()); + + ctx.WriteBuffer(out_device_name); + + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(1); } void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) { @@ -237,11 +296,31 @@ private: rb.Push<u32>(1); } + // Should be similar to QueryAudioDeviceOutputEvent + void QueryAudioDeviceInputEvent(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(audio_input_device_switch_event.readable); + } + + void QueryAudioDeviceOutputEvent(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(audio_output_device_switch_event.readable); + } + + u32_le revision = 0; Kernel::EventPair buffer_event; + Kernel::EventPair audio_input_device_switch_event; + Kernel::EventPair audio_output_device_switch_event; }; // namespace Audio -AudRenU::AudRenU() : ServiceFramework("audren:u") { +AudRenU::AudRenU(Core::System& system_) : ServiceFramework("audren:u"), system{system_} { // clang-format off static const FunctionInfo functions[] = { {0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"}, @@ -314,7 +393,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { }; // Calculates the portion of the size related to the mix data (and the sorting thereof). - const auto calculate_mix_info_size = [this](const AudioCore::AudioRendererParameter& params) { + const auto calculate_mix_info_size = [](const AudioCore::AudioRendererParameter& params) { // The size of the mixing info data structure. constexpr u64 mix_info_size = 0x940; @@ -386,7 +465,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { // Calculates the part of the size related to the splitter context. const auto calculate_splitter_context_size = - [this](const AudioCore::AudioRendererParameter& params) -> u64 { + [](const AudioCore::AudioRendererParameter& params) -> u64 { if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { return 0; } @@ -433,7 +512,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { }; // Calculates the part of the size related to performance statistics. - const auto calculate_perf_size = [this](const AudioCore::AudioRendererParameter& params) { + const auto calculate_perf_size = [](const AudioCore::AudioRendererParameter& params) { // Extra size value appended to the end of the calculation. constexpr u64 appended = 128; @@ -460,78 +539,76 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { }; // Calculates the part of the size that relates to the audio command buffer. - const auto calculate_command_buffer_size = - [this](const AudioCore::AudioRendererParameter& params) { - constexpr u64 alignment = (buffer_alignment_size - 1) * 2; + const auto calculate_command_buffer_size = [](const AudioCore::AudioRendererParameter& params) { + constexpr u64 alignment = (buffer_alignment_size - 1) * 2; - if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) { - constexpr u64 command_buffer_size = 0x18000; + if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) { + constexpr u64 command_buffer_size = 0x18000; - return command_buffer_size + alignment; - } + return command_buffer_size + alignment; + } - // When the variadic command buffer is supported, this means - // the command generator for the audio renderer can issue commands - // that are (as one would expect), variable in size. So what we need to do - // is determine the maximum possible size for a few command data structures - // then multiply them by the amount of present commands indicated by the given - // respective audio parameters. + // When the variadic command buffer is supported, this means + // the command generator for the audio renderer can issue commands + // that are (as one would expect), variable in size. So what we need to do + // is determine the maximum possible size for a few command data structures + // then multiply them by the amount of present commands indicated by the given + // respective audio parameters. - constexpr u64 max_biquad_filters = 2; - constexpr u64 max_mix_buffers = 24; + constexpr u64 max_biquad_filters = 2; + constexpr u64 max_mix_buffers = 24; - constexpr u64 biquad_filter_command_size = 0x2C; + constexpr u64 biquad_filter_command_size = 0x2C; - constexpr u64 depop_mix_command_size = 0x24; - constexpr u64 depop_setup_command_size = 0x50; + constexpr u64 depop_mix_command_size = 0x24; + constexpr u64 depop_setup_command_size = 0x50; - constexpr u64 effect_command_max_size = 0x540; + constexpr u64 effect_command_max_size = 0x540; - constexpr u64 mix_command_size = 0x1C; - constexpr u64 mix_ramp_command_size = 0x24; - constexpr u64 mix_ramp_grouped_command_size = 0x13C; + constexpr u64 mix_command_size = 0x1C; + constexpr u64 mix_ramp_command_size = 0x24; + constexpr u64 mix_ramp_grouped_command_size = 0x13C; - constexpr u64 perf_command_size = 0x28; + constexpr u64 perf_command_size = 0x28; - constexpr u64 sink_command_size = 0x130; + constexpr u64 sink_command_size = 0x130; - constexpr u64 submix_command_max_size = - depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers; + constexpr u64 submix_command_max_size = + depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers; - constexpr u64 volume_command_size = 0x1C; - constexpr u64 volume_ramp_command_size = 0x20; + constexpr u64 volume_command_size = 0x1C; + constexpr u64 volume_ramp_command_size = 0x20; - constexpr u64 voice_biquad_filter_command_size = - biquad_filter_command_size * max_biquad_filters; - constexpr u64 voice_data_command_size = 0x9C; - const u64 voice_command_max_size = - (params.splitter_count * depop_setup_command_size) + - (voice_data_command_size + voice_biquad_filter_command_size + - volume_ramp_command_size + mix_ramp_grouped_command_size); + constexpr u64 voice_biquad_filter_command_size = + biquad_filter_command_size * max_biquad_filters; + constexpr u64 voice_data_command_size = 0x9C; + const u64 voice_command_max_size = + (params.splitter_count * depop_setup_command_size) + + (voice_data_command_size + voice_biquad_filter_command_size + volume_ramp_command_size + + mix_ramp_grouped_command_size); - // Now calculate the individual elements that comprise the size and add them together. - const u64 effect_commands_size = params.effect_count * effect_command_max_size; + // Now calculate the individual elements that comprise the size and add them together. + const u64 effect_commands_size = params.effect_count * effect_command_max_size; - const u64 final_mix_commands_size = - depop_mix_command_size + volume_command_size * max_mix_buffers; + const u64 final_mix_commands_size = + depop_mix_command_size + volume_command_size * max_mix_buffers; - const u64 perf_commands_size = - perf_command_size * - (CalculateNumPerformanceEntries(params) + max_perf_detail_entries); + const u64 perf_commands_size = + perf_command_size * (CalculateNumPerformanceEntries(params) + max_perf_detail_entries); - const u64 sink_commands_size = params.sink_count * sink_command_size; + const u64 sink_commands_size = params.sink_count * sink_command_size; - const u64 splitter_commands_size = - params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size; + const u64 splitter_commands_size = + params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size; - const u64 submix_commands_size = params.submix_count * submix_command_max_size; + const u64 submix_commands_size = params.submix_count * submix_command_max_size; - const u64 voice_commands_size = params.voice_count * voice_command_max_size; + const u64 voice_commands_size = params.voice_count * voice_command_max_size; - return effect_commands_size + final_mix_commands_size + perf_commands_size + - sink_commands_size + splitter_commands_size + submix_commands_size + - voice_commands_size + alignment; - }; + return effect_commands_size + final_mix_commands_size + perf_commands_size + + sink_commands_size + splitter_commands_size + submix_commands_size + + voice_commands_size + alignment; + }; IPC::RequestParser rp{ctx}; const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); @@ -564,12 +641,16 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { } void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + IPC::RequestParser rp{ctx}; + const u64 aruid = rp.Pop<u64>(); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + LOG_DEBUG(Service_Audio, "called. aruid={:016X}", aruid); + // Revisionless variant of GetAudioDeviceServiceWithRevisionInfo that + // always assumes the initial release revision (REV1). + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<Audio::IAudioDevice>(); + rb.PushIpcInterface<IAudioDevice>(system, Common::MakeMagic('R', 'E', 'V', '1')); } void AudRenU::OpenAudioRendererAuto(Kernel::HLERequestContext& ctx) { @@ -579,13 +660,19 @@ void AudRenU::OpenAudioRendererAuto(Kernel::HLERequestContext& ctx) { } void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + struct Parameters { + u32 revision; + u64 aruid; + }; - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + IPC::RequestParser rp{ctx}; + const auto [revision, aruid] = rp.PopRaw<Parameters>(); + + LOG_DEBUG(Service_Audio, "called. revision={:08X}, aruid={:016X}", revision, aruid); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<Audio::IAudioDevice>(); // TODO(ogniK): Figure out what is different - // based on the current revision + rb.PushIpcInterface<IAudioDevice>(system, revision); } void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) { @@ -594,14 +681,16 @@ void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IAudioRenderer>(params); + rb.PushIpcInterface<IAudioRenderer>(system, params, audren_instance_count++); } -bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const { +bool IsFeatureSupported(AudioFeatures feature, u32_le revision) { // Byte swap const u32_be version_num = revision - Common::MakeMagic('R', 'E', 'V', '0'); switch (feature) { + case AudioFeatures::AudioUSBDeviceOutput: + return version_num >= 4U; case AudioFeatures::Splitter: return version_num >= 2U; case AudioFeatures::PerformanceMetricsVersion2: diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h index 1d3c8df61..4e0ccc792 100644 --- a/src/core/hle/service/audio/audren_u.h +++ b/src/core/hle/service/audio/audren_u.h @@ -6,6 +6,10 @@ #include "core/hle/service/service.h" +namespace Core { +class System; +} + namespace Kernel { class HLERequestContext; } @@ -14,7 +18,7 @@ namespace Service::Audio { class AudRenU final : public ServiceFramework<AudRenU> { public: - explicit AudRenU(); + explicit AudRenU(Core::System& system_); ~AudRenU() override; private: @@ -26,13 +30,19 @@ private: void OpenAudioRendererImpl(Kernel::HLERequestContext& ctx); - enum class AudioFeatures : u32 { - Splitter, - PerformanceMetricsVersion2, - VariadicCommandBuffer, - }; + std::size_t audren_instance_count = 0; + Core::System& system; +}; - bool IsFeatureSupported(AudioFeatures feature, u32_le revision) const; +// Describes a particular audio feature that may be supported in a particular revision. +enum class AudioFeatures : u32 { + AudioUSBDeviceOutput, + Splitter, + PerformanceMetricsVersion2, + VariadicCommandBuffer, }; +// Tests if a particular audio feature is supported with a given audio revision. +bool IsFeatureSupported(AudioFeatures feature, u32_le revision); + } // namespace Service::Audio diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index 6701cb913..af70d174d 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp @@ -2,32 +2,37 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/crypto/key_manager.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/service/service.h" namespace Service::ES { +constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::ETicket, 2}; +constexpr ResultCode ERROR_INVALID_RIGHTS_ID{ErrorModule::ETicket, 3}; + class ETicket final : public ServiceFramework<ETicket> { public: explicit ETicket() : ServiceFramework{"es"} { // clang-format off static const FunctionInfo functions[] = { - {1, nullptr, "ImportTicket"}, + {1, &ETicket::ImportTicket, "ImportTicket"}, {2, nullptr, "ImportTicketCertificateSet"}, {3, nullptr, "DeleteTicket"}, {4, nullptr, "DeletePersonalizedTicket"}, {5, nullptr, "DeleteAllCommonTicket"}, {6, nullptr, "DeleteAllPersonalizedTicket"}, {7, nullptr, "DeleteAllPersonalizedTicketEx"}, - {8, nullptr, "GetTitleKey"}, - {9, nullptr, "CountCommonTicket"}, - {10, nullptr, "CountPersonalizedTicket"}, - {11, nullptr, "ListCommonTicket"}, - {12, nullptr, "ListPersonalizedTicket"}, + {8, &ETicket::GetTitleKey, "GetTitleKey"}, + {9, &ETicket::CountCommonTicket, "CountCommonTicket"}, + {10, &ETicket::CountPersonalizedTicket, "CountPersonalizedTicket"}, + {11, &ETicket::ListCommonTicket, "ListCommonTicket"}, + {12, &ETicket::ListPersonalizedTicket, "ListPersonalizedTicket"}, {13, nullptr, "ListMissingPersonalizedTicket"}, - {14, nullptr, "GetCommonTicketSize"}, - {15, nullptr, "GetPersonalizedTicketSize"}, - {16, nullptr, "GetCommonTicketData"}, - {17, nullptr, "GetPersonalizedTicketData"}, + {14, &ETicket::GetCommonTicketSize, "GetCommonTicketSize"}, + {15, &ETicket::GetPersonalizedTicketSize, "GetPersonalizedTicketSize"}, + {16, &ETicket::GetCommonTicketData, "GetCommonTicketData"}, + {17, &ETicket::GetPersonalizedTicketData, "GetPersonalizedTicketData"}, {18, nullptr, "OwnTicket"}, {19, nullptr, "GetTicketInfo"}, {20, nullptr, "ListLightTicketInfo"}, @@ -51,7 +56,212 @@ public: }; // clang-format on RegisterHandlers(functions); + + keys.PopulateTickets(); + keys.SynthesizeTickets(); + } + +private: + bool CheckRightsId(Kernel::HLERequestContext& ctx, const u128& rights_id) { + if (rights_id == u128{}) { + LOG_ERROR(Service_ETicket, "The rights ID was invalid!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_RIGHTS_ID); + return false; + } + + return true; + } + + void ImportTicket(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto ticket = ctx.ReadBuffer(); + const auto cert = ctx.ReadBuffer(1); + + if (ticket.size() < sizeof(Core::Crypto::Ticket)) { + LOG_ERROR(Service_ETicket, "The input buffer is not large enough!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ARGUMENT); + return; + } + + Core::Crypto::Ticket raw{}; + std::memcpy(&raw, ticket.data(), sizeof(Core::Crypto::Ticket)); + + if (!keys.AddTicketPersonalized(raw)) { + LOG_ERROR(Service_ETicket, "The ticket could not be imported!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ARGUMENT); + return; + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetTitleKey(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto rights_id = rp.PopRaw<u128>(); + + LOG_DEBUG(Service_ETicket, "called, rights_id={:016X}{:016X}", rights_id[1], rights_id[0]); + + if (!CheckRightsId(ctx, rights_id)) + return; + + const auto key = + keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]); + + if (key == Core::Crypto::Key128{}) { + LOG_ERROR(Service_ETicket, + "The titlekey doesn't exist in the KeyManager or the rights ID was invalid!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_RIGHTS_ID); + return; + } + + ctx.WriteBuffer(key.data(), key.size()); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void CountCommonTicket(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_ETicket, "called"); + + const auto count = keys.GetCommonTickets().size(); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(count); + } + + void CountPersonalizedTicket(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_ETicket, "called"); + + const auto count = keys.GetPersonalizedTickets().size(); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(count); + } + + void ListCommonTicket(Kernel::HLERequestContext& ctx) { + u32 out_entries; + if (keys.GetCommonTickets().empty()) + out_entries = 0; + else + out_entries = ctx.GetWriteBufferSize() / sizeof(u128); + + LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries); + + keys.PopulateTickets(); + const auto tickets = keys.GetCommonTickets(); + std::vector<u128> ids; + std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids), + [](const auto& pair) { return pair.first; }); + + out_entries = std::min<u32>(ids.size(), out_entries); + ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128)); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(out_entries); } + + void ListPersonalizedTicket(Kernel::HLERequestContext& ctx) { + u32 out_entries; + if (keys.GetPersonalizedTickets().empty()) + out_entries = 0; + else + out_entries = ctx.GetWriteBufferSize() / sizeof(u128); + + LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries); + + keys.PopulateTickets(); + const auto tickets = keys.GetPersonalizedTickets(); + std::vector<u128> ids; + std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids), + [](const auto& pair) { return pair.first; }); + + out_entries = std::min<u32>(ids.size(), out_entries); + ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128)); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(out_entries); + } + + void GetCommonTicketSize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto rights_id = rp.PopRaw<u128>(); + + LOG_DEBUG(Service_ETicket, "called, rights_id={:016X}{:016X}", rights_id[1], rights_id[0]); + + if (!CheckRightsId(ctx, rights_id)) + return; + + const auto ticket = keys.GetCommonTickets().at(rights_id); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(ticket.GetSize()); + } + + void GetPersonalizedTicketSize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto rights_id = rp.PopRaw<u128>(); + + LOG_DEBUG(Service_ETicket, "called, rights_id={:016X}{:016X}", rights_id[1], rights_id[0]); + + if (!CheckRightsId(ctx, rights_id)) + return; + + const auto ticket = keys.GetPersonalizedTickets().at(rights_id); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(ticket.GetSize()); + } + + void GetCommonTicketData(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto rights_id = rp.PopRaw<u128>(); + + LOG_DEBUG(Service_ETicket, "called, rights_id={:016X}{:016X}", rights_id[1], rights_id[0]); + + if (!CheckRightsId(ctx, rights_id)) + return; + + const auto ticket = keys.GetCommonTickets().at(rights_id); + + const auto write_size = std::min<u64>(ticket.GetSize(), ctx.GetWriteBufferSize()); + ctx.WriteBuffer(&ticket, write_size); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(write_size); + } + + void GetPersonalizedTicketData(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto rights_id = rp.PopRaw<u128>(); + + LOG_DEBUG(Service_ETicket, "called, rights_id={:016X}{:016X}", rights_id[1], rights_id[0]); + + if (!CheckRightsId(ctx, rights_id)) + return; + + const auto ticket = keys.GetPersonalizedTickets().at(rights_id); + + const auto write_size = std::min<u64>(ticket.GetSize(), ctx.GetWriteBufferSize()); + ctx.WriteBuffer(&ticket, write_size); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(write_size); + } + + Core::Crypto::KeyManager keys; }; void InstallInterfaces(SM::ServiceManager& service_manager) { diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp index fe49c2161..01fa06ad3 100644 --- a/src/core/hle/service/fatal/fatal.cpp +++ b/src/core/hle/service/fatal/fatal.cpp @@ -5,7 +5,7 @@ #include <array> #include <cstring> #include <ctime> -#include <fmt/time.h> +#include <fmt/chrono.h> #include "common/file_util.h" #include "common/logging/log.h" #include "common/scm_rev.h" diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 1ebfeb4bf..8ce110dd1 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -472,12 +472,12 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { } } -void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) { +void InstallInterfaces(Core::System& system) { romfs_factory = nullptr; - CreateFactories(vfs, false); - std::make_shared<FSP_LDR>()->InstallAsService(service_manager); - std::make_shared<FSP_PR>()->InstallAsService(service_manager); - std::make_shared<FSP_SRV>()->InstallAsService(service_manager); + CreateFactories(*system.GetFilesystem(), false); + std::make_shared<FSP_LDR>()->InstallAsService(system.ServiceManager()); + std::make_shared<FSP_PR>()->InstallAsService(system.ServiceManager()); + std::make_shared<FSP_SRV>(system.GetReporter())->InstallAsService(system.ServiceManager()); } } // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 6481f237c..3849dd89e 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -65,7 +65,7 @@ FileSys::VirtualDir GetModificationDumpRoot(u64 title_id); // above is called. void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); -void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs); +void InstallInterfaces(Core::System& system); // A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of // pointers and booleans. This makes using a VfsDirectory with switch services much easier and diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index e7df8fd98..d3cd46a9b 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -26,6 +26,7 @@ #include "core/hle/kernel/process.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/fsp_srv.h" +#include "core/reporter.h" namespace Service::FileSystem { @@ -613,7 +614,7 @@ private: u64 next_entry_index = 0; }; -FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { +FSP_SRV::FSP_SRV(const Core::Reporter& reporter) : ServiceFramework("fsp-srv"), reporter(reporter) { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "OpenFileSystem"}, @@ -710,14 +711,14 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { {1001, nullptr, "SetSaveDataSize"}, {1002, nullptr, "SetSaveDataRootPath"}, {1003, nullptr, "DisableAutoSaveDataCreation"}, - {1004, nullptr, "SetGlobalAccessLogMode"}, + {1004, &FSP_SRV::SetGlobalAccessLogMode, "SetGlobalAccessLogMode"}, {1005, &FSP_SRV::GetGlobalAccessLogMode, "GetGlobalAccessLogMode"}, - {1006, nullptr, "OutputAccessLogToSdCard"}, + {1006, &FSP_SRV::OutputAccessLogToSdCard, "OutputAccessLogToSdCard"}, {1007, nullptr, "RegisterUpdatePartition"}, {1008, nullptr, "OpenRegisteredUpdatePartition"}, {1009, nullptr, "GetAndClearMemoryReportInfo"}, {1010, nullptr, "SetDataStorageRedirectTarget"}, - {1011, nullptr, "OutputAccessLogToSdCard2"}, + {1011, &FSP_SRV::GetAccessLogVersionInfo, "GetAccessLogVersionInfo"}, {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, {1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"}, {1200, nullptr, "OpenMultiCommitManager"}, @@ -814,21 +815,22 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space)); } -void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_FS, "(STUBBED) called"); +void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + log_mode = rp.PopEnum<LogMode>(); - enum class LogMode : u32 { - Off, - Log, - RedirectToSdCard, - LogToSdCard = Log | RedirectToSdCard, - }; + LOG_DEBUG(Service_FS, "called, log_mode={:08X}", static_cast<u32>(log_mode)); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); - // Given we always want to receive logging information, - // we always specify logging as enabled. IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.PushEnum(LogMode::Log); + rb.PushEnum(log_mode); } void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { @@ -902,4 +904,26 @@ void FSP_SRV::OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ct rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); } +void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) { + const auto raw = ctx.ReadBuffer(); + auto log = Common::StringFromFixedZeroTerminatedBuffer( + reinterpret_cast<const char*>(raw.data()), raw.size()); + + LOG_DEBUG(Service_FS, "called, log='{}'", log); + + reporter.SaveFilesystemAccessReport(log_mode, std::move(log)); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void FSP_SRV::GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.PushEnum(AccessLogVersion::Latest); + rb.Push(access_log_program_index); +} + } // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index d7572ba7a..b5486a193 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h @@ -7,15 +7,32 @@ #include <memory> #include "core/hle/service/service.h" +namespace Core { +class Reporter; +} + namespace FileSys { class FileSystemBackend; } namespace Service::FileSystem { +enum class AccessLogVersion : u32 { + V7_0_0 = 2, + + Latest = V7_0_0, +}; + +enum class LogMode : u32 { + Off, + Log, + RedirectToSdCard, + LogToSdCard = Log | RedirectToSdCard, +}; + class FSP_SRV final : public ServiceFramework<FSP_SRV> { public: - explicit FSP_SRV(); + explicit FSP_SRV(const Core::Reporter& reporter); ~FSP_SRV() override; private: @@ -26,13 +43,20 @@ private: void OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx); void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx); void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx); + void SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx); void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); + void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx); + void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx); FileSys::VirtualFile romfs; u64 current_process_id = 0; + u32 access_log_program_index = 0; + LogMode log_mode = LogMode::LogToSdCard; + + const Core::Reporter& reporter; }; } // namespace Service::FileSystem diff --git a/src/core/hle/service/friend/errors.h b/src/core/hle/service/friend/errors.h new file mode 100644 index 000000000..b3996e275 --- /dev/null +++ b/src/core/hle/service/friend/errors.h @@ -0,0 +1,12 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/result.h" + +namespace Service::Friend { + +constexpr ResultCode ERR_NO_NOTIFICATIONS{ErrorModule::Account, 15}; +} diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index 5100e376c..d1ec12ef9 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -2,8 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <queue> #include "common/logging/log.h" +#include "common/uuid.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" +#include "core/hle/service/friend/errors.h" #include "core/hle/service/friend/friend.h" #include "core/hle/service/friend/interface.h" @@ -17,7 +22,7 @@ public: {0, nullptr, "GetCompletionEvent"}, {1, nullptr, "Cancel"}, {10100, nullptr, "GetFriendListIds"}, - {10101, nullptr, "GetFriendList"}, + {10101, &IFriendService::GetFriendList, "GetFriendList"}, {10102, nullptr, "UpdateFriendInfo"}, {10110, nullptr, "GetFriendProfileImage"}, {10200, nullptr, "SendFriendRequestForApplication"}, @@ -94,6 +99,23 @@ public: } private: + enum class PresenceFilter : u32 { + None = 0, + Online = 1, + OnlinePlay = 2, + OnlineOrOnlinePlay = 3, + }; + + struct SizedFriendFilter { + PresenceFilter presence; + u8 is_favorite; + u8 same_app; + u8 same_app_played; + u8 arbitary_app_played; + u64 group_id; + }; + static_assert(sizeof(SizedFriendFilter) == 0x10, "SizedFriendFilter is an invalid size"); + void DeclareCloseOnlinePlaySession(Kernel::HLERequestContext& ctx) { // Stub used by Splatoon 2 LOG_WARNING(Service_ACC, "(STUBBED) called"); @@ -107,6 +129,121 @@ private: IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } + + void GetFriendList(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto friend_offset = rp.Pop<u32>(); + const auto uuid = rp.PopRaw<Common::UUID>(); + [[maybe_unused]] const auto filter = rp.PopRaw<SizedFriendFilter>(); + const auto pid = rp.Pop<u64>(); + LOG_WARNING(Service_ACC, "(STUBBED) called, offset={}, uuid={}, pid={}", friend_offset, + uuid.Format(), pid); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + + rb.Push<u32>(0); // Friend count + // TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId" + } +}; + +class INotificationService final : public ServiceFramework<INotificationService> { +public: + INotificationService(Common::UUID uuid) : ServiceFramework("INotificationService"), uuid(uuid) { + // clang-format off + static const FunctionInfo functions[] = { + {0, &INotificationService::GetEvent, "GetEvent"}, + {1, &INotificationService::Clear, "Clear"}, + {2, &INotificationService::Pop, "Pop"} + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void GetEvent(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_ACC, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + + if (!is_event_created) { + auto& kernel = Core::System::GetInstance().Kernel(); + notification_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::Manual, "INotificationService:NotifyEvent"); + is_event_created = true; + } + rb.PushCopyObjects(notification_event.readable); + } + + void Clear(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_ACC, "called"); + while (!notifications.empty()) { + notifications.pop(); + } + std::memset(&states, 0, sizeof(States)); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void Pop(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_ACC, "called"); + + if (notifications.empty()) { + LOG_ERROR(Service_ACC, "No notifications in queue!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_NO_NOTIFICATIONS); + return; + } + + const auto notification = notifications.front(); + notifications.pop(); + + switch (notification.notification_type) { + case NotificationTypes::HasUpdatedFriendsList: + states.has_updated_friends = false; + break; + case NotificationTypes::HasReceivedFriendRequest: + states.has_received_friend_request = false; + break; + default: + // HOS seems not have an error case for an unknown notification + LOG_WARNING(Service_ACC, "Unknown notification {:08X}", + static_cast<u32>(notification.notification_type)); + break; + } + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw<SizedNotificationInfo>(notification); + } + + enum class NotificationTypes : u32 { + HasUpdatedFriendsList = 0x65, + HasReceivedFriendRequest = 0x1 + }; + + struct SizedNotificationInfo { + NotificationTypes notification_type; + INSERT_PADDING_WORDS( + 1); // TODO(ogniK): This doesn't seem to be used within any IPC returns as of now + u64_le account_id; + }; + static_assert(sizeof(SizedNotificationInfo) == 0x10, + "SizedNotificationInfo is an incorrect size"); + + struct States { + bool has_updated_friends; + bool has_received_friend_request; + }; + + Common::UUID uuid; + bool is_event_created = false; + Kernel::EventPair notification_event; + std::queue<SizedNotificationInfo> notifications; + States states{}; }; void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) { @@ -116,6 +253,17 @@ void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); } +void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto uuid = rp.PopRaw<Common::UUID>(); + + LOG_DEBUG(Service_ACC, "called, uuid={}", uuid.Format()); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<INotificationService>(uuid); +} + Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) : ServiceFramework(name), module(std::move(module)) {} diff --git a/src/core/hle/service/friend/friend.h b/src/core/hle/service/friend/friend.h index e762840cb..38d05fa8e 100644 --- a/src/core/hle/service/friend/friend.h +++ b/src/core/hle/service/friend/friend.h @@ -16,6 +16,7 @@ public: ~Interface() override; void CreateFriendService(Kernel::HLERequestContext& ctx); + void CreateNotificationService(Kernel::HLERequestContext& ctx); protected: std::shared_ptr<Module> module; diff --git a/src/core/hle/service/friend/interface.cpp b/src/core/hle/service/friend/interface.cpp index 5a6840af5..5b384f733 100644 --- a/src/core/hle/service/friend/interface.cpp +++ b/src/core/hle/service/friend/interface.cpp @@ -10,7 +10,7 @@ Friend::Friend(std::shared_ptr<Module> module, const char* name) : Interface(std::move(module), name) { static const FunctionInfo functions[] = { {0, &Friend::CreateFriendService, "CreateFriendService"}, - {1, nullptr, "CreateNotificationService"}, + {1, &Friend::CreateNotificationService, "CreateNotificationService"}, {2, nullptr, "CreateDaemonSuspendSessionService"}, }; RegisterHandlers(functions); diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index fdd6d79a2..e47fe8188 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -548,6 +548,37 @@ void Controller_NPad::DisconnectNPad(u32 npad_id) { connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; } +void Controller_NPad::StartLRAssignmentMode() { + // Nothing internally is used for lr assignment mode. Since we have the ability to set the + // controller types from boot, it doesn't really matter about showing a selection screen + is_in_lr_assignment_mode = true; +} + +void Controller_NPad::StopLRAssignmentMode() { + is_in_lr_assignment_mode = false; +} + +bool Controller_NPad::SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2) { + if (npad_id_1 == NPAD_HANDHELD || npad_id_2 == NPAD_HANDHELD || npad_id_1 == NPAD_UNKNOWN || + npad_id_2 == NPAD_UNKNOWN) { + return true; + } + const auto npad_index_1 = NPadIdToIndex(npad_id_1); + const auto npad_index_2 = NPadIdToIndex(npad_id_2); + + if (!IsControllerSupported(connected_controllers[npad_index_1].type) || + !IsControllerSupported(connected_controllers[npad_index_2].type)) { + return false; + } + + std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type); + + InitNewlyAddedControler(npad_index_1); + InitNewlyAddedControler(npad_index_2); + + return true; +} + bool Controller_NPad::IsControllerSupported(NPadControllerType controller) { if (controller == NPadControllerType::Handheld) { // Handheld is not even a supported type, lets stop here @@ -605,10 +636,15 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { return LedPattern{0, 0, 0, 0}; }; } + void Controller_NPad::SetVibrationEnabled(bool can_vibrate) { can_controllers_vibrate = can_vibrate; } +bool Controller_NPad::IsVibrationEnabled() const { + return can_controllers_vibrate; +} + void Controller_NPad::ClearAllConnectedControllers() { for (auto& controller : connected_controllers) { if (controller.is_connected && controller.type != NPadControllerType::None) { @@ -617,6 +653,7 @@ void Controller_NPad::ClearAllConnectedControllers() { } } } + void Controller_NPad::DisconnectAllConnectedControllers() { std::for_each(connected_controllers.begin(), connected_controllers.end(), [](ControllerHolder& controller) { controller.is_connected = false; }); diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 4ff50b3cd..f28b36806 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -119,11 +119,16 @@ public: void DisconnectNPad(u32 npad_id); LedPattern GetLedPattern(u32 npad_id); void SetVibrationEnabled(bool can_vibrate); + bool IsVibrationEnabled() const; void ClearAllConnectedControllers(); void DisconnectAllConnectedControllers(); void ConnectAllDisconnectedControllers(); void ClearAllControllers(); + void StartLRAssignmentMode(); + void StopLRAssignmentMode(); + bool SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2); + // Logical OR for all buttons presses on all controllers // Specifically for cheat engine and other features. u32 GetAndResetPressState(); @@ -321,5 +326,6 @@ private: void RequestPadStateUpdate(u32 npad_id); std::array<ControllerPad, 10> npad_pad_states{}; bool IsControllerSupported(NPadControllerType controller); + bool is_in_lr_assignment_mode{false}; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h new file mode 100644 index 000000000..3583642e7 --- /dev/null +++ b/src/core/hle/service/hid/errors.h @@ -0,0 +1,13 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/result.h" + +namespace Service::HID { + +constexpr ResultCode ERR_NPAD_NOT_CONNECTED{ErrorModule::HID, 710}; + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index a4ad95d96..f8b1ca816 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -16,6 +16,7 @@ #include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/writable_event.h" +#include "core/hle/service/hid/errors.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/hid/irs.h" #include "core/hle/service/hid/xcd.h" @@ -202,11 +203,11 @@ Hid::Hid() : ServiceFramework("hid") { {123, nullptr, "SetNpadJoyAssignmentModeSingleByDefault"}, {124, &Hid::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"}, {125, &Hid::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"}, - {126, nullptr, "StartLrAssignmentMode"}, - {127, nullptr, "StopLrAssignmentMode"}, + {126, &Hid::StartLrAssignmentMode, "StartLrAssignmentMode"}, + {127, &Hid::StopLrAssignmentMode, "StopLrAssignmentMode"}, {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"}, {129, nullptr, "GetNpadHandheldActivationMode"}, - {130, nullptr, "SwapNpadAssignment"}, + {130, &Hid::SwapNpadAssignment, "SwapNpadAssignment"}, {131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"}, {132, nullptr, "EnableUnintendedHomeButtonInputProtection"}, {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"}, @@ -215,8 +216,8 @@ Hid::Hid() : ServiceFramework("hid") { {201, &Hid::SendVibrationValue, "SendVibrationValue"}, {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"}, {203, &Hid::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"}, - {204, nullptr, "PermitVibration"}, - {205, nullptr, "IsVibrationPermitted"}, + {204, &Hid::PermitVibration, "PermitVibration"}, + {205, &Hid::IsVibrationPermitted, "IsVibrationPermitted"}, {206, &Hid::SendVibrationValues, "SendVibrationValues"}, {207, nullptr, "SendVibrationGcErmCommand"}, {208, nullptr, "GetActualVibrationGcErmCommand"}, @@ -678,6 +679,27 @@ void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { rb.PushIpcInterface<IActiveVibrationDeviceList>(); } +void Hid::PermitVibration(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto can_vibrate{rp.Pop<bool>()}; + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .SetVibrationEnabled(can_vibrate); + + LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_HID, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push( + applet_resource->GetController<Controller_NPad>(HidController::NPad).IsVibrationEnabled()); +} + void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; @@ -733,6 +755,49 @@ void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } +void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); + controller.StartLRAssignmentMode(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void Hid::StopLrAssignmentMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); + controller.StopLRAssignmentMode(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_1{rp.Pop<u32>()}; + const auto npad_2{rp.Pop<u32>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, npad_1={}, npad_2={}", + applet_resource_user_id, npad_1, npad_2); + + auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); + IPC::ResponseBuilder rb{ctx, 2}; + if (controller.SwapNpadAssignment(npad_1, npad_2)) { + rb.Push(RESULT_SUCCESS); + } else { + LOG_ERROR(Service_HID, "Npads are not connected!"); + rb.Push(ERR_NPAD_NOT_CONNECTED); + } +} + class HidDbg final : public ServiceFramework<HidDbg> { public: explicit HidDbg() : ServiceFramework{"hid:dbg"} { diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index d3660cad2..2fd6d9fc7 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -114,11 +114,16 @@ private: void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx); void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx); void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx); + void PermitVibration(Kernel::HLERequestContext& ctx); + void IsVibrationPermitted(Kernel::HLERequestContext& ctx); void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); void StopSixAxisSensor(Kernel::HLERequestContext& ctx); void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); + void StartLrAssignmentMode(Kernel::HLERequestContext& ctx); + void StopLrAssignmentMode(Kernel::HLERequestContext& ctx); + void SwapNpadAssignment(Kernel::HLERequestContext& ctx); std::shared_ptr<IAppletResource> applet_resource; }; diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index b839303ac..8ddad8682 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -345,14 +345,16 @@ public: vm_manager .MirrorMemory(*map_address, nro_address, nro_size, Kernel::MemoryState::ModuleCode) .IsSuccess()); - ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess()); + ASSERT(vm_manager.ReprotectRange(nro_address, nro_size, Kernel::VMAPermission::None) + .IsSuccess()); if (bss_size > 0) { ASSERT(vm_manager .MirrorMemory(*map_address + nro_size, bss_address, bss_size, Kernel::MemoryState::ModuleCode) .IsSuccess()); - ASSERT(vm_manager.UnmapRange(bss_address, bss_size).IsSuccess()); + ASSERT(vm_manager.ReprotectRange(bss_address, bss_size, Kernel::VMAPermission::None) + .IsSuccess()); } vm_manager.ReprotectRange(*map_address, header.text_size, @@ -364,7 +366,8 @@ public: Core::System::GetInstance().InvalidateCpuInstructionCaches(); - nro.insert_or_assign(*map_address, NROInfo{hash, nro_size + bss_size}); + nro.insert_or_assign(*map_address, + NROInfo{hash, nro_address, nro_size, bss_address, bss_size}); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); @@ -409,9 +412,23 @@ public: } auto& vm_manager = Core::CurrentProcess()->VMManager(); - const auto& nro_size = iter->second.size; + const auto& nro_info = iter->second; - ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess()); + // Unmap the mirrored memory + ASSERT( + vm_manager.UnmapRange(nro_address, nro_info.nro_size + nro_info.bss_size).IsSuccess()); + + // Reprotect the source memory + ASSERT(vm_manager + .ReprotectRange(nro_info.nro_address, nro_info.nro_size, + Kernel::VMAPermission::ReadWrite) + .IsSuccess()); + if (nro_info.bss_size > 0) { + ASSERT(vm_manager + .ReprotectRange(nro_info.bss_address, nro_info.bss_size, + Kernel::VMAPermission::ReadWrite) + .IsSuccess()); + } Core::System::GetInstance().InvalidateCpuInstructionCaches(); @@ -473,7 +490,10 @@ private: struct NROInfo { SHA256Hash hash; - u64 size; + VAddr nro_address; + u64 nro_size; + VAddr bss_address; + u64 bss_size; }; bool initialized = false; diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp index ce84e25ed..0b3923ad9 100644 --- a/src/core/hle/service/mii/mii.cpp +++ b/src/core/hle/service/mii/mii.cpp @@ -48,7 +48,7 @@ public: {19, nullptr, "Export"}, {20, nullptr, "IsBrokenDatabaseWithClearFlag"}, {21, &IDatabaseService::GetIndex, "GetIndex"}, - {22, nullptr, "SetInterfaceVersion"}, + {22, &IDatabaseService::SetInterfaceVersion, "SetInterfaceVersion"}, {23, nullptr, "Convert"}, }; // clang-format on @@ -350,8 +350,22 @@ private: rb.Push(index); } + void SetInterfaceVersion(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + current_interface_version = rp.PopRaw<u32>(); + + LOG_DEBUG(Service_Mii, "called, interface_version={:08X}", current_interface_version); + + UNIMPLEMENTED_IF(current_interface_version != 1); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + MiiManager db; + u32 current_interface_version = 0; + // Last read offsets of Get functions std::array<u32, 4> offsets{}; }; diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index 131b01d62..8d0353075 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -175,6 +175,10 @@ MiiStoreData ConvertInfoToStoreData(const MiiInfo& info) { } // namespace std::ostream& operator<<(std::ostream& os, Source source) { + if (static_cast<std::size_t>(source) >= SOURCE_NAMES.size()) { + return os << "[UNKNOWN SOURCE]"; + } + os << SOURCE_NAMES.at(static_cast<std::size_t>(source)); return os; } diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp index ad176f89d..2a522136d 100644 --- a/src/core/hle/service/ns/pl_u.cpp +++ b/src/core/hle/service/ns/pl_u.cpp @@ -77,7 +77,7 @@ enum class LoadState : u32 { Done = 1, }; -static void DecryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, +static void DecryptSharedFont(const std::vector<u32>& input, Kernel::PhysicalMemory& output, std::size_t& offset) { ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE, "Shared fonts exceeds 17mb!"); @@ -94,7 +94,7 @@ static void DecryptSharedFont(const std::vector<u32>& input, std::vector<u8>& ou offset += transformed_font.size() * sizeof(u32); } -static void EncryptSharedFont(const std::vector<u8>& input, std::vector<u8>& output, +static void EncryptSharedFont(const std::vector<u8>& input, Kernel::PhysicalMemory& output, std::size_t& offset) { ASSERT_MSG(offset + input.size() + 8 < SHARED_FONT_MEM_SIZE, "Shared fonts exceeds 17mb!"); const u32 KEY = EXPECTED_MAGIC ^ EXPECTED_RESULT; @@ -121,7 +121,7 @@ struct PL_U::Impl { return shared_font_regions.at(index); } - void BuildSharedFontsRawRegions(const std::vector<u8>& input) { + void BuildSharedFontsRawRegions(const Kernel::PhysicalMemory& input) { // As we can derive the xor key we can just populate the offsets // based on the shared memory dump unsigned cur_offset = 0; @@ -144,7 +144,7 @@ struct PL_U::Impl { Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem; /// Backing memory for the shared font data - std::shared_ptr<std::vector<u8>> shared_font; + std::shared_ptr<Kernel::PhysicalMemory> shared_font; // Automatically populated based on shared_fonts dump or system archives. std::vector<FontRegion> shared_font_regions; @@ -166,7 +166,7 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} { // Rebuild shared fonts from data ncas if (nand->HasEntry(static_cast<u64>(FontArchives::Standard), FileSys::ContentRecordType::Data)) { - impl->shared_font = std::make_shared<std::vector<u8>>(SHARED_FONT_MEM_SIZE); + impl->shared_font = std::make_shared<Kernel::PhysicalMemory>(SHARED_FONT_MEM_SIZE); for (auto font : SHARED_FONTS) { const auto nca = nand->GetEntry(static_cast<u64>(font.first), FileSys::ContentRecordType::Data); @@ -207,7 +207,7 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} { } } else { - impl->shared_font = std::make_shared<std::vector<u8>>( + impl->shared_font = std::make_shared<Kernel::PhysicalMemory>( SHARED_FONT_MEM_SIZE); // Shared memory needs to always be allocated and a fixed size const std::string user_path = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir); diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h index 4f6042b00..5b8248433 100644 --- a/src/core/hle/service/nvdrv/devices/nvdevice.h +++ b/src/core/hle/service/nvdrv/devices/nvdevice.h @@ -8,6 +8,11 @@ #include "common/bit_field.h" #include "common/common_types.h" #include "common/swap.h" +#include "core/hle/service/nvdrv/nvdata.h" + +namespace Core { +class System; +} namespace Service::Nvidia::Devices { @@ -15,7 +20,7 @@ namespace Service::Nvidia::Devices { /// implement the ioctl interface. class nvdevice { public: - nvdevice() = default; + explicit nvdevice(Core::System& system) : system{system} {}; virtual ~nvdevice() = default; union Ioctl { u32_le raw; @@ -33,7 +38,11 @@ public: * @param output A buffer where the output data will be written to. * @returns The result code of the ioctl. */ - virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) = 0; + virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) = 0; + +protected: + Core::System& system; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 20c7c39aa..926a1285d 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -13,10 +13,12 @@ namespace Service::Nvidia::Devices { -nvdisp_disp0::nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} +nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) + : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} nvdisp_disp0 ::~nvdisp_disp0() = default; -u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) { UNIMPLEMENTED_MSG("Unimplemented ioctl"); return 0; } @@ -34,9 +36,8 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3 addr, offset, width, height, stride, static_cast<PixelFormat>(format), transform, crop_rect}; - auto& instance = Core::System::GetInstance(); - instance.GetPerfStats().EndGameFrame(); - instance.GPU().SwapBuffers(framebuffer); + system.GetPerfStats().EndGameFrame(); + system.GPU().SwapBuffers(&framebuffer); } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index 12f3ef825..e79e490ff 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -17,10 +17,11 @@ class nvmap; class nvdisp_disp0 final : public nvdevice { public: - explicit nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev); + explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); ~nvdisp_disp0() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) override; /// Performs a screen flip, drawing the buffer pointed to by the handle. void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index af62d33d2..24ab3f2e9 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -22,10 +22,12 @@ enum { }; } -nvhost_as_gpu::nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} +nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) + : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} nvhost_as_gpu::~nvhost_as_gpu() = default; -u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) { LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", command.raw, input.size(), output.size()); @@ -65,7 +67,7 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, params.page_size, params.flags); - auto& gpu = Core::System::GetInstance().GPU(); + auto& gpu = system.GPU(); const u64 size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)}; if (params.flags & 1) { params.offset = gpu.MemoryManager().AllocateSpace(params.offset, size, 1); @@ -85,7 +87,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) std::vector<IoctlRemapEntry> entries(num_entries); std::memcpy(entries.data(), input.data(), input.size()); - auto& gpu = Core::System::GetInstance().GPU(); + auto& gpu = system.GPU(); for (const auto& entry : entries) { LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", entry.offset, entry.nvmap_handle, entry.pages); @@ -136,7 +138,7 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou // case to prevent unexpected behavior. ASSERT(object->id == params.nvmap_handle); - auto& gpu = Core::System::GetInstance().GPU(); + auto& gpu = system.GPU(); if (params.flags & 1) { params.offset = gpu.MemoryManager().MapBufferEx(object->addr, params.offset, object->size); @@ -173,8 +175,7 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou return 0; } - params.offset = Core::System::GetInstance().GPU().MemoryManager().UnmapBuffer(params.offset, - itr->second.size); + params.offset = system.GPU().MemoryManager().UnmapBuffer(params.offset, itr->second.size); buffer_mappings.erase(itr->second.offset); std::memcpy(output.data(), ¶ms, output.size()); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index eb14b1da8..30ca5f4c3 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -17,10 +17,11 @@ class nvmap; class nvhost_as_gpu final : public nvdevice { public: - explicit nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev); + explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); ~nvhost_as_gpu() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) override; private: enum class IoctlCommand : u32_le { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index b39fb9ef9..9a66a5f88 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -7,14 +7,20 @@ #include "common/assert.h" #include "common/logging/log.h" +#include "core/core.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" +#include "video_core/gpu.h" namespace Service::Nvidia::Devices { -nvhost_ctrl::nvhost_ctrl() = default; +nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface) + : nvdevice(system), events_interface{events_interface} {} nvhost_ctrl::~nvhost_ctrl() = default; -u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) { LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", command.raw, input.size(), output.size()); @@ -22,11 +28,15 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector< case IoctlCommand::IocGetConfigCommand: return NvOsGetConfigU32(input, output); case IoctlCommand::IocCtrlEventWaitCommand: - return IocCtrlEventWait(input, output, false); + return IocCtrlEventWait(input, output, false, ctrl); case IoctlCommand::IocCtrlEventWaitAsyncCommand: - return IocCtrlEventWait(input, output, true); + return IocCtrlEventWait(input, output, true, ctrl); case IoctlCommand::IocCtrlEventRegisterCommand: return IocCtrlEventRegister(input, output); + case IoctlCommand::IocCtrlEventUnregisterCommand: + return IocCtrlEventUnregister(input, output); + case IoctlCommand::IocCtrlEventSignalCommand: + return IocCtrlEventSignal(input, output); } UNIMPLEMENTED_MSG("Unimplemented ioctl"); return 0; @@ -41,23 +51,137 @@ u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& } u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, - bool is_async) { + bool is_async, IoctlCtrl& ctrl) { IocCtrlEventWaitParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service_NVDRV, - "(STUBBED) called, syncpt_id={}, threshold={}, timeout={}, is_async={}", - params.syncpt_id, params.threshold, params.timeout, is_async); + LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}", + params.syncpt_id, params.threshold, params.timeout, is_async); - // TODO(Subv): Implement actual syncpt waiting. - params.value = 0; + if (params.syncpt_id >= MaxSyncPoints) { + return NvResult::BadParameter; + } + + auto& gpu = system.GPU(); + // This is mostly to take into account unimplemented features. As synced + // gpu is always synced. + if (!gpu.IsAsync()) { + return NvResult::Success; + } + auto lock = gpu.LockSync(); + const u32 current_syncpoint_value = gpu.GetSyncpointValue(params.syncpt_id); + const s32 diff = current_syncpoint_value - params.threshold; + if (diff >= 0) { + params.value = current_syncpoint_value; + std::memcpy(output.data(), ¶ms, sizeof(params)); + return NvResult::Success; + } + const u32 target_value = current_syncpoint_value - diff; + + if (!is_async) { + params.value = 0; + } + + if (params.timeout == 0) { + std::memcpy(output.data(), ¶ms, sizeof(params)); + return NvResult::Timeout; + } + + u32 event_id; + if (is_async) { + event_id = params.value & 0x00FF; + if (event_id >= MaxNvEvents) { + std::memcpy(output.data(), ¶ms, sizeof(params)); + return NvResult::BadParameter; + } + } else { + if (ctrl.fresh_call) { + const auto result = events_interface.GetFreeEvent(); + if (result) { + event_id = *result; + } else { + LOG_CRITICAL(Service_NVDRV, "No Free Events available!"); + event_id = params.value & 0x00FF; + } + } else { + event_id = ctrl.event_id; + } + } + + EventState status = events_interface.status[event_id]; + if (event_id < MaxNvEvents || status == EventState::Free || status == EventState::Registered) { + events_interface.SetEventStatus(event_id, EventState::Waiting); + events_interface.assigned_syncpt[event_id] = params.syncpt_id; + events_interface.assigned_value[event_id] = target_value; + if (is_async) { + params.value = params.syncpt_id << 4; + } else { + params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000; + } + params.value |= event_id; + events_interface.events[event_id].writable->Clear(); + gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); + if (!is_async && ctrl.fresh_call) { + ctrl.must_delay = true; + ctrl.timeout = params.timeout; + ctrl.event_id = event_id; + return NvResult::Timeout; + } + std::memcpy(output.data(), ¶ms, sizeof(params)); + return NvResult::Timeout; + } std::memcpy(output.data(), ¶ms, sizeof(params)); - return 0; + return NvResult::BadParameter; } u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) { - LOG_WARNING(Service_NVDRV, "(STUBBED) called"); - // TODO(bunnei): Implement this. - return 0; + IocCtrlEventRegisterParams params{}; + std::memcpy(¶ms, input.data(), sizeof(params)); + const u32 event_id = params.user_event_id & 0x00FF; + LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); + if (event_id >= MaxNvEvents) { + return NvResult::BadParameter; + } + if (events_interface.registered[event_id]) { + return NvResult::BadParameter; + } + events_interface.RegisterEvent(event_id); + return NvResult::Success; +} + +u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output) { + IocCtrlEventUnregisterParams params{}; + std::memcpy(¶ms, input.data(), sizeof(params)); + const u32 event_id = params.user_event_id & 0x00FF; + LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); + if (event_id >= MaxNvEvents) { + return NvResult::BadParameter; + } + if (!events_interface.registered[event_id]) { + return NvResult::BadParameter; + } + events_interface.UnregisterEvent(event_id); + return NvResult::Success; +} + +u32 nvhost_ctrl::IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output) { + IocCtrlEventSignalParams params{}; + std::memcpy(¶ms, input.data(), sizeof(params)); + // TODO(Blinkhawk): This is normally called when an NvEvents timeout on WaitSynchronization + // It is believed from RE to cancel the GPU Event. However, better research is required + u32 event_id = params.user_event_id & 0x00FF; + LOG_WARNING(Service_NVDRV, "(STUBBED) called, user_event_id: {:X}", event_id); + if (event_id >= MaxNvEvents) { + return NvResult::BadParameter; + } + if (events_interface.status[event_id] == EventState::Waiting) { + auto& gpu = system.GPU(); + if (gpu.CancelSyncptInterrupt(events_interface.assigned_syncpt[event_id], + events_interface.assigned_value[event_id])) { + events_interface.LiberateEvent(event_id); + events_interface.events[event_id].writable->Signal(); + } + } + return NvResult::Success; } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index 6d0de2212..14e6e7e57 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -8,15 +8,17 @@ #include <vector> #include "common/common_types.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" +#include "core/hle/service/nvdrv/nvdrv.h" namespace Service::Nvidia::Devices { class nvhost_ctrl final : public nvdevice { public: - nvhost_ctrl(); + explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface); ~nvhost_ctrl() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) override; private: enum class IoctlCommand : u32_le { @@ -132,9 +134,16 @@ private: u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); - u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async); + u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async, + IoctlCtrl& ctrl); u32 IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output); + + u32 IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output); + + u32 IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output); + + EventInterface& events_interface; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 0e28755bd..988effd90 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -12,10 +12,11 @@ namespace Service::Nvidia::Devices { -nvhost_ctrl_gpu::nvhost_ctrl_gpu() = default; +nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {} nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; -u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) { LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", command.raw, input.size(), output.size()); @@ -185,7 +186,7 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o IoctlGetGpuTime params{}; std::memcpy(¶ms, input.data(), input.size()); - const auto ns = Core::Timing::CyclesToNs(Core::System::GetInstance().CoreTiming().GetTicks()); + const auto ns = Core::Timing::CyclesToNs(system.CoreTiming().GetTicks()); params.gpu_time = static_cast<u64_le>(ns.count()); std::memcpy(output.data(), ¶ms, output.size()); return 0; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index 240435eea..2b035ae3f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -13,10 +13,11 @@ namespace Service::Nvidia::Devices { class nvhost_ctrl_gpu final : public nvdevice { public: - nvhost_ctrl_gpu(); + explicit nvhost_ctrl_gpu(Core::System& system); ~nvhost_ctrl_gpu() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) override; private: enum class IoctlCommand : u32_le { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 8ce7bc7a5..241dac881 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -13,10 +13,12 @@ namespace Service::Nvidia::Devices { -nvhost_gpu::nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} +nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) + : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} nvhost_gpu::~nvhost_gpu() = default; -u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) { LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", command.raw, input.size(), output.size()); @@ -119,8 +121,10 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou params.num_entries, params.flags, params.unk0, params.unk1, params.unk2, params.unk3); - params.fence_out.id = 0; - params.fence_out.value = 0; + auto& gpu = system.GPU(); + params.fence_out.id = assigned_syncpoints; + params.fence_out.value = gpu.GetSyncpointValue(assigned_syncpoints); + assigned_syncpoints++; std::memcpy(output.data(), ¶ms, output.size()); return 0; } @@ -143,7 +147,7 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp IoctlSubmitGpfifo params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo)); LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", - params.address, params.num_entries, params.flags); + params.address, params.num_entries, params.flags.raw); ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) + params.num_entries * sizeof(Tegra::CommandListHeader), @@ -153,10 +157,18 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)], params.num_entries * sizeof(Tegra::CommandListHeader)); - Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries)); + UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0); + UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0); + + auto& gpu = system.GPU(); + u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id); + if (params.flags.increment.Value()) { + params.fence_out.value += current_syncpoint_value; + } else { + params.fence_out.value = current_syncpoint_value; + } + gpu.PushGPUEntries(std::move(entries)); - params.fence_out.id = 0; - params.fence_out.value = 0; std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmitGpfifo)); return 0; } @@ -168,16 +180,24 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output) IoctlSubmitGpfifo params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo)); LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", - params.address, params.num_entries, params.flags); + params.address, params.num_entries, params.flags.raw); Tegra::CommandList entries(params.num_entries); Memory::ReadBlock(params.address, entries.data(), params.num_entries * sizeof(Tegra::CommandListHeader)); - Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries)); + UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0); + UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0); + + auto& gpu = system.GPU(); + u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id); + if (params.flags.increment.Value()) { + params.fence_out.value += current_syncpoint_value; + } else { + params.fence_out.value = current_syncpoint_value; + } + gpu.PushGPUEntries(std::move(entries)); - params.fence_out.id = 0; - params.fence_out.value = 0; std::memcpy(output.data(), ¶ms, output.size()); return 0; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index 62beb5c0c..d2e8fbae9 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -10,6 +10,7 @@ #include "common/common_types.h" #include "common/swap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" +#include "core/hle/service/nvdrv/nvdata.h" namespace Service::Nvidia::Devices { @@ -20,10 +21,11 @@ constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b); class nvhost_gpu final : public nvdevice { public: - explicit nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev); + explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); ~nvhost_gpu() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) override; private: enum class IoctlCommand : u32_le { @@ -113,11 +115,7 @@ private: static_assert(sizeof(IoctlGetErrorNotification) == 16, "IoctlGetErrorNotification is incorrect size"); - struct IoctlFence { - u32_le id; - u32_le value; - }; - static_assert(sizeof(IoctlFence) == 8, "IoctlFence is incorrect size"); + static_assert(sizeof(Fence) == 8, "Fence is incorrect size"); struct IoctlAllocGpfifoEx { u32_le num_entries; @@ -132,13 +130,13 @@ private: static_assert(sizeof(IoctlAllocGpfifoEx) == 32, "IoctlAllocGpfifoEx is incorrect size"); struct IoctlAllocGpfifoEx2 { - u32_le num_entries; // in - u32_le flags; // in - u32_le unk0; // in (1 works) - IoctlFence fence_out; // out - u32_le unk1; // in - u32_le unk2; // in - u32_le unk3; // in + u32_le num_entries; // in + u32_le flags; // in + u32_le unk0; // in (1 works) + Fence fence_out; // out + u32_le unk1; // in + u32_le unk2; // in + u32_le unk3; // in }; static_assert(sizeof(IoctlAllocGpfifoEx2) == 32, "IoctlAllocGpfifoEx2 is incorrect size"); @@ -153,10 +151,16 @@ private: struct IoctlSubmitGpfifo { u64_le address; // pointer to gpfifo entry structs u32_le num_entries; // number of fence objects being submitted - u32_le flags; - IoctlFence fence_out; // returned new fence object for others to wait on - }; - static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(IoctlFence), + union { + u32_le raw; + BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list + BitField<1, 1, u32_le> add_increment; // append an increment to the list + BitField<2, 1, u32_le> new_hw_format; // Mostly ignored + BitField<8, 1, u32_le> increment; // increment the returned fence + } flags; + Fence fence_out; // returned new fence object for others to wait on + }; + static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence), "IoctlSubmitGpfifo is incorrect size"); struct IoctlGetWaitbase { @@ -184,6 +188,7 @@ private: u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); std::shared_ptr<nvmap> nvmap_dev; + u32 assigned_syncpoints{}; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index f5e8ea7c3..f572ad30f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -10,10 +10,11 @@ namespace Service::Nvidia::Devices { -nvhost_nvdec::nvhost_nvdec() = default; +nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system) {} nvhost_nvdec::~nvhost_nvdec() = default; -u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) { LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", command.raw, input.size(), output.size()); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index 0e7b284f8..2710f0511 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h @@ -13,10 +13,11 @@ namespace Service::Nvidia::Devices { class nvhost_nvdec final : public nvdevice { public: - nvhost_nvdec(); + explicit nvhost_nvdec(Core::System& system); ~nvhost_nvdec() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) override; private: enum class IoctlCommand : u32_le { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp index 3e0951ab0..38282956f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp @@ -10,10 +10,11 @@ namespace Service::Nvidia::Devices { -nvhost_nvjpg::nvhost_nvjpg() = default; +nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {} nvhost_nvjpg::~nvhost_nvjpg() = default; -u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) { LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", command.raw, input.size(), output.size()); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h index 89fd5e95e..379766693 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h @@ -13,10 +13,11 @@ namespace Service::Nvidia::Devices { class nvhost_nvjpg final : public nvdevice { public: - nvhost_nvjpg(); + explicit nvhost_nvjpg(Core::System& system); ~nvhost_nvjpg() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) override; private: enum class IoctlCommand : u32_le { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index d544f0f31..70e8091db 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp @@ -10,10 +10,11 @@ namespace Service::Nvidia::Devices { -nvhost_vic::nvhost_vic() = default; +nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system) {} nvhost_vic::~nvhost_vic() = default; -u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) { LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", command.raw, input.size(), output.size()); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index fc24c3f9c..7d111977e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h @@ -13,10 +13,11 @@ namespace Service::Nvidia::Devices { class nvhost_vic final : public nvdevice { public: - nvhost_vic(); + explicit nvhost_vic(Core::System& system); ~nvhost_vic() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) override; private: enum class IoctlCommand : u32_le { diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 1ec796fc6..223b496b7 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -18,7 +18,7 @@ enum { }; } -nvmap::nvmap() = default; +nvmap::nvmap(Core::System& system) : nvdevice(system) {} nvmap::~nvmap() = default; VAddr nvmap::GetObjectAddress(u32 handle) const { @@ -28,7 +28,8 @@ VAddr nvmap::GetObjectAddress(u32 handle) const { return object->addr; } -u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) { switch (static_cast<IoctlCommand>(command.raw)) { case IoctlCommand::Create: return IocCreate(input, output); diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index 396230c19..bf4a101c2 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h @@ -16,13 +16,14 @@ namespace Service::Nvidia::Devices { class nvmap final : public nvdevice { public: - nvmap(); + explicit nvmap(Core::System& system); ~nvmap() override; /// Returns the allocated address of an nvmap object given its handle. VAddr GetObjectAddress(u32 handle) const; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) override; /// Represents an nvmap object. struct Object { diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp index b60fc748b..d5be64ed2 100644 --- a/src/core/hle/service/nvdrv/interface.cpp +++ b/src/core/hle/service/nvdrv/interface.cpp @@ -8,12 +8,18 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/thread.h" #include "core/hle/kernel/writable_event.h" #include "core/hle/service/nvdrv/interface.h" +#include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/nvdrv.h" namespace Service::Nvidia { +void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) { + nvdrv->SignalSyncpt(syncpoint_id, value); +} + void NVDRV::Open(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NVDRV, "called"); @@ -36,11 +42,31 @@ void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) { std::vector<u8> output(ctx.GetWriteBufferSize()); + IoctlCtrl ctrl{}; + + u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output, ctrl); + + if (ctrl.must_delay) { + ctrl.fresh_call = false; + ctx.SleepClientThread( + "NVServices::DelayedResponse", ctrl.timeout, + [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, + Kernel::ThreadWakeupReason reason) { + IoctlCtrl ctrl2{ctrl}; + std::vector<u8> output2 = output; + u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output2, ctrl2); + ctx.WriteBuffer(output2); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(result); + }, + nvdrv->GetEventWriteable(ctrl.event_id)); + } else { + ctx.WriteBuffer(output); + } IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output)); - - ctx.WriteBuffer(output); + rb.Push(result); } void NVDRV::Close(Kernel::HLERequestContext& ctx) { @@ -66,13 +92,19 @@ void NVDRV::Initialize(Kernel::HLERequestContext& ctx) { void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; u32 fd = rp.Pop<u32>(); - u32 event_id = rp.Pop<u32>(); + // TODO(Blinkhawk): Figure the meaning of the flag at bit 16 + u32 event_id = rp.Pop<u32>() & 0x000000FF; LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id); IPC::ResponseBuilder rb{ctx, 3, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(query_event.readable); - rb.Push<u32>(0); + if (event_id < MaxNvEvents) { + rb.PushCopyObjects(nvdrv->GetEvent(event_id)); + rb.Push<u32>(NvResult::Success); + } else { + rb.Push<u32>(0); + rb.Push<u32>(NvResult::BadParameter); + } } void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) { @@ -127,10 +159,6 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name) {13, &NVDRV::FinishInitialize, "FinishInitialize"}, }; RegisterHandlers(functions); - - auto& kernel = Core::System::GetInstance().Kernel(); - query_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, - "NVDRV::query_event"); } NVDRV::~NVDRV() = default; diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h index 5b4889910..10a0ecd52 100644 --- a/src/core/hle/service/nvdrv/interface.h +++ b/src/core/hle/service/nvdrv/interface.h @@ -19,6 +19,8 @@ public: NVDRV(std::shared_ptr<Module> nvdrv, const char* name); ~NVDRV() override; + void SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value); + private: void Open(Kernel::HLERequestContext& ctx); void Ioctl(Kernel::HLERequestContext& ctx); @@ -33,8 +35,6 @@ private: std::shared_ptr<Module> nvdrv; u64 pid{}; - - Kernel::EventPair query_event; }; } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h new file mode 100644 index 000000000..ac03cbc23 --- /dev/null +++ b/src/core/hle/service/nvdrv/nvdata.h @@ -0,0 +1,48 @@ +#pragma once + +#include <array> +#include "common/common_types.h" + +namespace Service::Nvidia { + +constexpr u32 MaxSyncPoints = 192; +constexpr u32 MaxNvEvents = 64; + +struct Fence { + s32 id; + u32 value; +}; + +static_assert(sizeof(Fence) == 8, "Fence has wrong size"); + +struct MultiFence { + u32 num_fences; + std::array<Fence, 4> fences; +}; + +enum NvResult : u32 { + Success = 0, + BadParameter = 4, + Timeout = 5, + ResourceError = 15, +}; + +enum class EventState { + Free = 0, + Registered = 1, + Waiting = 2, + Busy = 3, +}; + +struct IoctlCtrl { + // First call done to the servioce for services that call itself again after a call. + bool fresh_call{true}; + // Tells the Ioctl Wrapper that it must delay the IPC response and send the thread to sleep + bool must_delay{}; + // Timeout for the delay + s64 timeout{}; + // NV Event Id + s32 event_id{-1}; +}; + +} // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 6e4b8f2c6..2011a226a 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -4,7 +4,10 @@ #include <utility> +#include <fmt/format.h> #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" @@ -22,8 +25,9 @@ namespace Service::Nvidia { -void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger) { - auto module_ = std::make_shared<Module>(); +void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, + Core::System& system) { + auto module_ = std::make_shared<Module>(system); std::make_shared<NVDRV>(module_, "nvdrv")->InstallAsService(service_manager); std::make_shared<NVDRV>(module_, "nvdrv:a")->InstallAsService(service_manager); std::make_shared<NVDRV>(module_, "nvdrv:s")->InstallAsService(service_manager); @@ -32,17 +36,25 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger nvflinger.SetNVDrvInstance(module_); } -Module::Module() { - auto nvmap_dev = std::make_shared<Devices::nvmap>(); - devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(nvmap_dev); - devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(nvmap_dev); - devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(); +Module::Module(Core::System& system) { + auto& kernel = system.Kernel(); + for (u32 i = 0; i < MaxNvEvents; i++) { + std::string event_label = fmt::format("NVDRV::NvEvent_{}", i); + events_interface.events[i] = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::Automatic, event_label); + events_interface.status[i] = EventState::Free; + events_interface.registered[i] = false; + } + auto nvmap_dev = std::make_shared<Devices::nvmap>(system); + devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev); + devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev); + devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(system); devices["/dev/nvmap"] = nvmap_dev; - devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(nvmap_dev); - devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(); - devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(); - devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(); - devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(); + devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev); + devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(system, events_interface); + devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system); + devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system); + devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system); } Module::~Module() = default; @@ -59,12 +71,13 @@ u32 Module::Open(const std::string& device_name) { return fd; } -u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) { auto itr = open_files.find(fd); ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); auto& device = itr->second; - return device->ioctl({command}, input, output); + return device->ioctl({command}, input, output, ctrl); } ResultCode Module::Close(u32 fd) { @@ -77,4 +90,22 @@ ResultCode Module::Close(u32 fd) { return RESULT_SUCCESS; } +void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) { + for (u32 i = 0; i < MaxNvEvents; i++) { + if (events_interface.assigned_syncpt[i] == syncpoint_id && + events_interface.assigned_value[i] == value) { + events_interface.LiberateEvent(i); + events_interface.events[i].writable->Signal(); + } + } +} + +Kernel::SharedPtr<Kernel::ReadableEvent> Module::GetEvent(const u32 event_id) const { + return events_interface.events[event_id].readable; +} + +Kernel::SharedPtr<Kernel::WritableEvent> Module::GetEventWriteable(const u32 event_id) const { + return events_interface.events[event_id].writable; +} + } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index 53564f696..a339ab672 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -8,8 +8,14 @@ #include <unordered_map> #include <vector> #include "common/common_types.h" +#include "core/hle/kernel/writable_event.h" +#include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/service.h" +namespace Core { +class System; +} + namespace Service::NVFlinger { class NVFlinger; } @@ -20,16 +26,72 @@ namespace Devices { class nvdevice; } -struct IoctlFence { - u32 id; - u32 value; +struct EventInterface { + // Mask representing currently busy events + u64 events_mask{}; + // Each kernel event associated to an NV event + std::array<Kernel::EventPair, MaxNvEvents> events; + // The status of the current NVEvent + std::array<EventState, MaxNvEvents> status{}; + // Tells if an NVEvent is registered or not + std::array<bool, MaxNvEvents> registered{}; + // When an NVEvent is waiting on GPU interrupt, this is the sync_point + // associated with it. + std::array<u32, MaxNvEvents> assigned_syncpt{}; + // This is the value of the GPU interrupt for which the NVEvent is waiting + // for. + std::array<u32, MaxNvEvents> assigned_value{}; + // Constant to denote an unasigned syncpoint. + static constexpr u32 unassigned_syncpt = 0xFFFFFFFF; + std::optional<u32> GetFreeEvent() const { + u64 mask = events_mask; + for (u32 i = 0; i < MaxNvEvents; i++) { + const bool is_free = (mask & 0x1) == 0; + if (is_free) { + if (status[i] == EventState::Registered || status[i] == EventState::Free) { + return {i}; + } + } + mask = mask >> 1; + } + return {}; + } + void SetEventStatus(const u32 event_id, EventState new_status) { + EventState old_status = status[event_id]; + if (old_status == new_status) { + return; + } + status[event_id] = new_status; + if (new_status == EventState::Registered) { + registered[event_id] = true; + } + if (new_status == EventState::Waiting || new_status == EventState::Busy) { + events_mask |= (1ULL << event_id); + } + } + void RegisterEvent(const u32 event_id) { + registered[event_id] = true; + if (status[event_id] == EventState::Free) { + status[event_id] = EventState::Registered; + } + } + void UnregisterEvent(const u32 event_id) { + registered[event_id] = false; + if (status[event_id] == EventState::Registered) { + status[event_id] = EventState::Free; + } + } + void LiberateEvent(const u32 event_id) { + status[event_id] = registered[event_id] ? EventState::Registered : EventState::Free; + events_mask &= ~(1ULL << event_id); + assigned_syncpt[event_id] = unassigned_syncpt; + assigned_value[event_id] = 0; + } }; -static_assert(sizeof(IoctlFence) == 8, "IoctlFence has wrong size"); - class Module final { public: - Module(); + Module(Core::System& system); ~Module(); /// Returns a pointer to one of the available devices, identified by its name. @@ -44,10 +106,17 @@ public: /// Opens a device node and returns a file descriptor to it. u32 Open(const std::string& device_name); /// Sends an ioctl command to the specified file descriptor. - u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output); + u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl); /// Closes a device file descriptor and returns operation success. ResultCode Close(u32 fd); + void SignalSyncpt(const u32 syncpoint_id, const u32 value); + + Kernel::SharedPtr<Kernel::ReadableEvent> GetEvent(u32 event_id) const; + + Kernel::SharedPtr<Kernel::WritableEvent> GetEventWriteable(u32 event_id) const; + private: /// Id to use for the next open file descriptor. u32 next_fd = 1; @@ -57,9 +126,12 @@ private: /// Mapping of device node names to their implementation. std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices; + + EventInterface events_interface; }; /// Registers all NVDRV services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger); +void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, + Core::System& system); } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index 5731e815f..e1a07d3ee 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp @@ -34,7 +34,8 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) buffer_wait_event.writable->Signal(); } -std::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) { +std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> 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. @@ -51,7 +52,7 @@ std::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) { } itr->status = Buffer::Status::Dequeued; - return itr->slot; + return {{itr->slot, &itr->multi_fence}}; } const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const { @@ -63,7 +64,8 @@ const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const { } void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform, - const Common::Rectangle<int>& crop_rect) { + const Common::Rectangle<int>& crop_rect, u32 swap_interval, + Service::Nvidia::MultiFence& multi_fence) { auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { return buffer.slot == slot; }); ASSERT(itr != queue.end()); @@ -71,12 +73,21 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform, itr->status = Buffer::Status::Queued; itr->transform = transform; itr->crop_rect = crop_rect; + itr->swap_interval = swap_interval; + itr->multi_fence = multi_fence; + queue_sequence.push_back(slot); } std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() { - auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) { - return buffer.status == Buffer::Status::Queued; - }); + auto itr = queue.end(); + // Iterate to find a queued buffer matching the requested slot. + while (itr == queue.end() && !queue_sequence.empty()) { + u32 slot = queue_sequence.front(); + itr = std::find_if(queue.begin(), queue.end(), [&slot](const Buffer& buffer) { + return buffer.status == Buffer::Status::Queued && buffer.slot == slot; + }); + queue_sequence.pop_front(); + } if (itr == queue.end()) return {}; itr->status = Buffer::Status::Acquired; diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h index e1ccb6171..356bedb81 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ b/src/core/hle/service/nvflinger/buffer_queue.h @@ -4,6 +4,7 @@ #pragma once +#include <list> #include <optional> #include <vector> @@ -12,6 +13,7 @@ #include "common/swap.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/writable_event.h" +#include "core/hle/service/nvdrv/nvdata.h" namespace Service::NVFlinger { @@ -68,13 +70,17 @@ public: IGBPBuffer igbp_buffer; BufferTransformFlags transform; Common::Rectangle<int> crop_rect; + u32 swap_interval; + Service::Nvidia::MultiFence multi_fence; }; void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer); - std::optional<u32> DequeueBuffer(u32 width, u32 height); + std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> DequeueBuffer(u32 width, + u32 height); const IGBPBuffer& RequestBuffer(u32 slot) const; void QueueBuffer(u32 slot, BufferTransformFlags transform, - const Common::Rectangle<int>& crop_rect); + const Common::Rectangle<int>& crop_rect, u32 swap_interval, + Service::Nvidia::MultiFence& multi_fence); std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer(); void ReleaseBuffer(u32 slot); u32 Query(QueryType type); @@ -92,6 +98,7 @@ private: u64 layer_id; std::vector<Buffer> queue; + std::list<u32> queue_sequence; Kernel::EventPair buffer_wait_event; }; diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 3c5c53e24..f9db79370 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -37,15 +37,14 @@ NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_t displays.emplace_back(4, "Null"); // Schedule the screen composition events - const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : frame_ticks; - - composition_event = core_timing.RegisterEvent( - "ScreenComposition", [this, ticks](u64 userdata, s64 cycles_late) { - Compose(); - this->core_timing.ScheduleEvent(ticks - cycles_late, composition_event); - }); - - core_timing.ScheduleEvent(ticks, composition_event); + composition_event = core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, + s64 cycles_late) { + Compose(); + const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks(); + this->core_timing.ScheduleEvent(std::max<s64>(0LL, ticks - cycles_late), composition_event); + }); + + core_timing.ScheduleEvent(frame_ticks, composition_event); } NVFlinger::~NVFlinger() { @@ -206,8 +205,14 @@ void NVFlinger::Compose() { igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->get().transform, buffer->get().crop_rect); + swap_interval = buffer->get().swap_interval; buffer_queue.ReleaseBuffer(buffer->get().slot); } } +s64 NVFlinger::GetNextTicks() const { + constexpr s64 max_hertz = 120LL; + return (Core::Timing::BASE_CLOCK_RATE * (1LL << swap_interval)) / max_hertz; +} + } // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index c0a83fffb..988be8726 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -74,6 +74,8 @@ public: /// finished. void Compose(); + s64 GetNextTicks() const; + private: /// Finds the display identified by the specified ID. VI::Display* FindDisplay(u64 display_id); @@ -98,6 +100,8 @@ private: /// layers. u32 next_buffer_queue_id = 1; + u32 swap_interval = 1; + /// Event that handles screen composition. Core::Timing::EventType* composition_event; diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp index ebcc41a43..fe6b5f798 100644 --- a/src/core/hle/service/pm/pm.cpp +++ b/src/core/hle/service/pm/pm.cpp @@ -3,11 +3,44 @@ // Refer to the license.txt file included. #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/process.h" #include "core/hle/service/pm/pm.h" #include "core/hle/service/service.h" namespace Service::PM { +namespace { + +constexpr ResultCode ERROR_PROCESS_NOT_FOUND{ErrorModule::PM, 1}; + +constexpr u64 NO_PROCESS_FOUND_PID{0}; + +std::optional<Kernel::SharedPtr<Kernel::Process>> SearchProcessList( + const std::vector<Kernel::SharedPtr<Kernel::Process>>& process_list, + std::function<bool(const Kernel::SharedPtr<Kernel::Process>&)> predicate) { + const auto iter = std::find_if(process_list.begin(), process_list.end(), predicate); + + if (iter == process_list.end()) { + return std::nullopt; + } + + return *iter; +} + +void GetApplicationPidGeneric(Kernel::HLERequestContext& ctx, + const std::vector<Kernel::SharedPtr<Kernel::Process>>& process_list) { + const auto process = SearchProcessList(process_list, [](const auto& process) { + return process->GetProcessID() == Kernel::Process::ProcessIDMin; + }); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(process.has_value() ? (*process)->GetProcessID() : NO_PROCESS_FOUND_PID); +} + +} // Anonymous namespace + class BootMode final : public ServiceFramework<BootMode> { public: explicit BootMode() : ServiceFramework{"pm:bm"} { @@ -41,14 +74,15 @@ private: class DebugMonitor final : public ServiceFramework<DebugMonitor> { public: - explicit DebugMonitor() : ServiceFramework{"pm:dmnt"} { + explicit DebugMonitor(const Kernel::KernelCore& kernel) + : ServiceFramework{"pm:dmnt"}, kernel(kernel) { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetDebugProcesses"}, {1, nullptr, "StartDebugProcess"}, - {2, nullptr, "GetTitlePid"}, + {2, &DebugMonitor::GetTitlePid, "GetTitlePid"}, {3, nullptr, "EnableDebugForTitleId"}, - {4, nullptr, "GetApplicationPid"}, + {4, &DebugMonitor::GetApplicationPid, "GetApplicationPid"}, {5, nullptr, "EnableDebugForApplication"}, {6, nullptr, "DisableDebug"}, }; @@ -56,21 +90,77 @@ public: RegisterHandlers(functions); } + +private: + void GetTitlePid(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto title_id = rp.PopRaw<u64>(); + + LOG_DEBUG(Service_PM, "called, title_id={:016X}", title_id); + + const auto process = + SearchProcessList(kernel.GetProcessList(), [title_id](const auto& process) { + return process->GetTitleID() == title_id; + }); + + if (!process.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_PROCESS_NOT_FOUND); + return; + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push((*process)->GetProcessID()); + } + + void GetApplicationPid(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PM, "called"); + GetApplicationPidGeneric(ctx, kernel.GetProcessList()); + } + + const Kernel::KernelCore& kernel; }; class Info final : public ServiceFramework<Info> { public: - explicit Info() : ServiceFramework{"pm:info"} { + explicit Info(const std::vector<Kernel::SharedPtr<Kernel::Process>>& process_list) + : ServiceFramework{"pm:info"}, process_list(process_list) { static const FunctionInfo functions[] = { - {0, nullptr, "GetTitleId"}, + {0, &Info::GetTitleId, "GetTitleId"}, }; RegisterHandlers(functions); } + +private: + void GetTitleId(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto process_id = rp.PopRaw<u64>(); + + LOG_DEBUG(Service_PM, "called, process_id={:016X}", process_id); + + const auto process = SearchProcessList(process_list, [process_id](const auto& process) { + return process->GetProcessID() == process_id; + }); + + if (!process.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_PROCESS_NOT_FOUND); + return; + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push((*process)->GetTitleID()); + } + + const std::vector<Kernel::SharedPtr<Kernel::Process>>& process_list; }; class Shell final : public ServiceFramework<Shell> { public: - explicit Shell() : ServiceFramework{"pm:shell"} { + explicit Shell(const Kernel::KernelCore& kernel) + : ServiceFramework{"pm:shell"}, kernel(kernel) { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "LaunchProcess"}, @@ -79,21 +169,31 @@ public: {3, nullptr, "GetProcessEventWaiter"}, {4, nullptr, "GetProcessEventType"}, {5, nullptr, "NotifyBootFinished"}, - {6, nullptr, "GetApplicationPid"}, + {6, &Shell::GetApplicationPid, "GetApplicationPid"}, {7, nullptr, "BoostSystemMemoryResourceLimit"}, {8, nullptr, "EnableAdditionalSystemThreads"}, + {9, nullptr, "GetUnimplementedEventHandle"}, }; // clang-format on RegisterHandlers(functions); } + +private: + void GetApplicationPid(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PM, "called"); + GetApplicationPidGeneric(ctx, kernel.GetProcessList()); + } + + const Kernel::KernelCore& kernel; }; -void InstallInterfaces(SM::ServiceManager& sm) { - std::make_shared<BootMode>()->InstallAsService(sm); - std::make_shared<DebugMonitor>()->InstallAsService(sm); - std::make_shared<Info>()->InstallAsService(sm); - std::make_shared<Shell>()->InstallAsService(sm); +void InstallInterfaces(Core::System& system) { + std::make_shared<BootMode>()->InstallAsService(system.ServiceManager()); + std::make_shared<DebugMonitor>(system.Kernel())->InstallAsService(system.ServiceManager()); + std::make_shared<Info>(system.Kernel().GetProcessList()) + ->InstallAsService(system.ServiceManager()); + std::make_shared<Shell>(system.Kernel())->InstallAsService(system.ServiceManager()); } } // namespace Service::PM diff --git a/src/core/hle/service/pm/pm.h b/src/core/hle/service/pm/pm.h index cc8d3f215..852e7050c 100644 --- a/src/core/hle/service/pm/pm.h +++ b/src/core/hle/service/pm/pm.h @@ -4,8 +4,8 @@ #pragma once -namespace Service::SM { -class ServiceManager; +namespace Core { +class System; } namespace Service::PM { @@ -16,6 +16,6 @@ enum class SystemBootMode { }; /// Registers all PM services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(Core::System& system); } // namespace Service::PM diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index ec9d755b7..3a0f8c3f6 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -195,8 +195,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co // Module interface /// Initialize ServiceManager -void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system, - FileSys::VfsFilesystem& vfs) { +void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it // here and pass it into the respective InstallInterfaces functions. auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system.CoreTiming()); @@ -206,8 +205,8 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system, Account::InstallInterfaces(system); AM::InstallInterfaces(*sm, nv_flinger, system); AOC::InstallInterfaces(*sm); - APM::InstallInterfaces(*sm); - Audio::InstallInterfaces(*sm); + APM::InstallInterfaces(system); + Audio::InstallInterfaces(*sm, system); BCAT::InstallInterfaces(*sm); BPC::InstallInterfaces(*sm); BtDrv::InstallInterfaces(*sm); @@ -218,7 +217,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system, EUPLD::InstallInterfaces(*sm); Fatal::InstallInterfaces(*sm); FGM::InstallInterfaces(*sm); - FileSystem::InstallInterfaces(*sm, vfs); + FileSystem::InstallInterfaces(system); Friend::InstallInterfaces(*sm); Glue::InstallInterfaces(system); GRC::InstallInterfaces(*sm); @@ -237,19 +236,19 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system, NIM::InstallInterfaces(*sm); NPNS::InstallInterfaces(*sm); NS::InstallInterfaces(*sm); - Nvidia::InstallInterfaces(*sm, *nv_flinger); + Nvidia::InstallInterfaces(*sm, *nv_flinger, system); PCIe::InstallInterfaces(*sm); PCTL::InstallInterfaces(*sm); PCV::InstallInterfaces(*sm); PlayReport::InstallInterfaces(*sm); - PM::InstallInterfaces(*sm); + PM::InstallInterfaces(system); PSC::InstallInterfaces(*sm); PSM::InstallInterfaces(*sm); Set::InstallInterfaces(*sm); Sockets::InstallInterfaces(*sm); SPL::InstallInterfaces(*sm); SSL::InstallInterfaces(*sm); - Time::InstallInterfaces(*sm); + Time::InstallInterfaces(system); USB::InstallInterfaces(*sm); VI::InstallInterfaces(*sm, nv_flinger); WLAN::InstallInterfaces(*sm); diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index abbfe5524..c6c4bdae5 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -182,8 +182,7 @@ private: }; /// Initialize ServiceManager -void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system, - FileSys::VfsFilesystem& vfs); +void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system); /// Shutdown ServiceManager void Shutdown(); diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index 298d85011..b54214421 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp @@ -95,6 +95,14 @@ void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) { PushResponseLanguageCode(ctx, post4_0_0_max_entries); } +void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast<u32>(Settings::values.quest_flag)); +} + void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index); @@ -114,7 +122,7 @@ SET::SET() : ServiceFramework("set") { {5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"}, {6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"}, {7, nullptr, "GetKeyCodeMap"}, - {8, nullptr, "GetQuestFlag"}, + {8, &SET::GetQuestFlag, "GetQuestFlag"}, {9, nullptr, "GetKeyCodeMap2"}, }; // clang-format on diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h index 31f9cb296..b154e08aa 100644 --- a/src/core/hle/service/set/set.h +++ b/src/core/hle/service/set/set.h @@ -42,6 +42,7 @@ private: void GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx); void GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx); void GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx); + void GetQuestFlag(Kernel::HLERequestContext& ctx); }; } // namespace Service::Set diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp index 8d122ae33..1030185e0 100644 --- a/src/core/hle/service/time/interface.cpp +++ b/src/core/hle/service/time/interface.cpp @@ -6,8 +6,9 @@ namespace Service::Time { -Time::Time(std::shared_ptr<Module> time, const char* name) - : Module::Interface(std::move(time), name) { +Time::Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory, + const char* name) + : Module::Interface(std::move(time), std::move(shared_memory), name) { // clang-format off static const FunctionInfo functions[] = { {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, @@ -16,12 +17,12 @@ Time::Time(std::shared_ptr<Module> time, const char* name) {3, &Time::GetTimeZoneService, "GetTimeZoneService"}, {4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, {5, nullptr, "GetEphemeralNetworkSystemClock"}, - {20, nullptr, "GetSharedMemoryNativeHandle"}, + {20, &Time::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, {30, nullptr, "GetStandardNetworkClockOperationEventReadableHandle"}, {31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"}, {50, nullptr, "SetStandardSteadyClockInternalOffset"}, - {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, - {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, + {100, &Time::IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, + {101, &Time::SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, {102, nullptr, "GetStandardUserSystemClockInitialYear"}, {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"}, {201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"}, diff --git a/src/core/hle/service/time/interface.h b/src/core/hle/service/time/interface.h index cd6b44dec..bdf0883e2 100644 --- a/src/core/hle/service/time/interface.h +++ b/src/core/hle/service/time/interface.h @@ -8,9 +8,12 @@ namespace Service::Time { +class SharedMemory; + class Time final : public Module::Interface { public: - explicit Time(std::shared_ptr<Module> time, const char* name); + explicit Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory, + const char* name); ~Time() override; }; diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 346bad80d..ae6446204 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -13,6 +13,7 @@ #include "core/hle/kernel/client_session.h" #include "core/hle/service/time/interface.h" #include "core/hle/service/time/time.h" +#include "core/hle/service/time/time_sharedmemory.h" #include "core/settings.h" namespace Service::Time { @@ -61,9 +62,18 @@ static u64 CalendarToPosix(const CalendarTime& calendar_time, return static_cast<u64>(epoch_time); } +enum class ClockContextType { + StandardSteady, + StandardUserSystem, + StandardNetworkSystem, + StandardLocalSystem, +}; + class ISystemClock final : public ServiceFramework<ISystemClock> { public: - ISystemClock() : ServiceFramework("ISystemClock") { + ISystemClock(std::shared_ptr<Service::Time::SharedMemory> shared_memory, + ClockContextType clock_type) + : ServiceFramework("ISystemClock"), shared_memory(shared_memory), clock_type(clock_type) { static const FunctionInfo functions[] = { {0, &ISystemClock::GetCurrentTime, "GetCurrentTime"}, {1, nullptr, "SetCurrentTime"}, @@ -72,6 +82,8 @@ public: }; RegisterHandlers(functions); + + UpdateSharedMemoryContext(system_clock_context); } private: @@ -87,34 +99,63 @@ private: void GetSystemClockContext(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_Time, "(STUBBED) called"); - SystemClockContext system_clock_ontext{}; + // TODO(ogniK): This should be updated periodically however since we have it stubbed we'll + // only update when we get a new context + UpdateSharedMemoryContext(system_clock_context); + IPC::ResponseBuilder rb{ctx, (sizeof(SystemClockContext) / 4) + 2}; rb.Push(RESULT_SUCCESS); - rb.PushRaw(system_clock_ontext); + rb.PushRaw(system_clock_context); } + + void UpdateSharedMemoryContext(const SystemClockContext& clock_context) { + switch (clock_type) { + case ClockContextType::StandardLocalSystem: + shared_memory->SetStandardLocalSystemClockContext(clock_context); + break; + case ClockContextType::StandardNetworkSystem: + shared_memory->SetStandardNetworkSystemClockContext(clock_context); + break; + } + } + + SystemClockContext system_clock_context{}; + std::shared_ptr<Service::Time::SharedMemory> shared_memory; + ClockContextType clock_type; }; class ISteadyClock final : public ServiceFramework<ISteadyClock> { public: - ISteadyClock() : ServiceFramework("ISteadyClock") { + ISteadyClock(std::shared_ptr<SharedMemory> shared_memory) + : ServiceFramework("ISteadyClock"), shared_memory(shared_memory) { static const FunctionInfo functions[] = { {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"}, }; RegisterHandlers(functions); + + shared_memory->SetStandardSteadyClockTimepoint(GetCurrentTimePoint()); } private: void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called"); - const auto& core_timing = Core::System::GetInstance().CoreTiming(); - const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks()); - const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000), - {}}; + const auto time_point = GetCurrentTimePoint(); + // TODO(ogniK): This should be updated periodically + shared_memory->SetStandardSteadyClockTimepoint(time_point); + IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2}; rb.Push(RESULT_SUCCESS); - rb.PushRaw(steady_clock_time_point); + rb.PushRaw(time_point); } + + SteadyClockTimePoint GetCurrentTimePoint() const { + const auto& core_timing = Core::System::GetInstance().CoreTiming(); + const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks()); + return {static_cast<u64_le>(ms.count() / 1000), {}}; + } + + std::shared_ptr<SharedMemory> shared_memory; }; class ITimeZoneService final : public ServiceFramework<ITimeZoneService> { @@ -233,7 +274,7 @@ void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ct IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISystemClock>(); + rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardUserSystem); } void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) { @@ -241,7 +282,7 @@ void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISystemClock>(); + rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardNetworkSystem); } void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) { @@ -249,7 +290,7 @@ void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISteadyClock>(); + rb.PushIpcInterface<ISteadyClock>(shared_memory); } void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) { @@ -265,7 +306,7 @@ void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& c IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISystemClock>(); + rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardLocalSystem); } void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { @@ -333,16 +374,52 @@ void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser( rb.PushRaw<u64>(difference); } -Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) - : ServiceFramework(name), time(std::move(time)) {} +void Module::Interface::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called"); + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(shared_memory->GetSharedMemoryHolder()); +} + +void Module::Interface::IsStandardUserSystemClockAutomaticCorrectionEnabled( + Kernel::HLERequestContext& ctx) { + // ogniK(TODO): When clock contexts are implemented, the value should be read from the context + // instead of our shared memory holder + LOG_DEBUG(Service_Time, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u8>(shared_memory->GetStandardUserSystemClockAutomaticCorrectionEnabled()); +} + +void Module::Interface::SetStandardUserSystemClockAutomaticCorrectionEnabled( + Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto enabled = rp.Pop<u8>(); + + LOG_WARNING(Service_Time, "(PARTIAL IMPLEMENTATION) called"); + + // TODO(ogniK): Update clock contexts and correct timespans + + shared_memory->SetStandardUserSystemClockAutomaticCorrectionEnabled(enabled > 0); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +Module::Interface::Interface(std::shared_ptr<Module> time, + std::shared_ptr<SharedMemory> shared_memory, const char* name) + : ServiceFramework(name), time(std::move(time)), shared_memory(std::move(shared_memory)) {} Module::Interface::~Interface() = default; -void InstallInterfaces(SM::ServiceManager& service_manager) { +void InstallInterfaces(Core::System& system) { auto time = std::make_shared<Module>(); - std::make_shared<Time>(time, "time:a")->InstallAsService(service_manager); - std::make_shared<Time>(time, "time:s")->InstallAsService(service_manager); - std::make_shared<Time>(time, "time:u")->InstallAsService(service_manager); + auto shared_mem = std::make_shared<SharedMemory>(system); + + std::make_shared<Time>(time, shared_mem, "time:a")->InstallAsService(system.ServiceManager()); + std::make_shared<Time>(time, shared_mem, "time:s")->InstallAsService(system.ServiceManager()); + std::make_shared<Time>(std::move(time), shared_mem, "time:u") + ->InstallAsService(system.ServiceManager()); } } // namespace Service::Time diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h index f11affe95..e0708f856 100644 --- a/src/core/hle/service/time/time.h +++ b/src/core/hle/service/time/time.h @@ -10,6 +10,8 @@ namespace Service::Time { +class SharedMemory; + struct LocationName { std::array<u8, 0x24> name; }; @@ -77,7 +79,8 @@ class Module final { public: class Interface : public ServiceFramework<Interface> { public: - explicit Interface(std::shared_ptr<Module> time, const char* name); + explicit Interface(std::shared_ptr<Module> time, + std::shared_ptr<SharedMemory> shared_memory, const char* name); ~Interface() override; void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx); @@ -87,13 +90,17 @@ public: void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx); void GetClockSnapshot(Kernel::HLERequestContext& ctx); void CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext& ctx); + void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx); + void IsStandardUserSystemClockAutomaticCorrectionEnabled(Kernel::HLERequestContext& ctx); + void SetStandardUserSystemClockAutomaticCorrectionEnabled(Kernel::HLERequestContext& ctx); protected: std::shared_ptr<Module> time; + std::shared_ptr<SharedMemory> shared_memory; }; }; /// Registers all Time services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(Core::System& system); } // namespace Service::Time diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp new file mode 100644 index 000000000..bfc81b83c --- /dev/null +++ b/src/core/hle/service/time/time_sharedmemory.cpp @@ -0,0 +1,68 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/core.h" +#include "core/hle/service/time/time_sharedmemory.h" + +namespace Service::Time { +const std::size_t SHARED_MEMORY_SIZE = 0x1000; + +SharedMemory::SharedMemory(Core::System& system) : system(system) { + shared_memory_holder = Kernel::SharedMemory::Create( + system.Kernel(), nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite, + Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "Time:SharedMemory"); + + // Seems static from 1.0.0 -> 8.1.0. Specific games seem to check this value and crash + // if it's set to anything else + shared_memory_format.format_version = 14; + std::memcpy(shared_memory_holder->GetPointer(), &shared_memory_format, sizeof(Format)); +} + +SharedMemory::~SharedMemory() = default; + +Kernel::SharedPtr<Kernel::SharedMemory> SharedMemory::GetSharedMemoryHolder() const { + return shared_memory_holder; +} + +void SharedMemory::SetStandardSteadyClockTimepoint(const SteadyClockTimePoint& timepoint) { + shared_memory_format.standard_steady_clock_timepoint.StoreData( + shared_memory_holder->GetPointer(), timepoint); +} + +void SharedMemory::SetStandardLocalSystemClockContext(const SystemClockContext& context) { + shared_memory_format.standard_local_system_clock_context.StoreData( + shared_memory_holder->GetPointer(), context); +} + +void SharedMemory::SetStandardNetworkSystemClockContext(const SystemClockContext& context) { + shared_memory_format.standard_network_system_clock_context.StoreData( + shared_memory_holder->GetPointer(), context); +} + +void SharedMemory::SetStandardUserSystemClockAutomaticCorrectionEnabled(bool enabled) { + shared_memory_format.standard_user_system_clock_automatic_correction.StoreData( + shared_memory_holder->GetPointer(), enabled); +} + +SteadyClockTimePoint SharedMemory::GetStandardSteadyClockTimepoint() { + return shared_memory_format.standard_steady_clock_timepoint.ReadData( + shared_memory_holder->GetPointer()); +} + +SystemClockContext SharedMemory::GetStandardLocalSystemClockContext() { + return shared_memory_format.standard_local_system_clock_context.ReadData( + shared_memory_holder->GetPointer()); +} + +SystemClockContext SharedMemory::GetStandardNetworkSystemClockContext() { + return shared_memory_format.standard_network_system_clock_context.ReadData( + shared_memory_holder->GetPointer()); +} + +bool SharedMemory::GetStandardUserSystemClockAutomaticCorrectionEnabled() { + return shared_memory_format.standard_user_system_clock_automatic_correction.ReadData( + shared_memory_holder->GetPointer()); +} + +} // namespace Service::Time diff --git a/src/core/hle/service/time/time_sharedmemory.h b/src/core/hle/service/time/time_sharedmemory.h new file mode 100644 index 000000000..cb8253541 --- /dev/null +++ b/src/core/hle/service/time/time_sharedmemory.h @@ -0,0 +1,74 @@ +// Copyright 2019 yuzu emulator team +// 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/shared_memory.h" +#include "core/hle/service/time/time.h" + +namespace Service::Time { +class SharedMemory { +public: + explicit SharedMemory(Core::System& system); + ~SharedMemory(); + + // Return the shared memory handle + Kernel::SharedPtr<Kernel::SharedMemory> GetSharedMemoryHolder() const; + + // Set memory barriers in shared memory and update them + void SetStandardSteadyClockTimepoint(const SteadyClockTimePoint& timepoint); + void SetStandardLocalSystemClockContext(const SystemClockContext& context); + void SetStandardNetworkSystemClockContext(const SystemClockContext& context); + void SetStandardUserSystemClockAutomaticCorrectionEnabled(bool enabled); + + // Pull from memory barriers in the shared memory + SteadyClockTimePoint GetStandardSteadyClockTimepoint(); + SystemClockContext GetStandardLocalSystemClockContext(); + SystemClockContext GetStandardNetworkSystemClockContext(); + bool GetStandardUserSystemClockAutomaticCorrectionEnabled(); + + // TODO(ogniK): We have to properly simulate memory barriers, how are we going to do this? + template <typename T, std::size_t Offset> + struct MemoryBarrier { + static_assert(std::is_trivially_constructible_v<T>, "T must be trivially constructable"); + u32_le read_attempt{}; + std::array<T, 2> data{}; + + // These are not actually memory barriers at the moment as we don't have multicore and all + // HLE is mutexed. This will need to properly be implemented when we start updating the time + // points on threads. As of right now, we'll be updated both values synchronously and just + // incrementing the read_attempt to indicate that we waited. + void StoreData(u8* shared_memory, T data_to_store) { + std::memcpy(this, shared_memory + Offset, sizeof(*this)); + read_attempt++; + data[read_attempt & 1] = data_to_store; + std::memcpy(shared_memory + Offset, this, sizeof(*this)); + } + + // For reading we're just going to read the last stored value. If there was no value stored + // it will just end up reading an empty value as intended. + T ReadData(u8* shared_memory) { + std::memcpy(this, shared_memory + Offset, sizeof(*this)); + return data[(read_attempt - 1) & 1]; + } + }; + + // Shared memory format + struct Format { + MemoryBarrier<SteadyClockTimePoint, 0x0> standard_steady_clock_timepoint; + MemoryBarrier<SystemClockContext, 0x38> standard_local_system_clock_context; + MemoryBarrier<SystemClockContext, 0x80> standard_network_system_clock_context; + MemoryBarrier<bool, 0xc8> standard_user_system_clock_automatic_correction; + u32_le format_version; + }; + static_assert(sizeof(Format) == 0xd8, "Format is an invalid size"); + +private: + Kernel::SharedPtr<Kernel::SharedMemory> shared_memory_holder{}; + Core::System& system; + Format shared_memory_format{}; +}; + +} // namespace Service::Time diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index f1fa6ccd1..199b30635 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -21,6 +21,7 @@ #include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/writable_event.h" +#include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvflinger/buffer_queue.h" #include "core/hle/service/nvflinger/nvflinger.h" @@ -328,32 +329,22 @@ public: Data data; }; -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: - explicit IGBPDequeueBufferResponseParcel(u32 slot) : slot(slot) {} + explicit IGBPDequeueBufferResponseParcel(u32 slot, Service::Nvidia::MultiFence& multi_fence) + : slot(slot), multi_fence(multi_fence) {} ~IGBPDequeueBufferResponseParcel() override = default; protected: void SerializeData() override { - // 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); + WriteObject(multi_fence); Write<u32_le>(0); } u32_le slot; + Service::Nvidia::MultiFence multi_fence; }; class IGBPRequestBufferRequestParcel : public Parcel { @@ -400,12 +391,6 @@ public: data = Read<Data>(); } - struct Fence { - u32_le id; - u32_le value; - }; - static_assert(sizeof(Fence) == 8, "Fence has wrong size"); - struct Data { u32_le slot; INSERT_PADDING_WORDS(3); @@ -418,15 +403,15 @@ public: s32_le scaling_mode; NVFlinger::BufferQueue::BufferTransformFlags transform; u32_le sticky_transform; - INSERT_PADDING_WORDS(2); - u32_le fence_is_valid; - std::array<Fence, 2> fences; + INSERT_PADDING_WORDS(1); + u32_le swap_interval; + Service::Nvidia::MultiFence multi_fence; Common::Rectangle<int> GetCropRect() const { return {crop_left, crop_top, crop_right, crop_bottom}; } }; - static_assert(sizeof(Data) == 80, "ParcelData has wrong size"); + static_assert(sizeof(Data) == 96, "ParcelData has wrong size"); Data data; }; @@ -547,11 +532,11 @@ private: IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()}; const u32 width{request.data.width}; const u32 height{request.data.height}; - std::optional<u32> slot = buffer_queue.DequeueBuffer(width, height); + auto result = buffer_queue.DequeueBuffer(width, height); - if (slot) { + if (result) { // Buffer is available - IGBPDequeueBufferResponseParcel response{*slot}; + IGBPDequeueBufferResponseParcel response{result->first, *result->second}; ctx.WriteBuffer(response.Serialize()); } else { // Wait the current thread until a buffer becomes available @@ -561,10 +546,10 @@ private: Kernel::ThreadWakeupReason reason) { // Repeat TransactParcel DequeueBuffer when a buffer is available auto& buffer_queue = nv_flinger->FindBufferQueue(id); - std::optional<u32> slot = buffer_queue.DequeueBuffer(width, height); - ASSERT_MSG(slot != std::nullopt, "Could not dequeue buffer."); + auto result = buffer_queue.DequeueBuffer(width, height); + ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer."); - IGBPDequeueBufferResponseParcel response{*slot}; + IGBPDequeueBufferResponseParcel response{result->first, *result->second}; ctx.WriteBuffer(response.Serialize()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -582,7 +567,8 @@ private: IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()}; buffer_queue.QueueBuffer(request.data.slot, request.data.transform, - request.data.GetCropRect()); + request.data.GetCropRect(), request.data.swap_interval, + request.data.multi_fence); IGBPQueueBufferResponseParcel response{1280, 720}; ctx.WriteBuffer(response.Serialize()); diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 6d4b02375..f1795fdd6 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -295,7 +295,7 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) { } } - std::vector<u8> program_image(total_image_size); + Kernel::PhysicalMemory program_image(total_image_size); std::size_t current_image_position = 0; Kernel::CodeSet codeset; diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp index 70051c13a..474b55cb1 100644 --- a/src/core/loader/kip.cpp +++ b/src/core/loader/kip.cpp @@ -69,7 +69,7 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process) { const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); Kernel::CodeSet codeset; - std::vector<u8> program_image; + Kernel::PhysicalMemory program_image; const auto load_segment = [&program_image](Kernel::CodeSet::Segment& segment, const std::vector<u8>& data, u32 offset) { diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 6a0ca389b..3a5361fdd 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -143,7 +143,7 @@ static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data, } // Build program image - std::vector<u8> program_image(PageAlignSize(nro_header.file_size)); + Kernel::PhysicalMemory program_image(PageAlignSize(nro_header.file_size)); std::memcpy(program_image.data(), data.data(), program_image.size()); if (program_image.size() != PageAlignSize(nro_header.file_size)) { return {}; @@ -258,6 +258,15 @@ ResultStatus AppLoader_NRO::ReadTitle(std::string& title) { return ResultStatus::Success; } +ResultStatus AppLoader_NRO::ReadControlData(FileSys::NACP& control) { + if (nacp == nullptr) { + return ResultStatus::ErrorNoControl; + } + + control = *nacp; + return ResultStatus::Success; +} + bool AppLoader_NRO::IsRomFSUpdatable() const { return false; } diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index 1ffdae805..71811bc29 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h @@ -43,6 +43,7 @@ public: ResultStatus ReadProgramId(u64& out_program_id) override; ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; ResultStatus ReadTitle(std::string& title) override; + ResultStatus ReadControlData(FileSys::NACP& control) override; bool IsRomFSUpdatable() const override; private: diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 29311404a..70c90109f 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -89,7 +89,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, // Build program image Kernel::CodeSet codeset; - std::vector<u8> program_image; + Kernel::PhysicalMemory program_image; for (std::size_t i = 0; i < nso_header.segments.size(); ++i) { std::vector<u8> data = file.ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index f18f6226b..8555691c0 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -16,11 +16,9 @@ #include "core/core.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/vm_manager.h" -#include "core/hle/lock.h" #include "core/memory.h" #include "core/memory_setup.h" #include "video_core/gpu.h" -#include "video_core/renderer_base.h" namespace Memory { diff --git a/src/core/memory.h b/src/core/memory.h index 04e2c5f1d..09008e1dd 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -8,10 +8,6 @@ #include <string> #include "common/common_types.h" -namespace Common { -struct PageTable; -} - namespace Kernel { class Process; } diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index 774022569..cfe0771e2 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -2,8 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <ctime> #include <fstream> + +#include <fmt/chrono.h> +#include <fmt/format.h> #include <json.hpp> + #include "common/file_util.h" #include "common/hex_util.h" #include "common/scm_rev.h" @@ -14,7 +19,6 @@ #include "core/hle/result.h" #include "core/reporter.h" #include "core/settings.h" -#include "fmt/time.h" namespace { @@ -30,9 +34,11 @@ std::string GetTimestamp() { using namespace nlohmann; -void SaveToFile(const json& json, const std::string& filename) { - if (!FileUtil::CreateFullPath(filename)) +void SaveToFile(json json, const std::string& filename) { + if (!FileUtil::CreateFullPath(filename)) { LOG_ERROR(Core, "Failed to create path for '{}' to save report!", filename); + return; + } std::ofstream file( FileUtil::SanitizePath(filename, FileUtil::DirectorySeparator::PlatformDefault)); @@ -61,8 +67,11 @@ json GetReportCommonData(u64 title_id, ResultCode result, const std::string& tim {"result_description", fmt::format("{:08X}", result.description.Value())}, {"timestamp", timestamp}, }; - if (user_id.has_value()) + + if (user_id.has_value()) { out["user_id"] = fmt::format("{:016X}{:016X}", (*user_id)[1], (*user_id)[0]); + } + return out; } @@ -171,14 +180,14 @@ json GetHLERequestContextData(Kernel::HLERequestContext& ctx) { out["buffer_descriptor_c"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorC()); out["buffer_descriptor_x"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorX()); - return std::move(out); + return out; } } // Anonymous namespace namespace Core { -Reporter::Reporter(Core::System& system) : system(system) {} +Reporter::Reporter(System& system) : system(system) {} Reporter::~Reporter() = default; @@ -187,8 +196,9 @@ void Reporter::SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u const std::array<u64, 31>& registers, const std::array<u64, 32>& backtrace, u32 backtrace_size, const std::string& arch, u32 unk10) const { - if (!IsReportingEnabled()) + if (!IsReportingEnabled()) { return; + } const auto timestamp = GetTimestamp(); json out; @@ -212,8 +222,9 @@ void Reporter::SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u void Reporter::SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2, std::optional<std::vector<u8>> resolved_buffer) const { - if (!IsReportingEnabled()) + if (!IsReportingEnabled()) { return; + } const auto timestamp = GetTimestamp(); const auto title_id = system.CurrentProcess()->GetTitleID(); @@ -238,8 +249,9 @@ void Reporter::SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 void Reporter::SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id, const std::string& name, const std::string& service_name) const { - if (!IsReportingEnabled()) + if (!IsReportingEnabled()) { return; + } const auto timestamp = GetTimestamp(); const auto title_id = system.CurrentProcess()->GetTitleID(); @@ -259,8 +271,9 @@ void Reporter::SaveUnimplementedAppletReport( u32 applet_id, u32 common_args_version, u32 library_version, u32 theme_color, bool startup_sound, u64 system_tick, std::vector<std::vector<u8>> normal_channel, std::vector<std::vector<u8>> interactive_channel) const { - if (!IsReportingEnabled()) + if (!IsReportingEnabled()) { return; + } const auto timestamp = GetTimestamp(); const auto title_id = system.CurrentProcess()->GetTitleID(); @@ -293,8 +306,9 @@ void Reporter::SaveUnimplementedAppletReport( void Reporter::SavePlayReport(u64 title_id, u64 process_id, std::vector<std::vector<u8>> data, std::optional<u128> user_id) const { - if (!IsReportingEnabled()) + if (!IsReportingEnabled()) { return; + } const auto timestamp = GetTimestamp(); json out; @@ -316,8 +330,9 @@ void Reporter::SavePlayReport(u64 title_id, u64 process_id, std::vector<std::vec void Reporter::SaveErrorReport(u64 title_id, ResultCode result, std::optional<std::string> custom_text_main, std::optional<std::string> custom_text_detail) const { - if (!IsReportingEnabled()) + if (!IsReportingEnabled()) { return; + } const auto timestamp = GetTimestamp(); json out; @@ -335,12 +350,31 @@ void Reporter::SaveErrorReport(u64 title_id, ResultCode result, SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp)); } -void Reporter::SaveUserReport() const { +void Reporter::SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, + std::string log_message) const { if (!IsReportingEnabled()) return; const auto timestamp = GetTimestamp(); const auto title_id = system.CurrentProcess()->GetTitleID(); + json out; + + out["yuzu_version"] = GetYuzuVersionData(); + out["report_common"] = GetReportCommonData(title_id, RESULT_SUCCESS, timestamp); + + out["log_mode"] = fmt::format("{:08X}", static_cast<u32>(log_mode)); + out["log_message"] = std::move(log_message); + + SaveToFile(std::move(out), GetPath("filesystem_access_report", title_id, timestamp)); +} + +void Reporter::SaveUserReport() const { + if (!IsReportingEnabled()) { + return; + } + + const auto timestamp = GetTimestamp(); + const auto title_id = system.CurrentProcess()->GetTitleID(); SaveToFile(GetFullDataAuto(timestamp, title_id, system), GetPath("user_report", title_id, timestamp)); diff --git a/src/core/reporter.h b/src/core/reporter.h index 3de19c0f7..44256de50 100644 --- a/src/core/reporter.h +++ b/src/core/reporter.h @@ -4,7 +4,9 @@ #pragma once +#include <array> #include <optional> +#include <string> #include <vector> #include "common/common_types.h" @@ -14,11 +16,17 @@ namespace Kernel { class HLERequestContext; } // namespace Kernel +namespace Service::FileSystem { +enum class LogMode : u32; +} + namespace Core { +class System; + class Reporter { public: - explicit Reporter(Core::System& system); + explicit Reporter(System& system); ~Reporter(); void SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point, u64 sp, @@ -45,12 +53,15 @@ public: std::optional<std::string> custom_text_main = {}, std::optional<std::string> custom_text_detail = {}) const; + void SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, + std::string log_message) const; + void SaveUserReport() const; private: bool IsReportingEnabled() const; - Core::System& system; + System& system; }; } // namespace Core diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 6d32ebea3..0dd1632ac 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -85,7 +85,6 @@ void LogSettings() { LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0)); LogSetting("System_CurrentUser", Settings::values.current_user); LogSetting("System_LanguageIndex", Settings::values.language_index); - LogSetting("Core_UseCpuJit", Settings::values.use_cpu_jit); LogSetting("Core_UseMultiCore", Settings::values.use_multi_core); LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor); LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit); diff --git a/src/core/settings.h b/src/core/settings.h index e2ffcaaf7..6638ce8f9 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -378,7 +378,6 @@ struct Values { std::atomic_bool is_device_reload_pending{true}; // Core - bool use_cpu_jit; bool use_multi_core; // Data Storage @@ -416,6 +415,7 @@ struct Values { bool dump_exefs; bool dump_nso; bool reporting_services; + bool quest_flag; // WebService bool enable_telemetry; diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 90d06830f..793d102d3 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -168,7 +168,6 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) { AddField(Telemetry::FieldType::UserConfig, "Audio_SinkId", Settings::values.sink_id); AddField(Telemetry::FieldType::UserConfig, "Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); - AddField(Telemetry::FieldType::UserConfig, "Core_UseCpuJit", Settings::values.use_cpu_jit); AddField(Telemetry::FieldType::UserConfig, "Core_UseMultiCore", Settings::values.use_multi_core); AddField(Telemetry::FieldType::UserConfig, "Renderer_ResolutionFactor", |