diff --git a/libraries/libstratosphere/include/stratosphere.hpp b/libraries/libstratosphere/include/stratosphere.hpp
index bc5547f97..92ac1defe 100644
--- a/libraries/libstratosphere/include/stratosphere.hpp
+++ b/libraries/libstratosphere/include/stratosphere.hpp
@@ -73,6 +73,7 @@
 #include <stratosphere/ldr.hpp>
 #include <stratosphere/lr.hpp>
 #include <stratosphere/lm.hpp>
+#include <stratosphere/mitm.hpp>
 #include <stratosphere/ncm.hpp>
 #include <stratosphere/nim.hpp>
 #include <stratosphere/ns.hpp>
diff --git a/libraries/libstratosphere/include/stratosphere/mitm.hpp b/libraries/libstratosphere/include/stratosphere/mitm.hpp
new file mode 100644
index 000000000..c4e7afe64
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/mitm.hpp
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stratosphere/mitm/impl/mitm_pm_interface.hpp>
+#include <stratosphere/mitm/mitm_pm_api.hpp>
diff --git a/libraries/libstratosphere/include/stratosphere/mitm/impl/mitm_pm_interface.hpp b/libraries/libstratosphere/include/stratosphere/mitm/impl/mitm_pm_interface.hpp
new file mode 100644
index 000000000..704fdb3ab
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/mitm/impl/mitm_pm_interface.hpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+#include <vapours.hpp>
+#include <stratosphere/ncm/ncm_program_id.hpp>
+#include <stratosphere/cfg/cfg_types.hpp>
+#include <stratosphere/sf.hpp>
+
+#define AMS_MITM_PM_IMPL_I_PM_INTERFACE_INFO(C, H) \
+    AMS_SF_METHOD_INFO(C, H, 65000, Result, PrepareLaunchProgram, (sf::Out<u64> out_boost_size, ncm::ProgramId program_id, const cfg::OverrideStatus &override_status, bool is_application), (out_boost_size, program_id, override_status, is_application))
+
+AMS_SF_DEFINE_INTERFACE(ams::mitm::pm::impl, IPmInterface, AMS_MITM_PM_IMPL_I_PM_INTERFACE_INFO, 0xEA88789C)
+
diff --git a/libraries/libstratosphere/include/stratosphere/mitm/mitm_pm_api.hpp b/libraries/libstratosphere/include/stratosphere/mitm/mitm_pm_api.hpp
new file mode 100644
index 000000000..feab8db66
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/mitm/mitm_pm_api.hpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+#include <vapours.hpp>
+#include <stratosphere/ncm/ncm_program_id.hpp>
+
+namespace ams::mitm::pm {
+
+    /* PM API. */
+    void Initialize();
+    void Finalize();
+
+    Result PrepareLaunchProgram(u64 *out, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application);
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/pm/impl/pm_shell_interface.hpp b/libraries/libstratosphere/include/stratosphere/pm/impl/pm_shell_interface.hpp
index 1da93af4c..1cccdab65 100644
--- a/libraries/libstratosphere/include/stratosphere/pm/impl/pm_shell_interface.hpp
+++ b/libraries/libstratosphere/include/stratosphere/pm/impl/pm_shell_interface.hpp
@@ -19,31 +19,32 @@
 #include <stratosphere/pm/pm_types.hpp>
 #include <stratosphere/sf.hpp>
 
-#define AMS_PM_I_SHELL_INTERFACE_INTERFACE_INFO(C, H)                                                                                                                                                                \
-    AMS_SF_METHOD_INFO(C, H,  0, Result, LaunchProgram,                       (sf::Out<os::ProcessId> out_process_id, const ncm::ProgramLocation &loc, u32 flags), (out_process_id, loc, flags))                     \
-    AMS_SF_METHOD_INFO(C, H,  1, Result, TerminateProcess,                    (os::ProcessId process_id),                                                          (process_id))                                     \
-    AMS_SF_METHOD_INFO(C, H,  2, Result, TerminateProgram,                    (ncm::ProgramId program_id),                                                         (program_id))                                     \
-    AMS_SF_METHOD_INFO(C, H,  3, void,   GetProcessEventHandle,               (sf::OutCopyHandle out),                                                             (out))                                            \
-    AMS_SF_METHOD_INFO(C, H,  4, void,   GetProcessEventInfo,                 (sf::Out<pm::ProcessEventInfo> out),                                                 (out))                                            \
-    AMS_SF_METHOD_INFO(C, H,  5, void,   NotifyBootFinished,                  (),                                                                                  ())                                               \
-    AMS_SF_METHOD_INFO(C, H,  6, Result, GetApplicationProcessIdForShell,     (sf::Out<os::ProcessId> out),                                                        (out))                                            \
-    AMS_SF_METHOD_INFO(C, H,  7, Result, BoostSystemMemoryResourceLimit,      (u64 boost_size),                                                                    (boost_size))                                     \
-    AMS_SF_METHOD_INFO(C, H,  8, Result, BoostApplicationThreadResourceLimit, (),                                                                                  ())                                               \
-    AMS_SF_METHOD_INFO(C, H,  9, void,   GetBootFinishedEventHandle,          (sf::OutCopyHandle out),                                                             (out),                        hos::Version_8_0_0) \
-    AMS_SF_METHOD_INFO(C, H, 10, Result, BoostSystemThreadResourceLimit,      (),                                                                                  ())
+#define AMS_PM_I_SHELL_INTERFACE_INTERFACE_INFO(C, H)                                                                                                                                                                               \
+    AMS_SF_METHOD_INFO(C, H,  0,    Result, LaunchProgram,                                   (sf::Out<os::ProcessId> out_process_id, const ncm::ProgramLocation &loc, u32 flags), (out_process_id, loc, flags))                     \
+    AMS_SF_METHOD_INFO(C, H,  1,    Result, TerminateProcess,                                (os::ProcessId process_id),                                                          (process_id))                                     \
+    AMS_SF_METHOD_INFO(C, H,  2,    Result, TerminateProgram,                                (ncm::ProgramId program_id),                                                         (program_id))                                     \
+    AMS_SF_METHOD_INFO(C, H,  3,    void,   GetProcessEventHandle,                           (sf::OutCopyHandle out),                                                             (out))                                            \
+    AMS_SF_METHOD_INFO(C, H,  4,    void,   GetProcessEventInfo,                             (sf::Out<pm::ProcessEventInfo> out),                                                 (out))                                            \
+    AMS_SF_METHOD_INFO(C, H,  5,    void,   NotifyBootFinished,                              (),                                                                                  ())                                               \
+    AMS_SF_METHOD_INFO(C, H,  6,    Result, GetApplicationProcessIdForShell,                 (sf::Out<os::ProcessId> out),                                                        (out))                                            \
+    AMS_SF_METHOD_INFO(C, H,  7,    Result, BoostSystemMemoryResourceLimit,                  (u64 boost_size),                                                                    (boost_size))                                     \
+    AMS_SF_METHOD_INFO(C, H,  8,    Result, BoostApplicationThreadResourceLimit,             (),                                                                                  ())                                               \
+    AMS_SF_METHOD_INFO(C, H,  9,    void,   GetBootFinishedEventHandle,                      (sf::OutCopyHandle out),                                                             (out),                        hos::Version_8_0_0) \
+    AMS_SF_METHOD_INFO(C, H, 10,    Result, BoostSystemThreadResourceLimit,                  (),                                                                                  ())
 
 AMS_SF_DEFINE_INTERFACE(ams::pm::impl, IShellInterface, AMS_PM_I_SHELL_INTERFACE_INTERFACE_INFO, 0x387D60C0)
 
-#define AMS_PM_I_DEPRECATED_SHELL_INTERFACE_INTERFACE_INFO(C, H)                                                                                                                                                \
-    AMS_SF_METHOD_INFO(C, H, 0, Result, LaunchProgram,                   (sf::Out<os::ProcessId> out_process_id, const ncm::ProgramLocation &loc, u32 flags), (out_process_id, loc, flags))                     \
-    AMS_SF_METHOD_INFO(C, H, 1, Result, TerminateProcess,                (os::ProcessId process_id),                                                          (process_id))                                     \
-    AMS_SF_METHOD_INFO(C, H, 2, Result, TerminateProgram,                (ncm::ProgramId program_id),                                                         (program_id))                                     \
-    AMS_SF_METHOD_INFO(C, H, 3, void,   GetProcessEventHandle,           (sf::OutCopyHandle out),                                                             (out))                                            \
-    AMS_SF_METHOD_INFO(C, H, 4, void,   GetProcessEventInfo,             (sf::Out<pm::ProcessEventInfo> out),                                                 (out))                                            \
-    AMS_SF_METHOD_INFO(C, H, 5, Result, CleanupProcess,                  (os::ProcessId process_id),                                                          (process_id))                                     \
-    AMS_SF_METHOD_INFO(C, H, 6, Result, ClearExceptionOccurred,          (os::ProcessId process_id),                                                          (process_id))                                     \
-    AMS_SF_METHOD_INFO(C, H, 7, void,   NotifyBootFinished,              (),                                                                                  ())                                               \
-    AMS_SF_METHOD_INFO(C, H, 8, Result, GetApplicationProcessIdForShell, (sf::Out<os::ProcessId> out),                                                        (out))                                            \
-    AMS_SF_METHOD_INFO(C, H, 9, Result, BoostSystemMemoryResourceLimit,  (u64 boost_size),                                                                    (boost_size),                 hos::Version_4_0_0)
+#define AMS_PM_I_DEPRECATED_SHELL_INTERFACE_INTERFACE_INFO(C, H)                                                                                                                                                                    \
+    AMS_SF_METHOD_INFO(C, H, 0,     Result, LaunchProgram,                                   (sf::Out<os::ProcessId> out_process_id, const ncm::ProgramLocation &loc, u32 flags), (out_process_id, loc, flags))                     \
+    AMS_SF_METHOD_INFO(C, H, 1,     Result, TerminateProcess,                                (os::ProcessId process_id),                                                          (process_id))                                     \
+    AMS_SF_METHOD_INFO(C, H, 2,     Result, TerminateProgram,                                (ncm::ProgramId program_id),                                                         (program_id))                                     \
+    AMS_SF_METHOD_INFO(C, H, 3,     void,   GetProcessEventHandle,                           (sf::OutCopyHandle out),                                                             (out))                                            \
+    AMS_SF_METHOD_INFO(C, H, 4,     void,   GetProcessEventInfo,                             (sf::Out<pm::ProcessEventInfo> out),                                                 (out))                                            \
+    AMS_SF_METHOD_INFO(C, H, 5,     Result, CleanupProcess,                                  (os::ProcessId process_id),                                                          (process_id))                                     \
+    AMS_SF_METHOD_INFO(C, H, 6,     Result, ClearExceptionOccurred,                          (os::ProcessId process_id),                                                          (process_id))                                     \
+    AMS_SF_METHOD_INFO(C, H, 7,     void,   NotifyBootFinished,                              (),                                                                                  ())                                               \
+    AMS_SF_METHOD_INFO(C, H, 8,     Result, GetApplicationProcessIdForShell,                 (sf::Out<os::ProcessId> out),                                                        (out))                                            \
+    AMS_SF_METHOD_INFO(C, H, 9,     Result, BoostSystemMemoryResourceLimit,                  (u64 boost_size),                                                                    (boost_size),                 hos::Version_4_0_0) \
+    AMS_SF_METHOD_INFO(C, H, 10,    Result, BoostSystemThreadResourceLimit,                  (),                                                                                  ())
 
 AMS_SF_DEFINE_INTERFACE(ams::pm::impl, IDeprecatedShellInterface, AMS_PM_I_DEPRECATED_SHELL_INTERFACE_INTERFACE_INFO, 0x387D60C0)
diff --git a/libraries/libstratosphere/source/mitm/mitm_pm.os.horizon.c b/libraries/libstratosphere/source/mitm/mitm_pm.os.horizon.c
new file mode 100644
index 000000000..adfabd4fa
--- /dev/null
+++ b/libraries/libstratosphere/source/mitm/mitm_pm.os.horizon.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#define NX_SERVICE_ASSUME_NON_DOMAIN
+#include "../service_guard.h"
+#include "mitm_pm.os.horizon.h"
+
+static Service g_amsMitmPmSrv;
+
+NX_GENERATE_SERVICE_GUARD(amsMitmPm);
+
+Result _amsMitmPmInitialize(void) {
+    return smGetService(&g_amsMitmPmSrv, "mitm:pm");
+}
+
+void _amsMitmPmCleanup(void) {
+    serviceClose(&g_amsMitmPmSrv);
+}
+
+Service *amsMitmPmGetServiceSession(void) {
+    return &g_amsMitmPmSrv;
+}
+
+Result amsMitmPmPrepareLaunchProgram(u64 *out, u64 program_id, const CfgOverrideStatus *status, bool is_application) {
+    const struct {
+        u8 is_application;
+        u64 program_id;
+        CfgOverrideStatus status;
+    } in = { is_application ? 1 : 0, program_id, *status };
+
+    return serviceDispatchInOut(&g_amsMitmPmSrv, 65000, in, *out);
+}
diff --git a/libraries/libstratosphere/source/mitm/mitm_pm.os.horizon.h b/libraries/libstratosphere/source/mitm/mitm_pm.os.horizon.h
new file mode 100644
index 000000000..2d8da12c4
--- /dev/null
+++ b/libraries/libstratosphere/source/mitm/mitm_pm.os.horizon.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+#include <switch.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+    u64 keys_down;
+    u64 flags;
+} CfgOverrideStatus;
+
+Result amsMitmPmInitialize(void);
+void amsMitmPmExit(void);
+Service *amsMitmPmGetServiceSession(void);
+
+Result amsMitmPmPrepareLaunchProgram(u64 *out, u64 program_id, const CfgOverrideStatus *status, bool is_application);
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/libraries/libstratosphere/source/mitm/mitm_pm_api.cpp b/libraries/libstratosphere/source/mitm/mitm_pm_api.cpp
new file mode 100644
index 000000000..a49cf9ce9
--- /dev/null
+++ b/libraries/libstratosphere/source/mitm/mitm_pm_api.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stratosphere.hpp>
+#include "mitm_pm.os.horizon.h"
+
+namespace ams::mitm::pm {
+
+    /* PM API. */
+    #if defined(ATMOSPHERE_OS_HORIZON)
+    void Initialize() {
+        R_ABORT_UNLESS(amsMitmPmInitialize());
+    }
+
+    void Finalize() {
+        amsMitmPmExit();
+    }
+
+    Result PrepareLaunchProgram(u64 *out, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application) {
+        static_assert(sizeof(status) == sizeof(CfgOverrideStatus), "CfgOverrideStatus definition!");
+        R_RETURN(amsMitmPmPrepareLaunchProgram(out, program_id.value, reinterpret_cast<const CfgOverrideStatus *>(std::addressof(status)), is_application));
+    }
+    #endif
+
+}
diff --git a/libraries/libstratosphere/source/pm/pm_shell_api.cpp b/libraries/libstratosphere/source/pm/pm_shell_api.cpp
index 03470e47f..247d88cbb 100644
--- a/libraries/libstratosphere/source/pm/pm_shell_api.cpp
+++ b/libraries/libstratosphere/source/pm/pm_shell_api.cpp
@@ -14,6 +14,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 #include <stratosphere.hpp>
+#include "pm_ams.os.horizon.h"
 
 namespace ams::pm::shell {
 
diff --git a/libraries/libvapours/include/vapours/results/fs_results.hpp b/libraries/libvapours/include/vapours/results/fs_results.hpp
index e47e9f55b..104a299f3 100644
--- a/libraries/libvapours/include/vapours/results/fs_results.hpp
+++ b/libraries/libvapours/include/vapours/results/fs_results.hpp
@@ -41,8 +41,9 @@ namespace ams::fs {
         R_DEFINE_ERROR_RESULT(MountNameAlreadyExists, 60);
 
     R_DEFINE_ERROR_RANGE(HandledBySystemProcess, 1000, 2999);
-        R_DEFINE_ERROR_RESULT(PartitionNotFound,    1001);
-        R_DEFINE_ERROR_RESULT(TargetNotFound,       1002);
+        R_DEFINE_ERROR_RESULT(PartitionNotFound,      1001);
+        R_DEFINE_ERROR_RESULT(TargetNotFound,         1002);
+        R_DEFINE_ERROR_RESULT(NcaExternalKeyNotFound, 1004);
 
         R_DEFINE_ERROR_RANGE(SdCardAccessFailed, 2000, 2499);
             R_DEFINE_ERROR_RESULT(SdCardNotPresent, 2001);
diff --git a/stratosphere/ams_mitm/ams_mitm.json b/stratosphere/ams_mitm/ams_mitm.json
index 8eae1c76f..0b98a2588 100644
--- a/stratosphere/ams_mitm/ams_mitm.json
+++ b/stratosphere/ams_mitm/ams_mitm.json
@@ -65,6 +65,8 @@
                 "svcReplyAndReceive":	"0x43",
                 "svcReplyAndReceiveWithUserBuffer":	"0x44",
                 "svcCreateEvent":	"0x45",
+				"svcMapPhysicalMemoryUnsafe":	"0x48",
+				"svcUnmapPhysicalMemoryUnsafe":	"0x49",
                 "svcMapTransferMemory":	"0x51",
                 "svcUnmapTransferMemory":	"0x52",
                 "svcCreateInterruptEvent":	"0x53",
diff --git a/stratosphere/ams_mitm/source/amsmitm_module_management.cpp b/stratosphere/ams_mitm/source/amsmitm_module_management.cpp
index 6905f5e6a..c8087a075 100644
--- a/stratosphere/ams_mitm/source/amsmitm_module_management.cpp
+++ b/stratosphere/ams_mitm/source/amsmitm_module_management.cpp
@@ -24,6 +24,7 @@
 #include "ns_mitm/nsmitm_module.hpp"
 #include "dns_mitm/dnsmitm_module.hpp"
 #include "sysupdater/sysupdater_module.hpp"
+#include "mitm_pm/mitm_pm_module.hpp"
 
 namespace ams::mitm {
 
@@ -37,6 +38,7 @@ namespace ams::mitm {
             ModuleId_NsMitm,
             ModuleId_DnsMitm,
             ModuleId_Sysupdater,
+            ModuleId_PmService,
 
             ModuleId_Count,
         };
@@ -70,6 +72,7 @@ namespace ams::mitm {
             GetModuleDefinition<ns::MitmModule>(),
             GetModuleDefinition<socket::resolver::MitmModule>(),
             GetModuleDefinition<sysupdater::MitmModule>(),
+            GetModuleDefinition<pm::MitmModule>(),
         };
 
     }
diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp
index 12074c135..f82a6e806 100644
--- a/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp
+++ b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp
@@ -71,7 +71,7 @@ namespace ams::mitm::fs {
         Result OpenHblWebContentFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> &out, ncm::ProgramId program_id) {
             /* Verify eligibility. */
             bool is_hbl;
-            R_UNLESS(R_SUCCEEDED(pm::info::IsHblProgramId(std::addressof(is_hbl), program_id)), sm::mitm::ResultShouldForwardToSession());
+            R_UNLESS(R_SUCCEEDED(ams::pm::info::IsHblProgramId(std::addressof(is_hbl), program_id)), sm::mitm::ResultShouldForwardToSession());
             R_UNLESS(is_hbl,                                                     sm::mitm::ResultShouldForwardToSession());
 
             /* Hbl html directory must exist. */
diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.cpp
index de41c800f..f46e041de 100644
--- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.cpp
+++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.cpp
@@ -78,6 +78,7 @@ namespace ams::mitm::fs {
 
         constinit os::SdkRecursiveMutex g_storage_set_mutex;
         constinit LayeredRomfsStorageSet g_storage_set;
+        constinit os::SdkMutex g_initialization_mutex;
 
         void OpenReference(LayeredRomfsStorageImpl *impl) {
             std::scoped_lock lk(g_storage_set_mutex);
@@ -106,6 +107,8 @@ namespace ams::mitm::fs {
                 auto *impl = reinterpret_cast<LayeredRomfsStorageImpl *>(storage_uptr);
                 g_ack_mq.Send(storage_uptr);
 
+                std::scoped_lock lk(g_initialization_mutex);
+
                 impl->InitializeImpl();
 
                 /* Close the initial reference. */
@@ -255,6 +258,21 @@ namespace ams::mitm::fs {
         return std::make_shared<LayeredRomfsStorage>(impl);
     }
 
+    void FinalizeLayeredRomfsStorage(ncm::ProgramId program_id) {
+        std::scoped_lock lk(g_initialization_mutex);
+        std::scoped_lock lk2(g_storage_set_mutex);
+
+        /* Find an existing storage. */
+        if (auto it = g_storage_set.find_key(program_id.value); it != g_storage_set.end()) {
+            /* We need to delete the process romfs. Require invariant that it is unreferenced, by this point. */
+            AMS_ABORT_UNLESS(it->GetReferenceCount() == 0);
+
+            auto *holder = std::addressof(*it);
+            it = g_storage_set.erase(it);
+            delete holder;
+        }
+    }
+
     LayeredRomfsStorageImpl::LayeredRomfsStorageImpl(std::unique_ptr<IStorage> s_r, std::unique_ptr<IStorage> f_r, ncm::ProgramId pr_id) : m_storage_romfs(std::move(s_r)), m_file_romfs(std::move(f_r)), m_initialize_event(os::EventClearMode_ManualClear), m_program_id(std::move(pr_id)), m_is_initialized(false), m_started_initialize(false) {
         /* ... */
     }
diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.hpp
index ad22120c0..337e47c47 100644
--- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.hpp
+++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layered_romfs_storage.hpp
@@ -22,7 +22,7 @@ namespace ams::mitm::fs {
 
     class LayeredRomfsStorageImpl {
         private:
-            std::vector<romfs::SourceInfo> m_source_infos;
+            romfs::Builder::SourceInfoVector m_source_infos;
             std::unique_ptr<ams::fs::IStorage> m_storage_romfs;
             std::unique_ptr<ams::fs::IStorage> m_file_romfs;
             os::Event m_initialize_event;
@@ -51,4 +51,6 @@ namespace ams::mitm::fs {
 
     std::shared_ptr<ams::fs::IStorage> GetLayeredRomfsStorage(ncm::ProgramId program_id, ::FsStorage &data_storage, bool is_process_romfs);
 
+    void FinalizeLayeredRomfsStorage(ncm::ProgramId program_id);
+
 }
diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp
index 542d5e202..62fd74ef3 100644
--- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp
+++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp
@@ -16,6 +16,7 @@
 #include <stratosphere.hpp>
 #include "../amsmitm_fs_utils.hpp"
 #include "fsmitm_romfs.hpp"
+#include "fsmitm_layered_romfs_storage.hpp"
 
 namespace ams::mitm::fs {
 
@@ -23,6 +24,206 @@ namespace ams::mitm::fs {
 
     namespace romfs {
 
+        namespace {
+
+            struct ApplicationWithDynamicHeapInfo {
+                ncm::ProgramId program_id;
+                size_t dynamic_app_heap_size;
+                size_t dynamic_system_heap_size;
+            };
+
+            constexpr const ApplicationWithDynamicHeapInfo ApplicationsWithDynamicHeap[] = {
+                /* Animal Crossing: New Horizons. */
+                /* Requirement ~24 MB. */
+                /* No particular heap sensitivity. */
+                { 0x01006F8002326000,  16_MB, 0_MB },
+
+                /* Fire Emblem: Engage. */
+                /* Requirement ~32+ MB. */
+                /* No particular heap sensitivity. */
+                { 0x0100A6301214E000,  16_MB, 0_MB },
+
+                /* The Legend of Zelda: Tears of the Kingdom. */
+                /* Requirement ~48 MB. */
+                /* Game is highly sensitive to memory stolen from application heap. */
+                /* 1.0.0 tolerates no more than 16 MB stolen. 1.1.0 no more than 12 MB. */
+                { 0x0100F2C0115B6000,  10_MB, 8_MB },
+            };
+
+            constexpr size_t GetDynamicAppHeapSize(ncm::ProgramId program_id) {
+                for (const auto &info : ApplicationsWithDynamicHeap) {
+                    if (info.program_id == program_id) {
+                        return info.dynamic_app_heap_size;
+                    }
+                }
+
+                return 0;
+            }
+
+            constexpr size_t GetDynamicSysHeapSize(ncm::ProgramId program_id) {
+                for (const auto &info : ApplicationsWithDynamicHeap) {
+                    if (info.program_id == program_id) {
+                        return info.dynamic_system_heap_size;
+                    }
+                }
+
+                return 0;
+            }
+
+            template<auto MapImpl, auto UnmapImpl>
+            struct DynamicHeap {
+                uintptr_t heap_address{};
+                size_t heap_size{};
+                size_t outstanding_allocations{};
+                util::TypedStorage<mem::StandardAllocator> heap{};
+                os::SdkMutex release_heap_lock{};
+
+                constexpr DynamicHeap() = default;
+
+                void Map() {
+                    if (this->heap_address == 0) {
+                        /* NOTE: Lock not necessary, because this is the only location which do 0 -> non-zero. */
+
+                        R_ABORT_UNLESS(MapImpl(std::addressof(this->heap_address), this->heap_size));
+                        AMS_ABORT_UNLESS(this->heap_address != 0);
+
+                        /* Create heap. */
+                        util::ConstructAt(this->heap, reinterpret_cast<void *>(this->heap_address), this->heap_size);
+                    }
+                }
+
+                void TryRelease() {
+                    if (this->outstanding_allocations == 0) {
+                        std::scoped_lock lk(this->release_heap_lock);
+
+                        if (this->heap_address != 0) {
+                            util::DestroyAt(this->heap);
+                            this->heap = {};
+
+                            R_ABORT_UNLESS(UnmapImpl(this->heap_address, this->heap_size));
+
+                            this->heap_address = 0;
+                        }
+                    }
+                }
+
+                void *Allocate(size_t size) {
+                    void * const ret = util::GetReference(this->heap).Allocate(size);
+                    if (AMS_LIKELY(ret != nullptr)) {
+                        ++this->outstanding_allocations;
+                    }
+                    return ret;
+                }
+
+                bool TryFree(void *p) {
+                    if (this->IsAllocated(p)) {
+                        --this->outstanding_allocations;
+
+                        util::GetReference(this->heap).Free(p);
+
+                        return true;
+                    } else {
+                        return false;
+                    }
+                }
+
+                bool IsAllocated(void *p) const {
+                    const uintptr_t address = reinterpret_cast<uintptr_t>(p);
+
+                    return this->heap_address != 0 && (this->heap_address <= address && address < this->heap_address + this->heap_size);
+                }
+
+                void Reset() {
+                    /* This should require no remaining allocations. */
+                    AMS_ABORT_UNLESS(this->outstanding_allocations == 0);
+
+                    /* Free the heap. */
+                    this->TryRelease();
+                    AMS_ABORT_UNLESS(this->heap_address == 0);
+
+                    /* Clear the heap size. */
+                    this->heap_size = 0;
+                }
+            };
+
+            Result MapByHeap(uintptr_t *out, size_t size) {
+                R_TRY(os::SetMemoryHeapSize(size));
+                R_RETURN(os::AllocateMemoryBlock(out, size));
+            }
+
+            Result UnmapByHeap(uintptr_t address, size_t size) {
+                os::FreeMemoryBlock(address, size);
+                R_RETURN(os::SetMemoryHeapSize(0));
+            }
+
+            /* Dynamic allocation globals. */
+            constinit os::SdkMutex g_romfs_build_lock;
+            constinit ncm::ProgramId g_dynamic_heap_program_id{};
+
+            constinit bool g_building_from_dynamic_heap = false;
+
+            constinit DynamicHeap<os::AllocateUnsafeMemory, os::FreeUnsafeMemory> g_dynamic_app_heap;
+            constinit DynamicHeap<MapByHeap, UnmapByHeap> g_dynamic_sys_heap;
+
+            void InitializeDynamicHeapForBuildRomfs(ncm::ProgramId program_id) {
+                if (program_id == g_dynamic_heap_program_id && g_dynamic_app_heap.heap_size > 0) {
+                    /* This romfs will build out of dynamic heap. */
+                    g_building_from_dynamic_heap = true;
+
+                    g_dynamic_app_heap.Map();
+
+                    if (g_dynamic_sys_heap.heap_size > 0) {
+                        g_dynamic_sys_heap.Map();
+                    }
+                }
+            }
+
+            void FinalizeDynamicHeapForBuildRomfs() {
+                /* We are definitely no longer building out of dynamic heap. */
+                g_building_from_dynamic_heap = false;
+
+                g_dynamic_app_heap.TryRelease();
+            }
+
+        }
+
+        void *AllocateTracked(AllocationType type, size_t size) {
+            AMS_UNUSED(type);
+
+            if (g_building_from_dynamic_heap) {
+                void *ret = g_dynamic_app_heap.Allocate(size);
+
+                if (ret == nullptr && g_dynamic_sys_heap.heap_address != 0) {
+                    ret = g_dynamic_sys_heap.Allocate(size);
+                }
+
+                if (ret == nullptr) {
+                    ret = std::malloc(size);
+                }
+
+                return ret;
+            } else {
+                return std::malloc(size);
+            }
+        }
+
+        void FreeTracked(AllocationType type, void *p, size_t size) {
+            AMS_UNUSED(type);
+            AMS_UNUSED(size);
+
+            if (g_dynamic_app_heap.TryFree(p)) {
+                if (!g_building_from_dynamic_heap) {
+                    g_dynamic_app_heap.TryRelease();
+                }
+            } else if (g_dynamic_sys_heap.TryFree(p)) {
+                if (!g_building_from_dynamic_heap) {
+                    g_dynamic_sys_heap.TryRelease();
+                }
+            } else {
+                std::free(p);
+            }
+        }
+
         namespace {
 
             constexpr u32 EmptyEntry = 0xFFFFFFFF;
@@ -71,22 +272,23 @@ namespace ams::mitm::fs {
                     static constexpr size_t MaxCachedSize = (1_MB / 4);
                 private:
                     size_t m_cache_bitsize;
+                    size_t m_cache_size;
                 protected:
                     void *m_cache;
                 protected:
                     DynamicTableCache(size_t sz) {
-                        size_t cache_size = util::CeilingPowerOfTwo(std::min(sz, MaxCachedSize));
-                        m_cache = std::malloc(cache_size);
+                        m_cache_size = util::CeilingPowerOfTwo(std::min(sz, MaxCachedSize));
+                        m_cache = AllocateTracked(AllocationType_TableCache, m_cache_size);
                         while (m_cache == nullptr) {
-                            cache_size >>= 1;
-                            AMS_ABORT_UNLESS(cache_size >= 16_KB);
-                            m_cache = std::malloc(cache_size);
+                            m_cache_size  >>= 1;
+                            AMS_ABORT_UNLESS(m_cache_size  >= 16_KB);
+                            m_cache = AllocateTracked(AllocationType_TableCache, m_cache_size);
                         }
-                        m_cache_bitsize = util::CountTrailingZeros(cache_size);
+                        m_cache_bitsize = util::CountTrailingZeros(m_cache_size);
                     }
 
                     ~DynamicTableCache() {
-                        std::free(m_cache);
+                        FreeTracked(AllocationType_TableCache, m_cache, m_cache_size);
                     }
 
                     ALWAYS_INLINE size_t GetCacheSize() const { return static_cast<size_t>(1) << m_cache_bitsize; }
@@ -113,21 +315,33 @@ namespace ams::mitm::fs {
                     size_t m_cache_idx;
                     u8 m_fallback_cache[FallbackCacheSize];
                 private:
-                    ALWAYS_INLINE void Read(size_t ofs, void *dst, size_t size) {
-                        R_ABORT_UNLESS(m_storage->Read(m_offset + ofs, dst, size));
+                    ALWAYS_INLINE bool Read(size_t ofs, void *dst, size_t size) {
+                        R_TRY_CATCH(m_storage->Read(m_offset + ofs, dst, size)) {
+                            R_CATCH(fs::ResultNcaExternalKeyNotFound) { return false; }
+                        } R_END_TRY_CATCH_WITH_ABORT_UNLESS;
+
+                        return true;
                     }
-                    ALWAYS_INLINE void ReloadCacheImpl(size_t idx) {
+                    ALWAYS_INLINE bool ReloadCacheImpl(size_t idx) {
                         const size_t rel_ofs = idx * this->GetCacheSize();
                         AMS_ABORT_UNLESS(rel_ofs < m_size);
                         const size_t new_cache_size = std::min(m_size - rel_ofs, this->GetCacheSize());
-                        this->Read(rel_ofs, m_cache, new_cache_size);
+                        if (!this->Read(rel_ofs, m_cache, new_cache_size)) {
+                            return false;
+                        }
+
                         m_cache_idx = idx;
+                        return true;
                     }
 
-                    ALWAYS_INLINE void ReloadCache(size_t idx) {
+                    ALWAYS_INLINE bool ReloadCache(size_t idx) {
                         if (m_cache_idx != idx) {
-                            this->ReloadCacheImpl(idx);
+                            if (!this->ReloadCacheImpl(idx)) {
+                                return false;
+                            }
                         }
+
+                        return true;
                     }
 
                     ALWAYS_INLINE size_t GetCacheIndex(u32 ofs) {
@@ -140,13 +354,18 @@ namespace ams::mitm::fs {
                     }
 
                     const Entry *GetEntry(u32 entry_offset) {
-                        this->ReloadCache(this->GetCacheIndex(entry_offset));
+                        if (!this->ReloadCache(this->GetCacheIndex(entry_offset))) {
+                            return nullptr;
+                        }
 
                         const size_t ofs = entry_offset % this->GetCacheSize();
 
                         const Entry *entry = reinterpret_cast<const Entry *>(reinterpret_cast<uintptr_t>(m_cache) + ofs);
                         if (AMS_UNLIKELY(this->GetCacheIndex(entry_offset) != this->GetCacheIndex(entry_offset + sizeof(Entry) + entry->name_size + sizeof(u32)))) {
-                            this->Read(entry_offset, m_fallback_cache, std::min(m_size - entry_offset, FallbackCacheSize));
+                            if (!this->Read(entry_offset, m_fallback_cache, std::min(m_size - entry_offset, FallbackCacheSize))) {
+                                return nullptr;
+                            }
+
                             entry = reinterpret_cast<const Entry *>(m_fallback_cache);
                         }
                         return entry;
@@ -293,13 +512,28 @@ namespace ams::mitm::fs {
         }
 
         Builder::Builder(ncm::ProgramId pr_id) : m_program_id(pr_id), m_num_dirs(0), m_num_files(0), m_dir_table_size(0), m_file_table_size(0), m_dir_hash_table_size(0), m_file_hash_table_size(0), m_file_partition_size(0) {
-            auto res = m_directories.emplace(std::make_unique<BuildDirectoryContext>(BuildDirectoryContext::RootTag{}));
+            /* Ensure only one romfs is built at any time. */
+            g_romfs_build_lock.Lock();
+
+            /* If we should be using dynamic heap, turn it on. */
+            InitializeDynamicHeapForBuildRomfs(m_program_id);
+
+            auto res = m_directories.emplace(std::unique_ptr<BuildDirectoryContext>(AllocateTyped<BuildDirectoryContext>(AllocationType_BuildDirContext, BuildDirectoryContext::RootTag{})));
             AMS_ABORT_UNLESS(res.second);
             m_root = res.first->get();
             m_num_dirs = 1;
             m_dir_table_size = 0x18;
         }
 
+        Builder::~Builder() {
+            /* If we have nothing remaining in dynamic heap, release it. */
+            FinalizeDynamicHeapForBuildRomfs();
+
+            /* Release the romfs build lock. */
+            g_romfs_build_lock.Unlock();
+        }
+
+
         void Builder::AddDirectory(BuildDirectoryContext **out, BuildDirectoryContext *parent_ctx, std::unique_ptr<BuildDirectoryContext> child_ctx) {
             /* Set parent context member. */
             child_ctx->parent = parent_ctx;
@@ -347,9 +581,9 @@ namespace ams::mitm::fs {
             AMS_ABORT_UNLESS(num_child_dirs >= 0);
 
             {
-                BuildDirectoryContext **child_dirs = num_child_dirs != 0 ? reinterpret_cast<BuildDirectoryContext **>(std::malloc(sizeof(BuildDirectoryContext *) * num_child_dirs)) : nullptr;
+                BuildDirectoryContext **child_dirs = num_child_dirs != 0 ? reinterpret_cast<BuildDirectoryContext **>(AllocateTracked(AllocationType_DirPointerArray, sizeof(BuildDirectoryContext *) * num_child_dirs)) : nullptr;
                 AMS_ABORT_UNLESS(num_child_dirs == 0 || child_dirs != nullptr);
-                ON_SCOPE_EXIT { std::free(child_dirs); };
+                ON_SCOPE_EXIT { if (child_dirs != nullptr) { FreeTracked(AllocationType_DirPointerArray, child_dirs, sizeof(BuildDirectoryContext *) * num_child_dirs); } };
 
                 s64 cur_child_dir_ind = 0;
                 {
@@ -368,12 +602,12 @@ namespace ams::mitm::fs {
                             AMS_ABORT_UNLESS(child_dirs != nullptr);
 
                             BuildDirectoryContext *real_child = nullptr;
-                            this->AddDirectory(std::addressof(real_child), parent, std::make_unique<BuildDirectoryContext>(m_dir_entry.name, strlen(m_dir_entry.name)));
+                            this->AddDirectory(std::addressof(real_child), parent, std::unique_ptr<BuildDirectoryContext>(AllocateTyped<BuildDirectoryContext>(AllocationType_BuildDirContext, m_dir_entry.name, strlen(m_dir_entry.name))));
                             AMS_ABORT_UNLESS(real_child != nullptr);
                             child_dirs[cur_child_dir_ind++] = real_child;
                             AMS_ABORT_UNLESS(cur_child_dir_ind <= num_child_dirs);
                         } else /* if (m_dir_entry.type == FsDirEntryType_File) */ {
-                            this->AddFile(parent, std::make_unique<BuildFileContext>(m_dir_entry.name, strlen(m_dir_entry.name), m_dir_entry.file_size, 0, m_cur_source_type));
+                            this->AddFile(parent, std::unique_ptr<BuildFileContext>(AllocateTyped<BuildFileContext>(AllocationType_BuildFileContext, m_dir_entry.name, strlen(m_dir_entry.name), m_dir_entry.file_size, 0, m_cur_source_type)));
                         }
                     }
                 }
@@ -398,12 +632,18 @@ namespace ams::mitm::fs {
 
         void Builder::VisitDirectory(BuildDirectoryContext *parent, u32 parent_offset, DirectoryTableReader &dir_table, FileTableReader &file_table) {
             const DirectoryEntry *parent_entry = dir_table.GetEntry(parent_offset);
+            if (AMS_UNLIKELY(parent_entry == nullptr)) {
+                return;
+            }
 
             u32 cur_file_offset = parent_entry->file;
             while (cur_file_offset != EmptyEntry) {
                 const FileEntry *cur_file = file_table.GetEntry(cur_file_offset);
+                if (AMS_UNLIKELY(cur_file == nullptr)) {
+                    return;
+                }
 
-                this->AddFile(parent, std::make_unique<BuildFileContext>(cur_file->name, cur_file->name_size, cur_file->size, cur_file->offset, m_cur_source_type));
+                this->AddFile(parent, std::unique_ptr<BuildFileContext>(AllocateTyped<BuildFileContext>(AllocationType_BuildFileContext, cur_file->name, cur_file->name_size, cur_file->size, cur_file->offset, m_cur_source_type)));
 
                 cur_file_offset = cur_file->sibling;
             }
@@ -414,8 +654,11 @@ namespace ams::mitm::fs {
                 u32 next_child_offset = 0;
                 {
                     const DirectoryEntry *cur_child = dir_table.GetEntry(cur_child_offset);
+                    if (AMS_UNLIKELY(cur_child == nullptr)) {
+                        return;
+                    }
 
-                    this->AddDirectory(std::addressof(real_child), parent, std::make_unique<BuildDirectoryContext>(cur_child->name, cur_child->name_size));
+                    this->AddDirectory(std::addressof(real_child), parent, std::unique_ptr<BuildDirectoryContext>(AllocateTyped<BuildDirectoryContext>(AllocationType_BuildDirContext, cur_child->name, cur_child->name_size)));
                     AMS_ABORT_UNLESS(real_child != nullptr);
 
                     next_child_offset = cur_child->sibling;
@@ -438,7 +681,7 @@ namespace ams::mitm::fs {
             /* If there is no romfs folder on the SD, don't bother continuing. */
             {
                 FsDir dir;
-                if (R_FAILED(mitm::fs::OpenAtmosphereRomfsDirectory(std::addressof(dir), m_program_id, m_root->path.get(), OpenDirectoryMode_Directory, std::addressof(sd_filesystem)))) {
+                if (R_FAILED(mitm::fs::OpenAtmosphereRomfsDirectory(std::addressof(dir), m_program_id, m_root->path, OpenDirectoryMode_Directory, std::addressof(sd_filesystem)))) {
                     return;
                 }
                 fsDirClose(std::addressof(dir));
@@ -461,7 +704,7 @@ namespace ams::mitm::fs {
             this->VisitDirectory(m_root, 0x0, dir_table, file_table);
         }
 
-        void Builder::Build(std::vector<SourceInfo> *out_infos) {
+        void Builder::Build(SourceInfoVector *out_infos) {
             /* Clear output. */
             out_infos->clear();
 
@@ -477,7 +720,7 @@ namespace ams::mitm::fs {
             m_file_hash_table_size = sizeof(u32) * num_file_hash_table_entries;
 
             /* Allocate metadata, make pointers. */
-            Header *header = reinterpret_cast<Header *>(std::malloc(sizeof(Header)));
+            Header *header = reinterpret_cast<Header *>(AllocateTracked(AllocationType_Memory, sizeof(Header)));
             std::memset(header, 0x00, sizeof(*header));
 
             /* Open metadata file. */
@@ -552,13 +795,13 @@ namespace ams::mitm::fs {
             /* Set all files' hash value = hash index. */
             for (const auto &it : m_files) {
                 BuildFileContext *cur_file = it.get();
-                cur_file->hash_value = CalculatePathHash(cur_file->parent->entry_offset, cur_file->path.get(), 0, cur_file->path_len) % num_file_hash_table_entries;
+                cur_file->hash_value = CalculatePathHash(cur_file->parent->entry_offset, cur_file->path, 0, cur_file->path_len) % num_file_hash_table_entries;
             }
 
             /* Set all directories' hash value = hash index. */
             for (const auto &it : m_directories) {
                 BuildDirectoryContext *cur_dir = it.get();
-                cur_dir->hash_value = CalculatePathHash(cur_dir == m_root ? 0 : cur_dir->parent->entry_offset, cur_dir->path.get(), 0, cur_dir->path_len) % num_dir_hash_table_entries;
+                cur_dir->hash_value = CalculatePathHash(cur_dir == m_root ? 0 : cur_dir->parent->entry_offset, cur_dir->path, 0, cur_dir->path_len) % num_dir_hash_table_entries;
             }
 
             /* Write hash tables. */
@@ -661,7 +904,7 @@ namespace ams::mitm::fs {
                     const u32 name_size = cur_file->path_len;
                     cur_entry->name_size = name_size;
                     if (name_size) {
-                        std::memcpy(cur_entry->name, cur_file->path.get(), name_size);
+                        std::memcpy(cur_entry->name, cur_file->path, name_size);
                         for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) {
                             cur_entry->name[i] = 0;
                         }
@@ -688,9 +931,10 @@ namespace ams::mitm::fs {
                                 AMS_ABORT_UNLESS(path_needed_size <= sizeof(full_path));
                                 cur_file->GetPath(full_path);
 
-                                cur_file->path.reset();
+                                FreeTracked(AllocationType_FileName, cur_file->path, cur_file->path_len + 1);
+                                cur_file->path = nullptr;
 
-                                char *new_path = new char[path_needed_size];
+                                char *new_path = static_cast<char *>(AllocateTracked(AllocationType_FullPath, path_needed_size));
                                 std::memcpy(new_path, full_path, path_needed_size);
                                 out_infos->emplace_back(cur_file->offset + FilePartitionOffset, cur_file->size, cur_file->source_type, new_path);
                             }
@@ -719,7 +963,7 @@ namespace ams::mitm::fs {
                     const u32 name_size = cur_dir->path_len;
                     cur_entry->name_size = name_size;
                     if (name_size) {
-                        std::memcpy(cur_entry->name, cur_dir->path.get(), name_size);
+                        std::memcpy(cur_entry->name, cur_dir->path, name_size);
                         for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) {
                             cur_entry->name[i] = 0;
                         }
@@ -751,6 +995,39 @@ namespace ams::mitm::fs {
             }
         }
 
+        Result ConfigureDynamicHeap(u64 *out_size, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application) {
+            /* Baseline: use no dynamic heap. */
+            *out_size = 0;
+
+            /* If the process is not an application, we do not care about dynamic heap. */
+            R_SUCCEED_IF(!is_application);
+
+            /* First, we need to ensure that, if the game used dynamic heap, we clear it. */
+            if (g_dynamic_app_heap.heap_size > 0) {
+                mitm::fs::FinalizeLayeredRomfsStorage(g_dynamic_heap_program_id);
+
+                /* Free the heap. */
+                g_dynamic_app_heap.Reset();
+                g_dynamic_sys_heap.Reset();
+            }
+
+            /* Next, if we aren't going to end up building a romfs, we can ignore dynamic heap. */
+            R_SUCCEED_IF(!status.IsProgramSpecific());
+
+            /* Only mitm if there is actually an override romfs. */
+            R_SUCCEED_IF(!mitm::fs::HasSdRomfsContent(program_id));
+
+            /* Next, set the new program id for dynamic heap. */
+            g_dynamic_heap_program_id    = program_id;
+            g_dynamic_app_heap.heap_size = GetDynamicAppHeapSize(g_dynamic_heap_program_id);
+            g_dynamic_sys_heap.heap_size = GetDynamicSysHeapSize(g_dynamic_heap_program_id);
+
+            /* Set output. */
+            *out_size = g_dynamic_app_heap.heap_size;
+
+            R_SUCCEED();
+        }
+
     }
 
 }
diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.hpp
index 98e74c430..5eeb7dc08 100644
--- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.hpp
+++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.hpp
@@ -27,6 +27,52 @@ namespace ams::mitm::fs::romfs {
         Memory,
     };
 
+    enum AllocationType {
+        AllocationType_FileName,
+        AllocationType_DirName,
+        AllocationType_FullPath,
+        AllocationType_SourceInfo,
+        AllocationType_BuildFileContext,
+        AllocationType_BuildDirContext,
+        AllocationType_TableCache,
+        AllocationType_DirPointerArray,
+        AllocationType_DirContextSet,
+        AllocationType_FileContextSet,
+        AllocationType_Memory,
+
+        AllocationType_Count,
+    };
+
+    void *AllocateTracked(AllocationType type, size_t size);
+    void FreeTracked(AllocationType type, void *p, size_t size);
+
+    template<typename T, typename... Args>
+    T *AllocateTyped(AllocationType type, Args &&... args) {
+        void *mem = AllocateTracked(type, sizeof(T));
+        return std::construct_at(static_cast<T *>(mem), std::forward<Args>(args)...);
+    }
+
+    template<AllocationType AllocType, typename T>
+    class TrackedAllocator {
+        public:
+            using value_type = T;
+
+            template<typename U>
+            struct rebind {
+                using other = TrackedAllocator<AllocType, U>;
+            };
+        public:
+            TrackedAllocator() = default;
+
+            T *allocate(size_t n) {
+                return static_cast<T *>(AllocateTracked(AllocType, sizeof(T) * n));
+            }
+
+            void deallocate(T *p, size_t n) {
+                FreeTracked(AllocType, p, sizeof(T) * n);
+            }
+    };
+
     struct SourceInfo {
         s64 virtual_offset;
         s64 size;
@@ -89,10 +135,10 @@ namespace ams::mitm::fs::romfs {
                     delete this->metadata_source_info.file;
                     break;
                 case DataSourceType::LooseSdFile:
-                    delete[] this->loose_source_info.path;
+                    FreeTracked(AllocationType_FullPath, this->loose_source_info.path, std::strlen(this->loose_source_info.path) + 1);
                     break;
                 case DataSourceType::Memory:
-                    std::free(static_cast<void *>(this->memory_source_info.data));
+                    FreeTracked(AllocationType_Memory, this->memory_source_info.data, this->size);
                     break;
                 AMS_UNREACHABLE_DEFAULT_CASE();
             }
@@ -113,7 +159,7 @@ namespace ams::mitm::fs::romfs {
         NON_COPYABLE(BuildDirectoryContext);
         NON_MOVEABLE(BuildDirectoryContext);
 
-        std::unique_ptr<char[]> path;
+        char *path;
         union {
             BuildDirectoryContext *parent;
         };
@@ -139,16 +185,28 @@ namespace ams::mitm::fs::romfs {
         struct RootTag{};
 
         BuildDirectoryContext(RootTag) : parent(nullptr), child(nullptr), sibling(nullptr), file(nullptr), path_len(0), entry_offset(0), hash_value(0xFFFFFFFF) {
-            this->path = std::make_unique<char[]>(1);
+            this->path = static_cast<char *>(AllocateTracked(AllocationType_DirName, 1));
+            this->path[0] = '\x00';
         }
 
         BuildDirectoryContext(const char *entry_name, size_t entry_name_len) : parent(nullptr), child(nullptr), sibling(nullptr), file(nullptr), entry_offset(0) {
             this->path_len = entry_name_len;
-            this->path = std::unique_ptr<char[]>(new char[this->path_len + 1]);
-            std::memcpy(this->path.get(), entry_name, entry_name_len);
+            this->path = static_cast<char *>(AllocateTracked(AllocationType_DirName, this->path_len + 1));
+            std::memcpy(this->path, entry_name, entry_name_len);
             this->path[this->path_len] = '\x00';
         }
 
+        ~BuildDirectoryContext() {
+            if (this->path != nullptr) {
+                FreeTracked(AllocationType_DirName, this->path, this->path_len + 1);
+                this->path = nullptr;
+            }
+        }
+
+        void operator delete(void *p) {
+            FreeTracked(AllocationType_BuildDirContext, p, sizeof(BuildDirectoryContext));
+        }
+
         size_t GetPathLength() const {
             if (this->parent == nullptr) {
                 return 0;
@@ -165,7 +223,7 @@ namespace ams::mitm::fs::romfs {
 
             const size_t parent_len = this->parent->GetPath(dst);
             dst[parent_len] = '/';
-            std::memcpy(dst + parent_len + 1, this->path.get(), this->path_len);
+            std::memcpy(dst + parent_len + 1, this->path, this->path_len);
             dst[parent_len + 1 + this->path_len] = '\x00';
             return parent_len + 1 + this->path_len;
         }
@@ -187,7 +245,7 @@ namespace ams::mitm::fs::romfs {
         NON_COPYABLE(BuildFileContext);
         NON_MOVEABLE(BuildFileContext);
 
-        std::unique_ptr<char[]> path;
+        char *path;
         BuildDirectoryContext *parent;
         union {
             BuildFileContext *sibling;
@@ -203,11 +261,22 @@ namespace ams::mitm::fs::romfs {
 
         BuildFileContext(const char *entry_name, size_t entry_name_len, s64 sz, s64 o_o, DataSourceType type) : parent(nullptr), sibling(nullptr), offset(0), size(sz), orig_offset(o_o), entry_offset(0), hash_value(0xFFFFFFFF), source_type(type) {
             this->path_len = entry_name_len;
-            this->path = std::unique_ptr<char[]>(new char[this->path_len + 1]);
-            std::memcpy(this->path.get(), entry_name, entry_name_len);
+            this->path = static_cast<char *>(AllocateTracked(AllocationType_FileName, this->path_len + 1));
+            std::memcpy(this->path, entry_name, entry_name_len);
             this->path[this->path_len] = 0;
         }
 
+        ~BuildFileContext() {
+            if (this->path != nullptr) {
+                FreeTracked(AllocationType_FileName, this->path, this->path_len + 1);
+                this->path = nullptr;
+            }
+        }
+
+        void operator delete(void *p) {
+            FreeTracked(AllocationType_BuildFileContext, p, sizeof(BuildFileContext));
+        }
+
         size_t GetPathLength() const {
             if (this->parent == nullptr) {
                 return 0;
@@ -224,7 +293,7 @@ namespace ams::mitm::fs::romfs {
 
             const size_t parent_len = this->parent->GetPath(dst);
             dst[parent_len] = '/';
-            std::memcpy(dst + parent_len + 1, this->path.get(), this->path_len);
+            std::memcpy(dst + parent_len + 1, this->path, this->path_len);
             dst[parent_len + 1 + this->path_len] = '\x00';
             return parent_len + 1 + this->path_len;
         }
@@ -248,6 +317,8 @@ namespace ams::mitm::fs::romfs {
     class Builder {
         NON_COPYABLE(Builder);
         NON_MOVEABLE(Builder);
+        public:
+            using SourceInfoVector = std::vector<SourceInfo, TrackedAllocator<AllocationType_SourceInfo, SourceInfo>>;
         private:
             template<typename T>
             struct Comparator {
@@ -270,13 +341,13 @@ namespace ams::mitm::fs::romfs {
                 }
             };
 
-            template<typename T>
-            using ContextSet = std::set<std::unique_ptr<T>, Comparator<T>>;
+            template<AllocationType AllocType, typename T>
+            using ContextSet = std::set<std::unique_ptr<T>, Comparator<T>, TrackedAllocator<AllocType, std::unique_ptr<T>>>;
         private:
             ncm::ProgramId m_program_id;
             BuildDirectoryContext *m_root;
-            ContextSet<BuildDirectoryContext> m_directories;
-            ContextSet<BuildFileContext> m_files;
+            ContextSet<AllocationType_DirContextSet, BuildDirectoryContext> m_directories;
+            ContextSet<AllocationType_FileContextSet, BuildFileContext> m_files;
             size_t m_num_dirs;
             size_t m_num_files;
             size_t m_dir_table_size;
@@ -295,11 +366,14 @@ namespace ams::mitm::fs::romfs {
             void AddFile(BuildDirectoryContext *parent_ctx, std::unique_ptr<BuildFileContext> file_ctx);
         public:
             Builder(ncm::ProgramId pr_id);
+            ~Builder();
 
             void AddSdFiles();
             void AddStorageFiles(ams::fs::IStorage *storage, DataSourceType source_type);
 
-            void Build(std::vector<SourceInfo> *out_infos);
+            void Build(SourceInfoVector *out_infos);
     };
 
+    Result ConfigureDynamicHeap(u64 *out_size, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application);
+
 }
diff --git a/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_module.cpp b/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_module.cpp
new file mode 100644
index 000000000..3de64b402
--- /dev/null
+++ b/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_module.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stratosphere.hpp>
+#include "../amsmitm_initialization.hpp"
+#include "mitm_pm_module.hpp"
+#include "mitm_pm_service.hpp"
+
+namespace ams::mitm::pm {
+
+    namespace {
+
+        constexpr sm::ServiceName PmServiceName = sm::ServiceName::Encode("mitm:pm");
+        constexpr size_t          PmMaxSessions = 1;
+
+        constexpr size_t MaxServers = 1;
+        constexpr size_t MaxSessions = PmMaxSessions;
+        using ServerOptions = sf::hipc::DefaultServerManagerOptions;
+        sf::hipc::ServerManager<MaxServers, ServerOptions, MaxSessions> g_server_manager;
+
+        constinit sf::UnmanagedServiceObject<mitm::pm::impl::IPmInterface, mitm::pm::PmService> g_pm_service_object;
+
+    }
+
+    void MitmModule::ThreadFunction(void *) {
+        /* Create bpc:ams. */
+        R_ABORT_UNLESS(g_server_manager.RegisterObjectForServer(g_pm_service_object.GetShared(), PmServiceName, PmMaxSessions));
+
+        /* Loop forever, servicing our services. */
+        g_server_manager.LoopProcess();
+    }
+
+}
diff --git a/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_module.hpp b/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_module.hpp
new file mode 100644
index 000000000..d4e061b8d
--- /dev/null
+++ b/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_module.hpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+#include <stratosphere.hpp>
+#include "../amsmitm_module.hpp"
+
+namespace ams::mitm::pm {
+
+    DEFINE_MITM_MODULE_CLASS(0x1000, AMS_GET_SYSTEM_THREAD_PRIORITY(fs, WorkerThreadPool) - 2);
+
+}
diff --git a/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_service.cpp b/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_service.cpp
new file mode 100644
index 000000000..8428e74f2
--- /dev/null
+++ b/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_service.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stratosphere.hpp>
+#include "../amsmitm_initialization.hpp"
+#include "mitm_pm_service.hpp"
+#include "mitm_pm_service.hpp"
+#include "../fs_mitm/fsmitm_romfs.hpp"
+
+namespace ams::mitm::pm {
+
+    Result PmService::PrepareLaunchProgram(sf::Out<u64> out, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application) {
+        /* Default to zero heap. */
+        *out = 0;
+
+        /* Actually configure the required boost size for romfs. */
+        R_TRY(mitm::fs::romfs::ConfigureDynamicHeap(out.GetPointer(), program_id, status, is_application));
+
+        /* TODO: Is there anything else we should do, while we have the opportunity? */
+        R_SUCCEED();
+    }
+
+}
diff --git a/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_service.hpp b/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_service.hpp
new file mode 100644
index 000000000..2d28ed182
--- /dev/null
+++ b/stratosphere/ams_mitm/source/mitm_pm/mitm_pm_service.hpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+#include <stratosphere.hpp>
+
+namespace ams::mitm::pm {
+
+    class PmService {
+        public:
+            Result PrepareLaunchProgram(sf::Out<u64> out, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application);
+    };
+    static_assert(impl::IsIPmInterface<PmService>);
+
+}
diff --git a/stratosphere/ams_mitm/source/ns_mitm/ns_am_mitm_service.cpp b/stratosphere/ams_mitm/source/ns_mitm/ns_am_mitm_service.cpp
index 11f89bafe..0754f8a7b 100644
--- a/stratosphere/ams_mitm/source/ns_mitm/ns_am_mitm_service.cpp
+++ b/stratosphere/ams_mitm/source/ns_mitm/ns_am_mitm_service.cpp
@@ -27,7 +27,7 @@ namespace ams::mitm::ns {
     Result NsAmMitmService::ResolveApplicationContentPath(ncm::ProgramId application_id, u8 content_type) {
         /* Always succeed for web applets asking about HBL to enable hbl_html, and applications with manual_html to allow custom manual data. */
         bool is_hbl = false;
-        if ((R_SUCCEEDED(pm::info::IsHblProgramId(std::addressof(is_hbl), application_id)) && is_hbl) || (static_cast<ncm::ContentType>(content_type) == ncm::ContentType::HtmlDocument && mitm::fs::HasSdManualHtmlContent(application_id))) {
+        if ((R_SUCCEEDED(ams::pm::info::IsHblProgramId(std::addressof(is_hbl), application_id)) && is_hbl) || (static_cast<ncm::ContentType>(content_type) == ncm::ContentType::HtmlDocument && mitm::fs::HasSdManualHtmlContent(application_id))) {
             nsamResolveApplicationContentPathFwd(m_forward_service.get(), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type));
             R_SUCCEED();
         }
diff --git a/stratosphere/ams_mitm/source/ns_mitm/ns_web_mitm_service.cpp b/stratosphere/ams_mitm/source/ns_mitm/ns_web_mitm_service.cpp
index aa9972f65..9568f9108 100644
--- a/stratosphere/ams_mitm/source/ns_mitm/ns_web_mitm_service.cpp
+++ b/stratosphere/ams_mitm/source/ns_mitm/ns_web_mitm_service.cpp
@@ -27,7 +27,7 @@ namespace ams::mitm::ns {
     Result NsDocumentService::ResolveApplicationContentPath(ncm::ProgramId application_id, u8 content_type) {
         /* Always succeed for web applets asking about HBL to enable hbl_html, and applications with manual_html to allow custom manual data. */
         bool is_hbl = false;
-        if ((R_SUCCEEDED(pm::info::IsHblProgramId(std::addressof(is_hbl), application_id)) && is_hbl) || (static_cast<ncm::ContentType>(content_type) == ncm::ContentType::HtmlDocument && mitm::fs::HasSdManualHtmlContent(application_id))) {
+        if ((R_SUCCEEDED(ams::pm::info::IsHblProgramId(std::addressof(is_hbl), application_id)) && is_hbl) || (static_cast<ncm::ContentType>(content_type) == ncm::ContentType::HtmlDocument && mitm::fs::HasSdManualHtmlContent(application_id))) {
             nswebResolveApplicationContentPath(m_srv.get(), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type));
             R_SUCCEED();
         }
diff --git a/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp
index 07686ff77..0915a10ab 100644
--- a/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp
+++ b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp
@@ -32,7 +32,7 @@ namespace ams::mitm::settings {
     SetMitmService::SetMitmService(std::shared_ptr<::Service> &&s, const sm::MitmProcessInfo &c) : sf::MitmServiceImplBase(std::forward<std::shared_ptr<::Service>>(s), c) {
         if (m_client_info.program_id == ncm::SystemProgramId::Ns) {
             os::ProcessId application_process_id;
-            if (R_SUCCEEDED(pm::dmnt::GetApplicationProcessId(std::addressof(application_process_id))) && g_application_process_id == application_process_id) {
+            if (R_SUCCEEDED(ams::pm::dmnt::GetApplicationProcessId(std::addressof(application_process_id))) && g_application_process_id == application_process_id) {
                 m_locale = g_application_locale;
                 m_is_valid_language = g_valid_language;
                 m_is_valid_region   = g_valid_region;
@@ -61,8 +61,8 @@ namespace ams::mitm::settings {
 
             if (is_ns) {
                 /* When NS asks for a locale, refresh to get the current application locale. */
-                R_TRY(pm::dmnt::GetApplicationProcessId(std::addressof(application_process_id)));
-                R_TRY(pm::info::GetProgramId(std::addressof(program_id), application_process_id));
+                R_TRY(ams::pm::dmnt::GetApplicationProcessId(std::addressof(application_process_id)));
+                R_TRY(ams::pm::info::GetProgramId(std::addressof(program_id), application_process_id));
             }
             m_locale            = cfg::GetOverrideLocale(program_id);
             m_is_valid_language = settings::IsValidLanguageCode(m_locale.language_code);
diff --git a/stratosphere/ams_mitm/source/set_mitm/setmitm_module.cpp b/stratosphere/ams_mitm/source/set_mitm/setmitm_module.cpp
index 68579bb40..c756c028f 100644
--- a/stratosphere/ams_mitm/source/set_mitm/setmitm_module.cpp
+++ b/stratosphere/ams_mitm/source/set_mitm/setmitm_module.cpp
@@ -29,7 +29,7 @@ namespace ams::mitm::settings {
             PortIndex_Count,
         };
 
-        constexpr sm::ServiceName SetMitmServiceName = sm::ServiceName::Encode("set");
+        constexpr sm::ServiceName SetMitmServiceName    = sm::ServiceName::Encode("set");
         constexpr sm::ServiceName SetSysMitmServiceName = sm::ServiceName::Encode("set:sys");
 
         struct ServerOptions {
diff --git a/stratosphere/fatal/fatal.json b/stratosphere/fatal/fatal.json
index 70212ded5..45b718089 100644
--- a/stratosphere/fatal/fatal.json
+++ b/stratosphere/fatal/fatal.json
@@ -79,7 +79,7 @@
 				"svcReplyAndReceiveWithUserBuffer":	"0x44",
 				"svcCreateEvent":	"0x45",
 				"svcMapPhysicalMemoryUnsafe":	"0x48",
-				"svcUnmapPhysicalMemoryUnsafe":	"0x48",
+				"svcUnmapPhysicalMemoryUnsafe":	"0x49",
 				"svcSetUnsafeLimit":	"0x4A",
 				"svcReadWriteRegister":	"0x4E",
                 "svcDebugActiveProcess": "0x60",
diff --git a/stratosphere/fatal/source/fatal_task_screen.cpp b/stratosphere/fatal/source/fatal_task_screen.cpp
index 03078706e..08db5be4e 100644
--- a/stratosphere/fatal/source/fatal_task_screen.cpp
+++ b/stratosphere/fatal/source/fatal_task_screen.cpp
@@ -69,7 +69,7 @@ namespace ams::fatal::srv {
             /* Neither heap nor insecure is available, so we're going to have to try to raid the unsafe pool. */
             {
                 /* First, increase the limit to an extremely high value. */
-                size_t large_size = std::max(64_MB, FrameBufferRequiredSizeHeapAligned);
+                size_t large_size = std::max(128_MB, FrameBufferRequiredSizeHeapAligned);
                 while (svc::ResultLimitReached::Includes(svc::SetUnsafeLimit(large_size))) {
                     large_size *= 2;
                 }
diff --git a/stratosphere/pm/source/impl/pm_process_manager.cpp b/stratosphere/pm/source/impl/pm_process_manager.cpp
index 2f2d1b89a..db0648acc 100644
--- a/stratosphere/pm/source/impl/pm_process_manager.cpp
+++ b/stratosphere/pm/source/impl/pm_process_manager.cpp
@@ -244,6 +244,24 @@ namespace ams::pm::impl {
                 /* If we fail after now, unpin. */
                 ON_RESULT_FAILURE { ldr::pm::UnpinProgram(pin_id); };
 
+                /* Ensure we can talk to mitm services. */
+                {
+                    AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(bool, s_initialized_mitm, false);
+                    if (!s_initialized_mitm) {
+                        mitm::pm::Initialize();
+                        s_initialized_mitm = true;
+                    }
+                }
+
+                /* Determine boost size for mitm. */
+                u64 mitm_boost_size = 0;
+                R_TRY(mitm::pm::PrepareLaunchProgram(std::addressof(mitm_boost_size), program_info.program_id, override_status, is_application));
+
+                if (mitm_boost_size > 0 || is_application) {
+                    R_ABORT_UNLESS(BoostSystemMemoryResourceLimitForMitm(mitm_boost_size));
+                }
+                ON_RESULT_FAILURE_2 { if (mitm_boost_size > 0 || is_application) { R_ABORT_UNLESS(BoostSystemMemoryResourceLimitForMitm(0)); } };
+
                 /* Ensure resources are available. */
                 resource::WaitResourceAvailable(std::addressof(program_info));
 
@@ -713,4 +731,8 @@ namespace ams::pm::impl {
         R_RETURN(resource::GetResourceLimitValues(out_cur_val, out_lim_val, static_cast<ResourceLimitGroup>(group), static_cast<svc::LimitableResource>(resource)));
     }
 
+    Result BoostSystemMemoryResourceLimitForMitm(u64 boost_size) {
+        R_RETURN(resource::BoostSystemMemoryResourceLimitForMitm(boost_size));
+    }
+
 }
diff --git a/stratosphere/pm/source/impl/pm_process_manager.hpp b/stratosphere/pm/source/impl/pm_process_manager.hpp
index d2af22ac2..83ccc7a85 100644
--- a/stratosphere/pm/source/impl/pm_process_manager.hpp
+++ b/stratosphere/pm/source/impl/pm_process_manager.hpp
@@ -55,5 +55,6 @@ namespace ams::pm::impl {
     Result GetAppletCurrentResourceLimitValues(pm::ResourceLimitValues *out);
     Result GetAppletPeakResourceLimitValues(pm::ResourceLimitValues *out);
     Result AtmosphereGetCurrentLimitInfo(s64 *out_cur_val, s64 *out_lim_val, u32 group, u32 resource);
+    Result BoostSystemMemoryResourceLimitForMitm(u64 boost_size);
 
 }
diff --git a/stratosphere/pm/source/impl/pm_resource_manager.cpp b/stratosphere/pm/source/impl/pm_resource_manager.cpp
index 644a8bb0e..6c5805b5d 100644
--- a/stratosphere/pm/source/impl/pm_resource_manager.cpp
+++ b/stratosphere/pm/source/impl/pm_resource_manager.cpp
@@ -52,9 +52,16 @@ namespace ams::pm::resource {
         constinit os::SdkMutex g_resource_limit_lock;
         constinit os::NativeHandle g_resource_limit_handles[ResourceLimitGroup_Count];
         constinit spl::MemoryArrangement g_memory_arrangement = spl::MemoryArrangement_Standard;
-        constinit u64 g_system_memory_boost_size = 0;
         constinit u64 g_extra_threads_available[ResourceLimitGroup_Count];
 
+        constinit os::SdkMutex g_system_memory_boost_lock;
+        constinit u64 g_system_memory_boost_size          = 0;
+        constinit u64 g_system_memory_boost_size_for_mitm = 0;
+
+        ALWAYS_INLINE u64 GetCurrentSystemMemoryBoostSize() {
+            return g_system_memory_boost_size + g_system_memory_boost_size_for_mitm;
+        }
+
         constinit u64 g_resource_limits[ResourceLimitGroup_Count][svc::LimitableResource_Count] = {
             [ResourceLimitGroup_System] = {
                 [svc::LimitableResource_PhysicalMemoryMax]      = 0,   /* Initialized dynamically later. */
@@ -220,6 +227,47 @@ namespace ams::pm::resource {
             R_SUCCEED();
         }
 
+        Result BoostSystemMemoryResourceLimitLocked(u64 normal_boost, u64 mitm_boost) {
+            /* Check pre-conditions. */
+            AMS_ASSERT(g_system_memory_boost_lock.IsLockedByCurrentThread());
+
+            /* Determine total boost. */
+            const u64 boost_size = normal_boost + mitm_boost;
+
+            /* Don't allow all application memory to be taken away. */
+            R_UNLESS(boost_size <= g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_Application], pm::ResultInvalidSize());
+
+            const u64 new_app_size = g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_Application] - boost_size;
+            {
+                std::scoped_lock lk(g_resource_limit_lock);
+
+                if (hos::GetVersion() >= hos::Version_5_0_0) {
+                    /* Starting in 5.0.0, PM does not allow for only one of the sets to fail. */
+                    if (boost_size < GetCurrentSystemMemoryBoostSize()) {
+                        R_TRY(svc::SetUnsafeLimit(boost_size));
+                        R_ABORT_UNLESS(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
+                    } else {
+                        R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
+                        R_ABORT_UNLESS(svc::SetUnsafeLimit(boost_size));
+                    }
+                } else {
+                    const u64 new_sys_size = g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_System] + boost_size;
+                    if (boost_size < GetCurrentSystemMemoryBoostSize()) {
+                        R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_System,      new_sys_size));
+                        R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
+                    } else {
+                        R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
+                        R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_System,      new_sys_size));
+                    }
+                }
+
+                g_system_memory_boost_size         = normal_boost;
+                g_system_memory_boost_size_for_mitm = mitm_boost;
+            }
+
+            R_SUCCEED();
+        }
+
     }
 
     /* Resource API. */
@@ -352,37 +400,19 @@ namespace ams::pm::resource {
     }
 
     Result BoostSystemMemoryResourceLimit(u64 boost_size) {
-        /* Don't allow all application memory to be taken away. */
-        R_UNLESS(boost_size <= g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_Application], pm::ResultInvalidSize());
+        /* Ensure only one boost change happens at a time. */
+        std::scoped_lock lk(g_system_memory_boost_lock);
 
-        const u64 new_app_size = g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_Application] - boost_size;
-        {
-            std::scoped_lock lk(g_resource_limit_lock);
+        /* Boost to the appropriate total amount. */
+        R_RETURN(BoostSystemMemoryResourceLimitLocked(boost_size, g_system_memory_boost_size_for_mitm));
+    }
 
-            if (hos::GetVersion() >= hos::Version_5_0_0) {
-                /* Starting in 5.0.0, PM does not allow for only one of the sets to fail. */
-                if (boost_size < g_system_memory_boost_size) {
-                    R_TRY(svc::SetUnsafeLimit(boost_size));
-                    R_ABORT_UNLESS(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
-                } else {
-                    R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
-                    R_ABORT_UNLESS(svc::SetUnsafeLimit(boost_size));
-                }
-            } else {
-                const u64 new_sys_size = g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_System] + boost_size;
-                if (boost_size < g_system_memory_boost_size) {
-                    R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_System,      new_sys_size));
-                    R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
-                } else {
-                    R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
-                    R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_System,      new_sys_size));
-                }
-            }
+    Result BoostSystemMemoryResourceLimitForMitm(u64 boost_size) {
+        /* Ensure only one boost change happens at a time. */
+        std::scoped_lock lk(g_system_memory_boost_lock);
 
-            g_system_memory_boost_size = boost_size;
-        }
-
-        R_SUCCEED();
+        /* Boost to the appropriate total amount. */
+        R_RETURN(BoostSystemMemoryResourceLimitLocked(g_system_memory_boost_size, boost_size));
     }
 
     Result BoostApplicationThreadResourceLimit() {
diff --git a/stratosphere/pm/source/impl/pm_resource_manager.hpp b/stratosphere/pm/source/impl/pm_resource_manager.hpp
index bd59f28c8..029458203 100644
--- a/stratosphere/pm/source/impl/pm_resource_manager.hpp
+++ b/stratosphere/pm/source/impl/pm_resource_manager.hpp
@@ -24,6 +24,8 @@ namespace ams::pm::resource {
     Result BoostApplicationThreadResourceLimit();
     Result BoostSystemThreadResourceLimit();
 
+    Result BoostSystemMemoryResourceLimitForMitm(u64 boost_size);
+
     os::NativeHandle GetResourceLimitHandle(ResourceLimitGroup group);
     os::NativeHandle GetResourceLimitHandle(const ldr::ProgramInfo *info);