Services/APT: Implement PrepareToStartApplication, StartApplication, and WakeupApplication (#6280)

* Services/APT: Implemented PrepareToStartApplication and StartApplication.

This allows games to be launched from the Home Menu, however, there is still a bug with the GSP where the Home Menu doesn't release the GPU rights. It is unknown if the Home Menu should terminate itself after launching a new application.

To get the Home Menu to not hang when launching stuff, you need to have config block 0xF0006 (size 40 flags 8) in your config savegame, it doesn't matter if it's filled with zeros.

* Services/APT: Implement WakeupApplication.

With this, the Home Menu is now able to launch games when using an LLE NIM imlementation.

* Services/APT: Reset the app_start_parameters after launching the application with StartApplication.

* Services/APT: Simplify the StartApplication code by directly calling WakeupApplication.

---------

Co-authored-by: Subv <subv2112@gmail.com>
This commit is contained in:
Steveice10 2023-02-11 22:47:08 -08:00 committed by GitHub
parent c2903a6b9d
commit 1ab9b60a60
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 167 additions and 31 deletions

View file

@ -283,8 +283,15 @@ ResultVal<AppletManager::InitializeResult> AppletManager::Initialize(AppletId ap
slot_data->title_id = system.Kernel().GetCurrentProcess()->codeset->program_id; slot_data->title_id = system.Kernel().GetCurrentProcess()->codeset->program_id;
slot_data->attributes.raw = attributes.raw; slot_data->attributes.raw = attributes.raw;
if (slot_data->applet_id == AppletId::Application || const auto* home_menu_slot = GetAppletSlotData(AppletId::HomeMenu);
slot_data->applet_id == AppletId::HomeMenu) {
// Applications need to receive a Wakeup signal to actually start up, this signal is usually
// sent by the Home Menu after starting the app by way of APT::WakeupApplication. In some cases
// such as when starting a game directly or the Home Menu itself, we have to send the signal
// ourselves since the Home Menu is not running yet. We detect if the Home Menu is running by
// checking if there's an applet registered in the HomeMenu slot.
if (slot_data->applet_id == AppletId::HomeMenu ||
(slot_data->applet_id == AppletId::Application && !home_menu_slot)) {
// Initialize the APT parameter to wake up the application. // Initialize the APT parameter to wake up the application.
next_parameter.emplace(); next_parameter.emplace();
next_parameter->signal = SignalType::Wakeup; next_parameter->signal = SignalType::Wakeup;
@ -310,6 +317,12 @@ ResultCode AppletManager::Enable(AppletAttributes attributes) {
slot_data->registered = true; slot_data->registered = true;
// Send any outstanding parameters to the newly-registered application
if (delayed_parameter && delayed_parameter->destination_id == slot_data->applet_id) {
CancelAndSendParameter(*delayed_parameter);
delayed_parameter.reset();
}
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
@ -580,6 +593,89 @@ ResultCode AppletManager::DoApplicationJump(DeliverArg arg) {
*/ */
} }
ResultCode AppletManager::PrepareToStartApplication(u64 title_id, FS::MediaType media_type) {
// TODO(Subv): This should check that the current applet is of System type and return 0xc8a0cc04
// if not.
// TODO(Subv): This should return 0xc8a0cff0 if the applet preparation state is already set
const auto& application_slot = applet_slots[static_cast<size_t>(AppletSlot::Application)];
if (application_slot.registered) {
// TODO(Subv): Convert this to the enum constructor of ResultCode
return ResultCode(0xc8a0cffc);
}
ASSERT_MSG(!app_start_parameters,
"Trying to prepare an application when another is already prepared");
app_start_parameters.emplace();
app_start_parameters->next_title_id = title_id;
app_start_parameters->next_media_type = media_type;
return RESULT_SUCCESS;
}
ResultCode AppletManager::StartApplication(const std::vector<u8>& parameter,
const std::vector<u8>& hmac, bool paused) {
// The delivery argument is always unconditionally set.
deliver_arg.emplace(DeliverArg{parameter, hmac});
// Note: APT first checks if we can launch the application via AM::CheckDemoLaunchRights and
// returns 0xc8a12403 if we can't. We intentionally do not implement that check.
// TODO(Subv): The APT service performs several checks here related to the exheader flags of the
// process we're launching and other things like title id blacklists. We do not yet implement
// any of that.
// TODO(Subv): The real APT service doesn't seem to check whether the titleid to launch is set
// or not, it either launches NATIVE_FIRM if some internal state is set, or fails when calling
// PM::LaunchTitle. We should research more about that.
ASSERT_MSG(app_start_parameters, "Trying to start an application without preparing it first.");
// Launch the title directly.
const auto process =
NS::LaunchTitle(app_start_parameters->next_media_type, app_start_parameters->next_title_id);
if (!process) {
LOG_CRITICAL(Service_APT, "Failed to launch title during application start, exiting.");
system.RequestShutdown();
}
app_start_parameters.reset();
if (!paused) {
return WakeupApplication();
}
return RESULT_SUCCESS;
}
ResultCode AppletManager::WakeupApplication() {
// Send a Wakeup signal via the apt parameter to the application once it registers itself.
// The real APT service does this by spinwaiting on another thread until the application is
// registered.
MessageParameter wakeup_parameter{};
wakeup_parameter.signal = SignalType::Wakeup;
wakeup_parameter.sender_id = AppletId::HomeMenu;
wakeup_parameter.destination_id = AppletId::Application;
SendApplicationParameterAfterRegistration(wakeup_parameter);
return RESULT_SUCCESS;
}
void AppletManager::SendApplicationParameterAfterRegistration(const MessageParameter& parameter) {
const auto* slot = GetAppletSlotData(AppletId::Application);
// If the application is already registered, immediately send the parameter
if (slot && slot->registered) {
CancelAndSendParameter(parameter);
return;
}
// Otherwise queue it until the Application calls APT::Enable
delayed_parameter = parameter;
}
void AppletManager::EnsureHomeMenuLoaded() { void AppletManager::EnsureHomeMenuLoaded() {
const auto& system_slot = applet_slots[static_cast<size_t>(AppletSlot::SystemApplet)]; const auto& system_slot = applet_slots[static_cast<size_t>(AppletSlot::SystemApplet)];
// TODO(Subv): The real APT service sends signal 12 (WakeupByCancel) to the currently running // TODO(Subv): The real APT service sends signal 12 (WakeupByCancel) to the currently running

View file

@ -185,6 +185,11 @@ public:
deliver_arg = std::move(arg); deliver_arg = std::move(arg);
} }
ResultCode PrepareToStartApplication(u64 title_id, FS::MediaType media_type);
ResultCode StartApplication(const std::vector<u8>& parameter, const std::vector<u8>& hmac,
bool paused);
ResultCode WakeupApplication();
struct AppletInfo { struct AppletInfo {
u64 title_id; u64 title_id;
Service::FS::MediaType media_type; Service::FS::MediaType media_type;
@ -221,11 +226,28 @@ public:
return app_jump_parameters; return app_jump_parameters;
} }
struct ApplicationStartParameters {
u64 next_title_id;
FS::MediaType next_media_type;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& next_title_id;
ar& next_media_type;
}
friend class boost::serialization::access;
};
private: private:
/// Parameter data to be returned in the next call to Glance/ReceiveParameter. /// Parameter data to be returned in the next call to Glance/ReceiveParameter.
// NOTE: A bug in gcc prevents serializing std::optional // NOTE: A bug in gcc prevents serializing std::optional
boost::optional<MessageParameter> next_parameter; boost::optional<MessageParameter> next_parameter;
/// This parameter will be sent to the application/applet once they register themselves by using
/// APT::Initialize.
boost::optional<MessageParameter> delayed_parameter;
static constexpr std::size_t NumAppletSlot = 4; static constexpr std::size_t NumAppletSlot = 4;
enum class AppletSlot : u8 { enum class AppletSlot : u8 {
@ -271,6 +293,7 @@ private:
}; };
ApplicationJumpParameters app_jump_parameters{}; ApplicationJumpParameters app_jump_parameters{};
boost::optional<ApplicationStartParameters> app_start_parameters{};
boost::optional<DeliverArg> deliver_arg{}; boost::optional<DeliverArg> deliver_arg{};
// Holds data about the concurrently running applets in the system. // Holds data about the concurrently running applets in the system.
@ -280,6 +303,10 @@ private:
AppletSlotData* GetAppletSlotData(AppletId id); AppletSlotData* GetAppletSlotData(AppletId id);
AppletSlotData* GetAppletSlotData(AppletAttributes attributes); AppletSlotData* GetAppletSlotData(AppletAttributes attributes);
/// Checks if the Application slot has already been registered and sends the parameter to it,
/// otherwise it queues for sending when the application registers itself with APT::Enable.
void SendApplicationParameterAfterRegistration(const MessageParameter& parameter);
void EnsureHomeMenuLoaded(); void EnsureHomeMenuLoaded();
// Command that will be sent to the application when a library applet calls CloseLibraryApplet. // Command that will be sent to the application when a library applet calls CloseLibraryApplet.
@ -293,6 +320,8 @@ private:
ar& next_parameter; ar& next_parameter;
ar& app_jump_parameters; ar& app_jump_parameters;
if (file_version > 0) { if (file_version > 0) {
ar& delayed_parameter;
ar& app_start_parameters;
ar& deliver_arg; ar& deliver_arg;
} }
ar& applet_slots; ar& applet_slots;

View file

@ -540,39 +540,40 @@ void Module::APTInterface::ReceiveDeliverArg(Kernel::HLERequestContext& ctx) {
void Module::APTInterface::PrepareToStartApplication(Kernel::HLERequestContext& ctx) { void Module::APTInterface::PrepareToStartApplication(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x15, 5, 0); // 0x00150140 IPC::RequestParser rp(ctx, 0x15, 5, 0); // 0x00150140
u32 title_info1 = rp.Pop<u32>(); const u64 title_id = rp.Pop<u64>();
u32 title_info2 = rp.Pop<u32>(); const auto media_type = rp.PopEnum<FS::MediaType>();
u32 title_info3 = rp.Pop<u32>(); rp.Skip(1, false); // Padding
u32 title_info4 = rp.Pop<u32>(); const u32 flags = rp.Pop<u32>();
u32 flags = rp.Pop<u32>();
if (flags & 0x00000100) {
apt->unknown_ns_state_field = 1;
}
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS); // No error rb.Push(apt->applet_manager->PrepareToStartApplication(title_id, media_type));
LOG_WARNING(Service_APT, LOG_INFO(Service_APT, "called title_id={:#010X} media_type={} flags={:#010X}", title_id,
"(STUBBED) called title_info1={:#010X}, title_info2={:#010X}, title_info3={:#010X}," media_type, flags);
"title_info4={:#010X}, flags={:#010X}",
title_info1, title_info2, title_info3, title_info4, flags);
} }
void Module::APTInterface::StartApplication(Kernel::HLERequestContext& ctx) { void Module::APTInterface::StartApplication(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x1B, 3, 4); // 0x001B00C4 IPC::RequestParser rp(ctx, 0x1B, 3, 4); // 0x001B00C4
const auto buffer1_size = rp.Pop<u32>(); const u32 parameter_size = rp.Pop<u32>();
const auto buffer2_size = rp.Pop<u32>(); const u32 hmac_size = rp.Pop<u32>();
const auto flag = rp.Pop<u32>(); const bool paused = rp.Pop<bool>();
[[maybe_unused]] const std::vector<u8> buffer1 = rp.PopStaticBuffer(); const std::vector<u8> parameter = rp.PopStaticBuffer();
[[maybe_unused]] const std::vector<u8> buffer2 = rp.PopStaticBuffer(); const std::vector<u8> hmac = rp.PopStaticBuffer();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS); // No error rb.Push(apt->applet_manager->StartApplication(parameter, hmac, paused));
LOG_WARNING(Service_APT, LOG_INFO(Service_APT, "called parameter_size={:#010X}, hmac_size={:#010X}, paused={}",
"(STUBBED) called buffer1_size={:#010X}, buffer2_size={:#010X}, flag={:#010X}", parameter_size, hmac_size, paused);
buffer1_size, buffer2_size, flag); }
void Module::APTInterface::WakeupApplication(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x1C, 0, 0); // 0x001C0000
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(apt->applet_manager->WakeupApplication());
LOG_DEBUG(Service_APT, "called");
} }
void Module::APTInterface::AppletUtility(Kernel::HLERequestContext& ctx) { void Module::APTInterface::AppletUtility(Kernel::HLERequestContext& ctx) {

View file

@ -361,6 +361,16 @@ public:
*/ */
void StartApplication(Kernel::HLERequestContext& ctx); void StartApplication(Kernel::HLERequestContext& ctx);
/**
* APT::WakeupApplication service function.
* Inputs:
* 0 : Command header [0x001C0000]
* Outputs:
* 0 : Return Header
* 1 : Result of function, 0 on success, otherwise error code
*/
void WakeupApplication(Kernel::HLERequestContext& ctx);
/** /**
* APT::AppletUtility service function * APT::AppletUtility service function
* Inputs: * Inputs:

View file

@ -36,8 +36,8 @@ APT_A::APT_A(std::shared_ptr<Module> apt)
{0x00180040, &APT_A::PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, {0x00180040, &APT_A::PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
{0x00190040, nullptr, "PrepareToStartSystemApplet"}, {0x00190040, nullptr, "PrepareToStartSystemApplet"},
{0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
{0x001B00C4, nullptr, "StartApplication"}, {0x001B00C4, &APT_A::StartApplication, "StartApplication"},
{0x001C0000, nullptr, "WakeupApplication"}, {0x001C0000, &APT_A::WakeupApplication, "WakeupApplication"},
{0x001D0000, nullptr, "CancelApplication"}, {0x001D0000, nullptr, "CancelApplication"},
{0x001E0084, &APT_A::StartLibraryApplet, "StartLibraryApplet"}, {0x001E0084, &APT_A::StartLibraryApplet, "StartLibraryApplet"},
{0x001F0084, nullptr, "StartSystemApplet"}, {0x001F0084, nullptr, "StartSystemApplet"},

View file

@ -36,8 +36,8 @@ APT_S::APT_S(std::shared_ptr<Module> apt)
{0x00180040, &APT_S::PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, {0x00180040, &APT_S::PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
{0x00190040, nullptr, "PrepareToStartSystemApplet"}, {0x00190040, nullptr, "PrepareToStartSystemApplet"},
{0x001A0000, &APT_S::PrepareToStartNewestHomeMenu, "PrepareToStartNewestHomeMenu"}, {0x001A0000, &APT_S::PrepareToStartNewestHomeMenu, "PrepareToStartNewestHomeMenu"},
{0x001B00C4, nullptr, "StartApplication"}, {0x001B00C4, &APT_S::StartApplication, "StartApplication"},
{0x001C0000, nullptr, "WakeupApplication"}, {0x001C0000, &APT_S::WakeupApplication, "WakeupApplication"},
{0x001D0000, nullptr, "CancelApplication"}, {0x001D0000, nullptr, "CancelApplication"},
{0x001E0084, &APT_S::StartLibraryApplet, "StartLibraryApplet"}, {0x001E0084, &APT_S::StartLibraryApplet, "StartLibraryApplet"},
{0x001F0084, nullptr, "StartSystemApplet"}, {0x001F0084, nullptr, "StartSystemApplet"},

View file

@ -36,8 +36,8 @@ APT_U::APT_U(std::shared_ptr<Module> apt)
{0x00180040, &APT_U::PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, {0x00180040, &APT_U::PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
{0x00190040, nullptr, "PrepareToStartSystemApplet"}, {0x00190040, nullptr, "PrepareToStartSystemApplet"},
{0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
{0x001B00C4, nullptr, "StartApplication"}, {0x001B00C4, &APT_U::StartApplication, "StartApplication"},
{0x001C0000, nullptr, "WakeupApplication"}, {0x001C0000, &APT_U::WakeupApplication, "WakeupApplication"},
{0x001D0000, nullptr, "CancelApplication"}, {0x001D0000, nullptr, "CancelApplication"},
{0x001E0084, &APT_U::StartLibraryApplet, "StartLibraryApplet"}, {0x001E0084, &APT_U::StartLibraryApplet, "StartLibraryApplet"},
{0x001F0084, nullptr, "StartSystemApplet"}, {0x001F0084, nullptr, "StartSystemApplet"},