From 21fb9d63f45c2bafcc02996097028c55024f35db Mon Sep 17 00:00:00 2001 From: Pengfei Zhu Date: Wed, 25 Nov 2020 03:10:31 +0800 Subject: [PATCH] service/apt: Implement Deliver Args (#5611) * service/apt: Add GetModule and GetAppletManager These will be used to retrieve and set deliver args across system resets (which are currently implemented as complete restarts) * applet_manager: Implement DeliverArg `flags` was added to `ApplicationJumpParameters` as flags 0x2 is handled differently from 0x0. * service/apt: Add ReceiveDeliverArg, implement GetStartupArgument Some based on guesses. * Address review comments --- src/core/core.cpp | 13 +++ src/core/hle/service/apt/applet_manager.cpp | 12 ++- src/core/hle/service/apt/applet_manager.h | 42 +++++++- src/core/hle/service/apt/apt.cpp | 101 +++++++++++++++++--- src/core/hle/service/apt/apt.h | 25 +++++ src/core/hle/service/apt/apt_a.cpp | 2 +- src/core/hle/service/apt/apt_u.cpp | 2 +- 7 files changed, 177 insertions(+), 20 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index ad6e0129f..6a34995fb 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -31,6 +31,8 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/thread.h" +#include "core/hle/service/apt/applet_manager.h" +#include "core/hle/service/apt/apt.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/gsp/gsp.h" #include "core/hle/service/pm/pm_app.h" @@ -561,9 +563,20 @@ void System::Reset() { // reloading. // TODO: Properly implement the reset + // Since the system is completely reinitialized, we'll have to store the deliver arg manually. + boost::optional deliver_arg; + if (auto apt = Service::APT::GetModule(*this)) { + deliver_arg = apt->GetAppletManager()->ReceiveDeliverArg(); + } + Shutdown(); // Reload the system with the same setting Load(*m_emu_window, m_filepath); + + // Restore the deliver arg. + if (auto apt = Service::APT::GetModule(*this)) { + apt->GetAppletManager()->SetDeliverArg(std::move(deliver_arg)); + } } template diff --git a/src/core/hle/service/apt/applet_manager.cpp b/src/core/hle/service/apt/applet_manager.cpp index 88763787c..7e39b080f 100644 --- a/src/core/hle/service/apt/applet_manager.cpp +++ b/src/core/hle/service/apt/applet_manager.cpp @@ -498,6 +498,7 @@ ResultCode AppletManager::PrepareToDoApplicationJump(u64 title_id, FS::MediaType app_jump_parameters.current_media_type = FS::MediaType::NAND; app_jump_parameters.next_title_id = title_id; app_jump_parameters.next_media_type = media_type; + app_jump_parameters.flags = flags; // Note: The real console uses the Home Menu to perform the application jump, therefore the menu // needs to be running. The real APT module starts the Home Menu here if it's not already @@ -505,16 +506,23 @@ ResultCode AppletManager::PrepareToDoApplicationJump(u64 title_id, FS::MediaType return RESULT_SUCCESS; } -ResultCode AppletManager::DoApplicationJump() { +ResultCode AppletManager::DoApplicationJump(DeliverArg arg) { // Note: The real console uses the Home Menu to perform the application jump, it goes // OldApplication->Home Menu->NewApplication. We do not need to use the Home Menu to do this so // we launch the new application directly. In the real APT service, the Home Menu must be // running to do this, otherwise error 0xC8A0CFF0 is returned. auto& application_slot = applet_slots[static_cast(AppletSlot::Application)]; + + if (app_jump_parameters.flags != ApplicationJumpFlags::UseCurrentParameters) { + // The source program ID is not updated when using flags 0x2. + arg.source_program_id = application_slot.title_id; + } + application_slot.Reset(); - // TODO(Subv): Set the delivery parameters. + // Set the delivery parameters. + deliver_arg = std::move(arg); // TODO(Subv): Terminate the current Application. diff --git a/src/core/hle/service/apt/applet_manager.h b/src/core/hle/service/apt/applet_manager.h index 0f1a46a8b..830efa20f 100644 --- a/src/core/hle/service/apt/applet_manager.h +++ b/src/core/hle/service/apt/applet_manager.h @@ -5,7 +5,9 @@ #pragma once #include +#include #include +#include #include #include #include @@ -158,7 +160,30 @@ public: ResultCode PrepareToDoApplicationJump(u64 title_id, FS::MediaType media_type, ApplicationJumpFlags flags); - ResultCode DoApplicationJump(); + + struct DeliverArg { + std::vector param; + std::vector hmac; + u64 source_program_id = std::numeric_limits::max(); + + private: + template + void serialize(Archive& ar, const unsigned int) { + ar& param; + ar& hmac; + ar& source_program_id; + } + friend class boost::serialization::access; + }; + + ResultCode DoApplicationJump(DeliverArg arg); + + boost::optional ReceiveDeliverArg() const { + return deliver_arg; + } + void SetDeliverArg(boost::optional arg) { + deliver_arg = std::move(arg); + } struct AppletInfo { u64 title_id; @@ -173,15 +198,19 @@ public: struct ApplicationJumpParameters { u64 next_title_id; FS::MediaType next_media_type; + ApplicationJumpFlags flags; u64 current_title_id; FS::MediaType current_media_type; private: template - void serialize(Archive& ar, const unsigned int) { + void serialize(Archive& ar, const unsigned int file_version) { ar& next_title_id; ar& next_media_type; + if (file_version > 0) { + ar& flags; + } ar& current_title_id; ar& current_media_type; } @@ -242,6 +271,7 @@ private: }; ApplicationJumpParameters app_jump_parameters{}; + boost::optional deliver_arg{}; // Holds data about the concurrently running applets in the system. std::array applet_slots = {}; @@ -259,9 +289,12 @@ private: private: template - void serialize(Archive& ar, const unsigned int) { + void serialize(Archive& ar, const unsigned int file_version) { ar& next_parameter; ar& app_jump_parameters; + if (file_version > 0) { + ar& deliver_arg; + } ar& applet_slots; ar& library_applet_closing_command; } @@ -270,4 +303,7 @@ private: } // namespace Service::APT +BOOST_CLASS_VERSION(Service::APT::AppletManager::ApplicationJumpParameters, 1) +BOOST_CLASS_VERSION(Service::APT::AppletManager, 1) + SERVICE_CONSTRUCT(Service::APT::AppletManager) diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 0190cd43f..c0365b7c2 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -56,6 +56,10 @@ Module::NSInterface::NSInterface(std::shared_ptr apt, const char* name, Module::NSInterface::~NSInterface() = default; +std::shared_ptr Module::NSInterface::GetModule() const { + return apt; +} + void Module::NSInterface::SetWirelessRebootInfo(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x06, 1, 2); // 0x00060042 u32 size = rp.Pop(); @@ -476,19 +480,32 @@ void Module::APTInterface::PrepareToDoApplicationJump(Kernel::HLERequestContext& void Module::APTInterface::DoApplicationJump(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x32, 2, 4); // 0x00320084 - const auto param_size = rp.Pop(); - const auto hmac_size = rp.Pop(); + auto param_size = rp.Pop(); + auto hmac_size = rp.Pop(); - [[maybe_unused]] const auto param = rp.PopStaticBuffer(); - [[maybe_unused]] const auto hmac = rp.PopStaticBuffer(); + constexpr u32 max_param_size{0x300}; + constexpr u32 max_hmac_size{0x20}; + if (param_size > max_param_size) { + LOG_ERROR(Service_APT, + "Param size is outside the valid range (capped to {:#010X}): param_size={:#010X}", + max_param_size, param_size); + param_size = max_param_size; + } + if (hmac_size > max_hmac_size) { + LOG_ERROR(Service_APT, + "HMAC size is outside the valid range (capped to {:#010X}): hmac_size={:#010X}", + max_hmac_size, hmac_size); + hmac_size = max_hmac_size; + } - LOG_WARNING(Service_APT, "(STUBBED) called param_size={:08X}, hmac_size={:08X}", param_size, - hmac_size); + auto param = rp.PopStaticBuffer(); + auto hmac = rp.PopStaticBuffer(); - // TODO(Subv): Set the delivery parameters before starting the new application. + LOG_INFO(Service_APT, "called param_size={:08X}, hmac_size={:08X}", param_size, hmac_size); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(apt->applet_manager->DoApplicationJump()); + rb.Push(apt->applet_manager->DoApplicationJump( + AppletManager::DeliverArg{std::move(param), std::move(hmac)})); } void Module::APTInterface::GetProgramIdOnApplicationJump(Kernel::HLERequestContext& ctx) { @@ -506,6 +523,25 @@ void Module::APTInterface::GetProgramIdOnApplicationJump(Kernel::HLERequestConte rb.Push(static_cast(parameters.next_media_type)); } +void Module::APTInterface::ReceiveDeliverArg(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x35, 2, 4); // 0x00350080 + const auto param_size = rp.Pop(); + const auto hmac_size = rp.Pop(); + + LOG_DEBUG(Service_APT, "called param_size={:08X}, hmac_size={:08X}", param_size, hmac_size); + + auto arg = apt->applet_manager->ReceiveDeliverArg().value_or(AppletManager::DeliverArg{}); + arg.param.resize(param_size); + arg.hmac.resize(std::max(hmac_size, 0x20)); + + IPC::RequestBuilder rb = rp.MakeBuilder(4, 4); + rb.Push(RESULT_SUCCESS); + rb.Push(arg.source_program_id); + rb.Push(1); + rb.PushStaticBuffer(std::move(arg.param), 0); + rb.PushStaticBuffer(std::move(arg.hmac), 1); +} + void Module::APTInterface::PrepareToStartApplication(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x15, 5, 0); // 0x00150140 u32 title_info1 = rp.Pop(); @@ -809,6 +845,9 @@ void Module::APTInterface::GetStartupArgument(Kernel::HLERequestContext& ctx) { constexpr u32 max_parameter_size{0x1000}; const auto startup_argument_type = static_cast(rp.Pop()); + LOG_WARNING(Service_APT, "called, startup_argument_type={}, parameter_size={:#010X}", + static_cast(startup_argument_type), parameter_size); + if (parameter_size > max_parameter_size) { LOG_ERROR(Service_APT, "Parameter size is outside the valid range (capped to {:#010X}): " @@ -817,15 +856,36 @@ void Module::APTInterface::GetStartupArgument(Kernel::HLERequestContext& ctx) { parameter_size = max_parameter_size; } - std::vector parameter(parameter_size); + std::vector param; + bool exists = false; - LOG_WARNING(Service_APT, "(STUBBED) called, startup_argument_type={}, parameter_size={:#010X}", - static_cast(startup_argument_type), parameter_size); + if (auto arg = apt->applet_manager->ReceiveDeliverArg()) { + param = std::move(arg->param); + + // TODO: This is a complete guess based on observations. It is unknown how the OtherMedia + // type is handled and how it interacts with the OtherApp type, and it is unknown if + // this (checking the jump parameters) is indeed the way the 3DS checks the types. + const auto& jump_parameters = apt->applet_manager->GetApplicationJumpParameters(); + switch (startup_argument_type) { + case StartupArgumentType::OtherApp: + exists = jump_parameters.current_title_id != jump_parameters.next_title_id && + jump_parameters.current_media_type == jump_parameters.next_media_type; + break; + case StartupArgumentType::Restart: + exists = jump_parameters.current_title_id == jump_parameters.next_title_id; + break; + case StartupArgumentType::OtherMedia: + exists = jump_parameters.current_media_type != jump_parameters.next_media_type; + break; + } + } + + param.resize(parameter_size); IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); rb.Push(RESULT_SUCCESS); - rb.Push(0); - rb.PushStaticBuffer(std::move(parameter), 0); + rb.Push(exists); + rb.PushStaticBuffer(std::move(param), 0); } void Module::APTInterface::Wrap(Kernel::HLERequestContext& ctx) { @@ -967,6 +1027,10 @@ Module::APTInterface::APTInterface(std::shared_ptr apt, const char* name Module::APTInterface::~APTInterface() = default; +std::shared_ptr Module::APTInterface::GetModule() const { + return apt; +} + Module::Module(Core::System& system) : system(system) { applet_manager = std::make_shared(system); @@ -982,6 +1046,17 @@ Module::Module(Core::System& system) : system(system) { Module::~Module() {} +std::shared_ptr Module::GetAppletManager() const { + return applet_manager; +} + +std::shared_ptr GetModule(Core::System& system) { + auto apt = system.ServiceManager().GetService("APT:A"); + if (!apt) + return nullptr; + return apt->GetModule(); +} + void InstallInterfaces(Core::System& system) { auto& service_manager = system.ServiceManager(); auto apt = std::make_shared(system); diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index 5b9e7435b..abf2fc9ce 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -65,11 +65,15 @@ public: explicit Module(Core::System& system); ~Module(); + std::shared_ptr GetAppletManager() const; + class NSInterface : public ServiceFramework { public: NSInterface(std::shared_ptr apt, const char* name, u32 max_session); ~NSInterface(); + std::shared_ptr GetModule() const; + protected: std::shared_ptr apt; @@ -90,6 +94,8 @@ public: APTInterface(std::shared_ptr apt, const char* name, u32 max_session); ~APTInterface(); + std::shared_ptr GetModule() const; + protected: /** * APT::Initialize service function @@ -505,6 +511,23 @@ public: */ void GetProgramIdOnApplicationJump(Kernel::HLERequestContext& ctx); + /** + * APT::ReceiveDeliverArg service function + * Inputs: + * 0 : Command header [0x00350080] + * 1 : Parameter Size (capped to 0x300) + * 2 : HMAC Size (capped to 0x20) + * 64 : (Parameter Size << 14) | 2 + * 65 : Output buffer for Parameter + * 66 : (HMAC Size << 14) | 0x802 + * 67 : Output buffer for HMAC + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2-3 : Source program id + * 4 : u8, whether the arg is received (0 = not received, 1 = received) + */ + void ReceiveDeliverArg(Kernel::HLERequestContext& ctx); + /** * APT::CancelLibraryApplet service function * Inputs: @@ -725,6 +748,8 @@ private: friend class boost::serialization::access; }; +std::shared_ptr GetModule(Core::System& system); + void InstallInterfaces(Core::System& system); } // namespace Service::APT diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp index 4ddeae0aa..4f3a5f3d0 100644 --- a/src/core/hle/service/apt/apt_a.cpp +++ b/src/core/hle/service/apt/apt_a.cpp @@ -62,7 +62,7 @@ APT_A::APT_A(std::shared_ptr apt) {0x00320084, &APT_A::DoApplicationJump, "DoApplicationJump"}, {0x00330000, &APT_A::GetProgramIdOnApplicationJump, "GetProgramIdOnApplicationJump"}, {0x00340084, nullptr, "SendDeliverArg"}, - {0x00350080, nullptr, "ReceiveDeliverArg"}, + {0x00350080, &APT_A::ReceiveDeliverArg, "ReceiveDeliverArg"}, {0x00360040, nullptr, "LoadSysMenuArg"}, {0x00370042, nullptr, "StoreSysMenuArg"}, {0x00380040, nullptr, "PreloadResidentApplet"}, diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp index 0811544e7..43b821c39 100644 --- a/src/core/hle/service/apt/apt_u.cpp +++ b/src/core/hle/service/apt/apt_u.cpp @@ -62,7 +62,7 @@ APT_U::APT_U(std::shared_ptr apt) {0x00320084, &APT_U::DoApplicationJump, "DoApplicationJump"}, {0x00330000, &APT_U::GetProgramIdOnApplicationJump, "GetProgramIdOnApplicationJump"}, {0x00340084, nullptr, "SendDeliverArg"}, - {0x00350080, nullptr, "ReceiveDeliverArg"}, + {0x00350080, &APT_U::ReceiveDeliverArg, "ReceiveDeliverArg"}, {0x00360040, &APT_U::LoadSysMenuArg, "LoadSysMenuArg"}, {0x00370042, &APT_U::StoreSysMenuArg, "StoreSysMenuArg"}, {0x00380040, nullptr, "PreloadResidentApplet"},