From a7e9d7842dc78e09bfe50ba3bc471b8a75d29b96 Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Tue, 2 Jan 2024 19:22:02 -0500
Subject: [PATCH] am: add new datatypes for per-applet state

---
 src/core/CMakeLists.txt                       |  17 +
 src/core/hle/service/am/am_types.h            | 171 +++++++++
 src/core/hle/service/am/applet.cpp            |  63 ++++
 src/core/hle/service/am/applet.h              | 164 ++++++++
 src/core/hle/service/am/applet_manager.cpp    | 352 ++++++++++++++++++
 src/core/hle/service/am/applet_manager.h      |  59 +++
 src/core/hle/service/am/hid_registration.cpp  |  29 ++
 src/core/hle/service/am/hid_registration.h    |  30 ++
 .../hle/service/am/library_applet_storage.cpp | 140 +++++++
 .../hle/service/am/library_applet_storage.h   |  36 ++
 .../hle/service/am/managed_layer_holder.cpp   |  59 +++
 .../hle/service/am/managed_layer_holder.h     |  32 ++
 src/core/hle/service/am/process.cpp           | 138 +++++++
 src/core/hle/service/am/process.h             |  50 +++
 .../hle/service/am/system_buffer_manager.cpp  |  49 +++
 .../hle/service/am/system_buffer_manager.h    |  44 +++
 src/core/hle/service/event.cpp                |  31 ++
 src/core/hle/service/event.h                  |  31 ++
 .../nvnflinger/fb_share_buffer_manager.cpp    |   2 +-
 19 files changed, 1496 insertions(+), 1 deletion(-)
 create mode 100644 src/core/hle/service/am/am_types.h
 create mode 100644 src/core/hle/service/am/applet.cpp
 create mode 100644 src/core/hle/service/am/applet.h
 create mode 100644 src/core/hle/service/am/applet_manager.cpp
 create mode 100644 src/core/hle/service/am/applet_manager.h
 create mode 100644 src/core/hle/service/am/hid_registration.cpp
 create mode 100644 src/core/hle/service/am/hid_registration.h
 create mode 100644 src/core/hle/service/am/library_applet_storage.cpp
 create mode 100644 src/core/hle/service/am/library_applet_storage.h
 create mode 100644 src/core/hle/service/am/managed_layer_holder.cpp
 create mode 100644 src/core/hle/service/am/managed_layer_holder.h
 create mode 100644 src/core/hle/service/am/process.cpp
 create mode 100644 src/core/hle/service/am/process.h
 create mode 100644 src/core/hle/service/am/system_buffer_manager.cpp
 create mode 100644 src/core/hle/service/am/system_buffer_manager.h
 create mode 100644 src/core/hle/service/event.cpp
 create mode 100644 src/core/hle/service/event.h

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 3a2ba9ed4c..0499464264 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -393,8 +393,13 @@ add_library(core STATIC
     hle/service/am/am.cpp
     hle/service/am/am.h
     hle/service/am/am_results.h
+    hle/service/am/am_types.h
+    hle/service/am/applet.cpp
+    hle/service/am/applet.h
     hle/service/am/applet_ae.cpp
     hle/service/am/applet_ae.h
+    hle/service/am/applet_manager.cpp
+    hle/service/am/applet_manager.h
     hle/service/am/applet_oe.cpp
     hle/service/am/applet_oe.h
     hle/service/am/applets/applet_cabinet.cpp
@@ -438,6 +443,8 @@ add_library(core STATIC
     hle/service/am/display_controller.h
     hle/service/am/global_state_controller.cpp
     hle/service/am/global_state_controller.h
+    hle/service/am/hid_registration.cpp
+    hle/service/am/hid_registration.h
     hle/service/am/home_menu_functions.cpp
     hle/service/am/home_menu_functions.h
     hle/service/am/idle.cpp
@@ -450,16 +457,24 @@ add_library(core STATIC
     hle/service/am/library_applet_proxy.h
     hle/service/am/library_applet_self_accessor.cpp
     hle/service/am/library_applet_self_accessor.h
+    hle/service/am/library_applet_storage.cpp
+    hle/service/am/library_applet_storage.h
     hle/service/am/lock_accessor.cpp
     hle/service/am/lock_accessor.h
+    hle/service/am/managed_layer_holder.cpp
+    hle/service/am/managed_layer_holder.h
     hle/service/am/omm.cpp
     hle/service/am/omm.h
     hle/service/am/process_winding_controller.cpp
     hle/service/am/process_winding_controller.h
+    hle/service/am/process.cpp
+    hle/service/am/process.h
     hle/service/am/self_controller.cpp
     hle/service/am/self_controller.h
     hle/service/am/system_applet_proxy.cpp
     hle/service/am/system_applet_proxy.h
+    hle/service/am/system_buffer_manager.cpp
+    hle/service/am/system_buffer_manager.h
     hle/service/am/spsm.cpp
     hle/service/am/spsm.h
     hle/service/am/storage_accessor.cpp
@@ -531,6 +546,8 @@ add_library(core STATIC
     hle/service/es/es.h
     hle/service/eupld/eupld.cpp
     hle/service/eupld/eupld.h
+    hle/service/event.cpp
+    hle/service/event.h
     hle/service/fatal/fatal.cpp
     hle/service/fatal/fatal.h
     hle/service/fatal/fatal_p.cpp
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 0000000000..d0a237a7e7
--- /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 0000000000..8f44fab338
--- /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 0000000000..9650a2615c
--- /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 0000000000..9f7ccfbf25
--- /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 0000000000..4875de309e
--- /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 0000000000..b9426f7b6c
--- /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 0000000000..8a732349c2
--- /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 0000000000..46e6c0111d
--- /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 0000000000..7f53f3a9cd
--- /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 0000000000..61eb8641ab
--- /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 0000000000..f7fe03f242
--- /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 0000000000..16b685f860
--- /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 0000000000..4b908ade48
--- /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 0000000000..7211ef488a
--- /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 0000000000..c60d73416b
--- /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
diff --git a/src/core/hle/service/event.cpp b/src/core/hle/service/event.cpp
new file mode 100644
index 0000000000..375660d728
--- /dev/null
+++ b/src/core/hle/service/event.cpp
@@ -0,0 +1,31 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/service/event.h"
+#include "core/hle/service/kernel_helpers.h"
+
+namespace Service {
+
+Event::Event(KernelHelpers::ServiceContext& ctx) {
+    m_event = ctx.CreateEvent("Event");
+}
+
+Event::~Event() {
+    m_event->GetReadableEvent().Close();
+    m_event->Close();
+}
+
+void Event::Signal() {
+    m_event->Signal();
+}
+
+void Event::Clear() {
+    m_event->Clear();
+}
+
+Kernel::KReadableEvent* Event::GetHandle() {
+    return &m_event->GetReadableEvent();
+}
+
+} // namespace Service
diff --git a/src/core/hle/service/event.h b/src/core/hle/service/event.h
new file mode 100644
index 0000000000..cdbc4635a6
--- /dev/null
+++ b/src/core/hle/service/event.h
@@ -0,0 +1,31 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+namespace Kernel {
+class KEvent;
+class KReadableEvent;
+} // namespace Kernel
+
+namespace Service {
+
+namespace KernelHelpers {
+class ServiceContext;
+}
+
+class Event {
+public:
+    explicit Event(KernelHelpers::ServiceContext& ctx);
+    ~Event();
+
+    void Signal();
+    void Clear();
+
+    Kernel::KReadableEvent* GetHandle();
+
+private:
+    Kernel::KEvent* m_event;
+};
+
+} // namespace Service
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
index 86e272b418..e71652cdf1 100644
--- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
+++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
@@ -128,7 +128,7 @@ Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, Nvidia::D
 
     // Ensure we maintain a clean state on failure.
     ON_RESULT_FAILURE {
-        ASSERT(R_SUCCEEDED(FreeNvMapHandle(*nvmap, *out_handle, nvmap_fd)));
+        R_ASSERT(FreeNvMapHandle(*nvmap, *out_handle, nvmap_fd));
     };
 
     // Assign the allocated memory to the handle.