diff options
Diffstat (limited to 'src/core/hle/service/am')
-rw-r--r-- | src/core/hle/service/am/am_types.h | 171 | ||||
-rw-r--r-- | src/core/hle/service/am/applet.cpp | 63 | ||||
-rw-r--r-- | src/core/hle/service/am/applet.h | 164 | ||||
-rw-r--r-- | src/core/hle/service/am/applet_manager.cpp | 352 | ||||
-rw-r--r-- | src/core/hle/service/am/applet_manager.h | 59 | ||||
-rw-r--r-- | src/core/hle/service/am/hid_registration.cpp | 29 | ||||
-rw-r--r-- | src/core/hle/service/am/hid_registration.h | 30 | ||||
-rw-r--r-- | src/core/hle/service/am/library_applet_storage.cpp | 140 | ||||
-rw-r--r-- | src/core/hle/service/am/library_applet_storage.h | 36 | ||||
-rw-r--r-- | src/core/hle/service/am/managed_layer_holder.cpp | 59 | ||||
-rw-r--r-- | src/core/hle/service/am/managed_layer_holder.h | 32 | ||||
-rw-r--r-- | src/core/hle/service/am/process.cpp | 138 | ||||
-rw-r--r-- | src/core/hle/service/am/process.h | 50 | ||||
-rw-r--r-- | src/core/hle/service/am/system_buffer_manager.cpp | 49 | ||||
-rw-r--r-- | src/core/hle/service/am/system_buffer_manager.h | 44 |
15 files changed, 1416 insertions, 0 deletions
diff --git a/src/core/hle/service/am/am_types.h b/src/core/hle/service/am/am_types.h new file mode 100644 index 000000000..d0a237a7e --- /dev/null +++ b/src/core/hle/service/am/am_types.h @@ -0,0 +1,171 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace Service::AM { + +namespace Frontend { +class FrontendApplet; +} + +enum class AppletType { + Application, + LibraryApplet, + SystemApplet, +}; + +enum class GameplayRecordingState : u32 { + Disabled, + Enabled, +}; + +// This is nn::oe::FocusState +enum class FocusState : u8 { + InFocus = 1, + NotInFocus = 2, + Background = 3, +}; + +// This is nn::oe::OperationMode +enum class OperationMode : u8 { + Handheld = 0, + Docked = 1, +}; + +// This is nn::am::service::SystemButtonType +enum class SystemButtonType { + None, + HomeButtonShortPressing, + HomeButtonLongPressing, + PowerButtonShortPressing, + PowerButtonLongPressing, + ShutdownSystem, + CaptureButtonShortPressing, + CaptureButtonLongPressing, +}; + +enum class SysPlatformRegion : s32 { + Global = 1, + Terra = 2, +}; + +struct AppletProcessLaunchReason { + u8 flag; + INSERT_PADDING_BYTES(3); +}; +static_assert(sizeof(AppletProcessLaunchReason) == 0x4, + "AppletProcessLaunchReason is an invalid size"); + +enum class ScreenshotPermission : u32 { + Inherit = 0, + Enable = 1, + Disable = 2, +}; + +struct FocusHandlingMode { + bool unknown0; + bool unknown1; + bool unknown2; + bool unknown3; +}; + +enum class IdleTimeDetectionExtension : u32 { + Disabled = 0, + Extended = 1, + ExtendedUnsafe = 2, +}; + +enum class AppletId : u32 { + None = 0x00, + Application = 0x01, + OverlayDisplay = 0x02, + QLaunch = 0x03, + Starter = 0x04, + Auth = 0x0A, + Cabinet = 0x0B, + Controller = 0x0C, + DataErase = 0x0D, + Error = 0x0E, + NetConnect = 0x0F, + ProfileSelect = 0x10, + SoftwareKeyboard = 0x11, + MiiEdit = 0x12, + Web = 0x13, + Shop = 0x14, + PhotoViewer = 0x15, + Settings = 0x16, + OfflineWeb = 0x17, + LoginShare = 0x18, + WebAuth = 0x19, + MyPage = 0x1A, +}; + +enum class AppletProgramId : u64 { + QLaunch = 0x0100000000001000ull, + Auth = 0x0100000000001001ull, + Cabinet = 0x0100000000001002ull, + Controller = 0x0100000000001003ull, + DataErase = 0x0100000000001004ull, + Error = 0x0100000000001005ull, + NetConnect = 0x0100000000001006ull, + ProfileSelect = 0x0100000000001007ull, + SoftwareKeyboard = 0x0100000000001008ull, + MiiEdit = 0x0100000000001009ull, + Web = 0x010000000000100Aull, + Shop = 0x010000000000100Bull, + OverlayDisplay = 0x010000000000100Cull, + PhotoViewer = 0x010000000000100Dull, + Settings = 0x010000000000100Eull, + OfflineWeb = 0x010000000000100Full, + LoginShare = 0x0100000000001010ull, + WebAuth = 0x0100000000001011ull, + Starter = 0x0100000000001012ull, + MyPage = 0x0100000000001013ull, + MaxProgramId = 0x0100000000001FFFull, +}; + +enum class LibraryAppletMode : u32 { + AllForeground = 0, + Background = 1, + NoUI = 2, + BackgroundIndirectDisplay = 3, + AllForegroundInitiallyHidden = 4, +}; + +enum class CommonArgumentVersion : u32 { + Version0, + Version1, + Version2, + Version3, +}; + +enum class CommonArgumentSize : u32 { + Version3 = 0x20, +}; + +enum class ThemeColor : u32 { + BasicWhite = 0, + BasicBlack = 3, +}; + +struct CommonArguments { + CommonArgumentVersion arguments_version; + CommonArgumentSize size; + u32 library_version; + ThemeColor theme_color; + bool play_startup_sound; + u64 system_tick; +}; +static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size."); + +using AppletResourceUserId = u64; +using ProgramId = u64; + +struct Applet; +struct AppletStorageHolder; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/applet.cpp b/src/core/hle/service/am/applet.cpp new file mode 100644 index 000000000..8f44fab33 --- /dev/null +++ b/src/core/hle/service/am/applet.cpp @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/scope_exit.h" + +#include "core/hle/service/am/am_results.h" +#include "core/hle/service/am/applet.h" + +namespace Service::AM { + +AppletStorageChannel::AppletStorageChannel(KernelHelpers::ServiceContext& context) + : m_event(context) {} +AppletStorageChannel::~AppletStorageChannel() = default; + +void AppletStorageChannel::PushData(std::shared_ptr<IStorage> storage) { + std::scoped_lock lk{m_lock}; + + m_data.emplace_back(std::move(storage)); + m_event.Signal(); +} + +Result AppletStorageChannel::PopData(std::shared_ptr<IStorage>* out_storage) { + std::scoped_lock lk{m_lock}; + + SCOPE_EXIT({ + if (m_data.empty()) { + m_event.Clear(); + } + }); + + R_UNLESS(!m_data.empty(), AM::ResultNoDataInChannel); + + *out_storage = std::move(m_data.front()); + m_data.pop_front(); + + R_SUCCEED(); +} + +Kernel::KReadableEvent* AppletStorageChannel::GetEvent() { + return m_event.GetHandle(); +} + +AppletStorageHolder::AppletStorageHolder(Core::System& system) + : context(system, "AppletStorageHolder"), in_data(context), interactive_in_data(context), + out_data(context), interactive_out_data(context), state_changed_event(context) {} + +AppletStorageHolder::~AppletStorageHolder() = default; + +Applet::Applet(Core::System& system, std::unique_ptr<Process> process_) + : context(system, "Applet"), message_queue(system), process(std::move(process_)), + hid_registration(system, *process), gpu_error_detected_event(context), + friend_invitation_storage_channel_event(context), notification_storage_channel_event(context), + health_warning_disappeared_system_event(context), acquired_sleep_lock_event(context), + pop_from_general_channel_event(context), library_applet_launchable_event(context), + accumulated_suspended_tick_changed_event(context), sleep_lock_event(context) { + + aruid = process->GetProcessId(); + program_id = process->GetProgramId(); +} + +Applet::~Applet() = default; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/applet.h b/src/core/hle/service/am/applet.h new file mode 100644 index 000000000..9650a2615 --- /dev/null +++ b/src/core/hle/service/am/applet.h @@ -0,0 +1,164 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <list> +#include <mutex> + +#include "common/math_util.h" +#include "core/hle/service/apm/apm_controller.h" +#include "core/hle/service/caps/caps_types.h" +#include "core/hle/service/event.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +#include "core/hle/service/am/am_types.h" +#include "core/hle/service/am/applet_message_queue.h" +#include "core/hle/service/am/hid_registration.h" +#include "core/hle/service/am/managed_layer_holder.h" +#include "core/hle/service/am/process.h" +#include "core/hle/service/am/storage.h" +#include "core/hle/service/am/system_buffer_manager.h" + +namespace Service::Nvnflinger { +class FbShareBufferManager; +class Nvnflinger; +} // namespace Service::Nvnflinger + +namespace Service::AM { + +class AppletStorageChannel { +public: + explicit AppletStorageChannel(KernelHelpers::ServiceContext& ctx); + ~AppletStorageChannel(); + + void PushData(std::shared_ptr<IStorage> storage); + Result PopData(std::shared_ptr<IStorage>* out_storage); + Kernel::KReadableEvent* GetEvent(); + +private: + std::mutex m_lock{}; + std::deque<std::shared_ptr<IStorage>> m_data{}; + Event m_event; +}; + +struct AppletStorageHolder { + explicit AppletStorageHolder(Core::System& system); + ~AppletStorageHolder(); + + KernelHelpers::ServiceContext context; + + AppletStorageChannel in_data; + AppletStorageChannel interactive_in_data; + AppletStorageChannel out_data; + AppletStorageChannel interactive_out_data; + Event state_changed_event; +}; + +struct Applet { + explicit Applet(Core::System& system, std::unique_ptr<Process> process_); + ~Applet(); + + // Lock + std::mutex lock{}; + + // Event creation helper + KernelHelpers::ServiceContext context; + + // Applet message queue + AppletMessageQueue message_queue; + + // Process + std::unique_ptr<Process> process; + + // Creation state + AppletId applet_id{}; + AppletResourceUserId aruid{}; + AppletProcessLaunchReason launch_reason{}; + AppletType type{}; + ProgramId program_id{}; + LibraryAppletMode library_applet_mode{}; + s32 previous_program_index{-1}; + ScreenshotPermission previous_screenshot_permission{ScreenshotPermission::Enable}; + + // hid state + HidRegistration hid_registration; + + // vi state + SystemBufferManager system_buffer_manager{}; + ManagedLayerHolder managed_layer_holder{}; + + // Applet common functions + Result terminate_result{}; + s32 display_logical_width{}; + s32 display_logical_height{}; + Common::Rectangle<f32> display_magnification{0, 0, 1, 1}; + bool home_button_double_click_enabled{}; + bool home_button_short_pressed_blocked{}; + bool home_button_long_pressed_blocked{}; + bool vr_mode_curtain_required{}; + bool sleep_required_by_high_temperature{}; + bool sleep_required_by_low_battery{}; + s32 cpu_boost_request_priority{-1}; + bool handling_capture_button_short_pressed_message_enabled_for_applet{}; + bool handling_capture_button_long_pressed_message_enabled_for_applet{}; + u32 application_core_usage_mode{}; + + // Application functions + bool gameplay_recording_supported{}; + GameplayRecordingState gameplay_recording_state{GameplayRecordingState::Disabled}; + bool jit_service_launched{}; + bool is_running{}; + bool application_crash_report_enabled{}; + + // Common state + FocusState focus_state{}; + bool sleep_lock_enabled{}; + bool vr_mode_enabled{}; + bool lcd_backlight_off_enabled{}; + APM::CpuBoostMode boost_mode{}; + bool request_exit_to_library_applet_at_execute_next_program_enabled{}; + + // Channels + std::deque<std::vector<u8>> user_channel_launch_parameter{}; + std::deque<std::vector<u8>> preselected_user_launch_parameter{}; + + // Caller applet + std::weak_ptr<Applet> caller_applet{}; + std::shared_ptr<AppletStorageHolder> caller_applet_storage{}; + bool is_completed{}; + + // Self state + bool exit_locked{}; + s32 fatal_section_count{}; + bool operation_mode_changed_notification_enabled{true}; + bool performance_mode_changed_notification_enabled{true}; + FocusHandlingMode focus_handling_mode{}; + bool restart_message_enabled{}; + bool out_of_focus_suspension_enabled{true}; + Capture::AlbumImageOrientation album_image_orientation{}; + bool handles_request_to_display{}; + ScreenshotPermission screenshot_permission{}; + IdleTimeDetectionExtension idle_time_detection_extension{}; + bool auto_sleep_disabled{}; + u64 suspended_ticks{}; + bool album_image_taken_notification_enabled{}; + bool record_volume_muted{}; + + // Events + Event gpu_error_detected_event; + Event friend_invitation_storage_channel_event; + Event notification_storage_channel_event; + Event health_warning_disappeared_system_event; + Event acquired_sleep_lock_event; + Event pop_from_general_channel_event; + Event library_applet_launchable_event; + Event accumulated_suspended_tick_changed_event; + Event sleep_lock_event; + + // Frontend state + std::shared_ptr<Frontend::FrontendApplet> frontend{}; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/applet_manager.cpp b/src/core/hle/service/am/applet_manager.cpp new file mode 100644 index 000000000..9f7ccfbf2 --- /dev/null +++ b/src/core/hle/service/am/applet_manager.cpp @@ -0,0 +1,352 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/settings.h" +#include "common/uuid.h" +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/service/acc/profile_manager.h" +#include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/am/applets/applet_cabinet.h" +#include "core/hle/service/am/applets/applet_controller.h" +#include "core/hle/service/am/applets/applet_mii_edit_types.h" +#include "core/hle/service/am/applets/applet_software_keyboard_types.h" +#include "hid_core/hid_types.h" + +namespace Service::AM { + +namespace { + +constexpr u32 LaunchParameterAccountPreselectedUserMagic = 0xC79497CA; + +struct LaunchParameterAccountPreselectedUser { + u32 magic; + u32 is_account_selected; + Common::UUID current_user; + INSERT_PADDING_BYTES(0x70); +}; +static_assert(sizeof(LaunchParameterAccountPreselectedUser) == 0x88); + +AppletStorageChannel& InitializeFakeCallerApplet(Core::System& system, + std::shared_ptr<Applet>& applet) { + applet->caller_applet_storage = std::make_shared<AppletStorageHolder>(system); + return applet->caller_applet_storage->in_data; +} + +void PushInShowAlbum(Core::System& system, AppletStorageChannel& channel) { + const CommonArguments arguments{ + .arguments_version = CommonArgumentVersion::Version3, + .size = CommonArgumentSize::Version3, + .library_version = 1, + .theme_color = ThemeColor::BasicBlack, + .play_startup_sound = true, + .system_tick = system.CoreTiming().GetClockTicks(), + }; + + std::vector<u8> argument_data(sizeof(arguments)); + std::vector<u8> settings_data{2}; + std::memcpy(argument_data.data(), &arguments, sizeof(arguments)); + channel.PushData(std::make_shared<IStorage>(system, std::move(argument_data))); + channel.PushData(std::make_shared<IStorage>(system, std::move(settings_data))); +} + +void PushInShowController(Core::System& system, AppletStorageChannel& channel) { + const CommonArguments common_args = { + .arguments_version = CommonArgumentVersion::Version3, + .size = CommonArgumentSize::Version3, + .library_version = static_cast<u32>(Applets::ControllerAppletVersion::Version8), + .theme_color = ThemeColor::BasicBlack, + .play_startup_sound = true, + .system_tick = system.CoreTiming().GetClockTicks(), + }; + + Applets::ControllerSupportArgNew user_args = { + .header = {.player_count_min = 1, + .player_count_max = 4, + .enable_take_over_connection = true, + .enable_left_justify = false, + .enable_permit_joy_dual = true, + .enable_single_mode = false, + .enable_identification_color = false}, + .identification_colors = {}, + .enable_explain_text = false, + .explain_text = {}, + }; + + Applets::ControllerSupportArgPrivate private_args = { + .arg_private_size = sizeof(Applets::ControllerSupportArgPrivate), + .arg_size = sizeof(Applets::ControllerSupportArgNew), + .is_home_menu = true, + .flag_1 = true, + .mode = Applets::ControllerSupportMode::ShowControllerSupport, + .caller = Applets::ControllerSupportCaller:: + Application, // switchbrew: Always zero except with + // ShowControllerFirmwareUpdateForSystem/ShowControllerKeyRemappingForSystem, + // which sets this to the input param + .style_set = Core::HID::NpadStyleSet::None, + .joy_hold_type = 0, + }; + std::vector<u8> common_args_data(sizeof(common_args)); + std::vector<u8> private_args_data(sizeof(private_args)); + std::vector<u8> user_args_data(sizeof(user_args)); + + std::memcpy(common_args_data.data(), &common_args, sizeof(common_args)); + std::memcpy(private_args_data.data(), &private_args, sizeof(private_args)); + std::memcpy(user_args_data.data(), &user_args, sizeof(user_args)); + + channel.PushData(std::make_shared<IStorage>(system, std::move(common_args_data))); + channel.PushData(std::make_shared<IStorage>(system, std::move(private_args_data))); + channel.PushData(std::make_shared<IStorage>(system, std::move(user_args_data))); +} + +void PushInShowCabinetData(Core::System& system, AppletStorageChannel& channel) { + const CommonArguments arguments{ + .arguments_version = CommonArgumentVersion::Version3, + .size = CommonArgumentSize::Version3, + .library_version = static_cast<u32>(Applets::CabinetAppletVersion::Version1), + .theme_color = ThemeColor::BasicBlack, + .play_startup_sound = true, + .system_tick = system.CoreTiming().GetClockTicks(), + }; + + const Applets::StartParamForAmiiboSettings amiibo_settings{ + .param_1 = 0, + .applet_mode = system.GetAppletManager().GetCabinetMode(), + .flags = Applets::CabinetFlags::None, + .amiibo_settings_1 = 0, + .device_handle = 0, + .tag_info{}, + .register_info{}, + .amiibo_settings_3{}, + }; + + std::vector<u8> argument_data(sizeof(arguments)); + std::vector<u8> settings_data(sizeof(amiibo_settings)); + std::memcpy(argument_data.data(), &arguments, sizeof(arguments)); + std::memcpy(settings_data.data(), &amiibo_settings, sizeof(amiibo_settings)); + channel.PushData(std::make_shared<IStorage>(system, std::move(argument_data))); + channel.PushData(std::make_shared<IStorage>(system, std::move(settings_data))); +} + +void PushInShowMiiEditData(Core::System& system, AppletStorageChannel& channel) { + struct MiiEditV3 { + Applets::MiiEditAppletInputCommon common; + Applets::MiiEditAppletInputV3 input; + }; + static_assert(sizeof(MiiEditV3) == 0x100, "MiiEditV3 has incorrect size."); + + MiiEditV3 mii_arguments{ + .common = + { + .version = Applets::MiiEditAppletVersion::Version3, + .applet_mode = Applets::MiiEditAppletMode::ShowMiiEdit, + }, + .input{}, + }; + + std::vector<u8> argument_data(sizeof(mii_arguments)); + std::memcpy(argument_data.data(), &mii_arguments, sizeof(mii_arguments)); + + channel.PushData(std::make_shared<IStorage>(system, std::move(argument_data))); +} + +void PushInShowSoftwareKeyboard(Core::System& system, AppletStorageChannel& channel) { + const CommonArguments arguments{ + .arguments_version = CommonArgumentVersion::Version3, + .size = CommonArgumentSize::Version3, + .library_version = static_cast<u32>(Applets::SwkbdAppletVersion::Version524301), + .theme_color = ThemeColor::BasicBlack, + .play_startup_sound = true, + .system_tick = system.CoreTiming().GetClockTicks(), + }; + + std::vector<char16_t> initial_string(0); + + const Applets::SwkbdConfigCommon swkbd_config{ + .type = Applets::SwkbdType::Qwerty, + .ok_text{}, + .left_optional_symbol_key{}, + .right_optional_symbol_key{}, + .use_prediction = false, + .key_disable_flags{}, + .initial_cursor_position = Applets::SwkbdInitialCursorPosition::Start, + .header_text{}, + .sub_text{}, + .guide_text{}, + .max_text_length = 500, + .min_text_length = 0, + .password_mode = Applets::SwkbdPasswordMode::Disabled, + .text_draw_type = Applets::SwkbdTextDrawType::Box, + .enable_return_button = true, + .use_utf8 = false, + .use_blur_background = true, + .initial_string_offset{}, + .initial_string_length = static_cast<u32>(initial_string.size()), + .user_dictionary_offset{}, + .user_dictionary_entries{}, + .use_text_check = false, + }; + + Applets::SwkbdConfigNew swkbd_config_new{}; + + std::vector<u8> argument_data(sizeof(arguments)); + std::vector<u8> swkbd_data(sizeof(swkbd_config) + sizeof(swkbd_config_new)); + std::vector<u8> work_buffer(swkbd_config.initial_string_length * sizeof(char16_t)); + + std::memcpy(argument_data.data(), &arguments, sizeof(arguments)); + std::memcpy(swkbd_data.data(), &swkbd_config, sizeof(swkbd_config)); + std::memcpy(swkbd_data.data() + sizeof(swkbd_config), &swkbd_config_new, + sizeof(Applets::SwkbdConfigNew)); + std::memcpy(work_buffer.data(), initial_string.data(), + swkbd_config.initial_string_length * sizeof(char16_t)); + + channel.PushData(std::make_shared<IStorage>(system, std::move(argument_data))); + channel.PushData(std::make_shared<IStorage>(system, std::move(swkbd_data))); + channel.PushData(std::make_shared<IStorage>(system, std::move(work_buffer))); +} + +} // namespace + +AppletManager::AppletManager(Core::System& system) : m_system(system) {} +AppletManager::~AppletManager() { + this->Reset(); +} + +void AppletManager::InsertApplet(std::shared_ptr<Applet> applet) { + std::scoped_lock lk{m_lock}; + + m_applets.emplace(applet->aruid, std::move(applet)); +} + +void AppletManager::TerminateAndRemoveApplet(AppletResourceUserId aruid) { + std::shared_ptr<Applet> applet; + { + std::scoped_lock lk{m_lock}; + + const auto it = m_applets.find(aruid); + if (it == m_applets.end()) { + return; + } + + applet = it->second; + m_applets.erase(it); + } + + // Terminate process. + applet->process->Terminate(); +} + +void AppletManager::CreateAndInsertByFrontendAppletParameters( + AppletResourceUserId aruid, const FrontendAppletParameters& params) { + // TODO: this should be run inside AM so that the events will have a parent process + // TODO: have am create the guest process + auto applet = std::make_shared<Applet>(m_system, std::make_unique<Process>(m_system)); + + applet->aruid = aruid; + applet->program_id = params.program_id; + applet->applet_id = params.applet_id; + applet->type = params.applet_type; + applet->previous_program_index = params.previous_program_index; + + // Push UserChannel data from previous application + if (params.launch_type == LaunchType::ApplicationInitiated) { + applet->user_channel_launch_parameter.swap(m_system.GetUserChannel()); + } + + // TODO: Read whether we need a preselected user from NACP? + // TODO: This can be done quite easily from loader + { + LaunchParameterAccountPreselectedUser lp{}; + + lp.magic = LaunchParameterAccountPreselectedUserMagic; + lp.is_account_selected = 1; + + Account::ProfileManager profile_manager{}; + const auto uuid = profile_manager.GetUser(static_cast<s32>(Settings::values.current_user)); + ASSERT(uuid.has_value() && uuid->IsValid()); + lp.current_user = *uuid; + + std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser)); + std::memcpy(buffer.data(), &lp, buffer.size()); + + applet->preselected_user_launch_parameter.push_back(std::move(buffer)); + } + + // Starting from frontend, some applets require input data. + switch (applet->applet_id) { + case AppletId::Cabinet: + PushInShowCabinetData(m_system, InitializeFakeCallerApplet(m_system, applet)); + break; + case AppletId::MiiEdit: + PushInShowMiiEditData(m_system, InitializeFakeCallerApplet(m_system, applet)); + break; + case AppletId::PhotoViewer: + PushInShowAlbum(m_system, InitializeFakeCallerApplet(m_system, applet)); + break; + case AppletId::SoftwareKeyboard: + PushInShowSoftwareKeyboard(m_system, InitializeFakeCallerApplet(m_system, applet)); + break; + case AppletId::Controller: + PushInShowController(m_system, InitializeFakeCallerApplet(m_system, applet)); + break; + default: + break; + } + + // Applet was started by frontend, so it is foreground. + applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); + applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); + applet->focus_state = FocusState::InFocus; + + this->InsertApplet(std::move(applet)); +} + +std::shared_ptr<Applet> AppletManager::GetByAppletResourceUserId(AppletResourceUserId aruid) const { + std::scoped_lock lk{m_lock}; + + if (const auto it = m_applets.find(aruid); it != m_applets.end()) { + return it->second; + } + + return {}; +} + +void AppletManager::Reset() { + std::scoped_lock lk{m_lock}; + + m_applets.clear(); +} + +void AppletManager::RequestExit() { + std::scoped_lock lk{m_lock}; + + for (const auto& [aruid, applet] : m_applets) { + applet->message_queue.RequestExit(); + } +} + +void AppletManager::RequestResume() { + std::scoped_lock lk{m_lock}; + + for (const auto& [aruid, applet] : m_applets) { + applet->message_queue.RequestResume(); + } +} + +void AppletManager::OperationModeChanged() { + std::scoped_lock lk{m_lock}; + + for (const auto& [aruid, applet] : m_applets) { + applet->message_queue.OperationModeChanged(); + } +} + +void AppletManager::FocusStateChanged() { + std::scoped_lock lk{m_lock}; + + for (const auto& [aruid, applet] : m_applets) { + applet->message_queue.FocusStateChanged(); + } +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/applet_manager.h b/src/core/hle/service/am/applet_manager.h new file mode 100644 index 000000000..4875de309 --- /dev/null +++ b/src/core/hle/service/am/applet_manager.h @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <map> +#include <mutex> + +#include "core/hle/service/am/applet.h" + +namespace Core { +class System; +} + +namespace Service::AM { + +enum class LaunchType { + FrontendInitiated, + ApplicationInitiated, +}; + +struct FrontendAppletParameters { + ProgramId program_id{}; + AppletId applet_id{}; + AppletType applet_type{}; + LaunchType launch_type{}; + s32 program_index{}; + s32 previous_program_index{-1}; +}; + +class AppletManager { +public: + explicit AppletManager(Core::System& system); + ~AppletManager(); + + void InsertApplet(std::shared_ptr<Applet> applet); + void TerminateAndRemoveApplet(AppletResourceUserId aruid); + + void CreateAndInsertByFrontendAppletParameters(AppletResourceUserId aruid, + const FrontendAppletParameters& params); + std::shared_ptr<Applet> GetByAppletResourceUserId(AppletResourceUserId aruid) const; + + void Reset(); + + void RequestExit(); + void RequestResume(); + void OperationModeChanged(); + void FocusStateChanged(); + +private: + Core::System& m_system; + + mutable std::mutex m_lock{}; + std::map<AppletResourceUserId, std::shared_ptr<Applet>> m_applets{}; + + // AudioController state goes here +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/hid_registration.cpp b/src/core/hle/service/am/hid_registration.cpp new file mode 100644 index 000000000..b9426f7b6 --- /dev/null +++ b/src/core/hle/service/am/hid_registration.cpp @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/am/hid_registration.h" +#include "core/hle/service/am/process.h" +#include "core/hle/service/hid/hid_server.h" +#include "core/hle/service/sm/sm.h" +#include "hid_core/resource_manager.h" + +namespace Service::AM { + +HidRegistration::HidRegistration(Core::System& system, Process& process) : m_process(process) { + m_hid_server = system.ServiceManager().GetService<HID::IHidServer>("hid"); + + if (m_process.IsInitialized()) { + m_hid_server->GetResourceManager()->RegisterAppletResourceUserId(m_process.GetProcessId(), + true); + } +} + +HidRegistration::~HidRegistration() { + if (m_process.IsInitialized()) { + m_hid_server->GetResourceManager()->UnregisterAppletResourceUserId( + m_process.GetProcessId()); + } +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/hid_registration.h b/src/core/hle/service/am/hid_registration.h new file mode 100644 index 000000000..8a732349c --- /dev/null +++ b/src/core/hle/service/am/hid_registration.h @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> + +namespace Core { +class System; +} + +namespace Service::HID { +class IHidServer; +} + +namespace Service::AM { + +class Process; + +class HidRegistration { +public: + explicit HidRegistration(Core::System& system, Process& process); + ~HidRegistration(); + +private: + Process& m_process; + std::shared_ptr<Service::HID::IHidServer> m_hid_server; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/library_applet_storage.cpp b/src/core/hle/service/am/library_applet_storage.cpp new file mode 100644 index 000000000..46e6c0111 --- /dev/null +++ b/src/core/hle/service/am/library_applet_storage.cpp @@ -0,0 +1,140 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/k_transfer_memory.h" +#include "core/hle/service/am/am_results.h" +#include "core/hle/service/am/library_applet_storage.h" +#include "core/memory.h" + +namespace Service::AM { + +namespace { + +Result ValidateOffset(s64 offset, size_t size, size_t data_size) { + R_UNLESS(offset >= 0, AM::ResultInvalidOffset); + + const size_t begin = offset; + const size_t end = begin + size; + + R_UNLESS(begin <= end && end <= data_size, AM::ResultInvalidOffset); + R_SUCCEED(); +} + +class BufferLibraryAppletStorage final : public LibraryAppletStorage { +public: + explicit BufferLibraryAppletStorage(std::vector<u8>&& data) : m_data(std::move(data)) {} + ~BufferLibraryAppletStorage() = default; + + Result Read(s64 offset, void* buffer, size_t size) override { + R_TRY(ValidateOffset(offset, size, m_data.size())); + + std::memcpy(buffer, m_data.data() + offset, size); + + R_SUCCEED(); + } + + Result Write(s64 offset, const void* buffer, size_t size) override { + R_TRY(ValidateOffset(offset, size, m_data.size())); + + std::memcpy(m_data.data() + offset, buffer, size); + + R_SUCCEED(); + } + + s64 GetSize() override { + return m_data.size(); + } + + Kernel::KTransferMemory* GetHandle() override { + return nullptr; + } + +private: + std::vector<u8> m_data; +}; + +class TransferMemoryLibraryAppletStorage : public LibraryAppletStorage { +public: + explicit TransferMemoryLibraryAppletStorage(Core::Memory::Memory& memory, + Kernel::KTransferMemory* trmem, bool is_writable, + s64 size) + : m_memory(memory), m_trmem(trmem), m_is_writable(is_writable), m_size(size) { + m_trmem->Open(); + } + + ~TransferMemoryLibraryAppletStorage() { + m_trmem->Close(); + m_trmem = nullptr; + } + + Result Read(s64 offset, void* buffer, size_t size) override { + R_TRY(ValidateOffset(offset, size, m_size)); + + m_memory.ReadBlock(m_trmem->GetSourceAddress(), buffer, size); + + R_SUCCEED(); + } + + Result Write(s64 offset, const void* buffer, size_t size) override { + R_UNLESS(m_is_writable, ResultUnknown); + R_TRY(ValidateOffset(offset, size, m_size)); + + m_memory.WriteBlock(m_trmem->GetSourceAddress(), buffer, size); + + R_SUCCEED(); + } + + s64 GetSize() override { + return m_size; + } + + Kernel::KTransferMemory* GetHandle() override { + return nullptr; + } + +protected: + Core::Memory::Memory& m_memory; + Kernel::KTransferMemory* m_trmem; + bool m_is_writable; + s64 m_size; +}; + +class HandleLibraryAppletStorage : public TransferMemoryLibraryAppletStorage { +public: + explicit HandleLibraryAppletStorage(Core::Memory::Memory& memory, + Kernel::KTransferMemory* trmem, s64 size) + : TransferMemoryLibraryAppletStorage(memory, trmem, true, size) {} + ~HandleLibraryAppletStorage() = default; + + Kernel::KTransferMemory* GetHandle() override { + return m_trmem; + } +}; + +} // namespace + +LibraryAppletStorage::~LibraryAppletStorage() = default; + +std::vector<u8> LibraryAppletStorage::GetData() { + std::vector<u8> data(this->GetSize()); + this->Read(0, data.data(), data.size()); + return data; +} + +std::shared_ptr<LibraryAppletStorage> CreateStorage(std::vector<u8>&& data) { + return std::make_shared<BufferLibraryAppletStorage>(std::move(data)); +} + +std::shared_ptr<LibraryAppletStorage> CreateTransferMemoryStorage(Core::Memory::Memory& memory, + Kernel::KTransferMemory* trmem, + bool is_writable, s64 size) { + return std::make_shared<TransferMemoryLibraryAppletStorage>(memory, trmem, is_writable, size); +} + +std::shared_ptr<LibraryAppletStorage> CreateHandleStorage(Core::Memory::Memory& memory, + Kernel::KTransferMemory* trmem, + s64 size) { + return std::make_shared<HandleLibraryAppletStorage>(memory, trmem, size); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/library_applet_storage.h b/src/core/hle/service/am/library_applet_storage.h new file mode 100644 index 000000000..7f53f3a9c --- /dev/null +++ b/src/core/hle/service/am/library_applet_storage.h @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Core::Memory { +class Memory; +} + +namespace Kernel { +class KTransferMemory; +} + +namespace Service::AM { + +class LibraryAppletStorage { +public: + virtual ~LibraryAppletStorage(); + virtual Result Read(s64 offset, void* buffer, size_t size) = 0; + virtual Result Write(s64 offset, const void* buffer, size_t size) = 0; + virtual s64 GetSize() = 0; + virtual Kernel::KTransferMemory* GetHandle() = 0; + + std::vector<u8> GetData(); +}; + +std::shared_ptr<LibraryAppletStorage> CreateStorage(std::vector<u8>&& data); +std::shared_ptr<LibraryAppletStorage> CreateTransferMemoryStorage(Core::Memory::Memory& memory, + Kernel::KTransferMemory* trmem, + bool is_writable, s64 size); +std::shared_ptr<LibraryAppletStorage> CreateHandleStorage(Core::Memory::Memory& memory, + Kernel::KTransferMemory* trmem, s64 size); + +} // namespace Service::AM diff --git a/src/core/hle/service/am/managed_layer_holder.cpp b/src/core/hle/service/am/managed_layer_holder.cpp new file mode 100644 index 000000000..61eb8641a --- /dev/null +++ b/src/core/hle/service/am/managed_layer_holder.cpp @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/managed_layer_holder.h" +#include "core/hle/service/nvnflinger/nvnflinger.h" + +namespace Service::AM { + +ManagedLayerHolder::ManagedLayerHolder() = default; +ManagedLayerHolder::~ManagedLayerHolder() { + if (!m_nvnflinger) { + return; + } + + for (const auto& layer : m_managed_display_layers) { + m_nvnflinger->DestroyLayer(layer); + } + + for (const auto& layer : m_managed_display_recording_layers) { + m_nvnflinger->DestroyLayer(layer); + } + + m_nvnflinger = nullptr; +} + +void ManagedLayerHolder::Initialize(Nvnflinger::Nvnflinger* nvnflinger) { + m_nvnflinger = nvnflinger; +} + +void ManagedLayerHolder::CreateManagedDisplayLayer(u64* out_layer) { + // TODO(Subv): Find out how AM determines the display to use, for now just + // create the layer in the Default display. + const auto display_id = m_nvnflinger->OpenDisplay("Default"); + const auto layer_id = m_nvnflinger->CreateLayer(*display_id); + + m_managed_display_layers.emplace(*layer_id); + + *out_layer = *layer_id; +} + +void ManagedLayerHolder::CreateManagedDisplaySeparableLayer(u64* out_layer, + u64* out_recording_layer) { + // TODO(Subv): Find out how AM determines the display to use, for now just + // create the layer in the Default display. + // This calls nn::vi::CreateRecordingLayer() which creates another layer. + // Currently we do not support more than 1 layer per display, output 1 layer id for now. + // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse + // side effects. + // TODO: Support multiple layers + const auto display_id = m_nvnflinger->OpenDisplay("Default"); + const auto layer_id = m_nvnflinger->CreateLayer(*display_id); + + m_managed_display_layers.emplace(*layer_id); + + *out_layer = *layer_id; + *out_recording_layer = 0; +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/managed_layer_holder.h b/src/core/hle/service/am/managed_layer_holder.h new file mode 100644 index 000000000..f7fe03f24 --- /dev/null +++ b/src/core/hle/service/am/managed_layer_holder.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <set> + +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace Service::Nvnflinger { +class Nvnflinger; +} + +namespace Service::AM { + +class ManagedLayerHolder { +public: + ManagedLayerHolder(); + ~ManagedLayerHolder(); + + void Initialize(Nvnflinger::Nvnflinger* nvnflinger); + void CreateManagedDisplayLayer(u64* out_layer); + void CreateManagedDisplaySeparableLayer(u64* out_layer, u64* out_recording_layer); + +private: + Nvnflinger::Nvnflinger* m_nvnflinger{}; + std::set<u64> m_managed_display_layers{}; + std::set<u64> m_managed_display_recording_layers{}; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/process.cpp b/src/core/hle/service/am/process.cpp new file mode 100644 index 000000000..16b685f86 --- /dev/null +++ b/src/core/hle/service/am/process.cpp @@ -0,0 +1,138 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/scope_exit.h" + +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/registered_cache.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/service/am/process.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/loader/loader.h" + +namespace Service::AM { + +Process::Process(Core::System& system) + : m_system(system), m_process(), m_main_thread_priority(), m_main_thread_stack_size(), + m_program_id(), m_process_started() {} + +Process::~Process() { + this->Finalize(); +} + +bool Process::Initialize(u64 program_id) { + // First, ensure we are not holding another process. + this->Finalize(); + + // Get the filesystem controller. + auto& fsc = m_system.GetFileSystemController(); + + // Attempt to load program NCA. + const FileSys::RegisteredCache* bis_system{}; + FileSys::VirtualFile nca{}; + + // Get the program NCA from built-in storage. + bis_system = fsc.GetSystemNANDContents(); + if (bis_system) { + nca = bis_system->GetEntryRaw(program_id, FileSys::ContentRecordType::Program); + } + + // Ensure we retrieved a program NCA. + if (!nca) { + return false; + } + + // Get the appropriate loader to parse this NCA. + auto app_loader = Loader::GetLoader(m_system, nca, program_id, 0); + + // Ensure we have a loader which can parse the NCA. + if (!app_loader) { + return false; + } + + // Create the process. + auto* const process = Kernel::KProcess::Create(m_system.Kernel()); + Kernel::KProcess::Register(m_system.Kernel(), process); + + // On exit, ensure we free the additional reference to the process. + SCOPE_EXIT({ process->Close(); }); + + // Insert process modules into memory. + const auto [load_result, load_parameters] = app_loader->Load(*process, m_system); + + // Ensure loading was successful. + if (load_result != Loader::ResultStatus::Success) { + return false; + } + + // TODO: remove this, kernel already tracks this + m_system.Kernel().AppendNewProcess(process); + + // Note the load parameters from NPDM. + m_main_thread_priority = load_parameters->main_thread_priority; + m_main_thread_stack_size = load_parameters->main_thread_stack_size; + + // This process has not started yet. + m_process_started = false; + + // Take ownership of the process object. + m_process = process; + m_process->Open(); + + // We succeeded. + return true; +} + +void Process::Finalize() { + // Terminate, if we are currently holding a process. + this->Terminate(); + + // Close the process. + if (m_process) { + m_process->Close(); + + // TODO: remove this, kernel already tracks this + m_system.Kernel().RemoveProcess(m_process); + } + + // Clean up. + m_process = nullptr; + m_main_thread_priority = 0; + m_main_thread_stack_size = 0; + m_program_id = 0; + m_process_started = false; +} + +bool Process::Run() { + // If we already started the process, don't start again. + if (m_process_started) { + return false; + } + + // Start. + if (m_process) { + m_process->Run(m_main_thread_priority, m_main_thread_stack_size); + } + + // Mark as started. + m_process_started = true; + + // We succeeded. + return true; +} + +void Process::Terminate() { + if (m_process) { + m_process->Terminate(); + } +} + +u64 Process::GetProcessId() const { + if (m_process) { + return m_process->GetProcessId(); + } + + return 0; +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/process.h b/src/core/hle/service/am/process.h new file mode 100644 index 000000000..4b908ade4 --- /dev/null +++ b/src/core/hle/service/am/process.h @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace Kernel { +class KProcess; +} + +namespace Core { +class System; +} + +namespace Service::AM { + +class Process { +public: + explicit Process(Core::System& system); + ~Process(); + + bool Initialize(u64 program_id); + void Finalize(); + + bool Run(); + void Terminate(); + + bool IsInitialized() const { + return m_process != nullptr; + } + u64 GetProcessId() const; + u64 GetProgramId() const { + return m_program_id; + } + Kernel::KProcess* GetProcess() const { + return m_process; + } + +private: + Core::System& m_system; + Kernel::KProcess* m_process{}; + s32 m_main_thread_priority{}; + u64 m_main_thread_stack_size{}; + u64 m_program_id{}; + bool m_process_started{}; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/system_buffer_manager.cpp b/src/core/hle/service/am/system_buffer_manager.cpp new file mode 100644 index 000000000..7211ef488 --- /dev/null +++ b/src/core/hle/service/am/system_buffer_manager.cpp @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/system_buffer_manager.h" +#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" +#include "core/hle/service/nvnflinger/nvnflinger.h" + +namespace Service::AM { + +SystemBufferManager::SystemBufferManager() = default; + +SystemBufferManager::~SystemBufferManager() { + if (!m_nvnflinger) { + return; + } + + // Clean up shared layers. + if (m_buffer_sharing_enabled) { + } +} + +bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel::KProcess* process, + AppletId applet_id) { + if (m_nvnflinger) { + return m_buffer_sharing_enabled; + } + + m_process = process; + m_nvnflinger = nvnflinger; + m_buffer_sharing_enabled = false; + m_system_shared_buffer_id = 0; + m_system_shared_layer_id = 0; + + if (applet_id <= AppletId::Application) { + return false; + } + + const auto display_id = m_nvnflinger->OpenDisplay("Default").value(); + const auto res = m_nvnflinger->GetSystemBufferManager().Initialize( + &m_system_shared_buffer_id, &m_system_shared_layer_id, display_id); + + if (res.IsSuccess()) { + m_buffer_sharing_enabled = true; + } + + return m_buffer_sharing_enabled; +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/system_buffer_manager.h b/src/core/hle/service/am/system_buffer_manager.h new file mode 100644 index 000000000..c60d73416 --- /dev/null +++ b/src/core/hle/service/am/system_buffer_manager.h @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <set> + +#include "common/common_funcs.h" +#include "common/common_types.h" + +#include "core/hle/service/am/am_types.h" + +namespace Kernel { +class KProcess; +} + +namespace Service::Nvnflinger { +class Nvnflinger; +} + +namespace Service::AM { + +class SystemBufferManager { +public: + SystemBufferManager(); + ~SystemBufferManager(); + + bool Initialize(Nvnflinger::Nvnflinger* flinger, Kernel::KProcess* process, AppletId applet_id); + + void GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id, + u64* out_system_shared_layer_id) { + *out_system_shared_buffer_id = m_system_shared_buffer_id; + *out_system_shared_layer_id = m_system_shared_layer_id; + } + +private: + Kernel::KProcess* m_process{}; + Nvnflinger::Nvnflinger* m_nvnflinger{}; + bool m_buffer_sharing_enabled{}; + u64 m_system_shared_buffer_id{}; + u64 m_system_shared_layer_id{}; +}; + +} // namespace Service::AM |