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:
Pengfei Zhu 2020-11-25 03:10:31 +08:00 committed by GitHub
parent 182ffa4243
commit 21fb9d63f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 177 additions and 20 deletions

View File

@ -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>

View File

@ -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.

View File

@ -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)

View File

@ -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);

View File

@ -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

View File

@ -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"},

View File

@ -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"},