diff --git a/src/core/core.cpp b/src/core/core.cpp
index 50d5dab4b..bb268a319 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -421,6 +421,7 @@ struct System::Impl {
     bool is_async_gpu{};
 
     ExecuteProgramCallback execute_program_callback;
+    ExitCallback exit_callback;
 
     std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
     std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_dynarmic{};
@@ -798,6 +799,18 @@ void System::ExecuteProgram(std::size_t program_index) {
     }
 }
 
+void System::RegisterExitCallback(ExitCallback&& callback) {
+    impl->exit_callback = std::move(callback);
+}
+
+void System::Exit() {
+    if (impl->exit_callback) {
+        impl->exit_callback();
+    } else {
+        LOG_CRITICAL(Core, "exit_callback must be initialized by the frontend");
+    }
+}
+
 void System::ApplySettings() {
     if (IsPoweredOn()) {
         Renderer().RefreshBaseSettings();
diff --git a/src/core/core.h b/src/core/core.h
index 715ab88e7..a796472b2 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -387,6 +387,18 @@ public:
      */
     void ExecuteProgram(std::size_t program_index);
 
+    /// Type used for the frontend to designate a callback for System to exit the application.
+    using ExitCallback = std::function<void()>;
+
+    /**
+     * Registers a callback from the frontend for System to exit the application.
+     * @param callback Callback from the frontend to exit the application.
+     */
+    void RegisterExitCallback(ExitCallback&& callback);
+
+    /// Instructs the frontend to exit the application.
+    void Exit();
+
     /// Applies any changes to settings to this core instance.
     void ApplySettings();
 
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 8c2e2f920..49e9787a4 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -332,10 +332,10 @@ ISelfController::~ISelfController() = default;
 void ISelfController::Exit(Kernel::HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called");
 
-    system.Shutdown();
-
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
+
+    system.Exit();
 }
 
 void ISelfController::LockExit(Kernel::HLERequestContext& ctx) {
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 1519a46ed..8b9e186b0 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -302,12 +302,17 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
     connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
     connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram,
             Qt::QueuedConnection);
+    connect(this, &GRenderWindow::ExitSignal, parent, &GMainWindow::OnExit, Qt::QueuedConnection);
 }
 
 void GRenderWindow::ExecuteProgram(std::size_t program_index) {
     emit ExecuteProgramSignal(program_index);
 }
 
+void GRenderWindow::Exit() {
+    emit ExitSignal();
+}
+
 GRenderWindow::~GRenderWindow() {
     input_subsystem->Shutdown();
 }
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 402dd2ee1..54c4e2142 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -181,6 +181,9 @@ public:
      */
     void ExecuteProgram(std::size_t program_index);
 
+    /// Instructs the window to exit the application.
+    void Exit();
+
 public slots:
     void OnEmulationStarting(EmuThread* emu_thread);
     void OnEmulationStopping();
@@ -191,6 +194,7 @@ signals:
     void Closed();
     void FirstFrameDisplayed();
     void ExecuteProgramSignal(std::size_t program_index);
+    void ExitSignal();
     void MouseActivity();
 
 private:
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 3c2824362..0bd0c5b04 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1384,6 +1384,9 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
     system.RegisterExecuteProgramCallback(
         [this](std::size_t program_index) { render_window->ExecuteProgram(program_index); });
 
+    // Register an Exit callback such that Core can exit the currently running application.
+    system.RegisterExitCallback([this]() { render_window->Exit(); });
+
     connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
     connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity);
     // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
@@ -2469,6 +2472,10 @@ void GMainWindow::OnExecuteProgram(std::size_t program_index) {
     BootGame(last_filename_booted, 0, program_index);
 }
 
+void GMainWindow::OnExit() {
+    OnStopGame();
+}
+
 void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) {
     OverlayDialog dialog(render_window, Core::System::GetInstance(), error_code, error_text,
                          QString{}, tr("OK"), Qt::AlignLeft | Qt::AlignVCenter);
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 36eed6103..60ce01471 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -153,6 +153,7 @@ signals:
 public slots:
     void OnLoadComplete();
     void OnExecuteProgram(std::size_t program_index);
+    void OnExit();
     void ControllerSelectorReconfigureControllers(
         const Core::Frontend::ControllerParameters& parameters);
     void SoftwareKeyboardInitialize(