mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	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
This commit is contained in:
		
							parent
							
								
									182ffa4243
								
							
						
					
					
						commit
						21fb9d63f4
					
				
					 7 changed files with 177 additions and 20 deletions
				
			
		|  | @ -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<Service::APT::AppletManager::DeliverArg> 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 <class Archive> | ||||
|  |  | |||
|  | @ -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<size_t>(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.
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,7 +5,9 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <array> | ||||
| #include <limits> | ||||
| #include <memory> | ||||
| #include <optional> | ||||
| #include <vector> | ||||
| #include <boost/serialization/array.hpp> | ||||
| #include <boost/serialization/optional.hpp> | ||||
|  | @ -158,7 +160,30 @@ public: | |||
| 
 | ||||
|     ResultCode PrepareToDoApplicationJump(u64 title_id, FS::MediaType media_type, | ||||
|                                           ApplicationJumpFlags flags); | ||||
|     ResultCode DoApplicationJump(); | ||||
| 
 | ||||
|     struct DeliverArg { | ||||
|         std::vector<u8> param; | ||||
|         std::vector<u8> hmac; | ||||
|         u64 source_program_id = std::numeric_limits<u64>::max(); | ||||
| 
 | ||||
|     private: | ||||
|         template <class Archive> | ||||
|         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<DeliverArg> ReceiveDeliverArg() const { | ||||
|         return deliver_arg; | ||||
|     } | ||||
|     void SetDeliverArg(boost::optional<DeliverArg> 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 <class Archive> | ||||
|         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<DeliverArg> deliver_arg{}; | ||||
| 
 | ||||
|     // Holds data about the concurrently running applets in the system.
 | ||||
|     std::array<AppletSlotData, NumAppletSlot> applet_slots = {}; | ||||
|  | @ -259,9 +289,12 @@ private: | |||
| 
 | ||||
| private: | ||||
|     template <class Archive> | ||||
|     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) | ||||
|  |  | |||
|  | @ -56,6 +56,10 @@ Module::NSInterface::NSInterface(std::shared_ptr<Module> apt, const char* name, | |||
| 
 | ||||
| Module::NSInterface::~NSInterface() = default; | ||||
| 
 | ||||
| std::shared_ptr<Module> 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<u32>(); | ||||
|  | @ -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<u32>(); | ||||
|     const auto hmac_size = rp.Pop<u32>(); | ||||
|     auto param_size = rp.Pop<u32>(); | ||||
|     auto hmac_size = rp.Pop<u32>(); | ||||
| 
 | ||||
|     [[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<u8>(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<u32>(); | ||||
|     const auto hmac_size = rp.Pop<u32>(); | ||||
| 
 | ||||
|     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<std::size_t>(hmac_size, 0x20)); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(4, 4); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push(arg.source_program_id); | ||||
|     rb.Push<u8>(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<u32>(); | ||||
|  | @ -809,6 +845,9 @@ void Module::APTInterface::GetStartupArgument(Kernel::HLERequestContext& ctx) { | |||
|     constexpr u32 max_parameter_size{0x1000}; | ||||
|     const auto startup_argument_type = static_cast<StartupArgumentType>(rp.Pop<u8>()); | ||||
| 
 | ||||
|     LOG_WARNING(Service_APT, "called, startup_argument_type={}, parameter_size={:#010X}", | ||||
|                 static_cast<u32>(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<u8> parameter(parameter_size); | ||||
|     std::vector<u8> param; | ||||
|     bool exists = false; | ||||
| 
 | ||||
|     LOG_WARNING(Service_APT, "(STUBBED) called, startup_argument_type={}, parameter_size={:#010X}", | ||||
|                 static_cast<u32>(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<u32>(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<Module> apt, const char* name | |||
| 
 | ||||
| Module::APTInterface::~APTInterface() = default; | ||||
| 
 | ||||
| std::shared_ptr<Module> Module::APTInterface::GetModule() const { | ||||
|     return apt; | ||||
| } | ||||
| 
 | ||||
| Module::Module(Core::System& system) : system(system) { | ||||
|     applet_manager = std::make_shared<AppletManager>(system); | ||||
| 
 | ||||
|  | @ -982,6 +1046,17 @@ Module::Module(Core::System& system) : system(system) { | |||
| 
 | ||||
| Module::~Module() {} | ||||
| 
 | ||||
| std::shared_ptr<AppletManager> Module::GetAppletManager() const { | ||||
|     return applet_manager; | ||||
| } | ||||
| 
 | ||||
| std::shared_ptr<Module> GetModule(Core::System& system) { | ||||
|     auto apt = system.ServiceManager().GetService<Service::APT::Module::APTInterface>("APT:A"); | ||||
|     if (!apt) | ||||
|         return nullptr; | ||||
|     return apt->GetModule(); | ||||
| } | ||||
| 
 | ||||
| void InstallInterfaces(Core::System& system) { | ||||
|     auto& service_manager = system.ServiceManager(); | ||||
|     auto apt = std::make_shared<Module>(system); | ||||
|  |  | |||
|  | @ -65,11 +65,15 @@ public: | |||
|     explicit Module(Core::System& system); | ||||
|     ~Module(); | ||||
| 
 | ||||
|     std::shared_ptr<AppletManager> GetAppletManager() const; | ||||
| 
 | ||||
|     class NSInterface : public ServiceFramework<NSInterface> { | ||||
|     public: | ||||
|         NSInterface(std::shared_ptr<Module> apt, const char* name, u32 max_session); | ||||
|         ~NSInterface(); | ||||
| 
 | ||||
|         std::shared_ptr<Module> GetModule() const; | ||||
| 
 | ||||
|     protected: | ||||
|         std::shared_ptr<Module> apt; | ||||
| 
 | ||||
|  | @ -90,6 +94,8 @@ public: | |||
|         APTInterface(std::shared_ptr<Module> apt, const char* name, u32 max_session); | ||||
|         ~APTInterface(); | ||||
| 
 | ||||
|         std::shared_ptr<Module> 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<Module> GetModule(Core::System& system); | ||||
| 
 | ||||
| void InstallInterfaces(Core::System& system); | ||||
| 
 | ||||
| } // namespace Service::APT
 | ||||
|  |  | |||
|  | @ -62,7 +62,7 @@ APT_A::APT_A(std::shared_ptr<Module> 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"}, | ||||
|  |  | |||
|  | @ -62,7 +62,7 @@ APT_U::APT_U(std::shared_ptr<Module> 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"}, | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue