summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.ci/scripts/android/build.sh9
-rwxr-xr-x.ci/scripts/android/upload.sh13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt1
-rw-r--r--src/android/app/src/main/res/layout/list_item_setting_switch.xml2
-rw-r--r--src/android/app/src/main/res/values/arrays.xml17
-rw-r--r--src/android/app/src/main/res/values/strings.xml8
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/assert.cpp3
-rw-r--r--src/common/heap_tracker.cpp281
-rw-r--r--src/common/heap_tracker.h98
-rw-r--r--src/common/host_memory.cpp10
-rw-r--r--src/common/host_memory.h11
-rw-r--r--src/common/logging/backend.cpp17
-rw-r--r--src/common/logging/backend.h3
-rw-r--r--src/common/ring_buffer.h2
-rw-r--r--src/core/CMakeLists.txt3
-rw-r--r--src/core/arm/arm_interface.cpp2
-rw-r--r--src/core/arm/arm_interface.h2
-rw-r--r--src/core/arm/debug.cpp14
-rw-r--r--src/core/arm/debug.h6
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp49
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h20
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp11
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h2
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp11
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h2
-rw-r--r--src/core/core.cpp73
-rw-r--r--src/core/core.h13
-rw-r--r--src/core/file_sys/ips_layer.cpp7
-rw-r--r--src/core/file_sys/program_metadata.cpp6
-rw-r--r--src/core/file_sys/program_metadata.h13
-rw-r--r--src/core/hle/kernel/k_address_arbiter.cpp19
-rw-r--r--src/core/hle/kernel/k_auto_object_container.cpp9
-rw-r--r--src/core/hle/kernel/k_auto_object_container.h31
-rw-r--r--src/core/hle/kernel/k_capabilities.cpp6
-rw-r--r--src/core/hle/kernel/k_client_port.cpp5
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp8
-rw-r--r--src/core/hle/kernel/k_handle_table.h3
-rw-r--r--src/core/hle/kernel/k_page_table_base.cpp26
-rw-r--r--src/core/hle/kernel/k_page_table_base.h3
-rw-r--r--src/core/hle/kernel/k_process.cpp56
-rw-r--r--src/core/hle/kernel/k_process.h15
-rw-r--r--src/core/hle/kernel/k_server_session.cpp1424
-rw-r--r--src/core/hle/kernel/k_server_session.h15
-rw-r--r--src/core/hle/kernel/k_session.cpp3
-rw-r--r--src/core/hle/kernel/k_thread.cpp3
-rw-r--r--src/core/hle/kernel/k_thread.h6
-rw-r--r--src/core/hle/kernel/k_transfer_memory.h1
-rw-r--r--src/core/hle/kernel/kernel.cpp34
-rw-r--r--src/core/hle/kernel/kernel.h7
-rw-r--r--src/core/hle/kernel/message_buffer.h20
-rw-r--r--src/core/hle/kernel/svc/svc_info.cpp1
-rw-r--r--src/core/hle/kernel/svc/svc_ipc.cpp6
-rw-r--r--src/core/hle/kernel/svc_results.h2
-rw-r--r--src/core/hle/service/am/am.cpp12
-rw-r--r--src/core/hle/service/audio/audren_u.cpp6
-rw-r--r--src/core/hle/service/audio/hwopus.cpp16
-rw-r--r--src/core/hle/service/fatal/fatal.cpp2
-rw-r--r--src/core/hle/service/hid/hid_server.cpp9
-rw-r--r--src/core/hle/service/hid/hidbus.cpp3
-rw-r--r--src/core/hle/service/hid/irs.cpp6
-rw-r--r--src/core/hle/service/hle_ipc.cpp20
-rw-r--r--src/core/hle/service/hle_ipc.h20
-rw-r--r--src/core/hle/service/ipc_helpers.h4
-rw-r--r--src/core/hle/service/jit/jit.cpp69
-rw-r--r--src/core/hle/service/jit/jit_code_memory.cpp54
-rw-r--r--src/core/hle/service/jit/jit_code_memory.h49
-rw-r--r--src/core/hle/service/ro/ro.cpp12
-rw-r--r--src/core/hle/service/server_manager.cpp21
-rw-r--r--src/core/hle/service/service.cpp2
-rw-r--r--src/core/hle/service/set/set_sys.cpp12
-rw-r--r--src/core/hle/service/set/set_sys.h1
-rw-r--r--src/core/hle/service/sm/sm.cpp36
-rw-r--r--src/core/hle/service/sm/sm.h8
-rw-r--r--src/core/hle/service/sm/sm_controller.cpp7
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp7
-rw-r--r--src/core/memory.cpp114
-rw-r--r--src/core/memory.h7
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp60
-rw-r--r--src/tests/common/host_memory.cpp99
-rw-r--r--src/video_core/macro/macro_hle.cpp7
-rw-r--r--src/video_core/rasterizer_interface.h8
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp11
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h5
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp11
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h6
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.cpp17
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.h27
-rw-r--r--src/video_core/texture_cache/decode_bc.cpp50
-rw-r--r--src/video_core/texture_cache/decode_bc.h2
-rw-r--r--src/video_core/texture_cache/util.cpp16
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp16
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp4
-rw-r--r--src/yuzu/main.cpp4
-rw-r--r--src/yuzu/main.h8
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp3
99 files changed, 2499 insertions, 781 deletions
diff --git a/.ci/scripts/android/build.sh b/.ci/scripts/android/build.sh
index d135af029..98593017f 100755
--- a/.ci/scripts/android/build.sh
+++ b/.ci/scripts/android/build.sh
@@ -6,7 +6,12 @@
6export NDK_CCACHE="$(which ccache)" 6export NDK_CCACHE="$(which ccache)"
7ccache -s 7ccache -s
8 8
9BUILD_FLAVOR=mainline 9BUILD_FLAVOR="mainline"
10
11BUILD_TYPE="release"
12if [ "${GITHUB_REPOSITORY}" == "yuzu-emu/yuzu" ]; then
13 BUILD_TYPE="relWithDebInfo"
14fi
10 15
11if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then 16if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then
12 export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks" 17 export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks"
@@ -15,7 +20,7 @@ fi
15 20
16cd src/android 21cd src/android
17chmod +x ./gradlew 22chmod +x ./gradlew
18./gradlew "assemble${BUILD_FLAVOR}Release" "bundle${BUILD_FLAVOR}Release" 23./gradlew "assemble${BUILD_FLAVOR}${BUILD_TYPE}" "bundle${BUILD_FLAVOR}${BUILD_TYPE}"
19 24
20ccache -s 25ccache -s
21 26
diff --git a/.ci/scripts/android/upload.sh b/.ci/scripts/android/upload.sh
index 5f8ca73c0..26b1a7efa 100755
--- a/.ci/scripts/android/upload.sh
+++ b/.ci/scripts/android/upload.sh
@@ -7,9 +7,16 @@
7 7
8REV_NAME="yuzu-${GITDATE}-${GITREV}" 8REV_NAME="yuzu-${GITDATE}-${GITREV}"
9 9
10BUILD_FLAVOR=mainline 10BUILD_FLAVOR="mainline"
11 11
12cp src/android/app/build/outputs/apk/"${BUILD_FLAVOR}/release/app-${BUILD_FLAVOR}-release.apk" \ 12BUILD_TYPE_LOWER="release"
13BUILD_TYPE_UPPER="Release"
14if [ "${GITHUB_REPOSITORY}" == "yuzu-emu/yuzu" ]; then
15 BUILD_TYPE_LOWER="relWithDebInfo"
16 BUILD_TYPE_UPPER="RelWithDebInfo"
17fi
18
19cp src/android/app/build/outputs/apk/"${BUILD_FLAVOR}/${BUILD_TYPE_LOWER}/app-${BUILD_FLAVOR}-${BUILD_TYPE_LOWER}.apk" \
13 "artifacts/${REV_NAME}.apk" 20 "artifacts/${REV_NAME}.apk"
14cp src/android/app/build/outputs/bundle/"${BUILD_FLAVOR}Release"/"app-${BUILD_FLAVOR}-release.aab" \ 21cp src/android/app/build/outputs/bundle/"${BUILD_FLAVOR}${BUILD_TYPE_UPPER}"/"app-${BUILD_FLAVOR}-${BUILD_TYPE_LOWER}.aab" \
15 "artifacts/${REV_NAME}.aab" 22 "artifacts/${REV_NAME}.aab"
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
index 21e4e1afd..df760440f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
@@ -18,7 +18,8 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
18 RENDERER_ANTI_ALIASING("anti_aliasing"), 18 RENDERER_ANTI_ALIASING("anti_aliasing"),
19 RENDERER_SCREEN_LAYOUT("screen_layout"), 19 RENDERER_SCREEN_LAYOUT("screen_layout"),
20 RENDERER_ASPECT_RATIO("aspect_ratio"), 20 RENDERER_ASPECT_RATIO("aspect_ratio"),
21 AUDIO_OUTPUT_ENGINE("output_engine"); 21 AUDIO_OUTPUT_ENGINE("output_engine"),
22 MAX_ANISOTROPY("max_anisotropy");
22 23
23 override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal) 24 override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)
24 25
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
index 2e97aee2c..12f7aa1ab 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
@@ -245,6 +245,15 @@ abstract class SettingsItem(
245 ) 245 )
246 put( 246 put(
247 SingleChoiceSetting( 247 SingleChoiceSetting(
248 IntSetting.MAX_ANISOTROPY,
249 R.string.anisotropic_filtering,
250 R.string.anisotropic_filtering_description,
251 R.array.anisoEntries,
252 R.array.anisoValues
253 )
254 )
255 put(
256 SingleChoiceSetting(
248 IntSetting.AUDIO_OUTPUT_ENGINE, 257 IntSetting.AUDIO_OUTPUT_ENGINE,
249 R.string.audio_output_engine, 258 R.string.audio_output_engine,
250 0, 259 0,
@@ -298,6 +307,7 @@ abstract class SettingsItem(
298 307
299 override val key: String = FASTMEM_COMBINED 308 override val key: String = FASTMEM_COMBINED
300 override val isRuntimeModifiable: Boolean = false 309 override val isRuntimeModifiable: Boolean = false
310 override val pairedSettingKey = BooleanSetting.CPU_DEBUG_MODE.key
301 override val defaultValue: Boolean = true 311 override val defaultValue: Boolean = true
302 override val isSwitchable: Boolean = true 312 override val isSwitchable: Boolean = true
303 override var global: Boolean 313 override var global: Boolean
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
index a7e965589..db1a1076c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -149,6 +149,7 @@ class SettingsFragmentPresenter(
149 add(IntSetting.RENDERER_VSYNC.key) 149 add(IntSetting.RENDERER_VSYNC.key)
150 add(IntSetting.RENDERER_SCALING_FILTER.key) 150 add(IntSetting.RENDERER_SCALING_FILTER.key)
151 add(IntSetting.RENDERER_ANTI_ALIASING.key) 151 add(IntSetting.RENDERER_ANTI_ALIASING.key)
152 add(IntSetting.MAX_ANISOTROPY.key)
152 add(IntSetting.RENDERER_SCREEN_LAYOUT.key) 153 add(IntSetting.RENDERER_SCREEN_LAYOUT.key)
153 add(IntSetting.RENDERER_ASPECT_RATIO.key) 154 add(IntSetting.RENDERER_ASPECT_RATIO.key)
154 add(BooleanSetting.PICTURE_IN_PICTURE.key) 155 add(BooleanSetting.PICTURE_IN_PICTURE.key)
diff --git a/src/android/app/src/main/res/layout/list_item_setting_switch.xml b/src/android/app/src/main/res/layout/list_item_setting_switch.xml
index 5cb84182e..1c08e2e1b 100644
--- a/src/android/app/src/main/res/layout/list_item_setting_switch.xml
+++ b/src/android/app/src/main/res/layout/list_item_setting_switch.xml
@@ -24,7 +24,7 @@
24 android:layout_width="0dp" 24 android:layout_width="0dp"
25 android:layout_height="wrap_content" 25 android:layout_height="wrap_content"
26 android:layout_marginEnd="24dp" 26 android:layout_marginEnd="24dp"
27 android:gravity="center_vertical" 27 android:layout_gravity="center_vertical"
28 android:orientation="vertical" 28 android:orientation="vertical"
29 android:layout_weight="1"> 29 android:layout_weight="1">
30 30
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index e3915ef4f..c882a8e62 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -267,4 +267,21 @@
267 <item>3</item> 267 <item>3</item>
268 </integer-array> 268 </integer-array>
269 269
270 <string-array name="anisoEntries">
271 <item>@string/auto</item>
272 <item>@string/slider_default</item>
273 <item>@string/multiplier_two</item>
274 <item>@string/multiplier_four</item>
275 <item>@string/multiplier_eight</item>
276 <item>@string/multiplier_sixteen</item>
277 </string-array>
278 <integer-array name="anisoValues">
279 <item>0</item>
280 <item>1</item>
281 <item>2</item>
282 <item>3</item>
283 <item>4</item>
284 <item>5</item>
285 </integer-array>
286
270</resources> 287</resources>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 0b80b04a4..4d5c268fe 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -225,6 +225,8 @@
225 <string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string> 225 <string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
226 <string name="use_disk_shader_cache">Disk shader cache</string> 226 <string name="use_disk_shader_cache">Disk shader cache</string>
227 <string name="use_disk_shader_cache_description">Reduces stuttering by locally storing and loading generated shaders.</string> 227 <string name="use_disk_shader_cache_description">Reduces stuttering by locally storing and loading generated shaders.</string>
228 <string name="anisotropic_filtering">Anisotropic filtering</string>
229 <string name="anisotropic_filtering_description">Improves the quality of textures when viewed at oblique angles</string>
228 230
229 <!-- Debug settings strings --> 231 <!-- Debug settings strings -->
230 <string name="cpu">CPU</string> 232 <string name="cpu">CPU</string>
@@ -506,6 +508,12 @@
506 <string name="oboe">oboe</string> 508 <string name="oboe">oboe</string>
507 <string name="cubeb">cubeb</string> 509 <string name="cubeb">cubeb</string>
508 510
511 <!-- Anisotropic filtering options -->
512 <string name="multiplier_two">2x</string>
513 <string name="multiplier_four">4x</string>
514 <string name="multiplier_eight">8x</string>
515 <string name="multiplier_sixteen">16x</string>
516
509 <!-- Black backgrounds theme --> 517 <!-- Black backgrounds theme -->
510 <string name="use_black_backgrounds">Black backgrounds</string> 518 <string name="use_black_backgrounds">Black backgrounds</string>
511 <string name="use_black_backgrounds_description">When using the dark theme, apply black backgrounds.</string> 519 <string name="use_black_backgrounds_description">When using the dark theme, apply black backgrounds.</string>
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index b58a7073f..8c57d47c6 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -64,6 +64,8 @@ add_library(common STATIC
64 fs/path_util.cpp 64 fs/path_util.cpp
65 fs/path_util.h 65 fs/path_util.h
66 hash.h 66 hash.h
67 heap_tracker.cpp
68 heap_tracker.h
67 hex_util.cpp 69 hex_util.cpp
68 hex_util.h 70 hex_util.h
69 host_memory.cpp 71 host_memory.cpp
diff --git a/src/common/assert.cpp b/src/common/assert.cpp
index 6026b7dc2..e2c2cade3 100644
--- a/src/common/assert.cpp
+++ b/src/common/assert.cpp
@@ -3,16 +3,19 @@
3 3
4#include "common/assert.h" 4#include "common/assert.h"
5#include "common/common_funcs.h" 5#include "common/common_funcs.h"
6#include "common/logging/backend.h"
6 7
7#include "common/settings.h" 8#include "common/settings.h"
8 9
9void assert_fail_impl() { 10void assert_fail_impl() {
10 if (Settings::values.use_debug_asserts) { 11 if (Settings::values.use_debug_asserts) {
12 Common::Log::Stop();
11 Crash(); 13 Crash();
12 } 14 }
13} 15}
14 16
15[[noreturn]] void unreachable_impl() { 17[[noreturn]] void unreachable_impl() {
18 Common::Log::Stop();
16 Crash(); 19 Crash();
17 throw std::runtime_error("Unreachable code"); 20 throw std::runtime_error("Unreachable code");
18} 21}
diff --git a/src/common/heap_tracker.cpp b/src/common/heap_tracker.cpp
new file mode 100644
index 000000000..683208795
--- /dev/null
+++ b/src/common/heap_tracker.cpp
@@ -0,0 +1,281 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <fstream>
5#include <vector>
6
7#include "common/heap_tracker.h"
8#include "common/logging/log.h"
9
10namespace Common {
11
12namespace {
13
14s64 GetMaxPermissibleResidentMapCount() {
15 // Default value.
16 s64 value = 65530;
17
18 // Try to read how many mappings we can make.
19 std::ifstream s("/proc/sys/vm/max_map_count");
20 s >> value;
21
22 // Print, for debug.
23 LOG_INFO(HW_Memory, "Current maximum map count: {}", value);
24
25 // Allow 20000 maps for other code and to account for split inaccuracy.
26 return std::max<s64>(value - 20000, 0);
27}
28
29} // namespace
30
31HeapTracker::HeapTracker(Common::HostMemory& buffer)
32 : m_buffer(buffer), m_max_resident_map_count(GetMaxPermissibleResidentMapCount()) {}
33HeapTracker::~HeapTracker() = default;
34
35void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
36 MemoryPermission perm, bool is_separate_heap) {
37 // When mapping other memory, map pages immediately.
38 if (!is_separate_heap) {
39 m_buffer.Map(virtual_offset, host_offset, length, perm, false);
40 return;
41 }
42
43 {
44 // We are mapping part of a separate heap.
45 std::scoped_lock lk{m_lock};
46
47 auto* const map = new SeparateHeapMap{
48 .vaddr = virtual_offset,
49 .paddr = host_offset,
50 .size = length,
51 .tick = m_tick++,
52 .perm = perm,
53 .is_resident = false,
54 };
55
56 // Insert into mappings.
57 m_map_count++;
58 m_mappings.insert(*map);
59 }
60
61 // Finally, map.
62 this->DeferredMapSeparateHeap(virtual_offset);
63}
64
65void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_heap) {
66 // If this is a separate heap...
67 if (is_separate_heap) {
68 std::scoped_lock lk{m_lock};
69
70 const SeparateHeapMap key{
71 .vaddr = virtual_offset,
72 };
73
74 // Split at the boundaries of the region we are removing.
75 this->SplitHeapMapLocked(virtual_offset);
76 this->SplitHeapMapLocked(virtual_offset + size);
77
78 // Erase all mappings in range.
79 auto it = m_mappings.find(key);
80 while (it != m_mappings.end() && it->vaddr < virtual_offset + size) {
81 // Get underlying item.
82 auto* const item = std::addressof(*it);
83
84 // If resident, erase from resident map.
85 if (item->is_resident) {
86 ASSERT(--m_resident_map_count >= 0);
87 m_resident_mappings.erase(m_resident_mappings.iterator_to(*item));
88 }
89
90 // Erase from map.
91 ASSERT(--m_map_count >= 0);
92 it = m_mappings.erase(it);
93
94 // Free the item.
95 delete item;
96 }
97 }
98
99 // Unmap pages.
100 m_buffer.Unmap(virtual_offset, size, false);
101}
102
103void HeapTracker::Protect(size_t virtual_offset, size_t size, MemoryPermission perm) {
104 // Ensure no rebuild occurs while reprotecting.
105 std::shared_lock lk{m_rebuild_lock};
106
107 // Split at the boundaries of the region we are reprotecting.
108 this->SplitHeapMap(virtual_offset, size);
109
110 // Declare tracking variables.
111 const VAddr end = virtual_offset + size;
112 VAddr cur = virtual_offset;
113
114 while (cur < end) {
115 VAddr next = cur;
116 bool should_protect = false;
117
118 {
119 std::scoped_lock lk2{m_lock};
120
121 const SeparateHeapMap key{
122 .vaddr = next,
123 };
124
125 // Try to get the next mapping corresponding to this address.
126 const auto it = m_mappings.nfind(key);
127
128 if (it == m_mappings.end()) {
129 // There are no separate heap mappings remaining.
130 next = end;
131 should_protect = true;
132 } else if (it->vaddr == cur) {
133 // We are in range.
134 // Update permission bits.
135 it->perm = perm;
136
137 // Determine next address and whether we should protect.
138 next = cur + it->size;
139 should_protect = it->is_resident;
140 } else /* if (it->vaddr > cur) */ {
141 // We weren't in range, but there is a block coming up that will be.
142 next = it->vaddr;
143 should_protect = true;
144 }
145 }
146
147 // Clamp to end.
148 next = std::min(next, end);
149
150 // Reprotect, if we need to.
151 if (should_protect) {
152 m_buffer.Protect(cur, next - cur, perm);
153 }
154
155 // Advance.
156 cur = next;
157 }
158}
159
160bool HeapTracker::DeferredMapSeparateHeap(u8* fault_address) {
161 if (m_buffer.IsInVirtualRange(fault_address)) {
162 return this->DeferredMapSeparateHeap(fault_address - m_buffer.VirtualBasePointer());
163 }
164
165 return false;
166}
167
168bool HeapTracker::DeferredMapSeparateHeap(size_t virtual_offset) {
169 bool rebuild_required = false;
170
171 {
172 std::scoped_lock lk{m_lock};
173
174 // Check to ensure this was a non-resident separate heap mapping.
175 const auto it = this->GetNearestHeapMapLocked(virtual_offset);
176 if (it == m_mappings.end() || it->is_resident) {
177 return false;
178 }
179
180 // Update tick before possible rebuild.
181 it->tick = m_tick++;
182
183 // Check if we need to rebuild.
184 if (m_resident_map_count > m_max_resident_map_count) {
185 rebuild_required = true;
186 }
187
188 // Map the area.
189 m_buffer.Map(it->vaddr, it->paddr, it->size, it->perm, false);
190
191 // This map is now resident.
192 it->is_resident = true;
193 m_resident_map_count++;
194 m_resident_mappings.insert(*it);
195 }
196
197 if (rebuild_required) {
198 // A rebuild was required, so perform it now.
199 this->RebuildSeparateHeapAddressSpace();
200 }
201
202 return true;
203}
204
205void HeapTracker::RebuildSeparateHeapAddressSpace() {
206 std::scoped_lock lk{m_rebuild_lock, m_lock};
207
208 ASSERT(!m_resident_mappings.empty());
209
210 // Dump half of the mappings.
211 //
212 // Despite being worse in theory, this has proven to be better in practice than more
213 // regularly dumping a smaller amount, because it significantly reduces average case
214 // lock contention.
215 const size_t desired_count = std::min(m_resident_map_count, m_max_resident_map_count) / 2;
216 const size_t evict_count = m_resident_map_count - desired_count;
217 auto it = m_resident_mappings.begin();
218
219 for (size_t i = 0; i < evict_count && it != m_resident_mappings.end(); i++) {
220 // Unmark and unmap.
221 it->is_resident = false;
222 m_buffer.Unmap(it->vaddr, it->size, false);
223
224 // Advance.
225 ASSERT(--m_resident_map_count >= 0);
226 it = m_resident_mappings.erase(it);
227 }
228}
229
230void HeapTracker::SplitHeapMap(VAddr offset, size_t size) {
231 std::scoped_lock lk{m_lock};
232
233 this->SplitHeapMapLocked(offset);
234 this->SplitHeapMapLocked(offset + size);
235}
236
237void HeapTracker::SplitHeapMapLocked(VAddr offset) {
238 const auto it = this->GetNearestHeapMapLocked(offset);
239 if (it == m_mappings.end() || it->vaddr == offset) {
240 // Not contained or no split required.
241 return;
242 }
243
244 // Cache the original values.
245 auto* const left = std::addressof(*it);
246 const size_t orig_size = left->size;
247
248 // Adjust the left map.
249 const size_t left_size = offset - left->vaddr;
250 left->size = left_size;
251
252 // Create the new right map.
253 auto* const right = new SeparateHeapMap{
254 .vaddr = left->vaddr + left_size,
255 .paddr = left->paddr + left_size,
256 .size = orig_size - left_size,
257 .tick = left->tick,
258 .perm = left->perm,
259 .is_resident = left->is_resident,
260 };
261
262 // Insert the new right map.
263 m_map_count++;
264 m_mappings.insert(*right);
265
266 // If resident, also insert into resident map.
267 if (right->is_resident) {
268 m_resident_map_count++;
269 m_resident_mappings.insert(*right);
270 }
271}
272
273HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offset) {
274 const SeparateHeapMap key{
275 .vaddr = offset,
276 };
277
278 return m_mappings.find(key);
279}
280
281} // namespace Common
diff --git a/src/common/heap_tracker.h b/src/common/heap_tracker.h
new file mode 100644
index 000000000..ee5b0bf43
--- /dev/null
+++ b/src/common/heap_tracker.h
@@ -0,0 +1,98 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <atomic>
7#include <mutex>
8#include <set>
9#include <shared_mutex>
10
11#include "common/host_memory.h"
12#include "common/intrusive_red_black_tree.h"
13
14namespace Common {
15
16struct SeparateHeapMap {
17 Common::IntrusiveRedBlackTreeNode addr_node{};
18 Common::IntrusiveRedBlackTreeNode tick_node{};
19 VAddr vaddr{};
20 PAddr paddr{};
21 size_t size{};
22 size_t tick{};
23 MemoryPermission perm{};
24 bool is_resident{};
25};
26
27struct SeparateHeapMapAddrComparator {
28 static constexpr int Compare(const SeparateHeapMap& lhs, const SeparateHeapMap& rhs) {
29 if (lhs.vaddr < rhs.vaddr) {
30 return -1;
31 } else if (lhs.vaddr <= (rhs.vaddr + rhs.size - 1)) {
32 return 0;
33 } else {
34 return 1;
35 }
36 }
37};
38
39struct SeparateHeapMapTickComparator {
40 static constexpr int Compare(const SeparateHeapMap& lhs, const SeparateHeapMap& rhs) {
41 if (lhs.tick < rhs.tick) {
42 return -1;
43 } else if (lhs.tick > rhs.tick) {
44 return 1;
45 } else {
46 return SeparateHeapMapAddrComparator::Compare(lhs, rhs);
47 }
48 }
49};
50
51class HeapTracker {
52public:
53 explicit HeapTracker(Common::HostMemory& buffer);
54 ~HeapTracker();
55
56 void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm,
57 bool is_separate_heap);
58 void Unmap(size_t virtual_offset, size_t size, bool is_separate_heap);
59 void Protect(size_t virtual_offset, size_t length, MemoryPermission perm);
60 u8* VirtualBasePointer() {
61 return m_buffer.VirtualBasePointer();
62 }
63
64 bool DeferredMapSeparateHeap(u8* fault_address);
65 bool DeferredMapSeparateHeap(size_t virtual_offset);
66
67private:
68 using AddrTreeTraits =
69 Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::addr_node>;
70 using AddrTree = AddrTreeTraits::TreeType<SeparateHeapMapAddrComparator>;
71
72 using TickTreeTraits =
73 Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::tick_node>;
74 using TickTree = TickTreeTraits::TreeType<SeparateHeapMapTickComparator>;
75
76 AddrTree m_mappings{};
77 TickTree m_resident_mappings{};
78
79private:
80 void SplitHeapMap(VAddr offset, size_t size);
81 void SplitHeapMapLocked(VAddr offset);
82
83 AddrTree::iterator GetNearestHeapMapLocked(VAddr offset);
84
85 void RebuildSeparateHeapAddressSpace();
86
87private:
88 Common::HostMemory& m_buffer;
89 const s64 m_max_resident_map_count;
90
91 std::shared_mutex m_rebuild_lock{};
92 std::mutex m_lock{};
93 s64 m_map_count{};
94 s64 m_resident_map_count{};
95 size_t m_tick{};
96};
97
98} // namespace Common
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index e540375b8..860c39e6a 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -679,7 +679,7 @@ HostMemory::HostMemory(HostMemory&&) noexcept = default;
679HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default; 679HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default;
680 680
681void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, 681void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
682 MemoryPermission perms) { 682 MemoryPermission perms, bool separate_heap) {
683 ASSERT(virtual_offset % PageAlignment == 0); 683 ASSERT(virtual_offset % PageAlignment == 0);
684 ASSERT(host_offset % PageAlignment == 0); 684 ASSERT(host_offset % PageAlignment == 0);
685 ASSERT(length % PageAlignment == 0); 685 ASSERT(length % PageAlignment == 0);
@@ -691,7 +691,7 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
691 impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms); 691 impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms);
692} 692}
693 693
694void HostMemory::Unmap(size_t virtual_offset, size_t length) { 694void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap) {
695 ASSERT(virtual_offset % PageAlignment == 0); 695 ASSERT(virtual_offset % PageAlignment == 0);
696 ASSERT(length % PageAlignment == 0); 696 ASSERT(length % PageAlignment == 0);
697 ASSERT(virtual_offset + length <= virtual_size); 697 ASSERT(virtual_offset + length <= virtual_size);
@@ -701,14 +701,16 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length) {
701 impl->Unmap(virtual_offset + virtual_base_offset, length); 701 impl->Unmap(virtual_offset + virtual_base_offset, length);
702} 702}
703 703
704void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write, 704void HostMemory::Protect(size_t virtual_offset, size_t length, MemoryPermission perm) {
705 bool execute) {
706 ASSERT(virtual_offset % PageAlignment == 0); 705 ASSERT(virtual_offset % PageAlignment == 0);
707 ASSERT(length % PageAlignment == 0); 706 ASSERT(length % PageAlignment == 0);
708 ASSERT(virtual_offset + length <= virtual_size); 707 ASSERT(virtual_offset + length <= virtual_size);
709 if (length == 0 || !virtual_base || !impl) { 708 if (length == 0 || !virtual_base || !impl) {
710 return; 709 return;
711 } 710 }
711 const bool read = True(perm & MemoryPermission::Read);
712 const bool write = True(perm & MemoryPermission::Write);
713 const bool execute = True(perm & MemoryPermission::Execute);
712 impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute); 714 impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute);
713} 715}
714 716
diff --git a/src/common/host_memory.h b/src/common/host_memory.h
index 747c5850c..72fbb05af 100644
--- a/src/common/host_memory.h
+++ b/src/common/host_memory.h
@@ -40,11 +40,12 @@ public:
40 HostMemory(HostMemory&& other) noexcept; 40 HostMemory(HostMemory&& other) noexcept;
41 HostMemory& operator=(HostMemory&& other) noexcept; 41 HostMemory& operator=(HostMemory&& other) noexcept;
42 42
43 void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms); 43 void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms,
44 bool separate_heap);
44 45
45 void Unmap(size_t virtual_offset, size_t length); 46 void Unmap(size_t virtual_offset, size_t length, bool separate_heap);
46 47
47 void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute = false); 48 void Protect(size_t virtual_offset, size_t length, MemoryPermission perms);
48 49
49 void EnableDirectMappedAddress(); 50 void EnableDirectMappedAddress();
50 51
@@ -64,6 +65,10 @@ public:
64 return virtual_base; 65 return virtual_base;
65 } 66 }
66 67
68 bool IsInVirtualRange(void* address) const noexcept {
69 return address >= virtual_base && address < virtual_base + virtual_size;
70 }
71
67private: 72private:
68 size_t backing_size{}; 73 size_t backing_size{};
69 size_t virtual_size{}; 74 size_t virtual_size{};
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index d4f27197c..7a267f8c0 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -208,6 +208,10 @@ public:
208 instance->StartBackendThread(); 208 instance->StartBackendThread();
209 } 209 }
210 210
211 static void Stop() {
212 instance->StopBackendThread();
213 }
214
211 Impl(const Impl&) = delete; 215 Impl(const Impl&) = delete;
212 Impl& operator=(const Impl&) = delete; 216 Impl& operator=(const Impl&) = delete;
213 217
@@ -259,6 +263,15 @@ private:
259 }); 263 });
260 } 264 }
261 265
266 void StopBackendThread() {
267 backend_thread.request_stop();
268 if (backend_thread.joinable()) {
269 backend_thread.join();
270 }
271
272 ForEachBackend([](Backend& backend) { backend.Flush(); });
273 }
274
262 Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, 275 Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
263 const char* function, std::string&& message) const { 276 const char* function, std::string&& message) const {
264 using std::chrono::duration_cast; 277 using std::chrono::duration_cast;
@@ -313,6 +326,10 @@ void Start() {
313 Impl::Start(); 326 Impl::Start();
314} 327}
315 328
329void Stop() {
330 Impl::Stop();
331}
332
316void DisableLoggingInTests() { 333void DisableLoggingInTests() {
317 initialization_in_progress_suppress_logging = true; 334 initialization_in_progress_suppress_logging = true;
318} 335}
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index 12e5e2498..2a9926e9e 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -14,6 +14,9 @@ void Initialize();
14 14
15void Start(); 15void Start();
16 16
17/// Explicitly stops the logger thread and flushes the buffers
18void Stop();
19
17void DisableLoggingInTests(); 20void DisableLoggingInTests();
18 21
19/** 22/**
diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h
index 5c961b202..e7e9fdb38 100644
--- a/src/common/ring_buffer.h
+++ b/src/common/ring_buffer.h
@@ -103,7 +103,7 @@ private:
103 // Having them on the same cache-line would result in false-sharing between them. 103 // Having them on the same cache-line would result in false-sharing between them.
104 // TODO: Remove this ifdef whenever clang and GCC support 104 // TODO: Remove this ifdef whenever clang and GCC support
105 // std::hardware_destructive_interference_size. 105 // std::hardware_destructive_interference_size.
106#if defined(_MSC_VER) && _MSC_VER >= 1911 106#ifdef __cpp_lib_hardware_interference_size
107 alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_read_index{0}; 107 alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_read_index{0};
108 alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_write_index{0}; 108 alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_write_index{0};
109#else 109#else
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 96ab39cb8..367d01dc7 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -978,6 +978,7 @@ endif()
978 978
979if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) 979if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
980 target_sources(core PRIVATE 980 target_sources(core PRIVATE
981 arm/dynarmic/arm_dynarmic.cpp
981 arm/dynarmic/arm_dynarmic.h 982 arm/dynarmic/arm_dynarmic.h
982 arm/dynarmic/arm_dynarmic_64.cpp 983 arm/dynarmic/arm_dynarmic_64.cpp
983 arm/dynarmic/arm_dynarmic_64.h 984 arm/dynarmic/arm_dynarmic_64.h
@@ -987,6 +988,8 @@ if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
987 arm/dynarmic/dynarmic_cp15.h 988 arm/dynarmic/dynarmic_cp15.h
988 arm/dynarmic/dynarmic_exclusive_monitor.cpp 989 arm/dynarmic/dynarmic_exclusive_monitor.cpp
989 arm/dynarmic/dynarmic_exclusive_monitor.h 990 arm/dynarmic/dynarmic_exclusive_monitor.h
991 hle/service/jit/jit_code_memory.cpp
992 hle/service/jit/jit_code_memory.h
990 hle/service/jit/jit_context.cpp 993 hle/service/jit/jit_context.cpp
991 hle/service/jit/jit_context.h 994 hle/service/jit/jit_context.h
992 hle/service/jit/jit.cpp 995 hle/service/jit/jit.cpp
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 698c9c8ad..5dc7e5d59 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -9,7 +9,7 @@
9 9
10namespace Core { 10namespace Core {
11 11
12void ArmInterface::LogBacktrace(const Kernel::KProcess* process) const { 12void ArmInterface::LogBacktrace(Kernel::KProcess* process) const {
13 Kernel::Svc::ThreadContext ctx; 13 Kernel::Svc::ThreadContext ctx;
14 this->GetContext(ctx); 14 this->GetContext(ctx);
15 15
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 806c7c9e9..495963eef 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -95,7 +95,7 @@ public:
95 virtual void SignalInterrupt(Kernel::KThread* thread) = 0; 95 virtual void SignalInterrupt(Kernel::KThread* thread) = 0;
96 96
97 // Stack trace generation. 97 // Stack trace generation.
98 void LogBacktrace(const Kernel::KProcess* process) const; 98 void LogBacktrace(Kernel::KProcess* process) const;
99 99
100 // Debug functionality. 100 // Debug functionality.
101 virtual const Kernel::DebugWatchpoint* HaltedWatchpoint() const = 0; 101 virtual const Kernel::DebugWatchpoint* HaltedWatchpoint() const = 0;
diff --git a/src/core/arm/debug.cpp b/src/core/arm/debug.cpp
index af1c34bc3..854509463 100644
--- a/src/core/arm/debug.cpp
+++ b/src/core/arm/debug.cpp
@@ -79,7 +79,7 @@ constexpr std::array<u64, 2> SegmentBases{
79 0x7100000000ULL, 79 0x7100000000ULL,
80}; 80};
81 81
82void SymbolicateBacktrace(const Kernel::KProcess* process, std::vector<BacktraceEntry>& out) { 82void SymbolicateBacktrace(Kernel::KProcess* process, std::vector<BacktraceEntry>& out) {
83 auto modules = FindModules(process); 83 auto modules = FindModules(process);
84 84
85 const bool is_64 = process->Is64Bit(); 85 const bool is_64 = process->Is64Bit();
@@ -118,7 +118,7 @@ void SymbolicateBacktrace(const Kernel::KProcess* process, std::vector<Backtrace
118 } 118 }
119} 119}
120 120
121std::vector<BacktraceEntry> GetAArch64Backtrace(const Kernel::KProcess* process, 121std::vector<BacktraceEntry> GetAArch64Backtrace(Kernel::KProcess* process,
122 const Kernel::Svc::ThreadContext& ctx) { 122 const Kernel::Svc::ThreadContext& ctx) {
123 std::vector<BacktraceEntry> out; 123 std::vector<BacktraceEntry> out;
124 auto& memory = process->GetMemory(); 124 auto& memory = process->GetMemory();
@@ -144,7 +144,7 @@ std::vector<BacktraceEntry> GetAArch64Backtrace(const Kernel::KProcess* process,
144 return out; 144 return out;
145} 145}
146 146
147std::vector<BacktraceEntry> GetAArch32Backtrace(const Kernel::KProcess* process, 147std::vector<BacktraceEntry> GetAArch32Backtrace(Kernel::KProcess* process,
148 const Kernel::Svc::ThreadContext& ctx) { 148 const Kernel::Svc::ThreadContext& ctx) {
149 std::vector<BacktraceEntry> out; 149 std::vector<BacktraceEntry> out;
150 auto& memory = process->GetMemory(); 150 auto& memory = process->GetMemory();
@@ -173,7 +173,7 @@ std::vector<BacktraceEntry> GetAArch32Backtrace(const Kernel::KProcess* process,
173} // namespace 173} // namespace
174 174
175std::optional<std::string> GetThreadName(const Kernel::KThread* thread) { 175std::optional<std::string> GetThreadName(const Kernel::KThread* thread) {
176 const auto* process = thread->GetOwnerProcess(); 176 auto* process = thread->GetOwnerProcess();
177 if (process->Is64Bit()) { 177 if (process->Is64Bit()) {
178 return GetNameFromThreadType64(process->GetMemory(), *thread); 178 return GetNameFromThreadType64(process->GetMemory(), *thread);
179 } else { 179 } else {
@@ -248,7 +248,7 @@ Kernel::KProcessAddress GetModuleEnd(const Kernel::KProcess* process,
248 return cur_addr - 1; 248 return cur_addr - 1;
249} 249}
250 250
251Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process) { 251Loader::AppLoader::Modules FindModules(Kernel::KProcess* process) {
252 Loader::AppLoader::Modules modules; 252 Loader::AppLoader::Modules modules;
253 253
254 auto& page_table = process->GetPageTable(); 254 auto& page_table = process->GetPageTable();
@@ -312,7 +312,7 @@ Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process) {
312 return modules; 312 return modules;
313} 313}
314 314
315Kernel::KProcessAddress FindMainModuleEntrypoint(const Kernel::KProcess* process) { 315Kernel::KProcessAddress FindMainModuleEntrypoint(Kernel::KProcess* process) {
316 // Do we have any loaded executable sections? 316 // Do we have any loaded executable sections?
317 auto modules = FindModules(process); 317 auto modules = FindModules(process);
318 318
@@ -337,7 +337,7 @@ void InvalidateInstructionCacheRange(const Kernel::KProcess* process, u64 addres
337 } 337 }
338} 338}
339 339
340std::vector<BacktraceEntry> GetBacktraceFromContext(const Kernel::KProcess* process, 340std::vector<BacktraceEntry> GetBacktraceFromContext(Kernel::KProcess* process,
341 const Kernel::Svc::ThreadContext& ctx) { 341 const Kernel::Svc::ThreadContext& ctx) {
342 if (process->Is64Bit()) { 342 if (process->Is64Bit()) {
343 return GetAArch64Backtrace(process, ctx); 343 return GetAArch64Backtrace(process, ctx);
diff --git a/src/core/arm/debug.h b/src/core/arm/debug.h
index c542633db..3cd671365 100644
--- a/src/core/arm/debug.h
+++ b/src/core/arm/debug.h
@@ -14,9 +14,9 @@ std::optional<std::string> GetThreadName(const Kernel::KThread* thread);
14std::string_view GetThreadWaitReason(const Kernel::KThread* thread); 14std::string_view GetThreadWaitReason(const Kernel::KThread* thread);
15std::string GetThreadState(const Kernel::KThread* thread); 15std::string GetThreadState(const Kernel::KThread* thread);
16 16
17Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process); 17Loader::AppLoader::Modules FindModules(Kernel::KProcess* process);
18Kernel::KProcessAddress GetModuleEnd(const Kernel::KProcess* process, Kernel::KProcessAddress base); 18Kernel::KProcessAddress GetModuleEnd(const Kernel::KProcess* process, Kernel::KProcessAddress base);
19Kernel::KProcessAddress FindMainModuleEntrypoint(const Kernel::KProcess* process); 19Kernel::KProcessAddress FindMainModuleEntrypoint(Kernel::KProcess* process);
20 20
21void InvalidateInstructionCacheRange(const Kernel::KProcess* process, u64 address, u64 size); 21void InvalidateInstructionCacheRange(const Kernel::KProcess* process, u64 address, u64 size);
22 22
@@ -28,7 +28,7 @@ struct BacktraceEntry {
28 std::string name; 28 std::string name;
29}; 29};
30 30
31std::vector<BacktraceEntry> GetBacktraceFromContext(const Kernel::KProcess* process, 31std::vector<BacktraceEntry> GetBacktraceFromContext(Kernel::KProcess* process,
32 const Kernel::Svc::ThreadContext& ctx); 32 const Kernel::Svc::ThreadContext& ctx);
33std::vector<BacktraceEntry> GetBacktrace(const Kernel::KThread* thread); 33std::vector<BacktraceEntry> GetBacktrace(const Kernel::KThread* thread);
34 34
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
new file mode 100644
index 000000000..e6e9fc45b
--- /dev/null
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -0,0 +1,49 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#ifdef __linux__
5
6#include "common/signal_chain.h"
7
8#include "core/arm/dynarmic/arm_dynarmic.h"
9#include "core/hle/kernel/k_process.h"
10#include "core/memory.h"
11
12namespace Core {
13
14namespace {
15
16thread_local Core::Memory::Memory* g_current_memory{};
17std::once_flag g_registered{};
18struct sigaction g_old_segv {};
19
20void HandleSigSegv(int sig, siginfo_t* info, void* ctx) {
21 if (g_current_memory && g_current_memory->InvalidateSeparateHeap(info->si_addr)) {
22 return;
23 }
24
25 return g_old_segv.sa_sigaction(sig, info, ctx);
26}
27
28} // namespace
29
30ScopedJitExecution::ScopedJitExecution(Kernel::KProcess* process) {
31 g_current_memory = std::addressof(process->GetMemory());
32}
33
34ScopedJitExecution::~ScopedJitExecution() {
35 g_current_memory = nullptr;
36}
37
38void ScopedJitExecution::RegisterHandler() {
39 std::call_once(g_registered, [] {
40 struct sigaction sa {};
41 sa.sa_sigaction = &HandleSigSegv;
42 sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
43 Common::SigAction(SIGSEGV, std::addressof(sa), std::addressof(g_old_segv));
44 });
45}
46
47} // namespace Core
48
49#endif
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index eef7c3116..53dd18815 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -26,4 +26,24 @@ constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
26 return static_cast<HaltReason>(hr); 26 return static_cast<HaltReason>(hr);
27} 27}
28 28
29#ifdef __linux__
30
31class ScopedJitExecution {
32public:
33 explicit ScopedJitExecution(Kernel::KProcess* process);
34 ~ScopedJitExecution();
35 static void RegisterHandler();
36};
37
38#else
39
40class ScopedJitExecution {
41public:
42 explicit ScopedJitExecution(Kernel::KProcess* process) {}
43 ~ScopedJitExecution() {}
44 static void RegisterHandler() {}
45};
46
47#endif
48
29} // namespace Core 49} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index f34865e26..36478f722 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -15,7 +15,7 @@ using namespace Common::Literals;
15 15
16class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { 16class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
17public: 17public:
18 explicit DynarmicCallbacks32(ArmDynarmic32& parent, const Kernel::KProcess* process) 18 explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process)
19 : m_parent{parent}, m_memory(process->GetMemory()), 19 : m_parent{parent}, m_memory(process->GetMemory()),
20 m_process(process), m_debugger_enabled{parent.m_system.DebuggerEnabled()}, 20 m_process(process), m_debugger_enabled{parent.m_system.DebuggerEnabled()},
21 m_check_memory_access{m_debugger_enabled || 21 m_check_memory_access{m_debugger_enabled ||
@@ -169,7 +169,7 @@ public:
169 169
170 ArmDynarmic32& m_parent; 170 ArmDynarmic32& m_parent;
171 Core::Memory::Memory& m_memory; 171 Core::Memory::Memory& m_memory;
172 const Kernel::KProcess* m_process{}; 172 Kernel::KProcess* m_process{};
173 const bool m_debugger_enabled{}; 173 const bool m_debugger_enabled{};
174 const bool m_check_memory_access{}; 174 const bool m_check_memory_access{};
175 static constexpr u64 MinimumRunCycles = 10000U; 175 static constexpr u64 MinimumRunCycles = 10000U;
@@ -331,11 +331,15 @@ bool ArmDynarmic32::IsInThumbMode() const {
331} 331}
332 332
333HaltReason ArmDynarmic32::RunThread(Kernel::KThread* thread) { 333HaltReason ArmDynarmic32::RunThread(Kernel::KThread* thread) {
334 ScopedJitExecution sj(thread->GetOwnerProcess());
335
334 m_jit->ClearExclusiveState(); 336 m_jit->ClearExclusiveState();
335 return TranslateHaltReason(m_jit->Run()); 337 return TranslateHaltReason(m_jit->Run());
336} 338}
337 339
338HaltReason ArmDynarmic32::StepThread(Kernel::KThread* thread) { 340HaltReason ArmDynarmic32::StepThread(Kernel::KThread* thread) {
341 ScopedJitExecution sj(thread->GetOwnerProcess());
342
339 m_jit->ClearExclusiveState(); 343 m_jit->ClearExclusiveState();
340 return TranslateHaltReason(m_jit->Step()); 344 return TranslateHaltReason(m_jit->Step());
341} 345}
@@ -370,13 +374,14 @@ void ArmDynarmic32::RewindBreakpointInstruction() {
370 this->SetContext(m_breakpoint_context); 374 this->SetContext(m_breakpoint_context);
371} 375}
372 376
373ArmDynarmic32::ArmDynarmic32(System& system, bool uses_wall_clock, const Kernel::KProcess* process, 377ArmDynarmic32::ArmDynarmic32(System& system, bool uses_wall_clock, Kernel::KProcess* process,
374 DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index) 378 DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index)
375 : ArmInterface{uses_wall_clock}, m_system{system}, m_exclusive_monitor{exclusive_monitor}, 379 : ArmInterface{uses_wall_clock}, m_system{system}, m_exclusive_monitor{exclusive_monitor},
376 m_cb(std::make_unique<DynarmicCallbacks32>(*this, process)), 380 m_cb(std::make_unique<DynarmicCallbacks32>(*this, process)),
377 m_cp15(std::make_shared<DynarmicCP15>(*this)), m_core_index{core_index} { 381 m_cp15(std::make_shared<DynarmicCP15>(*this)), m_core_index{core_index} {
378 auto& page_table_impl = process->GetPageTable().GetBasePageTable().GetImpl(); 382 auto& page_table_impl = process->GetPageTable().GetBasePageTable().GetImpl();
379 m_jit = MakeJit(&page_table_impl); 383 m_jit = MakeJit(&page_table_impl);
384 ScopedJitExecution::RegisterHandler();
380} 385}
381 386
382ArmDynarmic32::~ArmDynarmic32() = default; 387ArmDynarmic32::~ArmDynarmic32() = default;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index 185ac7cbf..b580efe61 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -20,7 +20,7 @@ class System;
20 20
21class ArmDynarmic32 final : public ArmInterface { 21class ArmDynarmic32 final : public ArmInterface {
22public: 22public:
23 ArmDynarmic32(System& system, bool uses_wall_clock, const Kernel::KProcess* process, 23 ArmDynarmic32(System& system, bool uses_wall_clock, Kernel::KProcess* process,
24 DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index); 24 DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index);
25 ~ArmDynarmic32() override; 25 ~ArmDynarmic32() override;
26 26
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index dff14756e..c811c8ad5 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -15,7 +15,7 @@ using namespace Common::Literals;
15 15
16class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { 16class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
17public: 17public:
18 explicit DynarmicCallbacks64(ArmDynarmic64& parent, const Kernel::KProcess* process) 18 explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process)
19 : m_parent{parent}, m_memory(process->GetMemory()), 19 : m_parent{parent}, m_memory(process->GetMemory()),
20 m_process(process), m_debugger_enabled{parent.m_system.DebuggerEnabled()}, 20 m_process(process), m_debugger_enabled{parent.m_system.DebuggerEnabled()},
21 m_check_memory_access{m_debugger_enabled || 21 m_check_memory_access{m_debugger_enabled ||
@@ -216,7 +216,7 @@ public:
216 Core::Memory::Memory& m_memory; 216 Core::Memory::Memory& m_memory;
217 u64 m_tpidrro_el0{}; 217 u64 m_tpidrro_el0{};
218 u64 m_tpidr_el0{}; 218 u64 m_tpidr_el0{};
219 const Kernel::KProcess* m_process{}; 219 Kernel::KProcess* m_process{};
220 const bool m_debugger_enabled{}; 220 const bool m_debugger_enabled{};
221 const bool m_check_memory_access{}; 221 const bool m_check_memory_access{};
222 static constexpr u64 MinimumRunCycles = 10000U; 222 static constexpr u64 MinimumRunCycles = 10000U;
@@ -362,11 +362,15 @@ std::shared_ptr<Dynarmic::A64::Jit> ArmDynarmic64::MakeJit(Common::PageTable* pa
362} 362}
363 363
364HaltReason ArmDynarmic64::RunThread(Kernel::KThread* thread) { 364HaltReason ArmDynarmic64::RunThread(Kernel::KThread* thread) {
365 ScopedJitExecution sj(thread->GetOwnerProcess());
366
365 m_jit->ClearExclusiveState(); 367 m_jit->ClearExclusiveState();
366 return TranslateHaltReason(m_jit->Run()); 368 return TranslateHaltReason(m_jit->Run());
367} 369}
368 370
369HaltReason ArmDynarmic64::StepThread(Kernel::KThread* thread) { 371HaltReason ArmDynarmic64::StepThread(Kernel::KThread* thread) {
372 ScopedJitExecution sj(thread->GetOwnerProcess());
373
370 m_jit->ClearExclusiveState(); 374 m_jit->ClearExclusiveState();
371 return TranslateHaltReason(m_jit->Step()); 375 return TranslateHaltReason(m_jit->Step());
372} 376}
@@ -399,13 +403,14 @@ void ArmDynarmic64::RewindBreakpointInstruction() {
399 this->SetContext(m_breakpoint_context); 403 this->SetContext(m_breakpoint_context);
400} 404}
401 405
402ArmDynarmic64::ArmDynarmic64(System& system, bool uses_wall_clock, const Kernel::KProcess* process, 406ArmDynarmic64::ArmDynarmic64(System& system, bool uses_wall_clock, Kernel::KProcess* process,
403 DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index) 407 DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index)
404 : ArmInterface{uses_wall_clock}, m_system{system}, m_exclusive_monitor{exclusive_monitor}, 408 : ArmInterface{uses_wall_clock}, m_system{system}, m_exclusive_monitor{exclusive_monitor},
405 m_cb(std::make_unique<DynarmicCallbacks64>(*this, process)), m_core_index{core_index} { 409 m_cb(std::make_unique<DynarmicCallbacks64>(*this, process)), m_core_index{core_index} {
406 auto& page_table = process->GetPageTable().GetBasePageTable(); 410 auto& page_table = process->GetPageTable().GetBasePageTable();
407 auto& page_table_impl = page_table.GetImpl(); 411 auto& page_table_impl = page_table.GetImpl();
408 m_jit = MakeJit(&page_table_impl, page_table.GetAddressSpaceWidth()); 412 m_jit = MakeJit(&page_table_impl, page_table.GetAddressSpaceWidth());
413 ScopedJitExecution::RegisterHandler();
409} 414}
410 415
411ArmDynarmic64::~ArmDynarmic64() = default; 416ArmDynarmic64::~ArmDynarmic64() = default;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index 4f3dd026f..08cd982b3 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -25,7 +25,7 @@ class System;
25 25
26class ArmDynarmic64 final : public ArmInterface { 26class ArmDynarmic64 final : public ArmInterface {
27public: 27public:
28 ArmDynarmic64(System& system, bool uses_wall_clock, const Kernel::KProcess* process, 28 ArmDynarmic64(System& system, bool uses_wall_clock, Kernel::KProcess* process,
29 DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index); 29 DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index);
30 ~ArmDynarmic64() override; 30 ~ArmDynarmic64() override;
31 31
diff --git a/src/core/core.cpp b/src/core/core.cpp
index b14f74976..66f444d39 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -28,7 +28,6 @@
28#include "core/file_sys/savedata_factory.h" 28#include "core/file_sys/savedata_factory.h"
29#include "core/file_sys/vfs_concat.h" 29#include "core/file_sys/vfs_concat.h"
30#include "core/file_sys/vfs_real.h" 30#include "core/file_sys/vfs_real.h"
31#include "core/gpu_dirty_memory_manager.h"
32#include "core/hid/hid_core.h" 31#include "core/hid/hid_core.h"
33#include "core/hle/kernel/k_memory_manager.h" 32#include "core/hle/kernel/k_memory_manager.h"
34#include "core/hle/kernel/k_process.h" 33#include "core/hle/kernel/k_process.h"
@@ -130,11 +129,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
130 129
131struct System::Impl { 130struct System::Impl {
132 explicit Impl(System& system) 131 explicit Impl(System& system)
133 : kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{}, 132 : kernel{system}, fs_controller{system}, hid_core{}, room_network{}, cpu_manager{system},
134 cpu_manager{system}, reporter{system}, applet_manager{system}, profile_manager{}, 133 reporter{system}, applet_manager{system}, profile_manager{}, time_manager{system} {}
135 time_manager{system}, gpu_dirty_memory_write_manager{} {
136 memory.SetGPUDirtyManagers(gpu_dirty_memory_write_manager);
137 }
138 134
139 void Initialize(System& system) { 135 void Initialize(System& system) {
140 device_memory = std::make_unique<Core::DeviceMemory>(); 136 device_memory = std::make_unique<Core::DeviceMemory>();
@@ -241,17 +237,17 @@ struct System::Impl {
241 debugger = std::make_unique<Debugger>(system, port); 237 debugger = std::make_unique<Debugger>(system, port);
242 } 238 }
243 239
244 SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) { 240 void InitializeKernel(System& system) {
245 LOG_DEBUG(Core, "initialized OK"); 241 LOG_DEBUG(Core, "initialized OK");
246 242
247 // Setting changes may require a full system reinitialization (e.g., disabling multicore). 243 // Setting changes may require a full system reinitialization (e.g., disabling multicore).
248 ReinitializeIfNecessary(system); 244 ReinitializeIfNecessary(system);
249 245
250 memory.SetGPUDirtyManagers(gpu_dirty_memory_write_manager);
251
252 kernel.Initialize(); 246 kernel.Initialize();
253 cpu_manager.Initialize(); 247 cpu_manager.Initialize();
248 }
254 249
250 SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) {
255 /// Reset all glue registrations 251 /// Reset all glue registrations
256 arp_manager.ResetAll(); 252 arp_manager.ResetAll();
257 253
@@ -300,17 +296,9 @@ struct System::Impl {
300 return SystemResultStatus::ErrorGetLoader; 296 return SystemResultStatus::ErrorGetLoader;
301 } 297 }
302 298
303 SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)}; 299 InitializeKernel(system);
304 if (init_result != SystemResultStatus::Success) {
305 LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
306 static_cast<int>(init_result));
307 ShutdownMainProcess();
308 return init_result;
309 }
310
311 telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
312 300
313 // Create the process. 301 // Create the application process.
314 auto main_process = Kernel::KProcess::Create(system.Kernel()); 302 auto main_process = Kernel::KProcess::Create(system.Kernel());
315 Kernel::KProcess::Register(system.Kernel(), main_process); 303 Kernel::KProcess::Register(system.Kernel(), main_process);
316 kernel.AppendNewProcess(main_process); 304 kernel.AppendNewProcess(main_process);
@@ -323,7 +311,18 @@ struct System::Impl {
323 return static_cast<SystemResultStatus>( 311 return static_cast<SystemResultStatus>(
324 static_cast<u32>(SystemResultStatus::ErrorLoader) + static_cast<u32>(load_result)); 312 static_cast<u32>(SystemResultStatus::ErrorLoader) + static_cast<u32>(load_result));
325 } 313 }
314
315 // Set up the rest of the system.
316 SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)};
317 if (init_result != SystemResultStatus::Success) {
318 LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
319 static_cast<int>(init_result));
320 ShutdownMainProcess();
321 return init_result;
322 }
323
326 AddGlueRegistrationForProcess(*app_loader, *main_process); 324 AddGlueRegistrationForProcess(*app_loader, *main_process);
325 telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
327 326
328 // Initialize cheat engine 327 // Initialize cheat engine
329 if (cheat_engine) { 328 if (cheat_engine) {
@@ -426,7 +425,6 @@ struct System::Impl {
426 cpu_manager.Shutdown(); 425 cpu_manager.Shutdown();
427 debugger.reset(); 426 debugger.reset();
428 kernel.Shutdown(); 427 kernel.Shutdown();
429 memory.Reset();
430 Network::RestartSocketOperations(); 428 Network::RestartSocketOperations();
431 429
432 if (auto room_member = room_network.GetRoomMember().lock()) { 430 if (auto room_member = room_network.GetRoomMember().lock()) {
@@ -507,7 +505,6 @@ struct System::Impl {
507 std::unique_ptr<Tegra::Host1x::Host1x> host1x_core; 505 std::unique_ptr<Tegra::Host1x::Host1x> host1x_core;
508 std::unique_ptr<Core::DeviceMemory> device_memory; 506 std::unique_ptr<Core::DeviceMemory> device_memory;
509 std::unique_ptr<AudioCore::AudioCore> audio_core; 507 std::unique_ptr<AudioCore::AudioCore> audio_core;
510 Core::Memory::Memory memory;
511 Core::HID::HIDCore hid_core; 508 Core::HID::HIDCore hid_core;
512 Network::RoomNetwork room_network; 509 Network::RoomNetwork room_network;
513 510
@@ -567,9 +564,6 @@ struct System::Impl {
567 std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; 564 std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
568 std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{}; 565 std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{};
569 566
570 std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES>
571 gpu_dirty_memory_write_manager{};
572
573 std::deque<std::vector<u8>> user_channel; 567 std::deque<std::vector<u8>> user_channel;
574}; 568};
575 569
@@ -652,29 +646,12 @@ void System::PrepareReschedule(const u32 core_index) {
652 impl->kernel.PrepareReschedule(core_index); 646 impl->kernel.PrepareReschedule(core_index);
653} 647}
654 648
655Core::GPUDirtyMemoryManager& System::CurrentGPUDirtyMemoryManager() {
656 const std::size_t core = impl->kernel.GetCurrentHostThreadID();
657 return impl->gpu_dirty_memory_write_manager[core < Core::Hardware::NUM_CPU_CORES
658 ? core
659 : Core::Hardware::NUM_CPU_CORES - 1];
660}
661
662/// Provides a constant reference to the current gou dirty memory manager.
663const Core::GPUDirtyMemoryManager& System::CurrentGPUDirtyMemoryManager() const {
664 const std::size_t core = impl->kernel.GetCurrentHostThreadID();
665 return impl->gpu_dirty_memory_write_manager[core < Core::Hardware::NUM_CPU_CORES
666 ? core
667 : Core::Hardware::NUM_CPU_CORES - 1];
668}
669
670size_t System::GetCurrentHostThreadID() const { 649size_t System::GetCurrentHostThreadID() const {
671 return impl->kernel.GetCurrentHostThreadID(); 650 return impl->kernel.GetCurrentHostThreadID();
672} 651}
673 652
674void System::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) { 653void System::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) {
675 for (auto& manager : impl->gpu_dirty_memory_write_manager) { 654 return this->ApplicationProcess()->GatherGPUDirtyMemory(callback);
676 manager.Gather(callback);
677 }
678} 655}
679 656
680PerfStatsResults System::GetAndResetPerfStats() { 657PerfStatsResults System::GetAndResetPerfStats() {
@@ -723,20 +700,12 @@ const Kernel::KProcess* System::ApplicationProcess() const {
723 return impl->kernel.ApplicationProcess(); 700 return impl->kernel.ApplicationProcess();
724} 701}
725 702
726ExclusiveMonitor& System::Monitor() {
727 return impl->kernel.GetExclusiveMonitor();
728}
729
730const ExclusiveMonitor& System::Monitor() const {
731 return impl->kernel.GetExclusiveMonitor();
732}
733
734Memory::Memory& System::ApplicationMemory() { 703Memory::Memory& System::ApplicationMemory() {
735 return impl->memory; 704 return impl->kernel.ApplicationProcess()->GetMemory();
736} 705}
737 706
738const Core::Memory::Memory& System::ApplicationMemory() const { 707const Core::Memory::Memory& System::ApplicationMemory() const {
739 return impl->memory; 708 return impl->kernel.ApplicationProcess()->GetMemory();
740} 709}
741 710
742Tegra::GPU& System::GPU() { 711Tegra::GPU& System::GPU() {
diff --git a/src/core/core.h b/src/core/core.h
index 473204db7..ba5add0dc 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -116,7 +116,6 @@ class CpuManager;
116class Debugger; 116class Debugger;
117class DeviceMemory; 117class DeviceMemory;
118class ExclusiveMonitor; 118class ExclusiveMonitor;
119class GPUDirtyMemoryManager;
120class PerfStats; 119class PerfStats;
121class Reporter; 120class Reporter;
122class SpeedLimiter; 121class SpeedLimiter;
@@ -225,12 +224,6 @@ public:
225 /// Prepare the core emulation for a reschedule 224 /// Prepare the core emulation for a reschedule
226 void PrepareReschedule(u32 core_index); 225 void PrepareReschedule(u32 core_index);
227 226
228 /// Provides a reference to the gou dirty memory manager.
229 [[nodiscard]] Core::GPUDirtyMemoryManager& CurrentGPUDirtyMemoryManager();
230
231 /// Provides a constant reference to the current gou dirty memory manager.
232 [[nodiscard]] const Core::GPUDirtyMemoryManager& CurrentGPUDirtyMemoryManager() const;
233
234 void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback); 227 void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback);
235 228
236 [[nodiscard]] size_t GetCurrentHostThreadID() const; 229 [[nodiscard]] size_t GetCurrentHostThreadID() const;
@@ -250,12 +243,6 @@ public:
250 /// Gets a const reference to the underlying CPU manager 243 /// Gets a const reference to the underlying CPU manager
251 [[nodiscard]] const CpuManager& GetCpuManager() const; 244 [[nodiscard]] const CpuManager& GetCpuManager() const;
252 245
253 /// Gets a reference to the exclusive monitor
254 [[nodiscard]] ExclusiveMonitor& Monitor();
255
256 /// Gets a constant reference to the exclusive monitor
257 [[nodiscard]] const ExclusiveMonitor& Monitor() const;
258
259 /// Gets a mutable reference to the system memory instance. 246 /// Gets a mutable reference to the system memory instance.
260 [[nodiscard]] Core::Memory::Memory& ApplicationMemory(); 247 [[nodiscard]] Core::Memory::Memory& ApplicationMemory();
261 248
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp
index 7be1322cc..31033634c 100644
--- a/src/core/file_sys/ips_layer.cpp
+++ b/src/core/file_sys/ips_layer.cpp
@@ -73,6 +73,9 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
73 return nullptr; 73 return nullptr;
74 74
75 auto in_data = in->ReadAllBytes(); 75 auto in_data = in->ReadAllBytes();
76 if (in_data.size() == 0) {
77 return nullptr;
78 }
76 79
77 std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4); 80 std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
78 u64 offset = 5; // After header 81 u64 offset = 5; // After header
@@ -88,6 +91,10 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
88 else 91 else
89 real_offset = (temp[0] << 16) | (temp[1] << 8) | temp[2]; 92 real_offset = (temp[0] << 16) | (temp[1] << 8) | temp[2];
90 93
94 if (real_offset > in_data.size()) {
95 return nullptr;
96 }
97
91 u16 data_size{}; 98 u16 data_size{};
92 if (ips->ReadObject(&data_size, offset) != sizeof(u16)) 99 if (ips->ReadObject(&data_size, offset) != sizeof(u16))
93 return nullptr; 100 return nullptr;
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index 763a44fee..539c7f7af 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -166,6 +166,10 @@ u32 ProgramMetadata::GetSystemResourceSize() const {
166 return npdm_header.system_resource_size; 166 return npdm_header.system_resource_size;
167} 167}
168 168
169PoolPartition ProgramMetadata::GetPoolPartition() const {
170 return acid_header.pool_partition;
171}
172
169const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const { 173const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const {
170 return aci_kernel_capabilities; 174 return aci_kernel_capabilities;
171} 175}
@@ -201,7 +205,7 @@ void ProgramMetadata::Print() const {
201 // Begin ACID printing (potential perms, signed) 205 // Begin ACID printing (potential perms, signed)
202 LOG_DEBUG(Service_FS, "Magic: {:.4}", acid_header.magic.data()); 206 LOG_DEBUG(Service_FS, "Magic: {:.4}", acid_header.magic.data());
203 LOG_DEBUG(Service_FS, "Flags: 0x{:02X}", acid_header.flags); 207 LOG_DEBUG(Service_FS, "Flags: 0x{:02X}", acid_header.flags);
204 LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.is_retail ? "YES" : "NO"); 208 LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.production_flag ? "YES" : "NO");
205 LOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min); 209 LOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min);
206 LOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max); 210 LOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max);
207 LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", acid_file_access.permissions); 211 LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", acid_file_access.permissions);
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
index 76ee97d78..a53092b87 100644
--- a/src/core/file_sys/program_metadata.h
+++ b/src/core/file_sys/program_metadata.h
@@ -34,6 +34,13 @@ enum class ProgramFilePermission : u64 {
34 Everything = 1ULL << 63, 34 Everything = 1ULL << 63,
35}; 35};
36 36
37enum class PoolPartition : u32 {
38 Application = 0,
39 Applet = 1,
40 System = 2,
41 SystemNonSecure = 3,
42};
43
37/** 44/**
38 * Helper which implements an interface to parse Program Description Metadata (NPDM) 45 * Helper which implements an interface to parse Program Description Metadata (NPDM)
39 * Data can either be loaded from a file path or with data and an offset into it. 46 * Data can either be loaded from a file path or with data and an offset into it.
@@ -72,6 +79,7 @@ public:
72 u64 GetTitleID() const; 79 u64 GetTitleID() const;
73 u64 GetFilesystemPermissions() const; 80 u64 GetFilesystemPermissions() const;
74 u32 GetSystemResourceSize() const; 81 u32 GetSystemResourceSize() const;
82 PoolPartition GetPoolPartition() const;
75 const KernelCapabilityDescriptors& GetKernelCapabilities() const; 83 const KernelCapabilityDescriptors& GetKernelCapabilities() const;
76 const std::array<u8, 0x10>& GetName() const { 84 const std::array<u8, 0x10>& GetName() const {
77 return npdm_header.application_name; 85 return npdm_header.application_name;
@@ -116,8 +124,9 @@ private:
116 union { 124 union {
117 u32 flags; 125 u32 flags;
118 126
119 BitField<0, 1, u32> is_retail; 127 BitField<0, 1, u32> production_flag;
120 BitField<1, 31, u32> flags_unk; 128 BitField<1, 1, u32> unqualified_approval;
129 BitField<2, 4, PoolPartition> pool_partition;
121 }; 130 };
122 u64_le title_id_min; 131 u64_le title_id_min;
123 u64_le title_id_max; 132 u64_le title_id_max;
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp
index 78d43d729..48889253d 100644
--- a/src/core/hle/kernel/k_address_arbiter.cpp
+++ b/src/core/hle/kernel/k_address_arbiter.cpp
@@ -4,6 +4,7 @@
4#include "core/arm/exclusive_monitor.h" 4#include "core/arm/exclusive_monitor.h"
5#include "core/core.h" 5#include "core/core.h"
6#include "core/hle/kernel/k_address_arbiter.h" 6#include "core/hle/kernel/k_address_arbiter.h"
7#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_scheduler.h" 8#include "core/hle/kernel/k_scheduler.h"
8#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" 9#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
9#include "core/hle/kernel/k_thread.h" 10#include "core/hle/kernel/k_thread.h"
@@ -26,9 +27,9 @@ bool ReadFromUser(KernelCore& kernel, s32* out, KProcessAddress address) {
26 return true; 27 return true;
27} 28}
28 29
29bool DecrementIfLessThan(Core::System& system, s32* out, KProcessAddress address, s32 value) { 30bool DecrementIfLessThan(KernelCore& kernel, s32* out, KProcessAddress address, s32 value) {
30 auto& monitor = system.Monitor(); 31 auto& monitor = GetCurrentProcess(kernel).GetExclusiveMonitor();
31 const auto current_core = system.Kernel().CurrentPhysicalCoreIndex(); 32 const auto current_core = kernel.CurrentPhysicalCoreIndex();
32 33
33 // NOTE: If scheduler lock is not held here, interrupt disable is required. 34 // NOTE: If scheduler lock is not held here, interrupt disable is required.
34 // KScopedInterruptDisable di; 35 // KScopedInterruptDisable di;
@@ -66,10 +67,10 @@ bool DecrementIfLessThan(Core::System& system, s32* out, KProcessAddress address
66 return true; 67 return true;
67} 68}
68 69
69bool UpdateIfEqual(Core::System& system, s32* out, KProcessAddress address, s32 value, 70bool UpdateIfEqual(KernelCore& kernel, s32* out, KProcessAddress address, s32 value,
70 s32 new_value) { 71 s32 new_value) {
71 auto& monitor = system.Monitor(); 72 auto& monitor = GetCurrentProcess(kernel).GetExclusiveMonitor();
72 const auto current_core = system.Kernel().CurrentPhysicalCoreIndex(); 73 const auto current_core = kernel.CurrentPhysicalCoreIndex();
73 74
74 // NOTE: If scheduler lock is not held here, interrupt disable is required. 75 // NOTE: If scheduler lock is not held here, interrupt disable is required.
75 // KScopedInterruptDisable di; 76 // KScopedInterruptDisable di;
@@ -159,7 +160,7 @@ Result KAddressArbiter::SignalAndIncrementIfEqual(uint64_t addr, s32 value, s32
159 160
160 // Check the userspace value. 161 // Check the userspace value.
161 s32 user_value{}; 162 s32 user_value{};
162 R_UNLESS(UpdateIfEqual(m_system, std::addressof(user_value), addr, value, value + 1), 163 R_UNLESS(UpdateIfEqual(m_kernel, std::addressof(user_value), addr, value, value + 1),
163 ResultInvalidCurrentMemory); 164 ResultInvalidCurrentMemory);
164 R_UNLESS(user_value == value, ResultInvalidState); 165 R_UNLESS(user_value == value, ResultInvalidState);
165 166
@@ -219,7 +220,7 @@ Result KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(uint64_t addr, s32
219 s32 user_value{}; 220 s32 user_value{};
220 bool succeeded{}; 221 bool succeeded{};
221 if (value != new_value) { 222 if (value != new_value) {
222 succeeded = UpdateIfEqual(m_system, std::addressof(user_value), addr, value, new_value); 223 succeeded = UpdateIfEqual(m_kernel, std::addressof(user_value), addr, value, new_value);
223 } else { 224 } else {
224 succeeded = ReadFromUser(m_kernel, std::addressof(user_value), addr); 225 succeeded = ReadFromUser(m_kernel, std::addressof(user_value), addr);
225 } 226 }
@@ -262,7 +263,7 @@ Result KAddressArbiter::WaitIfLessThan(uint64_t addr, s32 value, bool decrement,
262 s32 user_value{}; 263 s32 user_value{};
263 bool succeeded{}; 264 bool succeeded{};
264 if (decrement) { 265 if (decrement) {
265 succeeded = DecrementIfLessThan(m_system, std::addressof(user_value), addr, value); 266 succeeded = DecrementIfLessThan(m_kernel, std::addressof(user_value), addr, value);
266 } else { 267 } else {
267 succeeded = ReadFromUser(m_kernel, std::addressof(user_value), addr); 268 succeeded = ReadFromUser(m_kernel, std::addressof(user_value), addr);
268 } 269 }
diff --git a/src/core/hle/kernel/k_auto_object_container.cpp b/src/core/hle/kernel/k_auto_object_container.cpp
index 636b3f993..7bea1a1c2 100644
--- a/src/core/hle/kernel/k_auto_object_container.cpp
+++ b/src/core/hle/kernel/k_auto_object_container.cpp
@@ -8,19 +8,22 @@
8namespace Kernel { 8namespace Kernel {
9 9
10void KAutoObjectWithListContainer::Register(KAutoObjectWithList* obj) { 10void KAutoObjectWithListContainer::Register(KAutoObjectWithList* obj) {
11 KScopedLightLock lk(m_lock); 11 // KScopedInterruptDisable di;
12 KScopedSpinLock lk(m_lock);
12 13
13 m_object_list.insert_unique(*obj); 14 m_object_list.insert_unique(*obj);
14} 15}
15 16
16void KAutoObjectWithListContainer::Unregister(KAutoObjectWithList* obj) { 17void KAutoObjectWithListContainer::Unregister(KAutoObjectWithList* obj) {
17 KScopedLightLock lk(m_lock); 18 // KScopedInterruptDisable di;
19 KScopedSpinLock lk(m_lock);
18 20
19 m_object_list.erase(*obj); 21 m_object_list.erase(*obj);
20} 22}
21 23
22size_t KAutoObjectWithListContainer::GetOwnedCount(KProcess* owner) { 24size_t KAutoObjectWithListContainer::GetOwnedCount(KProcess* owner) {
23 KScopedLightLock lk(m_lock); 25 // KScopedInterruptDisable di;
26 KScopedSpinLock lk(m_lock);
24 27
25 return std::count_if(m_object_list.begin(), m_object_list.end(), 28 return std::count_if(m_object_list.begin(), m_object_list.end(),
26 [&](const auto& obj) { return obj.GetOwner() == owner; }); 29 [&](const auto& obj) { return obj.GetOwner() == owner; });
diff --git a/src/core/hle/kernel/k_auto_object_container.h b/src/core/hle/kernel/k_auto_object_container.h
index badd75d2a..770743d21 100644
--- a/src/core/hle/kernel/k_auto_object_container.h
+++ b/src/core/hle/kernel/k_auto_object_container.h
@@ -7,7 +7,7 @@
7 7
8#include "common/common_funcs.h" 8#include "common/common_funcs.h"
9#include "core/hle/kernel/k_auto_object.h" 9#include "core/hle/kernel/k_auto_object.h"
10#include "core/hle/kernel/k_light_lock.h" 10#include "core/hle/kernel/k_spin_lock.h"
11 11
12namespace Kernel { 12namespace Kernel {
13 13
@@ -21,32 +21,7 @@ public:
21 21
22 using ListType = boost::intrusive::rbtree<KAutoObjectWithList>; 22 using ListType = boost::intrusive::rbtree<KAutoObjectWithList>;
23 23
24 class ListAccessor : public KScopedLightLock { 24 KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(), m_object_list() {}
25 public:
26 explicit ListAccessor(KAutoObjectWithListContainer* container)
27 : KScopedLightLock(container->m_lock), m_list(container->m_object_list) {}
28 explicit ListAccessor(KAutoObjectWithListContainer& container)
29 : KScopedLightLock(container.m_lock), m_list(container.m_object_list) {}
30
31 typename ListType::iterator begin() const {
32 return m_list.begin();
33 }
34
35 typename ListType::iterator end() const {
36 return m_list.end();
37 }
38
39 typename ListType::iterator find(typename ListType::const_reference ref) const {
40 return m_list.find(ref);
41 }
42
43 private:
44 ListType& m_list;
45 };
46
47 friend class ListAccessor;
48
49 KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(kernel), m_object_list() {}
50 25
51 void Initialize() {} 26 void Initialize() {}
52 void Finalize() {} 27 void Finalize() {}
@@ -56,7 +31,7 @@ public:
56 size_t GetOwnedCount(KProcess* owner); 31 size_t GetOwnedCount(KProcess* owner);
57 32
58private: 33private:
59 KLightLock m_lock; 34 KSpinLock m_lock;
60 ListType m_object_list; 35 ListType m_object_list;
61}; 36};
62 37
diff --git a/src/core/hle/kernel/k_capabilities.cpp b/src/core/hle/kernel/k_capabilities.cpp
index 274fee493..d2288c30d 100644
--- a/src/core/hle/kernel/k_capabilities.cpp
+++ b/src/core/hle/kernel/k_capabilities.cpp
@@ -185,6 +185,10 @@ Result KCapabilities::ProcessMapRegionCapability(const u32 cap, F f) {
185 case RegionType::NoMapping: 185 case RegionType::NoMapping:
186 break; 186 break;
187 case RegionType::KernelTraceBuffer: 187 case RegionType::KernelTraceBuffer:
188 if constexpr (!IsKTraceEnabled) {
189 break;
190 }
191 [[fallthrough]];
188 case RegionType::OnMemoryBootImage: 192 case RegionType::OnMemoryBootImage:
189 case RegionType::DTB: 193 case RegionType::DTB:
190 R_TRY(f(MemoryRegions[static_cast<u32>(type)], perm)); 194 R_TRY(f(MemoryRegions[static_cast<u32>(type)], perm));
@@ -330,8 +334,6 @@ Result KCapabilities::SetCapabilities(std::span<const u32> caps, KProcessPageTab
330 334
331 // Map the range. 335 // Map the range.
332 R_TRY(this->MapRange_(cap, size_cap, page_table)); 336 R_TRY(this->MapRange_(cap, size_cap, page_table));
333 } else if (GetCapabilityType(cap) == CapabilityType::MapRegion && !IsKTraceEnabled) {
334 continue;
335 } else { 337 } else {
336 R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table)); 338 R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table));
337 } 339 }
diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp
index 11b1b977e..68cea978a 100644
--- a/src/core/hle/kernel/k_client_port.cpp
+++ b/src/core/hle/kernel/k_client_port.cpp
@@ -58,9 +58,8 @@ Result KClientPort::CreateSession(KClientSession** out) {
58 KSession* session{}; 58 KSession* session{};
59 59
60 // Reserve a new session from the resource limit. 60 // Reserve a new session from the resource limit.
61 //! FIXME: we are reserving this from the wrong resource limit! 61 KScopedResourceReservation session_reservation(GetCurrentProcessPointer(m_kernel),
62 KScopedResourceReservation session_reservation( 62 LimitableResource::SessionCountMax);
63 m_kernel.ApplicationProcess()->GetResourceLimit(), LimitableResource::SessionCountMax);
64 R_UNLESS(session_reservation.Succeeded(), ResultLimitReached); 63 R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
65 64
66 // Allocate a session normally. 65 // Allocate a session normally.
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
index 7633a51fb..94ea3527a 100644
--- a/src/core/hle/kernel/k_condition_variable.cpp
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -28,10 +28,10 @@ bool WriteToUser(KernelCore& kernel, KProcessAddress address, const u32* p) {
28 return true; 28 return true;
29} 29}
30 30
31bool UpdateLockAtomic(Core::System& system, u32* out, KProcessAddress address, u32 if_zero, 31bool UpdateLockAtomic(KernelCore& kernel, u32* out, KProcessAddress address, u32 if_zero,
32 u32 new_orr_mask) { 32 u32 new_orr_mask) {
33 auto& monitor = system.Monitor(); 33 auto& monitor = GetCurrentProcess(kernel).GetExclusiveMonitor();
34 const auto current_core = system.Kernel().CurrentPhysicalCoreIndex(); 34 const auto current_core = kernel.CurrentPhysicalCoreIndex();
35 35
36 u32 expected{}; 36 u32 expected{};
37 37
@@ -208,7 +208,7 @@ void KConditionVariable::SignalImpl(KThread* thread) {
208 // TODO(bunnei): We should call CanAccessAtomic(..) here. 208 // TODO(bunnei): We should call CanAccessAtomic(..) here.
209 can_access = true; 209 can_access = true;
210 if (can_access) [[likely]] { 210 if (can_access) [[likely]] {
211 UpdateLockAtomic(m_system, std::addressof(prev_tag), address, own_tag, 211 UpdateLockAtomic(m_kernel, std::addressof(prev_tag), address, own_tag,
212 Svc::HandleWaitMask); 212 Svc::HandleWaitMask);
213 } 213 }
214 } 214 }
diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h
index d7660630c..1bf68e6b0 100644
--- a/src/core/hle/kernel/k_handle_table.h
+++ b/src/core/hle/kernel/k_handle_table.h
@@ -90,8 +90,7 @@ public:
90 // Handle pseudo-handles. 90 // Handle pseudo-handles.
91 if constexpr (std::derived_from<KProcess, T>) { 91 if constexpr (std::derived_from<KProcess, T>) {
92 if (handle == Svc::PseudoHandle::CurrentProcess) { 92 if (handle == Svc::PseudoHandle::CurrentProcess) {
93 //! FIXME: this is the wrong process! 93 auto* const cur_process = GetCurrentProcessPointer(m_kernel);
94 auto* const cur_process = m_kernel.ApplicationProcess();
95 ASSERT(cur_process != nullptr); 94 ASSERT(cur_process != nullptr);
96 return cur_process; 95 return cur_process;
97 } 96 }
diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp
index 423289145..8c1549559 100644
--- a/src/core/hle/kernel/k_page_table_base.cpp
+++ b/src/core/hle/kernel/k_page_table_base.cpp
@@ -434,7 +434,7 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
434void KPageTableBase::Finalize() { 434void KPageTableBase::Finalize() {
435 auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) { 435 auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) {
436 if (Settings::IsFastmemEnabled()) { 436 if (Settings::IsFastmemEnabled()) {
437 m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size); 437 m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size, false);
438 } 438 }
439 }; 439 };
440 440
@@ -5243,7 +5243,7 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) {
5243 // Unmap. 5243 // Unmap.
5244 R_ASSERT(this->Operate(updater.GetPageList(), cur_address, 5244 R_ASSERT(this->Operate(updater.GetPageList(), cur_address,
5245 cur_pages, 0, false, unmap_properties, 5245 cur_pages, 0, false, unmap_properties,
5246 OperationType::Unmap, true)); 5246 OperationType::UnmapPhysical, true));
5247 } 5247 }
5248 5248
5249 // Check if we're done. 5249 // Check if we're done.
@@ -5326,7 +5326,7 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) {
5326 // Map the papges. 5326 // Map the papges.
5327 R_TRY(this->Operate(updater.GetPageList(), cur_address, map_pages, 5327 R_TRY(this->Operate(updater.GetPageList(), cur_address, map_pages,
5328 cur_pg, map_properties, 5328 cur_pg, map_properties,
5329 OperationType::MapFirstGroup, false)); 5329 OperationType::MapFirstGroupPhysical, false));
5330 } 5330 }
5331 } 5331 }
5332 5332
@@ -5480,7 +5480,7 @@ Result KPageTableBase::UnmapPhysicalMemory(KProcessAddress address, size_t size)
5480 5480
5481 // Unmap. 5481 // Unmap.
5482 R_ASSERT(this->Operate(updater.GetPageList(), cur_address, cur_pages, 0, false, 5482 R_ASSERT(this->Operate(updater.GetPageList(), cur_address, cur_pages, 0, false,
5483 unmap_properties, OperationType::Unmap, false)); 5483 unmap_properties, OperationType::UnmapPhysical, false));
5484 } 5484 }
5485 5485
5486 // Check if we're done. 5486 // Check if we're done.
@@ -5655,7 +5655,10 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
5655 // or free them to the page list, and so it goes unused (along with page properties). 5655 // or free them to the page list, and so it goes unused (along with page properties).
5656 5656
5657 switch (operation) { 5657 switch (operation) {
5658 case OperationType::Unmap: { 5658 case OperationType::Unmap:
5659 case OperationType::UnmapPhysical: {
5660 const bool separate_heap = operation == OperationType::UnmapPhysical;
5661
5659 // Ensure that any pages we track are closed on exit. 5662 // Ensure that any pages we track are closed on exit.
5660 KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager()); 5663 KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager());
5661 SCOPE_EXIT({ pages_to_close.CloseAndReset(); }); 5664 SCOPE_EXIT({ pages_to_close.CloseAndReset(); });
@@ -5664,7 +5667,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
5664 this->MakePageGroup(pages_to_close, virt_addr, num_pages); 5667 this->MakePageGroup(pages_to_close, virt_addr, num_pages);
5665 5668
5666 // Unmap. 5669 // Unmap.
5667 m_memory->UnmapRegion(*m_impl, virt_addr, num_pages * PageSize); 5670 m_memory->UnmapRegion(*m_impl, virt_addr, num_pages * PageSize, separate_heap);
5668 5671
5669 R_SUCCEED(); 5672 R_SUCCEED();
5670 } 5673 }
@@ -5672,7 +5675,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
5672 ASSERT(virt_addr != 0); 5675 ASSERT(virt_addr != 0);
5673 ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize)); 5676 ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize));
5674 m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr, 5677 m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr,
5675 ConvertToMemoryPermission(properties.perm)); 5678 ConvertToMemoryPermission(properties.perm), false);
5676 5679
5677 // Open references to pages, if we should. 5680 // Open references to pages, if we should.
5678 if (this->IsHeapPhysicalAddress(phys_addr)) { 5681 if (this->IsHeapPhysicalAddress(phys_addr)) {
@@ -5711,16 +5714,19 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
5711 5714
5712 switch (operation) { 5715 switch (operation) {
5713 case OperationType::MapGroup: 5716 case OperationType::MapGroup:
5714 case OperationType::MapFirstGroup: { 5717 case OperationType::MapFirstGroup:
5718 case OperationType::MapFirstGroupPhysical: {
5719 const bool separate_heap = operation == OperationType::MapFirstGroupPhysical;
5720
5715 // We want to maintain a new reference to every page in the group. 5721 // We want to maintain a new reference to every page in the group.
5716 KScopedPageGroup spg(page_group, operation != OperationType::MapFirstGroup); 5722 KScopedPageGroup spg(page_group, operation == OperationType::MapGroup);
5717 5723
5718 for (const auto& node : page_group) { 5724 for (const auto& node : page_group) {
5719 const size_t size{node.GetNumPages() * PageSize}; 5725 const size_t size{node.GetNumPages() * PageSize};
5720 5726
5721 // Map the pages. 5727 // Map the pages.
5722 m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress(), 5728 m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress(),
5723 ConvertToMemoryPermission(properties.perm)); 5729 ConvertToMemoryPermission(properties.perm), separate_heap);
5724 5730
5725 virt_addr += size; 5731 virt_addr += size;
5726 } 5732 }
diff --git a/src/core/hle/kernel/k_page_table_base.h b/src/core/hle/kernel/k_page_table_base.h
index 556d230b3..077cafc96 100644
--- a/src/core/hle/kernel/k_page_table_base.h
+++ b/src/core/hle/kernel/k_page_table_base.h
@@ -104,6 +104,9 @@ protected:
104 ChangePermissionsAndRefresh = 5, 104 ChangePermissionsAndRefresh = 5,
105 ChangePermissionsAndRefreshAndFlush = 6, 105 ChangePermissionsAndRefreshAndFlush = 6,
106 Separate = 7, 106 Separate = 7,
107
108 MapFirstGroupPhysical = 65000,
109 UnmapPhysical = 65001,
107 }; 110 };
108 111
109 static constexpr size_t MaxPhysicalMapAlignment = 1_GiB; 112 static constexpr size_t MaxPhysicalMapAlignment = 1_GiB;
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 3a2635e1f..068e71dff 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -306,12 +306,16 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa
306 False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); 306 False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
307 R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, 307 R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
308 params.code_address, params.code_num_pages * PageSize, 308 params.code_address, params.code_num_pages * PageSize,
309 m_system_resource, res_limit, this->GetMemory(), 0)); 309 m_system_resource, res_limit, m_memory, 0));
310 } 310 }
311 ON_RESULT_FAILURE_2 { 311 ON_RESULT_FAILURE_2 {
312 m_page_table.Finalize(); 312 m_page_table.Finalize();
313 }; 313 };
314 314
315 // Ensure our memory is initialized.
316 m_memory.SetCurrentPageTable(*this);
317 m_memory.SetGPUDirtyManagers(m_dirty_memory_managers);
318
315 // Ensure we can insert the code region. 319 // Ensure we can insert the code region.
316 R_UNLESS(m_page_table.CanContain(params.code_address, params.code_num_pages * PageSize, 320 R_UNLESS(m_page_table.CanContain(params.code_address, params.code_num_pages * PageSize,
317 KMemoryState::Code), 321 KMemoryState::Code),
@@ -399,12 +403,16 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params,
399 False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); 403 False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
400 R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, 404 R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
401 params.code_address, code_size, m_system_resource, res_limit, 405 params.code_address, code_size, m_system_resource, res_limit,
402 this->GetMemory(), aslr_space_start)); 406 m_memory, aslr_space_start));
403 } 407 }
404 ON_RESULT_FAILURE_2 { 408 ON_RESULT_FAILURE_2 {
405 m_page_table.Finalize(); 409 m_page_table.Finalize();
406 }; 410 };
407 411
412 // Ensure our memory is initialized.
413 m_memory.SetCurrentPageTable(*this);
414 m_memory.SetGPUDirtyManagers(m_dirty_memory_managers);
415
408 // Ensure we can insert the code region. 416 // Ensure we can insert the code region.
409 R_UNLESS(m_page_table.CanContain(params.code_address, code_size, KMemoryState::Code), 417 R_UNLESS(m_page_table.CanContain(params.code_address, code_size, KMemoryState::Code),
410 ResultInvalidMemoryRegion); 418 ResultInvalidMemoryRegion);
@@ -1094,8 +1102,7 @@ void KProcess::UnpinThread(KThread* thread) {
1094 1102
1095Result KProcess::GetThreadList(s32* out_num_threads, KProcessAddress out_thread_ids, 1103Result KProcess::GetThreadList(s32* out_num_threads, KProcessAddress out_thread_ids,
1096 s32 max_out_count) { 1104 s32 max_out_count) {
1097 // TODO: use current memory reference 1105 auto& memory = this->GetMemory();
1098 auto& memory = m_kernel.System().ApplicationMemory();
1099 1106
1100 // Lock the list. 1107 // Lock the list.
1101 KScopedLightLock lk(m_list_lock); 1108 KScopedLightLock lk(m_list_lock);
@@ -1128,14 +1135,15 @@ void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {}
1128KProcess::KProcess(KernelCore& kernel) 1135KProcess::KProcess(KernelCore& kernel)
1129 : KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel}, m_state_lock{kernel}, 1136 : KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel}, m_state_lock{kernel},
1130 m_list_lock{kernel}, m_cond_var{kernel.System()}, m_address_arbiter{kernel.System()}, 1137 m_list_lock{kernel}, m_cond_var{kernel.System()}, m_address_arbiter{kernel.System()},
1131 m_handle_table{kernel} {} 1138 m_handle_table{kernel}, m_dirty_memory_managers{},
1139 m_exclusive_monitor{}, m_memory{kernel.System()} {}
1132KProcess::~KProcess() = default; 1140KProcess::~KProcess() = default;
1133 1141
1134Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, 1142Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
1135 KProcessAddress aslr_space_start, bool is_hbl) { 1143 KProcessAddress aslr_space_start, bool is_hbl) {
1136 // Create a resource limit for the process. 1144 // Create a resource limit for the process.
1137 const auto physical_memory_size = 1145 const auto pool = static_cast<KMemoryManager::Pool>(metadata.GetPoolPartition());
1138 m_kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application); 1146 const auto physical_memory_size = m_kernel.MemoryManager().GetSize(pool);
1139 auto* res_limit = 1147 auto* res_limit =
1140 Kernel::CreateResourceLimitForProcess(m_kernel.System(), physical_memory_size); 1148 Kernel::CreateResourceLimitForProcess(m_kernel.System(), physical_memory_size);
1141 1149
@@ -1146,8 +1154,10 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
1146 Svc::CreateProcessFlag flag{}; 1154 Svc::CreateProcessFlag flag{};
1147 u64 code_address{}; 1155 u64 code_address{};
1148 1156
1149 // We are an application. 1157 // Determine if we are an application.
1150 flag |= Svc::CreateProcessFlag::IsApplication; 1158 if (pool == KMemoryManager::Pool::Application) {
1159 flag |= Svc::CreateProcessFlag::IsApplication;
1160 }
1151 1161
1152 // If we are 64-bit, create as such. 1162 // If we are 64-bit, create as such.
1153 if (metadata.Is64BitProgram()) { 1163 if (metadata.Is64BitProgram()) {
@@ -1196,8 +1206,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
1196 std::memcpy(params.name.data(), name.data(), sizeof(params.name)); 1206 std::memcpy(params.name.data(), name.data(), sizeof(params.name));
1197 1207
1198 // Initialize for application process. 1208 // Initialize for application process.
1199 R_TRY(this->Initialize(params, metadata.GetKernelCapabilities(), res_limit, 1209 R_TRY(this->Initialize(params, metadata.GetKernelCapabilities(), res_limit, pool,
1200 KMemoryManager::Pool::Application, aslr_space_start)); 1210 aslr_space_start));
1201 1211
1202 // Assign remaining properties. 1212 // Assign remaining properties.
1203 m_is_hbl = is_hbl; 1213 m_is_hbl = is_hbl;
@@ -1223,22 +1233,25 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) {
1223 ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite); 1233 ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite);
1224 1234
1225#ifdef HAS_NCE 1235#ifdef HAS_NCE
1226 if (Settings::IsNceEnabled()) { 1236 if (this->IsApplication() && Settings::IsNceEnabled()) {
1227 auto& buffer = m_kernel.System().DeviceMemory().buffer; 1237 auto& buffer = m_kernel.System().DeviceMemory().buffer;
1228 const auto& code = code_set.CodeSegment(); 1238 const auto& code = code_set.CodeSegment();
1229 const auto& patch = code_set.PatchSegment(); 1239 const auto& patch = code_set.PatchSegment();
1230 buffer.Protect(GetInteger(base_addr + code.addr), code.size, true, true, true); 1240 buffer.Protect(GetInteger(base_addr + code.addr), code.size,
1231 buffer.Protect(GetInteger(base_addr + patch.addr), patch.size, true, true, true); 1241 Common::MemoryPermission::Read | Common::MemoryPermission::Execute);
1242 buffer.Protect(GetInteger(base_addr + patch.addr), patch.size,
1243 Common::MemoryPermission::Read | Common::MemoryPermission::Execute);
1232 ReprotectSegment(code_set.PatchSegment(), Svc::MemoryPermission::None); 1244 ReprotectSegment(code_set.PatchSegment(), Svc::MemoryPermission::None);
1233 } 1245 }
1234#endif 1246#endif
1235} 1247}
1236 1248
1237void KProcess::InitializeInterfaces() { 1249void KProcess::InitializeInterfaces() {
1238 this->GetMemory().SetCurrentPageTable(*this); 1250 m_exclusive_monitor =
1251 Core::MakeExclusiveMonitor(this->GetMemory(), Core::Hardware::NUM_CPU_CORES);
1239 1252
1240#ifdef HAS_NCE 1253#ifdef HAS_NCE
1241 if (this->Is64Bit() && Settings::IsNceEnabled()) { 1254 if (this->IsApplication() && Settings::IsNceEnabled()) {
1242 for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { 1255 for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
1243 m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i); 1256 m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i);
1244 } 1257 }
@@ -1248,13 +1261,13 @@ void KProcess::InitializeInterfaces() {
1248 for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { 1261 for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
1249 m_arm_interfaces[i] = std::make_unique<Core::ArmDynarmic64>( 1262 m_arm_interfaces[i] = std::make_unique<Core::ArmDynarmic64>(
1250 m_kernel.System(), m_kernel.IsMulticore(), this, 1263 m_kernel.System(), m_kernel.IsMulticore(), this,
1251 static_cast<Core::DynarmicExclusiveMonitor&>(m_kernel.GetExclusiveMonitor()), i); 1264 static_cast<Core::DynarmicExclusiveMonitor&>(*m_exclusive_monitor), i);
1252 } 1265 }
1253 } else { 1266 } else {
1254 for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { 1267 for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
1255 m_arm_interfaces[i] = std::make_unique<Core::ArmDynarmic32>( 1268 m_arm_interfaces[i] = std::make_unique<Core::ArmDynarmic32>(
1256 m_kernel.System(), m_kernel.IsMulticore(), this, 1269 m_kernel.System(), m_kernel.IsMulticore(), this,
1257 static_cast<Core::DynarmicExclusiveMonitor&>(m_kernel.GetExclusiveMonitor()), i); 1270 static_cast<Core::DynarmicExclusiveMonitor&>(*m_exclusive_monitor), i);
1258 } 1271 }
1259 } 1272 }
1260} 1273}
@@ -1305,9 +1318,10 @@ bool KProcess::RemoveWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointT
1305 return true; 1318 return true;
1306} 1319}
1307 1320
1308Core::Memory::Memory& KProcess::GetMemory() const { 1321void KProcess::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) {
1309 // TODO: per-process memory 1322 for (auto& manager : m_dirty_memory_managers) {
1310 return m_kernel.System().ApplicationMemory(); 1323 manager.Gather(callback);
1324 }
1311} 1325}
1312 1326
1313} // namespace Kernel 1327} // namespace Kernel
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index 4b114e39b..53c0e3316 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -7,6 +7,7 @@
7 7
8#include "core/arm/arm_interface.h" 8#include "core/arm/arm_interface.h"
9#include "core/file_sys/program_metadata.h" 9#include "core/file_sys/program_metadata.h"
10#include "core/gpu_dirty_memory_manager.h"
10#include "core/hle/kernel/code_set.h" 11#include "core/hle/kernel/code_set.h"
11#include "core/hle/kernel/k_address_arbiter.h" 12#include "core/hle/kernel/k_address_arbiter.h"
12#include "core/hle/kernel/k_capabilities.h" 13#include "core/hle/kernel/k_capabilities.h"
@@ -17,6 +18,7 @@
17#include "core/hle/kernel/k_system_resource.h" 18#include "core/hle/kernel/k_system_resource.h"
18#include "core/hle/kernel/k_thread.h" 19#include "core/hle/kernel/k_thread.h"
19#include "core/hle/kernel/k_thread_local_page.h" 20#include "core/hle/kernel/k_thread_local_page.h"
21#include "core/memory.h"
20 22
21namespace Kernel { 23namespace Kernel {
22 24
@@ -126,6 +128,9 @@ private:
126#ifdef HAS_NCE 128#ifdef HAS_NCE
127 std::unordered_map<u64, u64> m_post_handlers{}; 129 std::unordered_map<u64, u64> m_post_handlers{};
128#endif 130#endif
131 std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> m_dirty_memory_managers;
132 std::unique_ptr<Core::ExclusiveMonitor> m_exclusive_monitor;
133 Core::Memory::Memory m_memory;
129 134
130private: 135private:
131 Result StartTermination(); 136 Result StartTermination();
@@ -502,7 +507,15 @@ public:
502 507
503 void InitializeInterfaces(); 508 void InitializeInterfaces();
504 509
505 Core::Memory::Memory& GetMemory() const; 510 Core::Memory::Memory& GetMemory() {
511 return m_memory;
512 }
513
514 void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback);
515
516 Core::ExclusiveMonitor& GetExclusiveMonitor() const {
517 return *m_exclusive_monitor;
518 }
506 519
507public: 520public:
508 // Overridden parent functions. 521 // Overridden parent functions.
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index e33a88e24..adaabdd6d 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -8,6 +8,7 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/scope_exit.h" 10#include "common/scope_exit.h"
11#include "common/scratch_buffer.h"
11#include "core/core.h" 12#include "core/core.h"
12#include "core/core_timing.h" 13#include "core/core_timing.h"
13#include "core/hle/kernel/k_client_port.h" 14#include "core/hle/kernel/k_client_port.h"
@@ -29,12 +30,138 @@ namespace Kernel {
29 30
30namespace { 31namespace {
31 32
33constexpr inline size_t PointerTransferBufferAlignment = 0x10;
34constexpr inline size_t ReceiveListDataSize =
35 MessageBuffer::MessageHeader::ReceiveListCountType_CountMax *
36 MessageBuffer::ReceiveListEntry::GetDataSize() / sizeof(u32);
37
38using ThreadQueueImplForKServerSessionRequest = KThreadQueue;
39
40class ReceiveList {
41public:
42 static constexpr int GetEntryCount(const MessageBuffer::MessageHeader& header) {
43 const auto count = header.GetReceiveListCount();
44 switch (count) {
45 case MessageBuffer::MessageHeader::ReceiveListCountType_None:
46 return 0;
47 case MessageBuffer::MessageHeader::ReceiveListCountType_ToMessageBuffer:
48 return 0;
49 case MessageBuffer::MessageHeader::ReceiveListCountType_ToSingleBuffer:
50 return 1;
51 default:
52 return count - MessageBuffer::MessageHeader::ReceiveListCountType_CountOffset;
53 }
54 }
55
56 explicit ReceiveList(const u32* dst_msg, uint64_t dst_address,
57 KProcessPageTable& dst_page_table,
58 const MessageBuffer::MessageHeader& dst_header,
59 const MessageBuffer::SpecialHeader& dst_special_header, size_t msg_size,
60 size_t out_offset, s32 dst_recv_list_idx, bool is_tls) {
61 m_recv_list_count = dst_header.GetReceiveListCount();
62 m_msg_buffer_end = dst_address + sizeof(u32) * out_offset;
63 m_msg_buffer_space_end = dst_address + msg_size;
64
65 // NOTE: Nintendo calculates the receive list index here using the special header.
66 // We pre-calculate it in the caller, and pass it as a parameter.
67 (void)dst_special_header;
68
69 const u32* recv_list = dst_msg + dst_recv_list_idx;
70 const auto entry_count = GetEntryCount(dst_header);
71
72 if (is_tls) {
73 // Messages from TLS to TLS are contained within one page.
74 std::memcpy(m_data.data(), recv_list,
75 entry_count * MessageBuffer::ReceiveListEntry::GetDataSize());
76 } else {
77 // If any buffer is not from TLS, perform a normal read instead.
78 uint64_t cur_addr = dst_address + dst_recv_list_idx * sizeof(u32);
79 dst_page_table.GetMemory().ReadBlock(
80 cur_addr, m_data.data(),
81 entry_count * MessageBuffer::ReceiveListEntry::GetDataSize());
82 }
83 }
84
85 bool IsIndex() const {
86 return m_recv_list_count >
87 static_cast<s32>(MessageBuffer::MessageHeader::ReceiveListCountType_CountOffset);
88 }
89
90 bool IsToMessageBuffer() const {
91 return m_recv_list_count ==
92 MessageBuffer::MessageHeader::ReceiveListCountType_ToMessageBuffer;
93 }
94
95 void GetBuffer(uint64_t& out, size_t size, int& key) const {
96 switch (m_recv_list_count) {
97 case MessageBuffer::MessageHeader::ReceiveListCountType_None: {
98 out = 0;
99 break;
100 }
101 case MessageBuffer::MessageHeader::ReceiveListCountType_ToMessageBuffer: {
102 const uint64_t buf =
103 Common::AlignUp(m_msg_buffer_end + key, PointerTransferBufferAlignment);
104
105 if ((buf < buf + size) && (buf + size <= m_msg_buffer_space_end)) {
106 out = buf;
107 key = static_cast<int>(buf + size - m_msg_buffer_end);
108 } else {
109 out = 0;
110 }
111 break;
112 }
113 case MessageBuffer::MessageHeader::ReceiveListCountType_ToSingleBuffer: {
114 const MessageBuffer::ReceiveListEntry entry(m_data[0], m_data[1]);
115 const uint64_t buf =
116 Common::AlignUp(entry.GetAddress() + key, PointerTransferBufferAlignment);
117
118 const uint64_t entry_addr = entry.GetAddress();
119 const size_t entry_size = entry.GetSize();
120
121 if ((buf < buf + size) && (entry_addr < entry_addr + entry_size) &&
122 (buf + size <= entry_addr + entry_size)) {
123 out = buf;
124 key = static_cast<int>(buf + size - entry_addr);
125 } else {
126 out = 0;
127 }
128 break;
129 }
130 default: {
131 if (key < m_recv_list_count -
132 static_cast<s32>(
133 MessageBuffer::MessageHeader::ReceiveListCountType_CountOffset)) {
134 const MessageBuffer::ReceiveListEntry entry(m_data[2 * key + 0],
135 m_data[2 * key + 1]);
136
137 const uintptr_t entry_addr = entry.GetAddress();
138 const size_t entry_size = entry.GetSize();
139
140 if ((entry_addr < entry_addr + entry_size) && (entry_size >= size)) {
141 out = entry_addr;
142 }
143 } else {
144 out = 0;
145 }
146 break;
147 }
148 }
149 }
150
151private:
152 std::array<u32, ReceiveListDataSize> m_data;
153 s32 m_recv_list_count;
154 uint64_t m_msg_buffer_end;
155 uint64_t m_msg_buffer_space_end;
156};
157
32template <bool MoveHandleAllowed> 158template <bool MoveHandleAllowed>
33Result ProcessMessageSpecialData(KProcess& dst_process, KProcess& src_process, KThread& src_thread, 159Result ProcessMessageSpecialData(s32& offset, KProcess& dst_process, KProcess& src_process,
34 MessageBuffer& dst_msg, const MessageBuffer& src_msg, 160 KThread& src_thread, const MessageBuffer& dst_msg,
35 MessageBuffer::SpecialHeader& src_special_header) { 161 const MessageBuffer& src_msg,
162 const MessageBuffer::SpecialHeader& src_special_header) {
36 // Copy the special header to the destination. 163 // Copy the special header to the destination.
37 s32 offset = dst_msg.Set(src_special_header); 164 offset = dst_msg.Set(src_special_header);
38 165
39 // Copy the process ID. 166 // Copy the process ID.
40 if (src_special_header.GetHasProcessId()) { 167 if (src_special_header.GetHasProcessId()) {
@@ -110,6 +237,102 @@ Result ProcessMessageSpecialData(KProcess& dst_process, KProcess& src_process, K
110 R_RETURN(result); 237 R_RETURN(result);
111} 238}
112 239
240Result ProcessReceiveMessagePointerDescriptors(int& offset, int& pointer_key,
241 KProcessPageTable& dst_page_table,
242 KProcessPageTable& src_page_table,
243 const MessageBuffer& dst_msg,
244 const MessageBuffer& src_msg,
245 const ReceiveList& dst_recv_list, bool dst_user) {
246 // Get the offset at the start of processing.
247 const int cur_offset = offset;
248
249 // Get the pointer desc.
250 MessageBuffer::PointerDescriptor src_desc(src_msg, cur_offset);
251 offset += static_cast<int>(MessageBuffer::PointerDescriptor::GetDataSize() / sizeof(u32));
252
253 // Extract address/size.
254 const uint64_t src_pointer = src_desc.GetAddress();
255 const size_t recv_size = src_desc.GetSize();
256 uint64_t recv_pointer = 0;
257
258 // Process the buffer, if it has a size.
259 if (recv_size > 0) {
260 // If using indexing, set index.
261 if (dst_recv_list.IsIndex()) {
262 pointer_key = src_desc.GetIndex();
263 }
264
265 // Get the buffer.
266 dst_recv_list.GetBuffer(recv_pointer, recv_size, pointer_key);
267 R_UNLESS(recv_pointer != 0, ResultOutOfResource);
268
269 // Perform the pointer data copy.
270 if (dst_user) {
271 R_TRY(src_page_table.CopyMemoryFromHeapToHeapWithoutCheckDestination(
272 dst_page_table, recv_pointer, recv_size, KMemoryState::FlagReferenceCounted,
273 KMemoryState::FlagReferenceCounted,
274 KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite,
275 KMemoryAttribute::Uncached | KMemoryAttribute::Locked, KMemoryAttribute::Locked,
276 src_pointer, KMemoryState::FlagLinearMapped, KMemoryState::FlagLinearMapped,
277 KMemoryPermission::UserRead, KMemoryAttribute::Uncached, KMemoryAttribute::None));
278 } else {
279 R_TRY(src_page_table.CopyMemoryFromLinearToUser(
280 recv_pointer, recv_size, src_pointer, KMemoryState::FlagLinearMapped,
281 KMemoryState::FlagLinearMapped, KMemoryPermission::UserRead,
282 KMemoryAttribute::Uncached, KMemoryAttribute::None));
283 }
284 }
285
286 // Set the output descriptor.
287 dst_msg.Set(cur_offset, MessageBuffer::PointerDescriptor(reinterpret_cast<void*>(recv_pointer),
288 recv_size, src_desc.GetIndex()));
289
290 R_SUCCEED();
291}
292
293constexpr Result GetMapAliasMemoryState(KMemoryState& out,
294 MessageBuffer::MapAliasDescriptor::Attribute attr) {
295 switch (attr) {
296 case MessageBuffer::MapAliasDescriptor::Attribute::Ipc:
297 out = KMemoryState::Ipc;
298 break;
299 case MessageBuffer::MapAliasDescriptor::Attribute::NonSecureIpc:
300 out = KMemoryState::NonSecureIpc;
301 break;
302 case MessageBuffer::MapAliasDescriptor::Attribute::NonDeviceIpc:
303 out = KMemoryState::NonDeviceIpc;
304 break;
305 default:
306 R_THROW(ResultInvalidCombination);
307 }
308
309 R_SUCCEED();
310}
311
312constexpr Result GetMapAliasTestStateAndAttributeMask(KMemoryState& out_state,
313 KMemoryAttribute& out_attr_mask,
314 KMemoryState state) {
315 switch (state) {
316 case KMemoryState::Ipc:
317 out_state = KMemoryState::FlagCanUseIpc;
318 out_attr_mask =
319 KMemoryAttribute::Uncached | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked;
320 break;
321 case KMemoryState::NonSecureIpc:
322 out_state = KMemoryState::FlagCanUseNonSecureIpc;
323 out_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
324 break;
325 case KMemoryState::NonDeviceIpc:
326 out_state = KMemoryState::FlagCanUseNonDeviceIpc;
327 out_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
328 break;
329 default:
330 R_THROW(ResultInvalidCombination);
331 }
332
333 R_SUCCEED();
334}
335
113void CleanupSpecialData(KProcess& dst_process, u32* dst_msg_ptr, size_t dst_buffer_size) { 336void CleanupSpecialData(KProcess& dst_process, u32* dst_msg_ptr, size_t dst_buffer_size) {
114 // Parse the message. 337 // Parse the message.
115 const MessageBuffer dst_msg(dst_msg_ptr, dst_buffer_size); 338 const MessageBuffer dst_msg(dst_msg_ptr, dst_buffer_size);
@@ -144,166 +367,855 @@ void CleanupSpecialData(KProcess& dst_process, u32* dst_msg_ptr, size_t dst_buff
144 } 367 }
145} 368}
146 369
147} // namespace 370Result CleanupServerHandles(KernelCore& kernel, uint64_t message, size_t buffer_size,
371 KPhysicalAddress message_paddr) {
372 // Server is assumed to be current thread.
373 KThread& thread = GetCurrentThread(kernel);
148 374
149using ThreadQueueImplForKServerSessionRequest = KThreadQueue; 375 // Get the linear message pointer.
376 u32* msg_ptr;
377 if (message) {
378 msg_ptr = kernel.System().DeviceMemory().GetPointer<u32>(message_paddr);
379 } else {
380 msg_ptr = GetCurrentMemory(kernel).GetPointer<u32>(thread.GetTlsAddress());
381 buffer_size = MessageBufferSize;
382 message = GetInteger(thread.GetTlsAddress());
383 }
150 384
151KServerSession::KServerSession(KernelCore& kernel) 385 // Parse the message.
152 : KSynchronizationObject{kernel}, m_lock{m_kernel} {} 386 const MessageBuffer msg(msg_ptr, buffer_size);
387 const MessageBuffer::MessageHeader header(msg);
388 const MessageBuffer::SpecialHeader special_header(msg, header);
153 389
154KServerSession::~KServerSession() = default; 390 // Check that the size is big enough.
391 R_UNLESS(MessageBuffer::GetMessageBufferSize(header, special_header) <= buffer_size,
392 ResultInvalidCombination);
393
394 // If there's a special header, there may be move handles we need to close.
395 if (header.GetHasSpecialHeader()) {
396 // Determine the offset to the start of handles.
397 auto offset = msg.GetSpecialDataIndex(header, special_header);
398 if (special_header.GetHasProcessId()) {
399 offset += static_cast<int>(sizeof(u64) / sizeof(u32));
400 }
401 if (auto copy_count = special_header.GetCopyHandleCount(); copy_count > 0) {
402 offset += static_cast<int>((sizeof(Svc::Handle) * copy_count) / sizeof(u32));
403 }
155 404
156void KServerSession::Destroy() { 405 // Get the handle table.
157 m_parent->OnServerClosed(); 406 auto& handle_table = thread.GetOwnerProcess()->GetHandleTable();
158 407
159 this->CleanupRequests(); 408 // Close the handles.
409 for (auto i = 0; i < special_header.GetMoveHandleCount(); ++i) {
410 handle_table.Remove(msg.GetHandle(offset));
411 offset += static_cast<int>(sizeof(Svc::Handle) / sizeof(u32));
412 }
413 }
160 414
161 m_parent->Close(); 415 R_SUCCEED();
162} 416}
163 417
164void KServerSession::OnClientClosed() { 418Result CleanupServerMap(KSessionRequest* request, KProcess* server_process) {
165 KScopedLightLock lk{m_lock}; 419 // If there's no server process, there's nothing to clean up.
420 R_SUCCEED_IF(server_process == nullptr);
166 421
167 // Handle any pending requests. 422 // Get the page table.
168 KSessionRequest* prev_request = nullptr; 423 auto& server_page_table = server_process->GetPageTable();
169 while (true) {
170 // Declare variables for processing the request.
171 KSessionRequest* request = nullptr;
172 KEvent* event = nullptr;
173 KThread* thread = nullptr;
174 bool cur_request = false;
175 bool terminate = false;
176 424
177 // Get the next request. 425 // Cleanup Send mappings.
178 { 426 for (size_t i = 0; i < request->GetSendCount(); ++i) {
179 KScopedSchedulerLock sl{m_kernel}; 427 R_TRY(server_page_table.CleanupForIpcServer(request->GetSendServerAddress(i),
428 request->GetSendSize(i),
429 request->GetSendMemoryState(i)));
430 }
180 431
181 if (m_current_request != nullptr && m_current_request != prev_request) { 432 // Cleanup Receive mappings.
182 // Set the request, open a reference as we process it. 433 for (size_t i = 0; i < request->GetReceiveCount(); ++i) {
183 request = m_current_request; 434 R_TRY(server_page_table.CleanupForIpcServer(request->GetReceiveServerAddress(i),
184 request->Open(); 435 request->GetReceiveSize(i),
185 cur_request = true; 436 request->GetReceiveMemoryState(i)));
437 }
186 438
187 // Get thread and event for the request. 439 // Cleanup Exchange mappings.
188 thread = request->GetThread(); 440 for (size_t i = 0; i < request->GetExchangeCount(); ++i) {
189 event = request->GetEvent(); 441 R_TRY(server_page_table.CleanupForIpcServer(request->GetExchangeServerAddress(i),
442 request->GetExchangeSize(i),
443 request->GetExchangeMemoryState(i)));
444 }
190 445
191 // If the thread is terminating, handle that. 446 R_SUCCEED();
192 if (thread->IsTerminationRequested()) { 447}
193 request->ClearThread();
194 request->ClearEvent();
195 terminate = true;
196 }
197 448
198 prev_request = request; 449Result CleanupClientMap(KSessionRequest* request, KProcessPageTable* client_page_table) {
199 } else if (!m_request_list.empty()) { 450 // If there's no client page table, there's nothing to clean up.
200 // Pop the request from the front of the list. 451 R_SUCCEED_IF(client_page_table == nullptr);
201 request = std::addressof(m_request_list.front());
202 m_request_list.pop_front();
203 452
204 // Get thread and event for the request. 453 // Cleanup Send mappings.
205 thread = request->GetThread(); 454 for (size_t i = 0; i < request->GetSendCount(); ++i) {
206 event = request->GetEvent(); 455 R_TRY(client_page_table->CleanupForIpcClient(request->GetSendClientAddress(i),
207 } 456 request->GetSendSize(i),
457 request->GetSendMemoryState(i)));
458 }
459
460 // Cleanup Receive mappings.
461 for (size_t i = 0; i < request->GetReceiveCount(); ++i) {
462 R_TRY(client_page_table->CleanupForIpcClient(request->GetReceiveClientAddress(i),
463 request->GetReceiveSize(i),
464 request->GetReceiveMemoryState(i)));
465 }
466
467 // Cleanup Exchange mappings.
468 for (size_t i = 0; i < request->GetExchangeCount(); ++i) {
469 R_TRY(client_page_table->CleanupForIpcClient(request->GetExchangeClientAddress(i),
470 request->GetExchangeSize(i),
471 request->GetExchangeMemoryState(i)));
472 }
473
474 R_SUCCEED();
475}
476
477Result CleanupMap(KSessionRequest* request, KProcess* server_process,
478 KProcessPageTable* client_page_table) {
479 // Cleanup the server map.
480 R_TRY(CleanupServerMap(request, server_process));
481
482 // Cleanup the client map.
483 R_TRY(CleanupClientMap(request, client_page_table));
484
485 R_SUCCEED();
486}
487
488Result ProcessReceiveMessageMapAliasDescriptors(int& offset, KProcessPageTable& dst_page_table,
489 KProcessPageTable& src_page_table,
490 const MessageBuffer& dst_msg,
491 const MessageBuffer& src_msg,
492 KSessionRequest* request, KMemoryPermission perm,
493 bool send) {
494 // Get the offset at the start of processing.
495 const int cur_offset = offset;
496
497 // Get the map alias descriptor.
498 MessageBuffer::MapAliasDescriptor src_desc(src_msg, cur_offset);
499 offset += static_cast<int>(MessageBuffer::MapAliasDescriptor::GetDataSize() / sizeof(u32));
500
501 // Extract address/size.
502 const KProcessAddress src_address = src_desc.GetAddress();
503 const size_t size = src_desc.GetSize();
504 KProcessAddress dst_address = 0;
505
506 // Determine the result memory state.
507 KMemoryState dst_state;
508 R_TRY(GetMapAliasMemoryState(dst_state, src_desc.GetAttribute()));
509
510 // Process the buffer, if it has a size.
511 if (size > 0) {
512 // Set up the source pages for ipc.
513 R_TRY(dst_page_table.SetupForIpc(std::addressof(dst_address), size, src_address,
514 src_page_table, perm, dst_state, send));
515
516 // Ensure that we clean up on failure.
517 ON_RESULT_FAILURE {
518 dst_page_table.CleanupForIpcServer(dst_address, size, dst_state);
519 src_page_table.CleanupForIpcClient(src_address, size, dst_state);
520 };
521
522 // Push the appropriate mapping.
523 if (perm == KMemoryPermission::UserRead) {
524 R_TRY(request->PushSend(src_address, dst_address, size, dst_state));
525 } else if (send) {
526 R_TRY(request->PushExchange(src_address, dst_address, size, dst_state));
527 } else {
528 R_TRY(request->PushReceive(src_address, dst_address, size, dst_state));
208 } 529 }
530 }
209 531
210 // If there are no requests, we're done. 532 // Set the output descriptor.
211 if (request == nullptr) { 533 dst_msg.Set(cur_offset,
212 break; 534 MessageBuffer::MapAliasDescriptor(reinterpret_cast<void*>(GetInteger(dst_address)),
535 size, src_desc.GetAttribute()));
536
537 R_SUCCEED();
538}
539
540Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_message_buffer,
541 size_t dst_buffer_size, KPhysicalAddress dst_message_paddr,
542 KThread& src_thread, uint64_t src_message_buffer, size_t src_buffer_size,
543 KServerSession* session, KSessionRequest* request) {
544 // Prepare variables for receive.
545 KThread& dst_thread = GetCurrentThread(kernel);
546 KProcess& dst_process = *(dst_thread.GetOwnerProcess());
547 KProcess& src_process = *(src_thread.GetOwnerProcess());
548 auto& dst_page_table = dst_process.GetPageTable();
549 auto& src_page_table = src_process.GetPageTable();
550
551 // NOTE: Session is used only for debugging, and so may go unused.
552 (void)session;
553
554 // The receive list is initially not broken.
555 recv_list_broken = false;
556
557 // Set the server process for the request.
558 request->SetServerProcess(std::addressof(dst_process));
559
560 // Determine the message buffers.
561 u32 *dst_msg_ptr, *src_msg_ptr;
562 bool dst_user, src_user;
563
564 if (dst_message_buffer) {
565 dst_msg_ptr = kernel.System().DeviceMemory().GetPointer<u32>(dst_message_paddr);
566 dst_user = true;
567 } else {
568 dst_msg_ptr = dst_page_table.GetMemory().GetPointer<u32>(dst_thread.GetTlsAddress());
569 dst_buffer_size = MessageBufferSize;
570 dst_message_buffer = GetInteger(dst_thread.GetTlsAddress());
571 dst_user = false;
572 }
573
574 if (src_message_buffer) {
575 // NOTE: Nintendo does not check the result of this GetPhysicalAddress call.
576 src_msg_ptr = src_page_table.GetMemory().GetPointer<u32>(src_message_buffer);
577 src_user = true;
578 } else {
579 src_msg_ptr = src_page_table.GetMemory().GetPointer<u32>(src_thread.GetTlsAddress());
580 src_buffer_size = MessageBufferSize;
581 src_message_buffer = GetInteger(src_thread.GetTlsAddress());
582 src_user = false;
583 }
584
585 // Parse the headers.
586 const MessageBuffer dst_msg(dst_msg_ptr, dst_buffer_size);
587 const MessageBuffer src_msg(src_msg_ptr, src_buffer_size);
588 const MessageBuffer::MessageHeader dst_header(dst_msg);
589 const MessageBuffer::MessageHeader src_header(src_msg);
590 const MessageBuffer::SpecialHeader dst_special_header(dst_msg, dst_header);
591 const MessageBuffer::SpecialHeader src_special_header(src_msg, src_header);
592
593 // Get the end of the source message.
594 const size_t src_end_offset =
595 MessageBuffer::GetRawDataIndex(src_header, src_special_header) + src_header.GetRawCount();
596
597 // Ensure that the headers fit.
598 R_UNLESS(MessageBuffer::GetMessageBufferSize(dst_header, dst_special_header) <= dst_buffer_size,
599 ResultInvalidCombination);
600 R_UNLESS(MessageBuffer::GetMessageBufferSize(src_header, src_special_header) <= src_buffer_size,
601 ResultInvalidCombination);
602
603 // Ensure the receive list offset is after the end of raw data.
604 if (dst_header.GetReceiveListOffset()) {
605 R_UNLESS(dst_header.GetReceiveListOffset() >=
606 MessageBuffer::GetRawDataIndex(dst_header, dst_special_header) +
607 dst_header.GetRawCount(),
608 ResultInvalidCombination);
609 }
610
611 // Ensure that the destination buffer is big enough to receive the source.
612 R_UNLESS(dst_buffer_size >= src_end_offset * sizeof(u32), ResultMessageTooLarge);
613
614 // Get the receive list.
615 const s32 dst_recv_list_idx =
616 MessageBuffer::GetReceiveListIndex(dst_header, dst_special_header);
617 ReceiveList dst_recv_list(dst_msg_ptr, dst_message_buffer, dst_page_table, dst_header,
618 dst_special_header, dst_buffer_size, src_end_offset,
619 dst_recv_list_idx, !dst_user);
620
621 // Ensure that the source special header isn't invalid.
622 const bool src_has_special_header = src_header.GetHasSpecialHeader();
623 if (src_has_special_header) {
624 // Sending move handles from client -> server is not allowed.
625 R_UNLESS(src_special_header.GetMoveHandleCount() == 0, ResultInvalidCombination);
626 }
627
628 // Prepare for further processing.
629 int pointer_key = 0;
630 int offset = dst_msg.Set(src_header);
631
632 // Set up a guard to make sure that we end up in a clean state on error.
633 ON_RESULT_FAILURE {
634 // Cleanup mappings.
635 CleanupMap(request, std::addressof(dst_process), std::addressof(src_page_table));
636
637 // Cleanup special data.
638 if (src_header.GetHasSpecialHeader()) {
639 CleanupSpecialData(dst_process, dst_msg_ptr, dst_buffer_size);
213 } 640 }
214 641
215 // All requests must have threads. 642 // Cleanup the header if the receive list isn't broken.
216 ASSERT(thread != nullptr); 643 if (!recv_list_broken) {
644 dst_msg.Set(dst_header);
645 if (dst_header.GetHasSpecialHeader()) {
646 dst_msg.Set(dst_special_header);
647 }
648 }
649 };
650
651 // Process any special data.
652 if (src_header.GetHasSpecialHeader()) {
653 // After we process, make sure we track whether the receive list is broken.
654 SCOPE_EXIT({
655 if (offset > dst_recv_list_idx) {
656 recv_list_broken = true;
657 }
658 });
217 659
218 // Ensure that we close the request when done. 660 // Process special data.
219 SCOPE_EXIT({ request->Close(); }); 661 R_TRY(ProcessMessageSpecialData<false>(offset, dst_process, src_process, src_thread,
662 dst_msg, src_msg, src_special_header));
663 }
220 664
221 // If we're terminating, close a reference to the thread and event. 665 // Process any pointer buffers.
222 if (terminate) { 666 for (auto i = 0; i < src_header.GetPointerCount(); ++i) {
223 thread->Close(); 667 // After we process, make sure we track whether the receive list is broken.
224 if (event != nullptr) { 668 SCOPE_EXIT({
225 event->Close(); 669 if (offset > dst_recv_list_idx) {
670 recv_list_broken = true;
671 }
672 });
673
674 R_TRY(ProcessReceiveMessagePointerDescriptors(
675 offset, pointer_key, dst_page_table, src_page_table, dst_msg, src_msg, dst_recv_list,
676 dst_user && dst_header.GetReceiveListCount() ==
677 MessageBuffer::MessageHeader::ReceiveListCountType_ToMessageBuffer));
678 }
679
680 // Process any map alias buffers.
681 for (auto i = 0; i < src_header.GetMapAliasCount(); ++i) {
682 // After we process, make sure we track whether the receive list is broken.
683 SCOPE_EXIT({
684 if (offset > dst_recv_list_idx) {
685 recv_list_broken = true;
226 } 686 }
687 });
688
689 // We process in order send, recv, exch. Buffers after send (recv/exch) are ReadWrite.
690 const KMemoryPermission perm = (i >= src_header.GetSendCount())
691 ? KMemoryPermission::UserReadWrite
692 : KMemoryPermission::UserRead;
693
694 // Buffer is send if it is send or exch.
695 const bool send = (i < src_header.GetSendCount()) ||
696 (i >= src_header.GetSendCount() + src_header.GetReceiveCount());
697
698 R_TRY(ProcessReceiveMessageMapAliasDescriptors(offset, dst_page_table, src_page_table,
699 dst_msg, src_msg, request, perm, send));
700 }
701
702 // Process any raw data.
703 if (const auto raw_count = src_header.GetRawCount(); raw_count != 0) {
704 // After we process, make sure we track whether the receive list is broken.
705 SCOPE_EXIT({
706 if (offset + raw_count > dst_recv_list_idx) {
707 recv_list_broken = true;
708 }
709 });
710
711 // Get the offset and size.
712 const size_t offset_words = offset * sizeof(u32);
713 const size_t raw_size = raw_count * sizeof(u32);
714
715 if (!dst_user && !src_user) {
716 // Fast case is TLS -> TLS, do raw memcpy if we can.
717 std::memcpy(dst_msg_ptr + offset, src_msg_ptr + offset, raw_size);
718 } else if (dst_user) {
719 // Determine how much fast size we can copy.
720 const size_t max_fast_size = std::min<size_t>(offset_words + raw_size, PageSize);
721 const size_t fast_size = max_fast_size - offset_words;
722
723 // Determine source state; if user buffer, we require heap, and otherwise only linear
724 // mapped (to enable tls use).
725 const auto src_state =
726 src_user ? KMemoryState::FlagReferenceCounted : KMemoryState::FlagLinearMapped;
727
728 // Determine the source permission. User buffer should be unmapped + read, TLS should be
729 // user readable.
730 const KMemoryPermission src_perm = static_cast<KMemoryPermission>(
731 src_user ? KMemoryPermission::NotMapped | KMemoryPermission::KernelRead
732 : KMemoryPermission::UserRead);
733
734 // Perform the fast part of the copy.
735 R_TRY(src_page_table.CopyMemoryFromLinearToKernel(
736 dst_msg_ptr + offset, fast_size, src_message_buffer + offset_words, src_state,
737 src_state, src_perm, KMemoryAttribute::Uncached, KMemoryAttribute::None));
738
739 // If the fast part of the copy didn't get everything, perform the slow part of the
740 // copy.
741 if (fast_size < raw_size) {
742 R_TRY(src_page_table.CopyMemoryFromHeapToHeap(
743 dst_page_table, dst_message_buffer + max_fast_size, raw_size - fast_size,
744 KMemoryState::FlagReferenceCounted, KMemoryState::FlagReferenceCounted,
745 KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite,
746 KMemoryAttribute::Uncached | KMemoryAttribute::Locked, KMemoryAttribute::Locked,
747 src_message_buffer + max_fast_size, src_state, src_state, src_perm,
748 KMemoryAttribute::Uncached, KMemoryAttribute::None));
749 }
750 } else /* if (src_user) */ {
751 // The source is a user buffer, so it should be unmapped + readable.
752 constexpr KMemoryPermission SourcePermission = static_cast<KMemoryPermission>(
753 KMemoryPermission::NotMapped | KMemoryPermission::KernelRead);
754
755 // Copy the memory.
756 R_TRY(src_page_table.CopyMemoryFromLinearToUser(
757 dst_message_buffer + offset_words, raw_size, src_message_buffer + offset_words,
758 KMemoryState::FlagReferenceCounted, KMemoryState::FlagReferenceCounted,
759 SourcePermission, KMemoryAttribute::Uncached, KMemoryAttribute::None));
227 } 760 }
761 }
228 762
229 // If we need to, reply. 763 // We succeeded!
230 if (event != nullptr && !cur_request) { 764 R_SUCCEED();
231 // There must be no mappings. 765}
232 ASSERT(request->GetSendCount() == 0);
233 ASSERT(request->GetReceiveCount() == 0);
234 ASSERT(request->GetExchangeCount() == 0);
235 766
236 // // Get the process and page table. 767Result ProcessSendMessageReceiveMapping(KProcessPageTable& src_page_table,
237 // KProcess *client_process = thread->GetOwnerProcess(); 768 KProcessPageTable& dst_page_table,
238 // auto& client_pt = client_process->GetPageTable(); 769 KProcessAddress client_address,
770 KProcessAddress server_address, size_t size,
771 KMemoryState src_state) {
772 // If the size is zero, there's nothing to process.
773 R_SUCCEED_IF(size == 0);
774
775 // Get the memory state and attribute mask to test.
776 KMemoryState test_state;
777 KMemoryAttribute test_attr_mask;
778 R_TRY(GetMapAliasTestStateAndAttributeMask(test_state, test_attr_mask, src_state));
779
780 // Determine buffer extents.
781 KProcessAddress aligned_dst_start = Common::AlignDown(GetInteger(client_address), PageSize);
782 KProcessAddress aligned_dst_end = Common::AlignUp(GetInteger(client_address) + size, PageSize);
783 KProcessAddress mapping_dst_start = Common::AlignUp(GetInteger(client_address), PageSize);
784 KProcessAddress mapping_dst_end =
785 Common::AlignDown(GetInteger(client_address) + size, PageSize);
786
787 KProcessAddress mapping_src_end =
788 Common::AlignDown(GetInteger(server_address) + size, PageSize);
789
790 // If the start of the buffer is unaligned, handle that.
791 if (aligned_dst_start != mapping_dst_start) {
792 ASSERT(client_address < mapping_dst_start);
793 const size_t copy_size = std::min<size_t>(size, mapping_dst_start - client_address);
794 R_TRY(dst_page_table.CopyMemoryFromUserToLinear(
795 client_address, copy_size, test_state, test_state, KMemoryPermission::UserReadWrite,
796 test_attr_mask, KMemoryAttribute::None, server_address));
797 }
239 798
240 // // Reply to the request. 799 // If the end of the buffer is unaligned, handle that.
241 // ReplyAsyncError(client_process, request->GetAddress(), request->GetSize(), 800 if (mapping_dst_end < aligned_dst_end &&
242 // ResultSessionClosed); 801 (aligned_dst_start == mapping_dst_start || aligned_dst_start < mapping_dst_end)) {
802 const size_t copy_size = client_address + size - mapping_dst_end;
803 R_TRY(dst_page_table.CopyMemoryFromUserToLinear(
804 mapping_dst_end, copy_size, test_state, test_state, KMemoryPermission::UserReadWrite,
805 test_attr_mask, KMemoryAttribute::None, mapping_src_end));
806 }
243 807
244 // // Unlock the buffer. 808 R_SUCCEED();
245 // // NOTE: Nintendo does not check the result of this. 809}
246 // client_pt.UnlockForIpcUserBuffer(request->GetAddress(), request->GetSize());
247 810
248 // Signal the event. 811Result ProcessSendMessagePointerDescriptors(int& offset, int& pointer_key,
249 event->Signal(); 812 KProcessPageTable& src_page_table,
813 KProcessPageTable& dst_page_table,
814 const MessageBuffer& dst_msg,
815 const MessageBuffer& src_msg,
816 const ReceiveList& dst_recv_list, bool dst_user) {
817 // Get the offset at the start of processing.
818 const int cur_offset = offset;
819
820 // Get the pointer desc.
821 MessageBuffer::PointerDescriptor src_desc(src_msg, cur_offset);
822 offset += static_cast<int>(MessageBuffer::PointerDescriptor::GetDataSize() / sizeof(u32));
823
824 // Extract address/size.
825 const uint64_t src_pointer = src_desc.GetAddress();
826 const size_t recv_size = src_desc.GetSize();
827 uint64_t recv_pointer = 0;
828
829 // Process the buffer, if it has a size.
830 if (recv_size > 0) {
831 // If using indexing, set index.
832 if (dst_recv_list.IsIndex()) {
833 pointer_key = src_desc.GetIndex();
250 } 834 }
835
836 // Get the buffer.
837 dst_recv_list.GetBuffer(recv_pointer, recv_size, pointer_key);
838 R_UNLESS(recv_pointer != 0, ResultOutOfResource);
839
840 // Perform the pointer data copy.
841 const bool dst_heap = dst_user && dst_recv_list.IsToMessageBuffer();
842 const auto dst_state =
843 dst_heap ? KMemoryState::FlagReferenceCounted : KMemoryState::FlagLinearMapped;
844 const KMemoryPermission dst_perm =
845 dst_heap ? KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite
846 : KMemoryPermission::UserReadWrite;
847 R_TRY(dst_page_table.CopyMemoryFromUserToLinear(
848 recv_pointer, recv_size, dst_state, dst_state, dst_perm, KMemoryAttribute::Uncached,
849 KMemoryAttribute::None, src_pointer));
251 } 850 }
252 851
253 // Notify. 852 // Set the output descriptor.
254 this->NotifyAvailable(ResultSessionClosed); 853 dst_msg.Set(cur_offset, MessageBuffer::PointerDescriptor(reinterpret_cast<void*>(recv_pointer),
854 recv_size, src_desc.GetIndex()));
855
856 R_SUCCEED();
255} 857}
256 858
257bool KServerSession::IsSignaled() const { 859Result SendMessage(KernelCore& kernel, uint64_t src_message_buffer, size_t src_buffer_size,
258 ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); 860 KPhysicalAddress src_message_paddr, KThread& dst_thread,
861 uint64_t dst_message_buffer, size_t dst_buffer_size, KServerSession* session,
862 KSessionRequest* request) {
863 // Prepare variables for send.
864 KThread& src_thread = GetCurrentThread(kernel);
865 KProcess& dst_process = *(dst_thread.GetOwnerProcess());
866 KProcess& src_process = *(src_thread.GetOwnerProcess());
867 auto& dst_page_table = dst_process.GetPageTable();
868 auto& src_page_table = src_process.GetPageTable();
869
870 // NOTE: Session is used only for debugging, and so may go unused.
871 (void)session;
872
873 // Determine the message buffers.
874 u32 *dst_msg_ptr, *src_msg_ptr;
875 bool dst_user, src_user;
876
877 if (dst_message_buffer) {
878 // NOTE: Nintendo does not check the result of this GetPhysicalAddress call.
879 dst_msg_ptr = dst_page_table.GetMemory().GetPointer<u32>(dst_message_buffer);
880 dst_user = true;
881 } else {
882 dst_msg_ptr = dst_page_table.GetMemory().GetPointer<u32>(dst_thread.GetTlsAddress());
883 dst_buffer_size = MessageBufferSize;
884 dst_message_buffer = GetInteger(dst_thread.GetTlsAddress());
885 dst_user = false;
886 }
259 887
260 // If the client is closed, we're always signaled. 888 if (src_message_buffer) {
261 if (m_parent->IsClientClosed()) { 889 src_msg_ptr = src_page_table.GetMemory().GetPointer<u32>(src_message_buffer);
262 return true; 890 src_user = true;
891 } else {
892 src_msg_ptr = src_page_table.GetMemory().GetPointer<u32>(src_thread.GetTlsAddress());
893 src_buffer_size = MessageBufferSize;
894 src_message_buffer = GetInteger(src_thread.GetTlsAddress());
895 src_user = false;
263 } 896 }
264 897
265 // Otherwise, we're signaled if we have a request and aren't handling one. 898 // Parse the headers.
266 return !m_request_list.empty() && m_current_request == nullptr; 899 const MessageBuffer dst_msg(dst_msg_ptr, dst_buffer_size);
900 const MessageBuffer src_msg(src_msg_ptr, src_buffer_size);
901 const MessageBuffer::MessageHeader dst_header(dst_msg);
902 const MessageBuffer::MessageHeader src_header(src_msg);
903 const MessageBuffer::SpecialHeader dst_special_header(dst_msg, dst_header);
904 const MessageBuffer::SpecialHeader src_special_header(src_msg, src_header);
905
906 // Get the end of the source message.
907 const size_t src_end_offset =
908 MessageBuffer::GetRawDataIndex(src_header, src_special_header) + src_header.GetRawCount();
909
910 // Declare variables for processing.
911 int offset = 0;
912 int pointer_key = 0;
913 bool processed_special_data = false;
914
915 // Send the message.
916 {
917 // Make sure that we end up in a clean state on error.
918 ON_RESULT_FAILURE {
919 // Cleanup special data.
920 if (processed_special_data) {
921 if (src_header.GetHasSpecialHeader()) {
922 CleanupSpecialData(dst_process, dst_msg_ptr, dst_buffer_size);
923 }
924 } else {
925 CleanupServerHandles(kernel, src_user ? src_message_buffer : 0, src_buffer_size,
926 src_message_paddr);
927 }
928
929 // Cleanup mappings.
930 CleanupMap(request, std::addressof(src_process), std::addressof(dst_page_table));
931 };
932
933 // Ensure that the headers fit.
934 R_UNLESS(MessageBuffer::GetMessageBufferSize(src_header, src_special_header) <=
935 src_buffer_size,
936 ResultInvalidCombination);
937 R_UNLESS(MessageBuffer::GetMessageBufferSize(dst_header, dst_special_header) <=
938 dst_buffer_size,
939 ResultInvalidCombination);
940
941 // Ensure the receive list offset is after the end of raw data.
942 if (dst_header.GetReceiveListOffset()) {
943 R_UNLESS(dst_header.GetReceiveListOffset() >=
944 MessageBuffer::GetRawDataIndex(dst_header, dst_special_header) +
945 dst_header.GetRawCount(),
946 ResultInvalidCombination);
947 }
948
949 // Ensure that the destination buffer is big enough to receive the source.
950 R_UNLESS(dst_buffer_size >= src_end_offset * sizeof(u32), ResultMessageTooLarge);
951
952 // Replies must have no buffers.
953 R_UNLESS(src_header.GetSendCount() == 0, ResultInvalidCombination);
954 R_UNLESS(src_header.GetReceiveCount() == 0, ResultInvalidCombination);
955 R_UNLESS(src_header.GetExchangeCount() == 0, ResultInvalidCombination);
956
957 // Get the receive list.
958 const s32 dst_recv_list_idx =
959 MessageBuffer::GetReceiveListIndex(dst_header, dst_special_header);
960 ReceiveList dst_recv_list(dst_msg_ptr, dst_message_buffer, dst_page_table, dst_header,
961 dst_special_header, dst_buffer_size, src_end_offset,
962 dst_recv_list_idx, !dst_user);
963
964 // Handle any receive buffers.
965 for (size_t i = 0; i < request->GetReceiveCount(); ++i) {
966 R_TRY(ProcessSendMessageReceiveMapping(
967 src_page_table, dst_page_table, request->GetReceiveClientAddress(i),
968 request->GetReceiveServerAddress(i), request->GetReceiveSize(i),
969 request->GetReceiveMemoryState(i)));
970 }
971
972 // Handle any exchange buffers.
973 for (size_t i = 0; i < request->GetExchangeCount(); ++i) {
974 R_TRY(ProcessSendMessageReceiveMapping(
975 src_page_table, dst_page_table, request->GetExchangeClientAddress(i),
976 request->GetExchangeServerAddress(i), request->GetExchangeSize(i),
977 request->GetExchangeMemoryState(i)));
978 }
979
980 // Set the header.
981 offset = dst_msg.Set(src_header);
982
983 // Process any special data.
984 ASSERT(GetCurrentThreadPointer(kernel) == std::addressof(src_thread));
985 processed_special_data = true;
986 if (src_header.GetHasSpecialHeader()) {
987 R_TRY(ProcessMessageSpecialData<true>(offset, dst_process, src_process, src_thread,
988 dst_msg, src_msg, src_special_header));
989 }
990
991 // Process any pointer buffers.
992 for (auto i = 0; i < src_header.GetPointerCount(); ++i) {
993 R_TRY(ProcessSendMessagePointerDescriptors(
994 offset, pointer_key, src_page_table, dst_page_table, dst_msg, src_msg,
995 dst_recv_list,
996 dst_user &&
997 dst_header.GetReceiveListCount() ==
998 MessageBuffer::MessageHeader::ReceiveListCountType_ToMessageBuffer));
999 }
1000
1001 // Clear any map alias buffers.
1002 for (auto i = 0; i < src_header.GetMapAliasCount(); ++i) {
1003 offset = dst_msg.Set(offset, MessageBuffer::MapAliasDescriptor());
1004 }
1005
1006 // Process any raw data.
1007 if (const auto raw_count = src_header.GetRawCount(); raw_count != 0) {
1008 // Get the offset and size.
1009 const size_t offset_words = offset * sizeof(u32);
1010 const size_t raw_size = raw_count * sizeof(u32);
1011
1012 if (!dst_user && !src_user) {
1013 // Fast case is TLS -> TLS, do raw memcpy if we can.
1014 std::memcpy(dst_msg_ptr + offset, src_msg_ptr + offset, raw_size);
1015 } else if (src_user) {
1016 // Determine how much fast size we can copy.
1017 const size_t max_fast_size = std::min<size_t>(offset_words + raw_size, PageSize);
1018 const size_t fast_size = max_fast_size - offset_words;
1019
1020 // Determine dst state; if user buffer, we require heap, and otherwise only linear
1021 // mapped (to enable tls use).
1022 const auto dst_state =
1023 dst_user ? KMemoryState::FlagReferenceCounted : KMemoryState::FlagLinearMapped;
1024
1025 // Determine the dst permission. User buffer should be unmapped + read, TLS should
1026 // be user readable.
1027 const KMemoryPermission dst_perm =
1028 dst_user ? KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite
1029 : KMemoryPermission::UserReadWrite;
1030
1031 // Perform the fast part of the copy.
1032 R_TRY(dst_page_table.CopyMemoryFromKernelToLinear(
1033 dst_message_buffer + offset_words, fast_size, dst_state, dst_state, dst_perm,
1034 KMemoryAttribute::Uncached, KMemoryAttribute::None, src_msg_ptr + offset));
1035
1036 // If the fast part of the copy didn't get everything, perform the slow part of the
1037 // copy.
1038 if (fast_size < raw_size) {
1039 R_TRY(dst_page_table.CopyMemoryFromHeapToHeap(
1040 dst_page_table, dst_message_buffer + max_fast_size, raw_size - fast_size,
1041 dst_state, dst_state, dst_perm, KMemoryAttribute::Uncached,
1042 KMemoryAttribute::None, src_message_buffer + max_fast_size,
1043 KMemoryState::FlagReferenceCounted, KMemoryState::FlagReferenceCounted,
1044 KMemoryPermission::NotMapped | KMemoryPermission::KernelRead,
1045 KMemoryAttribute::Uncached | KMemoryAttribute::Locked,
1046 KMemoryAttribute::Locked));
1047 }
1048 } else /* if (dst_user) */ {
1049 // The destination is a user buffer, so it should be unmapped + readable.
1050 constexpr KMemoryPermission DestinationPermission =
1051 KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite;
1052
1053 // Copy the memory.
1054 R_TRY(dst_page_table.CopyMemoryFromUserToLinear(
1055 dst_message_buffer + offset_words, raw_size, KMemoryState::FlagReferenceCounted,
1056 KMemoryState::FlagReferenceCounted, DestinationPermission,
1057 KMemoryAttribute::Uncached, KMemoryAttribute::None,
1058 src_message_buffer + offset_words));
1059 }
1060 }
1061 }
1062
1063 // Perform (and validate) any remaining cleanup.
1064 R_RETURN(CleanupMap(request, std::addressof(src_process), std::addressof(dst_page_table)));
267} 1065}
268 1066
269Result KServerSession::OnRequest(KSessionRequest* request) { 1067void ReplyAsyncError(KProcess* to_process, uint64_t to_msg_buf, size_t to_msg_buf_size,
270 // Create the wait queue. 1068 Result result) {
271 ThreadQueueImplForKServerSessionRequest wait_queue{m_kernel}; 1069 // Convert the address to a linear pointer.
1070 u32* to_msg = to_process->GetMemory().GetPointer<u32>(to_msg_buf);
1071
1072 // Set the error.
1073 MessageBuffer msg(to_msg, to_msg_buf_size);
1074 msg.SetAsyncResult(result);
1075}
1076
1077} // namespace
1078
1079KServerSession::KServerSession(KernelCore& kernel)
1080 : KSynchronizationObject{kernel}, m_lock{m_kernel} {}
1081
1082KServerSession::~KServerSession() = default;
1083
1084void KServerSession::Destroy() {
1085 m_parent->OnServerClosed();
1086
1087 this->CleanupRequests();
1088
1089 m_parent->Close();
1090}
1091
1092Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server_buffer_size,
1093 KPhysicalAddress server_message_paddr,
1094 std::shared_ptr<Service::HLERequestContext>* out_context,
1095 std::weak_ptr<Service::SessionRequestManager> manager) {
1096 // Lock the session.
1097 KScopedLightLock lk{m_lock};
1098
1099 // Get the request and client thread.
1100 KSessionRequest* request;
1101 KThread* client_thread;
272 1102
273 { 1103 {
274 // Lock the scheduler.
275 KScopedSchedulerLock sl{m_kernel}; 1104 KScopedSchedulerLock sl{m_kernel};
276 1105
277 // Ensure that we can handle new requests. 1106 // Ensure that we can service the request.
278 R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed); 1107 R_UNLESS(!m_parent->IsClientClosed(), ResultSessionClosed);
279 1108
280 // Check that we're not terminating. 1109 // Ensure we aren't already servicing a request.
281 R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(), ResultTerminationRequested); 1110 R_UNLESS(m_current_request == nullptr, ResultNotFound);
282 1111
283 // Get whether we're empty. 1112 // Ensure we have a request to service.
284 const bool was_empty = m_request_list.empty(); 1113 R_UNLESS(!m_request_list.empty(), ResultNotFound);
285 1114
286 // Add the request to the list. 1115 // Pop the first request from the list.
287 request->Open(); 1116 request = std::addressof(m_request_list.front());
288 m_request_list.push_back(*request); 1117 m_request_list.pop_front();
289 1118
290 // If we were empty, signal. 1119 // Get the thread for the request.
291 if (was_empty) { 1120 client_thread = request->GetThread();
292 this->NotifyAvailable(); 1121 R_UNLESS(client_thread != nullptr, ResultSessionClosed);
1122
1123 // Open the client thread.
1124 client_thread->Open();
1125 }
1126
1127 SCOPE_EXIT({ client_thread->Close(); });
1128
1129 // Set the request as our current.
1130 m_current_request = request;
1131
1132 // Get the client address.
1133 uint64_t client_message = request->GetAddress();
1134 size_t client_buffer_size = request->GetSize();
1135 bool recv_list_broken = false;
1136
1137 // Receive the message.
1138 Result result = ResultSuccess;
1139
1140 if (out_context != nullptr) {
1141 // HLE request.
1142 if (!client_message) {
1143 client_message = GetInteger(client_thread->GetTlsAddress());
293 } 1144 }
1145 Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()};
1146 u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(client_message))};
1147 *out_context =
1148 std::make_shared<Service::HLERequestContext>(m_kernel, memory, this, client_thread);
1149 (*out_context)->SetSessionRequestManager(manager);
1150 (*out_context)->PopulateFromIncomingCommandBuffer(cmd_buf);
1151 // We succeeded.
1152 R_SUCCEED();
1153 } else {
1154 result = ReceiveMessage(m_kernel, recv_list_broken, server_message, server_buffer_size,
1155 server_message_paddr, *client_thread, client_message,
1156 client_buffer_size, this, request);
1157 }
294 1158
295 // If we have a request event, this is asynchronous, and we don't need to wait. 1159 // Handle cleanup on receive failure.
296 R_SUCCEED_IF(request->GetEvent() != nullptr); 1160 if (R_FAILED(result)) {
1161 // Cache the result to return it to the client.
1162 const Result result_for_client = result;
297 1163
298 // This is a synchronous request, so we should wait for our request to complete. 1164 // Clear the current request.
299 GetCurrentThread(m_kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); 1165 {
300 GetCurrentThread(m_kernel).BeginWait(std::addressof(wait_queue)); 1166 KScopedSchedulerLock sl(m_kernel);
1167 ASSERT(m_current_request == request);
1168 m_current_request = nullptr;
1169 if (!m_request_list.empty()) {
1170 this->NotifyAvailable();
1171 }
1172 }
1173
1174 // Reply to the client.
1175 {
1176 // After we reply, close our reference to the request.
1177 SCOPE_EXIT({ request->Close(); });
1178
1179 // Get the event to check whether the request is async.
1180 if (KEvent* event = request->GetEvent(); event != nullptr) {
1181 // The client sent an async request.
1182 KProcess* client = client_thread->GetOwnerProcess();
1183 auto& client_pt = client->GetPageTable();
1184
1185 // Send the async result.
1186 if (R_FAILED(result_for_client)) {
1187 ReplyAsyncError(client, client_message, client_buffer_size, result_for_client);
1188 }
1189
1190 // Unlock the client buffer.
1191 // NOTE: Nintendo does not check the result of this.
1192 client_pt.UnlockForIpcUserBuffer(client_message, client_buffer_size);
1193
1194 // Signal the event.
1195 event->Signal();
1196 } else {
1197 // End the client thread's wait.
1198 KScopedSchedulerLock sl(m_kernel);
1199
1200 if (!client_thread->IsTerminationRequested()) {
1201 client_thread->EndWait(result_for_client);
1202 }
1203 }
1204 }
1205
1206 // Set the server result.
1207 if (recv_list_broken) {
1208 result = ResultReceiveListBroken;
1209 } else {
1210 result = ResultNotFound;
1211 }
301 } 1212 }
302 1213
303 return GetCurrentThread(m_kernel).GetWaitResult(); 1214 R_RETURN(result);
304} 1215}
305 1216
306Result KServerSession::SendReply(bool is_hle) { 1217Result KServerSession::SendReply(uintptr_t server_message, uintptr_t server_buffer_size,
1218 KPhysicalAddress server_message_paddr, bool is_hle) {
307 // Lock the session. 1219 // Lock the session.
308 KScopedLightLock lk{m_lock}; 1220 KScopedLightLock lk{m_lock};
309 1221
@@ -327,7 +1239,7 @@ Result KServerSession::SendReply(bool is_hle) {
327 SCOPE_EXIT({ request->Close(); }); 1239 SCOPE_EXIT({ request->Close(); });
328 1240
329 // Extract relevant information from the request. 1241 // Extract relevant information from the request.
330 const uintptr_t client_message = request->GetAddress(); 1242 const uint64_t client_message = request->GetAddress();
331 const size_t client_buffer_size = request->GetSize(); 1243 const size_t client_buffer_size = request->GetSize();
332 KThread* client_thread = request->GetThread(); 1244 KThread* client_thread = request->GetThread();
333 KEvent* event = request->GetEvent(); 1245 KEvent* event = request->GetEvent();
@@ -342,31 +1254,28 @@ Result KServerSession::SendReply(bool is_hle) {
342 // HLE servers write directly to a pointer to the thread command buffer. Therefore 1254 // HLE servers write directly to a pointer to the thread command buffer. Therefore
343 // the reply has already been written in this case. 1255 // the reply has already been written in this case.
344 } else { 1256 } else {
345 Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()}; 1257 result = SendMessage(m_kernel, server_message, server_buffer_size, server_message_paddr,
346 KThread* server_thread = GetCurrentThreadPointer(m_kernel); 1258 *client_thread, client_message, client_buffer_size, this, request);
347 KProcess& src_process = *client_thread->GetOwnerProcess(); 1259 }
348 KProcess& dst_process = *server_thread->GetOwnerProcess(); 1260 } else if (!is_hle) {
349 UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); 1261 // Otherwise, we'll need to do some cleanup.
350 1262 KProcess* server_process = request->GetServerProcess();
351 auto* src_msg_buffer = memory.GetPointer<u32>(server_thread->GetTlsAddress()); 1263 KProcess* client_process =
352 auto* dst_msg_buffer = memory.GetPointer<u32>(client_message); 1264 (client_thread != nullptr) ? client_thread->GetOwnerProcess() : nullptr;
353 std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); 1265 KProcessPageTable* client_page_table =
354 1266 (client_process != nullptr) ? std::addressof(client_process->GetPageTable()) : nullptr;
355 // Translate special header ad-hoc. 1267
356 MessageBuffer src_msg(src_msg_buffer, client_buffer_size); 1268 // Cleanup server handles.
357 MessageBuffer::MessageHeader src_header(src_msg); 1269 result = CleanupServerHandles(m_kernel, server_message, server_buffer_size,
358 MessageBuffer::SpecialHeader src_special_header(src_msg, src_header); 1270 server_message_paddr);
359 if (src_header.GetHasSpecialHeader()) { 1271
360 MessageBuffer dst_msg(dst_msg_buffer, client_buffer_size); 1272 // Cleanup mappings.
361 result = ProcessMessageSpecialData<true>(dst_process, src_process, *server_thread, 1273 Result cleanup_map_result = CleanupMap(request, server_process, client_page_table);
362 dst_msg, src_msg, src_special_header); 1274
363 if (R_FAILED(result)) { 1275 // If we successfully cleaned up handles, use the map cleanup result as our result.
364 CleanupSpecialData(dst_process, dst_msg_buffer, client_buffer_size); 1276 if (R_SUCCEEDED(result)) {
365 } 1277 result = cleanup_map_result;
366 }
367 } 1278 }
368 } else {
369 result = ResultSessionClosed;
370 } 1279 }
371 1280
372 // Select a result for the client. 1281 // Select a result for the client.
@@ -381,19 +1290,18 @@ Result KServerSession::SendReply(bool is_hle) {
381 // If there's a client thread, update it. 1290 // If there's a client thread, update it.
382 if (client_thread != nullptr) { 1291 if (client_thread != nullptr) {
383 if (event != nullptr) { 1292 if (event != nullptr) {
384 // // Get the client process/page table. 1293 // Get the client process/page table.
385 // KProcess *client_process = client_thread->GetOwnerProcess(); 1294 KProcess* client_process = client_thread->GetOwnerProcess();
386 // KProcessPageTable *client_page_table = std::addressof(client_process->PageTable()); 1295 KProcessPageTable* client_page_table = std::addressof(client_process->GetPageTable());
387 1296
388 // // If we need to, reply with an async error. 1297 // If we need to, reply with an async error.
389 // if (R_FAILED(client_result)) { 1298 if (R_FAILED(client_result)) {
390 // ReplyAsyncError(client_process, client_message, client_buffer_size, 1299 ReplyAsyncError(client_process, client_message, client_buffer_size, client_result);
391 // client_result); 1300 }
392 // }
393 1301
394 // // Unlock the client buffer. 1302 // Unlock the client buffer.
395 // // NOTE: Nintendo does not check the result of this. 1303 // NOTE: Nintendo does not check the result of this.
396 // client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size); 1304 client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size);
397 1305
398 // Signal the event. 1306 // Signal the event.
399 event->Signal(); 1307 event->Signal();
@@ -410,91 +1318,53 @@ Result KServerSession::SendReply(bool is_hle) {
410 R_RETURN(result); 1318 R_RETURN(result);
411} 1319}
412 1320
413Result KServerSession::ReceiveRequest(std::shared_ptr<Service::HLERequestContext>* out_context, 1321Result KServerSession::OnRequest(KSessionRequest* request) {
414 std::weak_ptr<Service::SessionRequestManager> manager) { 1322 // Create the wait queue.
415 // Lock the session. 1323 ThreadQueueImplForKServerSessionRequest wait_queue{m_kernel};
416 KScopedLightLock lk{m_lock};
417
418 // Get the request and client thread.
419 KSessionRequest* request;
420 KThread* client_thread;
421 1324
422 { 1325 {
1326 // Lock the scheduler.
423 KScopedSchedulerLock sl{m_kernel}; 1327 KScopedSchedulerLock sl{m_kernel};
424 1328
425 // Ensure that we can service the request. 1329 // Ensure that we can handle new requests.
426 R_UNLESS(!m_parent->IsClientClosed(), ResultSessionClosed); 1330 R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed);
427
428 // Ensure we aren't already servicing a request.
429 R_UNLESS(m_current_request == nullptr, ResultNotFound);
430 1331
431 // Ensure we have a request to service. 1332 // Check that we're not terminating.
432 R_UNLESS(!m_request_list.empty(), ResultNotFound); 1333 R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(), ResultTerminationRequested);
433 1334
434 // Pop the first request from the list. 1335 // Get whether we're empty.
435 request = std::addressof(m_request_list.front()); 1336 const bool was_empty = m_request_list.empty();
436 m_request_list.pop_front();
437 1337
438 // Get the thread for the request. 1338 // Add the request to the list.
439 client_thread = request->GetThread(); 1339 request->Open();
440 R_UNLESS(client_thread != nullptr, ResultSessionClosed); 1340 m_request_list.push_back(*request);
441 1341
442 // Open the client thread. 1342 // If we were empty, signal.
443 client_thread->Open(); 1343 if (was_empty) {
444 } 1344 this->NotifyAvailable();
1345 }
445 1346
446 SCOPE_EXIT({ client_thread->Close(); }); 1347 // If we have a request event, this is asynchronous, and we don't need to wait.
1348 R_SUCCEED_IF(request->GetEvent() != nullptr);
447 1349
448 // Set the request as our current. 1350 // This is a synchronous request, so we should wait for our request to complete.
449 m_current_request = request; 1351 GetCurrentThread(m_kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
1352 GetCurrentThread(m_kernel).BeginWait(std::addressof(wait_queue));
1353 }
450 1354
451 // Get the client address. 1355 return GetCurrentThread(m_kernel).GetWaitResult();
452 uintptr_t client_message = request->GetAddress(); 1356}
453 size_t client_buffer_size = request->GetSize();
454 // bool recv_list_broken = false;
455 1357
456 if (!client_message) { 1358bool KServerSession::IsSignaled() const {
457 client_message = GetInteger(client_thread->GetTlsAddress()); 1359 ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel));
458 client_buffer_size = MessageBufferSize;
459 }
460 1360
461 // Receive the message. 1361 // If the client is closed, we're always signaled.
462 Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()}; 1362 if (m_parent->IsClientClosed()) {
463 if (out_context != nullptr) { 1363 return true;
464 // HLE request.
465 u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(client_message))};
466 *out_context =
467 std::make_shared<Service::HLERequestContext>(m_kernel, memory, this, client_thread);
468 (*out_context)->SetSessionRequestManager(manager);
469 (*out_context)
470 ->PopulateFromIncomingCommandBuffer(*client_thread->GetOwnerProcess(), cmd_buf);
471 } else {
472 KThread* server_thread = GetCurrentThreadPointer(m_kernel);
473 KProcess& src_process = *client_thread->GetOwnerProcess();
474 KProcess& dst_process = *server_thread->GetOwnerProcess();
475 UNIMPLEMENTED_IF(client_thread->GetOwnerProcess() != server_thread->GetOwnerProcess());
476
477 auto* src_msg_buffer = memory.GetPointer<u32>(client_message);
478 auto* dst_msg_buffer = memory.GetPointer<u32>(server_thread->GetTlsAddress());
479 std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
480
481 // Translate special header ad-hoc.
482 // TODO: fix this mess
483 MessageBuffer src_msg(src_msg_buffer, client_buffer_size);
484 MessageBuffer::MessageHeader src_header(src_msg);
485 MessageBuffer::SpecialHeader src_special_header(src_msg, src_header);
486 if (src_header.GetHasSpecialHeader()) {
487 MessageBuffer dst_msg(dst_msg_buffer, client_buffer_size);
488 Result res = ProcessMessageSpecialData<false>(dst_process, src_process, *client_thread,
489 dst_msg, src_msg, src_special_header);
490 if (R_FAILED(res)) {
491 CleanupSpecialData(dst_process, dst_msg_buffer, client_buffer_size);
492 }
493 }
494 } 1364 }
495 1365
496 // We succeeded. 1366 // Otherwise, we're signaled if we have a request and aren't handling one.
497 R_SUCCEED(); 1367 return !m_request_list.empty() && m_current_request == nullptr;
498} 1368}
499 1369
500void KServerSession::CleanupRequests() { 1370void KServerSession::CleanupRequests() {
@@ -527,31 +1397,30 @@ void KServerSession::CleanupRequests() {
527 SCOPE_EXIT({ request->Close(); }); 1397 SCOPE_EXIT({ request->Close(); });
528 1398
529 // Extract relevant information from the request. 1399 // Extract relevant information from the request.
530 // const uintptr_t client_message = request->GetAddress(); 1400 const uint64_t client_message = request->GetAddress();
531 // const size_t client_buffer_size = request->GetSize(); 1401 const size_t client_buffer_size = request->GetSize();
532 KThread* client_thread = request->GetThread(); 1402 KThread* client_thread = request->GetThread();
533 KEvent* event = request->GetEvent(); 1403 KEvent* event = request->GetEvent();
534 1404
535 // KProcess *server_process = request->GetServerProcess(); 1405 KProcess* server_process = request->GetServerProcess();
536 // KProcess *client_process = (client_thread != nullptr) ? 1406 KProcess* client_process =
537 // client_thread->GetOwnerProcess() : nullptr; 1407 (client_thread != nullptr) ? client_thread->GetOwnerProcess() : nullptr;
538 // KProcessPageTable *client_page_table = (client_process != nullptr) ? 1408 KProcessPageTable* client_page_table =
539 // std::addressof(client_process->GetPageTable()) 1409 (client_process != nullptr) ? std::addressof(client_process->GetPageTable()) : nullptr;
540 // : nullptr;
541 1410
542 // Cleanup the mappings. 1411 // Cleanup the mappings.
543 // Result result = CleanupMap(request, server_process, client_page_table); 1412 Result result = CleanupMap(request, server_process, client_page_table);
544 1413
545 // If there's a client thread, update it. 1414 // If there's a client thread, update it.
546 if (client_thread != nullptr) { 1415 if (client_thread != nullptr) {
547 if (event != nullptr) { 1416 if (event != nullptr) {
548 // // We need to reply async. 1417 // We need to reply async.
549 // ReplyAsyncError(client_process, client_message, client_buffer_size, 1418 ReplyAsyncError(client_process, client_message, client_buffer_size,
550 // (R_SUCCEEDED(result) ? ResultSessionClosed : result)); 1419 (R_SUCCEEDED(result) ? ResultSessionClosed : result));
551 1420
552 // // Unlock the client buffer. 1421 // Unlock the client buffer.
553 // NOTE: Nintendo does not check the result of this. 1422 // NOTE: Nintendo does not check the result of this.
554 // client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size); 1423 client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size);
555 1424
556 // Signal the event. 1425 // Signal the event.
557 event->Signal(); 1426 event->Signal();
@@ -567,4 +1436,97 @@ void KServerSession::CleanupRequests() {
567 } 1436 }
568} 1437}
569 1438
1439void KServerSession::OnClientClosed() {
1440 KScopedLightLock lk{m_lock};
1441
1442 // Handle any pending requests.
1443 KSessionRequest* prev_request = nullptr;
1444 while (true) {
1445 // Declare variables for processing the request.
1446 KSessionRequest* request = nullptr;
1447 KEvent* event = nullptr;
1448 KThread* thread = nullptr;
1449 bool cur_request = false;
1450 bool terminate = false;
1451
1452 // Get the next request.
1453 {
1454 KScopedSchedulerLock sl{m_kernel};
1455
1456 if (m_current_request != nullptr && m_current_request != prev_request) {
1457 // Set the request, open a reference as we process it.
1458 request = m_current_request;
1459 request->Open();
1460 cur_request = true;
1461
1462 // Get thread and event for the request.
1463 thread = request->GetThread();
1464 event = request->GetEvent();
1465
1466 // If the thread is terminating, handle that.
1467 if (thread->IsTerminationRequested()) {
1468 request->ClearThread();
1469 request->ClearEvent();
1470 terminate = true;
1471 }
1472
1473 prev_request = request;
1474 } else if (!m_request_list.empty()) {
1475 // Pop the request from the front of the list.
1476 request = std::addressof(m_request_list.front());
1477 m_request_list.pop_front();
1478
1479 // Get thread and event for the request.
1480 thread = request->GetThread();
1481 event = request->GetEvent();
1482 }
1483 }
1484
1485 // If there are no requests, we're done.
1486 if (request == nullptr) {
1487 break;
1488 }
1489
1490 // All requests must have threads.
1491 ASSERT(thread != nullptr);
1492
1493 // Ensure that we close the request when done.
1494 SCOPE_EXIT({ request->Close(); });
1495
1496 // If we're terminating, close a reference to the thread and event.
1497 if (terminate) {
1498 thread->Close();
1499 if (event != nullptr) {
1500 event->Close();
1501 }
1502 }
1503
1504 // If we need to, reply.
1505 if (event != nullptr && !cur_request) {
1506 // There must be no mappings.
1507 ASSERT(request->GetSendCount() == 0);
1508 ASSERT(request->GetReceiveCount() == 0);
1509 ASSERT(request->GetExchangeCount() == 0);
1510
1511 // Get the process and page table.
1512 KProcess* client_process = thread->GetOwnerProcess();
1513 auto& client_pt = client_process->GetPageTable();
1514
1515 // Reply to the request.
1516 ReplyAsyncError(client_process, request->GetAddress(), request->GetSize(),
1517 ResultSessionClosed);
1518
1519 // Unlock the buffer.
1520 // NOTE: Nintendo does not check the result of this.
1521 client_pt.UnlockForIpcUserBuffer(request->GetAddress(), request->GetSize());
1522
1523 // Signal the event.
1524 event->Signal();
1525 }
1526 }
1527
1528 // Notify.
1529 this->NotifyAvailable(ResultSessionClosed);
1530}
1531
570} // namespace Kernel 1532} // namespace Kernel
diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h
index 403891919..2876c231b 100644
--- a/src/core/hle/kernel/k_server_session.h
+++ b/src/core/hle/kernel/k_server_session.h
@@ -49,14 +49,21 @@ public:
49 bool IsSignaled() const override; 49 bool IsSignaled() const override;
50 void OnClientClosed(); 50 void OnClientClosed();
51 51
52 /// TODO: flesh these out to match the real kernel
53 Result OnRequest(KSessionRequest* request); 52 Result OnRequest(KSessionRequest* request);
54 Result SendReply(bool is_hle = false); 53 Result SendReply(uintptr_t server_message, uintptr_t server_buffer_size,
55 Result ReceiveRequest(std::shared_ptr<Service::HLERequestContext>* out_context = nullptr, 54 KPhysicalAddress server_message_paddr, bool is_hle = false);
55 Result ReceiveRequest(uintptr_t server_message, uintptr_t server_buffer_size,
56 KPhysicalAddress server_message_paddr,
57 std::shared_ptr<Service::HLERequestContext>* out_context = nullptr,
56 std::weak_ptr<Service::SessionRequestManager> manager = {}); 58 std::weak_ptr<Service::SessionRequestManager> manager = {});
57 59
58 Result SendReplyHLE() { 60 Result SendReplyHLE() {
59 return SendReply(true); 61 R_RETURN(this->SendReply(0, 0, 0, true));
62 }
63
64 Result ReceiveRequestHLE(std::shared_ptr<Service::HLERequestContext>* out_context,
65 std::weak_ptr<Service::SessionRequestManager> manager) {
66 R_RETURN(this->ReceiveRequest(0, 0, 0, out_context, manager));
60 } 67 }
61 68
62private: 69private:
diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp
index 44d7a8f02..4a1f6027e 100644
--- a/src/core/hle/kernel/k_session.cpp
+++ b/src/core/hle/kernel/k_session.cpp
@@ -33,8 +33,7 @@ void KSession::Initialize(KClientPort* client_port, uintptr_t name) {
33 m_name = name; 33 m_name = name;
34 34
35 // Set our owner process. 35 // Set our owner process.
36 //! FIXME: this is the wrong process! 36 m_process = GetCurrentProcessPointer(m_kernel);
37 m_process = m_kernel.ApplicationProcess();
38 m_process->Open(); 37 m_process->Open();
39 38
40 // Set our port. 39 // Set our port.
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 7d9a6e9cf..24394d222 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -1422,8 +1422,7 @@ s32 GetCurrentCoreId(KernelCore& kernel) {
1422} 1422}
1423 1423
1424Core::Memory::Memory& GetCurrentMemory(KernelCore& kernel) { 1424Core::Memory::Memory& GetCurrentMemory(KernelCore& kernel) {
1425 // TODO: per-process memory 1425 return GetCurrentProcess(kernel).GetMemory();
1426 return kernel.System().ApplicationMemory();
1427} 1426}
1428 1427
1429KScopedDisableDispatch::~KScopedDisableDispatch() { 1428KScopedDisableDispatch::~KScopedDisableDispatch() {
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index e9925d231..f13e232b2 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -314,11 +314,7 @@ public:
314 m_current_core_id = core; 314 m_current_core_id = core;
315 } 315 }
316 316
317 KProcess* GetOwnerProcess() { 317 KProcess* GetOwnerProcess() const {
318 return m_parent;
319 }
320
321 const KProcess* GetOwnerProcess() const {
322 return m_parent; 318 return m_parent;
323 } 319 }
324 320
diff --git a/src/core/hle/kernel/k_transfer_memory.h b/src/core/hle/kernel/k_transfer_memory.h
index 8a0b08761..530b45218 100644
--- a/src/core/hle/kernel/k_transfer_memory.h
+++ b/src/core/hle/kernel/k_transfer_memory.h
@@ -5,6 +5,7 @@
5 5
6#include <optional> 6#include <optional>
7 7
8#include "core/hle/kernel/k_light_lock.h"
8#include "core/hle/kernel/k_page_group.h" 9#include "core/hle/kernel/k_page_group.h"
9#include "core/hle/kernel/slab_helpers.h" 10#include "core/hle/kernel/slab_helpers.h"
10#include "core/hle/kernel/svc_types.h" 11#include "core/hle/kernel/svc_types.h"
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index b515f6a18..1030f0c12 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -68,8 +68,6 @@ struct KernelCore::Impl {
68 68
69 global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel); 69 global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel);
70 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); 70 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
71 global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
72 global_handle_table->Initialize(KHandleTable::MaxTableSize);
73 71
74 is_phantom_mode_for_singlecore = false; 72 is_phantom_mode_for_singlecore = false;
75 73
@@ -121,13 +119,8 @@ struct KernelCore::Impl {
121 next_user_process_id = KProcess::ProcessIdMin; 119 next_user_process_id = KProcess::ProcessIdMin;
122 next_thread_id = 1; 120 next_thread_id = 1;
123 121
124 global_handle_table->Finalize();
125 global_handle_table.reset();
126
127 preemption_event = nullptr; 122 preemption_event = nullptr;
128 123
129 exclusive_monitor.reset();
130
131 // Cleanup persistent kernel objects 124 // Cleanup persistent kernel objects
132 auto CleanupObject = [](KAutoObject* obj) { 125 auto CleanupObject = [](KAutoObject* obj) {
133 if (obj) { 126 if (obj) {
@@ -191,8 +184,6 @@ struct KernelCore::Impl {
191 } 184 }
192 185
193 void InitializePhysicalCores() { 186 void InitializePhysicalCores() {
194 exclusive_monitor =
195 Core::MakeExclusiveMonitor(system.ApplicationMemory(), Core::Hardware::NUM_CPU_CORES);
196 for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { 187 for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
197 const s32 core{static_cast<s32>(i)}; 188 const s32 core{static_cast<s32>(i)};
198 189
@@ -791,10 +782,6 @@ struct KernelCore::Impl {
791 782
792 std::shared_ptr<Core::Timing::EventType> preemption_event; 783 std::shared_ptr<Core::Timing::EventType> preemption_event;
793 784
794 // This is the kernel's handle table or supervisor handle table which
795 // stores all the objects in place.
796 std::unique_ptr<KHandleTable> global_handle_table;
797
798 std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container; 785 std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container;
799 786
800 std::unique_ptr<KObjectNameGlobalData> object_name_global_data; 787 std::unique_ptr<KObjectNameGlobalData> object_name_global_data;
@@ -805,7 +792,6 @@ struct KernelCore::Impl {
805 std::mutex server_lock; 792 std::mutex server_lock;
806 std::vector<std::unique_ptr<Service::ServerManager>> server_managers; 793 std::vector<std::unique_ptr<Service::ServerManager>> server_managers;
807 794
808 std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
809 std::array<std::unique_ptr<Kernel::PhysicalCore>, Core::Hardware::NUM_CPU_CORES> cores; 795 std::array<std::unique_ptr<Kernel::PhysicalCore>, Core::Hardware::NUM_CPU_CORES> cores;
810 796
811 // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others 797 // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
@@ -882,10 +868,6 @@ KResourceLimit* KernelCore::GetSystemResourceLimit() {
882 return impl->system_resource_limit; 868 return impl->system_resource_limit;
883} 869}
884 870
885KScopedAutoObject<KThread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const {
886 return impl->global_handle_table->GetObject<KThread>(handle);
887}
888
889void KernelCore::AppendNewProcess(KProcess* process) { 871void KernelCore::AppendNewProcess(KProcess* process) {
890 impl->process_list.push_back(process); 872 impl->process_list.push_back(process);
891} 873}
@@ -959,14 +941,6 @@ Kernel::KHardwareTimer& KernelCore::HardwareTimer() {
959 return *impl->hardware_timer; 941 return *impl->hardware_timer;
960} 942}
961 943
962Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() {
963 return *impl->exclusive_monitor;
964}
965
966const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const {
967 return *impl->exclusive_monitor;
968}
969
970KAutoObjectWithListContainer& KernelCore::ObjectListContainer() { 944KAutoObjectWithListContainer& KernelCore::ObjectListContainer() {
971 return *impl->global_object_list_container; 945 return *impl->global_object_list_container;
972} 946}
@@ -1030,14 +1004,6 @@ u64 KernelCore::CreateNewUserProcessID() {
1030 return impl->next_user_process_id++; 1004 return impl->next_user_process_id++;
1031} 1005}
1032 1006
1033KHandleTable& KernelCore::GlobalHandleTable() {
1034 return *impl->global_handle_table;
1035}
1036
1037const KHandleTable& KernelCore::GlobalHandleTable() const {
1038 return *impl->global_handle_table;
1039}
1040
1041void KernelCore::RegisterCoreThread(std::size_t core_id) { 1007void KernelCore::RegisterCoreThread(std::size_t core_id) {
1042 impl->RegisterCoreThread(core_id); 1008 impl->RegisterCoreThread(core_id);
1043} 1009}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 78c88902c..5d4102145 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -116,9 +116,6 @@ public:
116 /// Retrieves a shared pointer to the system resource limit instance. 116 /// Retrieves a shared pointer to the system resource limit instance.
117 KResourceLimit* GetSystemResourceLimit(); 117 KResourceLimit* GetSystemResourceLimit();
118 118
119 /// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
120 KScopedAutoObject<KThread> RetrieveThreadFromGlobalHandleTable(Handle handle) const;
121
122 /// Adds the given shared pointer to an internal list of active processes. 119 /// Adds the given shared pointer to an internal list of active processes.
123 void AppendNewProcess(KProcess* process); 120 void AppendNewProcess(KProcess* process);
124 121
@@ -170,10 +167,6 @@ public:
170 /// Stops execution of 'id' core, in order to reschedule a new thread. 167 /// Stops execution of 'id' core, in order to reschedule a new thread.
171 void PrepareReschedule(std::size_t id); 168 void PrepareReschedule(std::size_t id);
172 169
173 Core::ExclusiveMonitor& GetExclusiveMonitor();
174
175 const Core::ExclusiveMonitor& GetExclusiveMonitor() const;
176
177 KAutoObjectWithListContainer& ObjectListContainer(); 170 KAutoObjectWithListContainer& ObjectListContainer();
178 171
179 const KAutoObjectWithListContainer& ObjectListContainer() const; 172 const KAutoObjectWithListContainer& ObjectListContainer() const;
diff --git a/src/core/hle/kernel/message_buffer.h b/src/core/hle/kernel/message_buffer.h
index 75b275310..d528a9bb3 100644
--- a/src/core/hle/kernel/message_buffer.h
+++ b/src/core/hle/kernel/message_buffer.h
@@ -18,13 +18,13 @@ public:
18 static constexpr inline u64 NullTag = 0; 18 static constexpr inline u64 NullTag = 0;
19 19
20 public: 20 public:
21 enum class ReceiveListCountType : u32 { 21 enum ReceiveListCountType : u32 {
22 None = 0, 22 ReceiveListCountType_None = 0,
23 ToMessageBuffer = 1, 23 ReceiveListCountType_ToMessageBuffer = 1,
24 ToSingleBuffer = 2, 24 ReceiveListCountType_ToSingleBuffer = 2,
25 25
26 CountOffset = 2, 26 ReceiveListCountType_CountOffset = 2,
27 CountMax = 13, 27 ReceiveListCountType_CountMax = 13,
28 }; 28 };
29 29
30 private: 30 private:
@@ -591,16 +591,16 @@ public:
591 // Add the size of the receive list. 591 // Add the size of the receive list.
592 const auto count = hdr.GetReceiveListCount(); 592 const auto count = hdr.GetReceiveListCount();
593 switch (count) { 593 switch (count) {
594 case MessageHeader::ReceiveListCountType::None: 594 case MessageHeader::ReceiveListCountType_None:
595 break; 595 break;
596 case MessageHeader::ReceiveListCountType::ToMessageBuffer: 596 case MessageHeader::ReceiveListCountType_ToMessageBuffer:
597 break; 597 break;
598 case MessageHeader::ReceiveListCountType::ToSingleBuffer: 598 case MessageHeader::ReceiveListCountType_ToSingleBuffer:
599 msg_size += ReceiveListEntry::GetDataSize(); 599 msg_size += ReceiveListEntry::GetDataSize();
600 break; 600 break;
601 default: 601 default:
602 msg_size += (static_cast<s32>(count) - 602 msg_size += (static_cast<s32>(count) -
603 static_cast<s32>(MessageHeader::ReceiveListCountType::CountOffset)) * 603 static_cast<s32>(MessageHeader::ReceiveListCountType_CountOffset)) *
604 ReceiveListEntry::GetDataSize(); 604 ReceiveListEntry::GetDataSize();
605 break; 605 break;
606 } 606 }
diff --git a/src/core/hle/kernel/svc/svc_info.cpp b/src/core/hle/kernel/svc/svc_info.cpp
index ada998772..231e4d0e1 100644
--- a/src/core/hle/kernel/svc/svc_info.cpp
+++ b/src/core/hle/kernel/svc/svc_info.cpp
@@ -118,7 +118,6 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
118 R_SUCCEED(); 118 R_SUCCEED();
119 119
120 case InfoType::IsApplication: 120 case InfoType::IsApplication:
121 LOG_WARNING(Kernel_SVC, "(STUBBED) Assuming process is application");
122 *result = process->IsApplication(); 121 *result = process->IsApplication();
123 R_SUCCEED(); 122 R_SUCCEED();
124 123
diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp
index 47a3e7bb0..85cc4f561 100644
--- a/src/core/hle/kernel/svc/svc_ipc.cpp
+++ b/src/core/hle/kernel/svc/svc_ipc.cpp
@@ -48,8 +48,7 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes
48 }; 48 };
49 49
50 // Send the reply. 50 // Send the reply.
51 R_TRY(session->SendReply()); 51 R_TRY(session->SendReply(message, buffer_size, message_paddr));
52 // R_TRY(session->SendReply(message, buffer_size, message_paddr));
53 } 52 }
54 53
55 // Receive a message. 54 // Receive a message.
@@ -85,8 +84,7 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes
85 if (R_SUCCEEDED(result)) { 84 if (R_SUCCEEDED(result)) {
86 KServerSession* session = objs[index]->DynamicCast<KServerSession*>(); 85 KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
87 if (session != nullptr) { 86 if (session != nullptr) {
88 // result = session->ReceiveRequest(message, buffer_size, message_paddr); 87 result = session->ReceiveRequest(message, buffer_size, message_paddr);
89 result = session->ReceiveRequest();
90 if (ResultNotFound == result) { 88 if (ResultNotFound == result) {
91 continue; 89 continue;
92 } 90 }
diff --git a/src/core/hle/kernel/svc_results.h b/src/core/hle/kernel/svc_results.h
index e1ad78607..38e71d516 100644
--- a/src/core/hle/kernel/svc_results.h
+++ b/src/core/hle/kernel/svc_results.h
@@ -38,7 +38,9 @@ constexpr Result ResultInvalidState{ErrorModule::Kernel, 125};
38constexpr Result ResultReservedUsed{ErrorModule::Kernel, 126}; 38constexpr Result ResultReservedUsed{ErrorModule::Kernel, 126};
39constexpr Result ResultPortClosed{ErrorModule::Kernel, 131}; 39constexpr Result ResultPortClosed{ErrorModule::Kernel, 131};
40constexpr Result ResultLimitReached{ErrorModule::Kernel, 132}; 40constexpr Result ResultLimitReached{ErrorModule::Kernel, 132};
41constexpr Result ResultReceiveListBroken{ErrorModule::Kernel, 258};
41constexpr Result ResultOutOfAddressSpace{ErrorModule::Kernel, 259}; 42constexpr Result ResultOutOfAddressSpace{ErrorModule::Kernel, 259};
43constexpr Result ResultMessageTooLarge{ErrorModule::Kernel, 260};
42constexpr Result ResultInvalidId{ErrorModule::Kernel, 519}; 44constexpr Result ResultInvalidId{ErrorModule::Kernel, 519};
43 45
44} // namespace Kernel 46} // namespace Kernel
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index a266d7c21..97eb56ff0 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1513,8 +1513,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx)
1513 return; 1513 return;
1514 } 1514 }
1515 1515
1516 auto transfer_mem = 1516 auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle);
1517 system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(handle);
1518 1517
1519 if (transfer_mem.IsNull()) { 1518 if (transfer_mem.IsNull()) {
1520 LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle); 1519 LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
@@ -1524,8 +1523,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx)
1524 } 1523 }
1525 1524
1526 std::vector<u8> memory(transfer_mem->GetSize()); 1525 std::vector<u8> memory(transfer_mem->GetSize());
1527 system.ApplicationMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), 1526 ctx.GetMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), memory.size());
1528 memory.size());
1529 1527
1530 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 1528 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1531 rb.Push(ResultSuccess); 1529 rb.Push(ResultSuccess);
@@ -1547,8 +1545,7 @@ void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) {
1547 return; 1545 return;
1548 } 1546 }
1549 1547
1550 auto transfer_mem = 1548 auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle);
1551 system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(handle);
1552 1549
1553 if (transfer_mem.IsNull()) { 1550 if (transfer_mem.IsNull()) {
1554 LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle); 1551 LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
@@ -1558,8 +1555,7 @@ void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) {
1558 } 1555 }
1559 1556
1560 std::vector<u8> memory(transfer_mem->GetSize()); 1557 std::vector<u8> memory(transfer_mem->GetSize());
1561 system.ApplicationMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), 1558 ctx.GetMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), memory.size());
1562 memory.size());
1563 1559
1564 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 1560 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1565 rb.Push(ResultSuccess); 1561 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 23e56c77a..bd4ca753b 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -454,10 +454,8 @@ void AudRenU::OpenAudioRenderer(HLERequestContext& ctx) {
454 return; 454 return;
455 } 455 }
456 456
457 const auto& handle_table{system.ApplicationProcess()->GetHandleTable()}; 457 auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle)};
458 auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)}; 458 auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
459 auto transfer_memory{
460 process->GetHandleTable().GetObject<Kernel::KTransferMemory>(transfer_memory_handle)};
461 459
462 const auto session_id{impl->GetSessionId()}; 460 const auto session_id{impl->GetSessionId()};
463 if (session_id == -1) { 461 if (session_id == -1) {
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 6a7bf9416..91f33aabd 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -278,9 +278,7 @@ void HwOpus::OpenHardwareOpusDecoder(HLERequestContext& ctx) {
278 auto params = rp.PopRaw<OpusParameters>(); 278 auto params = rp.PopRaw<OpusParameters>();
279 auto transfer_memory_size{rp.Pop<u32>()}; 279 auto transfer_memory_size{rp.Pop<u32>()};
280 auto transfer_memory_handle{ctx.GetCopyHandle(0)}; 280 auto transfer_memory_handle{ctx.GetCopyHandle(0)};
281 auto transfer_memory{ 281 auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
282 system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
283 transfer_memory_handle)};
284 282
285 LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}", 283 LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}",
286 params.sample_rate, params.channel_count, transfer_memory_size); 284 params.sample_rate, params.channel_count, transfer_memory_size);
@@ -323,9 +321,7 @@ void HwOpus::OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx) {
323 321
324 auto transfer_memory_size{rp.Pop<u32>()}; 322 auto transfer_memory_size{rp.Pop<u32>()};
325 auto transfer_memory_handle{ctx.GetCopyHandle(0)}; 323 auto transfer_memory_handle{ctx.GetCopyHandle(0)};
326 auto transfer_memory{ 324 auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
327 system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
328 transfer_memory_handle)};
329 325
330 LOG_DEBUG(Service_Audio, 326 LOG_DEBUG(Service_Audio,
331 "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} " 327 "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
@@ -374,9 +370,7 @@ void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) {
374 auto params = rp.PopRaw<OpusParametersEx>(); 370 auto params = rp.PopRaw<OpusParametersEx>();
375 auto transfer_memory_size{rp.Pop<u32>()}; 371 auto transfer_memory_size{rp.Pop<u32>()};
376 auto transfer_memory_handle{ctx.GetCopyHandle(0)}; 372 auto transfer_memory_handle{ctx.GetCopyHandle(0)};
377 auto transfer_memory{ 373 auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
378 system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
379 transfer_memory_handle)};
380 374
381 LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}", 375 LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}",
382 params.sample_rate, params.channel_count, transfer_memory_size); 376 params.sample_rate, params.channel_count, transfer_memory_size);
@@ -414,9 +408,7 @@ void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) {
414 408
415 auto transfer_memory_size{rp.Pop<u32>()}; 409 auto transfer_memory_size{rp.Pop<u32>()};
416 auto transfer_memory_handle{ctx.GetCopyHandle(0)}; 410 auto transfer_memory_handle{ctx.GetCopyHandle(0)};
417 auto transfer_memory{ 411 auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
418 system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
419 transfer_memory_handle)};
420 412
421 LOG_DEBUG(Service_Audio, 413 LOG_DEBUG(Service_Audio,
422 "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} " 414 "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index fe2ed8df8..31da86074 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -89,7 +89,7 @@ static void GenerateErrorReport(Core::System& system, Result error_code, const F
89 crash_report += fmt::format(" ESR: {:016x}\n", info.esr); 89 crash_report += fmt::format(" ESR: {:016x}\n", info.esr);
90 crash_report += fmt::format(" FAR: {:016x}\n", info.far); 90 crash_report += fmt::format(" FAR: {:016x}\n", info.far);
91 crash_report += "\nBacktrace:\n"; 91 crash_report += "\nBacktrace:\n";
92 for (size_t i = 0; i < info.backtrace_size; i++) { 92 for (u32 i = 0; i < std::min<u32>(info.backtrace_size, 32); i++) {
93 crash_report += 93 crash_report +=
94 fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]); 94 fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]);
95 } 95 }
diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp
index 06a01c02c..3174672af 100644
--- a/src/core/hle/service/hid/hid_server.cpp
+++ b/src/core/hle/service/hid/hid_server.cpp
@@ -1850,8 +1850,7 @@ void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) {
1850 ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes"); 1850 ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes");
1851 ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes"); 1851 ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes");
1852 1852
1853 auto t_mem_1 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( 1853 auto t_mem_1 = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_1_handle);
1854 t_mem_1_handle);
1855 1854
1856 if (t_mem_1.IsNull()) { 1855 if (t_mem_1.IsNull()) {
1857 LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle); 1856 LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle);
@@ -1860,8 +1859,7 @@ void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) {
1860 return; 1859 return;
1861 } 1860 }
1862 1861
1863 auto t_mem_2 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( 1862 auto t_mem_2 = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_2_handle);
1864 t_mem_2_handle);
1865 1863
1866 if (t_mem_2.IsNull()) { 1864 if (t_mem_2.IsNull()) {
1867 LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle); 1865 LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle);
@@ -2142,8 +2140,7 @@ void IHidServer::WritePalmaWaveEntry(HLERequestContext& ctx) {
2142 2140
2143 ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes"); 2141 ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes");
2144 2142
2145 auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( 2143 auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
2146 t_mem_handle);
2147 2144
2148 if (t_mem.IsNull()) { 2145 if (t_mem.IsNull()) {
2149 LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); 2146 LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp
index 854de2fe2..ffa7e144d 100644
--- a/src/core/hle/service/hid/hidbus.cpp
+++ b/src/core/hle/service/hid/hidbus.cpp
@@ -448,8 +448,7 @@ void HidBus::EnableJoyPollingReceiveMode(HLERequestContext& ctx) {
448 448
449 ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes"); 449 ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes");
450 450
451 auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( 451 auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
452 t_mem_handle);
453 452
454 if (t_mem.IsNull()) { 453 if (t_mem.IsNull()) {
455 LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); 454 LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index 39b9a4474..008debfd1 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -197,8 +197,7 @@ void IRS::RunImageTransferProcessor(HLERequestContext& ctx) {
197 const auto parameters{rp.PopRaw<Parameters>()}; 197 const auto parameters{rp.PopRaw<Parameters>()};
198 const auto t_mem_handle{ctx.GetCopyHandle(0)}; 198 const auto t_mem_handle{ctx.GetCopyHandle(0)};
199 199
200 auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( 200 auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
201 t_mem_handle);
202 201
203 if (t_mem.IsNull()) { 202 if (t_mem.IsNull()) {
204 LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); 203 LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
@@ -444,8 +443,7 @@ void IRS::RunImageTransferExProcessor(HLERequestContext& ctx) {
444 const auto parameters{rp.PopRaw<Parameters>()}; 443 const auto parameters{rp.PopRaw<Parameters>()};
445 const auto t_mem_handle{ctx.GetCopyHandle(0)}; 444 const auto t_mem_handle{ctx.GetCopyHandle(0)};
446 445
447 auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( 446 auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
448 t_mem_handle);
449 447
450 LOG_INFO(Service_IRS, 448 LOG_INFO(Service_IRS,
451 "called, npad_type={}, npad_id={}, transfer_memory_size={}, " 449 "called, npad_type={}, npad_id={}, transfer_memory_size={}, "
diff --git a/src/core/hle/service/hle_ipc.cpp b/src/core/hle/service/hle_ipc.cpp
index 38955932c..39df77e43 100644
--- a/src/core/hle/service/hle_ipc.cpp
+++ b/src/core/hle/service/hle_ipc.cpp
@@ -146,10 +146,7 @@ HLERequestContext::HLERequestContext(Kernel::KernelCore& kernel_, Core::Memory::
146 146
147HLERequestContext::~HLERequestContext() = default; 147HLERequestContext::~HLERequestContext() = default;
148 148
149void HLERequestContext::ParseCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf, 149void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
150 bool incoming) {
151 client_handle_table = &process.GetHandleTable();
152
153 IPC::RequestParser rp(src_cmdbuf); 150 IPC::RequestParser rp(src_cmdbuf);
154 command_header = rp.PopRaw<IPC::CommandHeader>(); 151 command_header = rp.PopRaw<IPC::CommandHeader>();
155 152
@@ -162,7 +159,7 @@ void HLERequestContext::ParseCommandBuffer(Kernel::KProcess& process, u32_le* sr
162 if (command_header->enable_handle_descriptor) { 159 if (command_header->enable_handle_descriptor) {
163 handle_descriptor_header = rp.PopRaw<IPC::HandleDescriptorHeader>(); 160 handle_descriptor_header = rp.PopRaw<IPC::HandleDescriptorHeader>();
164 if (handle_descriptor_header->send_current_pid) { 161 if (handle_descriptor_header->send_current_pid) {
165 pid = process.GetProcessId(); 162 pid = thread->GetOwnerProcess()->GetProcessId();
166 rp.Skip(2, false); 163 rp.Skip(2, false);
167 } 164 }
168 if (incoming) { 165 if (incoming) {
@@ -270,9 +267,10 @@ void HLERequestContext::ParseCommandBuffer(Kernel::KProcess& process, u32_le* sr
270 rp.Skip(1, false); // The command is actually an u64, but we don't use the high part. 267 rp.Skip(1, false); // The command is actually an u64, but we don't use the high part.
271} 268}
272 269
273Result HLERequestContext::PopulateFromIncomingCommandBuffer(Kernel::KProcess& process, 270Result HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf) {
274 u32_le* src_cmdbuf) { 271 client_handle_table = &thread->GetOwnerProcess()->GetHandleTable();
275 ParseCommandBuffer(process, src_cmdbuf, true); 272
273 ParseCommandBuffer(src_cmdbuf, true);
276 274
277 if (command_header->IsCloseCommand()) { 275 if (command_header->IsCloseCommand()) {
278 // Close does not populate the rest of the IPC header 276 // Close does not populate the rest of the IPC header
@@ -284,9 +282,9 @@ Result HLERequestContext::PopulateFromIncomingCommandBuffer(Kernel::KProcess& pr
284 return ResultSuccess; 282 return ResultSuccess;
285} 283}
286 284
287Result HLERequestContext::WriteToOutgoingCommandBuffer(Kernel::KThread& requesting_thread) { 285Result HLERequestContext::WriteToOutgoingCommandBuffer() {
288 auto current_offset = handles_offset; 286 auto current_offset = handles_offset;
289 auto& owner_process = *requesting_thread.GetOwnerProcess(); 287 auto& owner_process = *thread->GetOwnerProcess();
290 auto& handle_table = owner_process.GetHandleTable(); 288 auto& handle_table = owner_process.GetHandleTable();
291 289
292 for (auto& object : outgoing_copy_objects) { 290 for (auto& object : outgoing_copy_objects) {
@@ -319,7 +317,7 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(Kernel::KThread& requesti
319 } 317 }
320 318
321 // Copy the translated command buffer back into the thread's command buffer area. 319 // Copy the translated command buffer back into the thread's command buffer area.
322 memory.WriteBlock(requesting_thread.GetTlsAddress(), cmd_buf.data(), write_size * sizeof(u32)); 320 memory.WriteBlock(thread->GetTlsAddress(), cmd_buf.data(), write_size * sizeof(u32));
323 321
324 return ResultSuccess; 322 return ResultSuccess;
325} 323}
diff --git a/src/core/hle/service/hle_ipc.h b/src/core/hle/service/hle_ipc.h
index 18d464c63..40d86943e 100644
--- a/src/core/hle/service/hle_ipc.h
+++ b/src/core/hle/service/hle_ipc.h
@@ -17,6 +17,7 @@
17#include "common/concepts.h" 17#include "common/concepts.h"
18#include "common/swap.h" 18#include "common/swap.h"
19#include "core/hle/ipc.h" 19#include "core/hle/ipc.h"
20#include "core/hle/kernel/k_handle_table.h"
20#include "core/hle/kernel/svc_common.h" 21#include "core/hle/kernel/svc_common.h"
21 22
22union Result; 23union Result;
@@ -196,10 +197,10 @@ public:
196 } 197 }
197 198
198 /// Populates this context with data from the requesting process/thread. 199 /// Populates this context with data from the requesting process/thread.
199 Result PopulateFromIncomingCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf); 200 Result PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf);
200 201
201 /// Writes data from this context back to the requesting process/thread. 202 /// Writes data from this context back to the requesting process/thread.
202 Result WriteToOutgoingCommandBuffer(Kernel::KThread& requesting_thread); 203 Result WriteToOutgoingCommandBuffer();
203 204
204 [[nodiscard]] u32_le GetHipcCommand() const { 205 [[nodiscard]] u32_le GetHipcCommand() const {
205 return command; 206 return command;
@@ -359,8 +360,17 @@ public:
359 return *thread; 360 return *thread;
360 } 361 }
361 362
362 Kernel::KHandleTable& GetClientHandleTable() { 363 [[nodiscard]] Core::Memory::Memory& GetMemory() const {
363 return *client_handle_table; 364 return memory;
365 }
366
367 template <typename T>
368 Kernel::KScopedAutoObject<T> GetObjectFromHandle(u32 handle) {
369 auto obj = client_handle_table->GetObjectForIpc(handle, thread);
370 if (obj.IsNotNull()) {
371 return obj->DynamicCast<T*>();
372 }
373 return nullptr;
364 } 374 }
365 375
366 [[nodiscard]] std::shared_ptr<SessionRequestManager> GetManager() const { 376 [[nodiscard]] std::shared_ptr<SessionRequestManager> GetManager() const {
@@ -378,7 +388,7 @@ public:
378private: 388private:
379 friend class IPC::ResponseBuilder; 389 friend class IPC::ResponseBuilder;
380 390
381 void ParseCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf, bool incoming); 391 void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
382 392
383 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; 393 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
384 Kernel::KServerSession* server_session{}; 394 Kernel::KServerSession* server_session{};
diff --git a/src/core/hle/service/ipc_helpers.h b/src/core/hle/service/ipc_helpers.h
index 0e222362e..4b02872fb 100644
--- a/src/core/hle/service/ipc_helpers.h
+++ b/src/core/hle/service/ipc_helpers.h
@@ -151,8 +151,8 @@ public:
151 if (manager->IsDomain()) { 151 if (manager->IsDomain()) {
152 context->AddDomainObject(std::move(iface)); 152 context->AddDomainObject(std::move(iface));
153 } else { 153 } else {
154 kernel.ApplicationProcess()->GetResourceLimit()->Reserve( 154 ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve(
155 Kernel::LimitableResource::SessionCountMax, 1); 155 Kernel::LimitableResource::SessionCountMax, 1));
156 156
157 auto* session = Kernel::KSession::Create(kernel); 157 auto* session = Kernel::KSession::Create(kernel);
158 session->Initialize(nullptr, 0); 158 session->Initialize(nullptr, 0);
diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp
index 65851fc05..77aa6d7d1 100644
--- a/src/core/hle/service/jit/jit.cpp
+++ b/src/core/hle/service/jit/jit.cpp
@@ -4,11 +4,11 @@
4#include "core/arm/debug.h" 4#include "core/arm/debug.h"
5#include "core/arm/symbols.h" 5#include "core/arm/symbols.h"
6#include "core/core.h" 6#include "core/core.h"
7#include "core/hle/kernel/k_code_memory.h"
8#include "core/hle/kernel/k_transfer_memory.h" 7#include "core/hle/kernel/k_transfer_memory.h"
9#include "core/hle/result.h" 8#include "core/hle/result.h"
10#include "core/hle/service/ipc_helpers.h" 9#include "core/hle/service/ipc_helpers.h"
11#include "core/hle/service/jit/jit.h" 10#include "core/hle/service/jit/jit.h"
11#include "core/hle/service/jit/jit_code_memory.h"
12#include "core/hle/service/jit/jit_context.h" 12#include "core/hle/service/jit/jit_context.h"
13#include "core/hle/service/server_manager.h" 13#include "core/hle/service/server_manager.h"
14#include "core/hle/service/service.h" 14#include "core/hle/service/service.h"
@@ -23,9 +23,11 @@ struct CodeRange {
23 23
24class IJitEnvironment final : public ServiceFramework<IJitEnvironment> { 24class IJitEnvironment final : public ServiceFramework<IJitEnvironment> {
25public: 25public:
26 explicit IJitEnvironment(Core::System& system_, Kernel::KProcess& process_, CodeRange user_rx, 26 explicit IJitEnvironment(Core::System& system_,
27 CodeRange user_ro) 27 Kernel::KScopedAutoObject<Kernel::KProcess>&& process_,
28 : ServiceFramework{system_, "IJitEnvironment"}, process{&process_}, 28 CodeMemory&& user_rx_, CodeMemory&& user_ro_)
29 : ServiceFramework{system_, "IJitEnvironment"}, process{std::move(process_)},
30 user_rx{std::move(user_rx_)}, user_ro{std::move(user_ro_)},
29 context{system_.ApplicationMemory()} { 31 context{system_.ApplicationMemory()} {
30 // clang-format off 32 // clang-format off
31 static const FunctionInfo functions[] = { 33 static const FunctionInfo functions[] = {
@@ -39,10 +41,13 @@ public:
39 RegisterHandlers(functions); 41 RegisterHandlers(functions);
40 42
41 // Identity map user code range into sysmodule context 43 // Identity map user code range into sysmodule context
42 configuration.user_ro_memory = user_ro; 44 configuration.user_rx_memory.size = user_rx.GetSize();
43 configuration.user_rx_memory = user_rx; 45 configuration.user_rx_memory.offset = user_rx.GetAddress();
44 configuration.sys_ro_memory = user_ro; 46 configuration.user_ro_memory.size = user_ro.GetSize();
45 configuration.sys_rx_memory = user_rx; 47 configuration.user_ro_memory.offset = user_ro.GetAddress();
48
49 configuration.sys_rx_memory = configuration.user_rx_memory;
50 configuration.sys_ro_memory = configuration.user_ro_memory;
46 } 51 }
47 52
48 void GenerateCode(HLERequestContext& ctx) { 53 void GenerateCode(HLERequestContext& ctx) {
@@ -188,7 +193,7 @@ public:
188 return; 193 return;
189 } 194 }
190 195
191 auto tmem{process->GetHandleTable().GetObject<Kernel::KTransferMemory>(tmem_handle)}; 196 auto tmem{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(tmem_handle)};
192 if (tmem.IsNull()) { 197 if (tmem.IsNull()) {
193 LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle"); 198 LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle");
194 IPC::ResponseBuilder rb{ctx, 2}; 199 IPC::ResponseBuilder rb{ctx, 2};
@@ -318,6 +323,8 @@ private:
318 } 323 }
319 324
320 Kernel::KScopedAutoObject<Kernel::KProcess> process; 325 Kernel::KScopedAutoObject<Kernel::KProcess> process;
326 CodeMemory user_rx;
327 CodeMemory user_ro;
321 GuestCallbacks callbacks; 328 GuestCallbacks callbacks;
322 JITConfiguration configuration; 329 JITConfiguration configuration;
323 JITContext context; 330 JITContext context;
@@ -335,6 +342,7 @@ public:
335 RegisterHandlers(functions); 342 RegisterHandlers(functions);
336 } 343 }
337 344
345private:
338 void CreateJitEnvironment(HLERequestContext& ctx) { 346 void CreateJitEnvironment(HLERequestContext& ctx) {
339 LOG_DEBUG(Service_JIT, "called"); 347 LOG_DEBUG(Service_JIT, "called");
340 348
@@ -356,11 +364,7 @@ public:
356 return; 364 return;
357 } 365 }
358 366
359 // Fetch using the handle table for the application process here, 367 auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle)};
360 // since we are not multiprocess yet.
361 const auto& handle_table{system.ApplicationProcess()->GetHandleTable()};
362
363 auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)};
364 if (process.IsNull()) { 368 if (process.IsNull()) {
365 LOG_ERROR(Service_JIT, "process is null for handle=0x{:08X}", process_handle); 369 LOG_ERROR(Service_JIT, "process is null for handle=0x{:08X}", process_handle);
366 IPC::ResponseBuilder rb{ctx, 2}; 370 IPC::ResponseBuilder rb{ctx, 2};
@@ -368,7 +372,7 @@ public:
368 return; 372 return;
369 } 373 }
370 374
371 auto rx_mem{handle_table.GetObject<Kernel::KCodeMemory>(rx_mem_handle)}; 375 auto rx_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(rx_mem_handle)};
372 if (rx_mem.IsNull()) { 376 if (rx_mem.IsNull()) {
373 LOG_ERROR(Service_JIT, "rx_mem is null for handle=0x{:08X}", rx_mem_handle); 377 LOG_ERROR(Service_JIT, "rx_mem is null for handle=0x{:08X}", rx_mem_handle);
374 IPC::ResponseBuilder rb{ctx, 2}; 378 IPC::ResponseBuilder rb{ctx, 2};
@@ -376,7 +380,7 @@ public:
376 return; 380 return;
377 } 381 }
378 382
379 auto ro_mem{handle_table.GetObject<Kernel::KCodeMemory>(ro_mem_handle)}; 383 auto ro_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(ro_mem_handle)};
380 if (ro_mem.IsNull()) { 384 if (ro_mem.IsNull()) {
381 LOG_ERROR(Service_JIT, "ro_mem is null for handle=0x{:08X}", ro_mem_handle); 385 LOG_ERROR(Service_JIT, "ro_mem is null for handle=0x{:08X}", ro_mem_handle);
382 IPC::ResponseBuilder rb{ctx, 2}; 386 IPC::ResponseBuilder rb{ctx, 2};
@@ -384,20 +388,35 @@ public:
384 return; 388 return;
385 } 389 }
386 390
387 const CodeRange user_rx{ 391 CodeMemory rx, ro;
388 .offset = GetInteger(rx_mem->GetSourceAddress()), 392 Result res;
389 .size = parameters.rx_size,
390 };
391 393
392 const CodeRange user_ro{ 394 res = rx.Initialize(*process, *rx_mem, parameters.rx_size,
393 .offset = GetInteger(ro_mem->GetSourceAddress()), 395 Kernel::Svc::MemoryPermission::ReadExecute, generate_random);
394 .size = parameters.ro_size, 396 if (R_FAILED(res)) {
395 }; 397 LOG_ERROR(Service_JIT, "rx_mem could not be mapped for handle=0x{:08X}", rx_mem_handle);
398 IPC::ResponseBuilder rb{ctx, 2};
399 rb.Push(res);
400 return;
401 }
402
403 res = ro.Initialize(*process, *ro_mem, parameters.ro_size,
404 Kernel::Svc::MemoryPermission::Read, generate_random);
405 if (R_FAILED(res)) {
406 LOG_ERROR(Service_JIT, "ro_mem could not be mapped for handle=0x{:08X}", ro_mem_handle);
407 IPC::ResponseBuilder rb{ctx, 2};
408 rb.Push(res);
409 return;
410 }
396 411
397 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 412 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
398 rb.Push(ResultSuccess); 413 rb.Push(ResultSuccess);
399 rb.PushIpcInterface<IJitEnvironment>(system, *process, user_rx, user_ro); 414 rb.PushIpcInterface<IJitEnvironment>(system, std::move(process), std::move(rx),
415 std::move(ro));
400 } 416 }
417
418private:
419 std::mt19937_64 generate_random{};
401}; 420};
402 421
403void LoopProcess(Core::System& system) { 422void LoopProcess(Core::System& system) {
diff --git a/src/core/hle/service/jit/jit_code_memory.cpp b/src/core/hle/service/jit/jit_code_memory.cpp
new file mode 100644
index 000000000..2b480488a
--- /dev/null
+++ b/src/core/hle/service/jit/jit_code_memory.cpp
@@ -0,0 +1,54 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/jit/jit_code_memory.h"
5
6namespace Service::JIT {
7
8Result CodeMemory::Initialize(Kernel::KProcess& process, Kernel::KCodeMemory& code_memory,
9 size_t size, Kernel::Svc::MemoryPermission perm,
10 std::mt19937_64& generate_random) {
11 auto& page_table = process.GetPageTable();
12 const u64 alias_code_start =
13 GetInteger(page_table.GetAliasCodeRegionStart()) / Kernel::PageSize;
14 const u64 alias_code_size = page_table.GetAliasCodeRegionSize() / Kernel::PageSize;
15
16 // NOTE: This will retry indefinitely until mapping the code memory succeeds.
17 while (true) {
18 // Generate a new trial address.
19 const u64 mapped_address =
20 (alias_code_start + (generate_random() % alias_code_size)) * Kernel::PageSize;
21
22 // Try to map the address
23 R_TRY_CATCH(code_memory.MapToOwner(mapped_address, size, perm)) {
24 R_CATCH(Kernel::ResultInvalidMemoryRegion) {
25 // If we could not map here, retry.
26 continue;
27 }
28 }
29 R_END_TRY_CATCH;
30
31 // Set members.
32 m_code_memory = std::addressof(code_memory);
33 m_size = size;
34 m_address = mapped_address;
35 m_perm = perm;
36
37 // Open a new reference to the code memory.
38 m_code_memory->Open();
39
40 // We succeeded.
41 R_SUCCEED();
42 }
43}
44
45void CodeMemory::Finalize() {
46 if (m_code_memory) {
47 R_ASSERT(m_code_memory->UnmapFromOwner(m_address, m_size));
48 m_code_memory->Close();
49 }
50
51 m_code_memory = nullptr;
52}
53
54} // namespace Service::JIT
diff --git a/src/core/hle/service/jit/jit_code_memory.h b/src/core/hle/service/jit/jit_code_memory.h
new file mode 100644
index 000000000..6376d4c4e
--- /dev/null
+++ b/src/core/hle/service/jit/jit_code_memory.h
@@ -0,0 +1,49 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <random>
7
8#include "core/hle/kernel/k_code_memory.h"
9
10namespace Service::JIT {
11
12class CodeMemory {
13public:
14 YUZU_NON_COPYABLE(CodeMemory);
15
16 explicit CodeMemory() = default;
17
18 CodeMemory(CodeMemory&& rhs) {
19 std::swap(m_code_memory, rhs.m_code_memory);
20 std::swap(m_size, rhs.m_size);
21 std::swap(m_address, rhs.m_address);
22 std::swap(m_perm, rhs.m_perm);
23 }
24
25 ~CodeMemory() {
26 this->Finalize();
27 }
28
29public:
30 Result Initialize(Kernel::KProcess& process, Kernel::KCodeMemory& code_memory, size_t size,
31 Kernel::Svc::MemoryPermission perm, std::mt19937_64& generate_random);
32 void Finalize();
33
34 size_t GetSize() const {
35 return m_size;
36 }
37
38 u64 GetAddress() const {
39 return m_address;
40 }
41
42private:
43 Kernel::KCodeMemory* m_code_memory{};
44 size_t m_size{};
45 u64 m_address{};
46 Kernel::Svc::MemoryPermission m_perm{};
47};
48
49} // namespace Service::JIT
diff --git a/src/core/hle/service/ro/ro.cpp b/src/core/hle/service/ro/ro.cpp
index 17110d3f1..f0658bb5d 100644
--- a/src/core/hle/service/ro/ro.cpp
+++ b/src/core/hle/service/ro/ro.cpp
@@ -651,10 +651,9 @@ private:
651 void RegisterProcessHandle(HLERequestContext& ctx) { 651 void RegisterProcessHandle(HLERequestContext& ctx) {
652 LOG_DEBUG(Service_LDR, "(called)"); 652 LOG_DEBUG(Service_LDR, "(called)");
653 653
654 auto process_h = ctx.GetClientHandleTable().GetObject(ctx.GetCopyHandle(0)); 654 auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0));
655 auto client_pid = ctx.GetPID(); 655 auto client_pid = ctx.GetPID();
656 auto result = interface.RegisterProcessHandle(client_pid, 656 auto result = interface.RegisterProcessHandle(client_pid, process.GetPointerUnsafe());
657 process_h->DynamicCast<Kernel::KProcess*>());
658 657
659 IPC::ResponseBuilder rb{ctx, 2}; 658 IPC::ResponseBuilder rb{ctx, 2};
660 rb.Push(result); 659 rb.Push(result);
@@ -671,12 +670,11 @@ private:
671 670
672 IPC::RequestParser rp{ctx}; 671 IPC::RequestParser rp{ctx};
673 auto params = rp.PopRaw<InputParameters>(); 672 auto params = rp.PopRaw<InputParameters>();
674 auto process_h = ctx.GetClientHandleTable().GetObject(ctx.GetCopyHandle(0)); 673 auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0));
675 674
676 auto client_pid = ctx.GetPID(); 675 auto client_pid = ctx.GetPID();
677 auto result = 676 auto result = interface.RegisterProcessModuleInfo(
678 interface.RegisterProcessModuleInfo(client_pid, params.nrr_address, params.nrr_size, 677 client_pid, params.nrr_address, params.nrr_size, process.GetPointerUnsafe());
679 process_h->DynamicCast<Kernel::KProcess*>());
680 678
681 IPC::ResponseBuilder rb{ctx, 2}; 679 IPC::ResponseBuilder rb{ctx, 2};
682 rb.Push(result); 680 rb.Push(result);
diff --git a/src/core/hle/service/server_manager.cpp b/src/core/hle/service/server_manager.cpp
index 6808247a9..15edb23e0 100644
--- a/src/core/hle/service/server_manager.cpp
+++ b/src/core/hle/service/server_manager.cpp
@@ -47,7 +47,7 @@ ServerManager::~ServerManager() {
47 m_stopped.Wait(); 47 m_stopped.Wait();
48 m_threads.clear(); 48 m_threads.clear();
49 49
50 // Clean up ports. 50 // Clean up server ports.
51 for (const auto& [port, handler] : m_ports) { 51 for (const auto& [port, handler] : m_ports) {
52 port->Close(); 52 port->Close();
53 } 53 }
@@ -97,22 +97,15 @@ Result ServerManager::RegisterNamedService(const std::string& service_name,
97 u32 max_sessions) { 97 u32 max_sessions) {
98 ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects); 98 ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects);
99 99
100 // Add the new server to sm:. 100 // Add the new server to sm: and get the moved server port.
101 ASSERT(R_SUCCEEDED( 101 Kernel::KServerPort* server_port{};
102 m_system.ServiceManager().RegisterService(service_name, max_sessions, handler_factory))); 102 R_ASSERT(m_system.ServiceManager().RegisterService(std::addressof(server_port), service_name,
103 103 max_sessions, handler_factory));
104 // Get the registered port.
105 Kernel::KPort* port{};
106 ASSERT(
107 R_SUCCEEDED(m_system.ServiceManager().GetServicePort(std::addressof(port), service_name)));
108
109 // Open a new reference to the server port.
110 port->GetServerPort().Open();
111 104
112 // Begin tracking the server port. 105 // Begin tracking the server port.
113 { 106 {
114 std::scoped_lock ll{m_list_mutex}; 107 std::scoped_lock ll{m_list_mutex};
115 m_ports.emplace(std::addressof(port->GetServerPort()), std::move(handler_factory)); 108 m_ports.emplace(server_port, std::move(handler_factory));
116 } 109 }
117 110
118 // Signal the wakeup event. 111 // Signal the wakeup event.
@@ -372,7 +365,7 @@ Result ServerManager::OnSessionEvent(Kernel::KServerSession* session,
372 365
373 // Try to receive a message. 366 // Try to receive a message.
374 std::shared_ptr<HLERequestContext> context; 367 std::shared_ptr<HLERequestContext> context;
375 rc = session->ReceiveRequest(&context, manager); 368 rc = session->ReceiveRequestHLE(&context, manager);
376 369
377 // If the session has been closed, we're done. 370 // If the session has been closed, we're done.
378 if (rc == Kernel::ResultSessionClosed) { 371 if (rc == Kernel::ResultSessionClosed) {
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 00531b021..39124c5fd 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -203,7 +203,7 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
203 // If emulation was shutdown, we are closing service threads, do not write the response back to 203 // If emulation was shutdown, we are closing service threads, do not write the response back to
204 // memory that may be shutting down as well. 204 // memory that may be shutting down as well.
205 if (system.IsPoweredOn()) { 205 if (system.IsPoweredOn()) {
206 ctx.WriteToOutgoingCommandBuffer(ctx.GetThread()); 206 ctx.WriteToOutgoingCommandBuffer();
207 } 207 }
208 208
209 return result; 209 return result;
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index 0653779d5..8e637f963 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -507,6 +507,14 @@ void SET_SYS::SetTvSettings(HLERequestContext& ctx) {
507 rb.Push(ResultSuccess); 507 rb.Push(ResultSuccess);
508} 508}
509 509
510void SET_SYS::GetDebugModeFlag(HLERequestContext& ctx) {
511 LOG_DEBUG(Service_SET, "called");
512
513 IPC::ResponseBuilder rb{ctx, 3};
514 rb.Push(ResultSuccess);
515 rb.Push<u32>(0);
516}
517
510void SET_SYS::GetQuestFlag(HLERequestContext& ctx) { 518void SET_SYS::GetQuestFlag(HLERequestContext& ctx) {
511 LOG_WARNING(Service_SET, "(STUBBED) called"); 519 LOG_WARNING(Service_SET, "(STUBBED) called");
512 520
@@ -926,7 +934,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"},
926 {59, &SET_SYS::SetNetworkSystemClockContext, "SetNetworkSystemClockContext"}, 934 {59, &SET_SYS::SetNetworkSystemClockContext, "SetNetworkSystemClockContext"},
927 {60, &SET_SYS::IsUserSystemClockAutomaticCorrectionEnabled, "IsUserSystemClockAutomaticCorrectionEnabled"}, 935 {60, &SET_SYS::IsUserSystemClockAutomaticCorrectionEnabled, "IsUserSystemClockAutomaticCorrectionEnabled"},
928 {61, &SET_SYS::SetUserSystemClockAutomaticCorrectionEnabled, "SetUserSystemClockAutomaticCorrectionEnabled"}, 936 {61, &SET_SYS::SetUserSystemClockAutomaticCorrectionEnabled, "SetUserSystemClockAutomaticCorrectionEnabled"},
929 {62, nullptr, "GetDebugModeFlag"}, 937 {62, &SET_SYS::GetDebugModeFlag, "GetDebugModeFlag"},
930 {63, &SET_SYS::GetPrimaryAlbumStorage, "GetPrimaryAlbumStorage"}, 938 {63, &SET_SYS::GetPrimaryAlbumStorage, "GetPrimaryAlbumStorage"},
931 {64, nullptr, "SetPrimaryAlbumStorage"}, 939 {64, nullptr, "SetPrimaryAlbumStorage"},
932 {65, nullptr, "GetUsb30EnableFlag"}, 940 {65, nullptr, "GetUsb30EnableFlag"},
@@ -1143,6 +1151,8 @@ void SET_SYS::StoreSettings() {
1143} 1151}
1144 1152
1145void SET_SYS::StoreSettingsThreadFunc(std::stop_token stop_token) { 1153void SET_SYS::StoreSettingsThreadFunc(std::stop_token stop_token) {
1154 Common::SetCurrentThreadName("SettingsStore");
1155
1146 while (Common::StoppableTimedWait(stop_token, std::chrono::minutes(1))) { 1156 while (Common::StoppableTimedWait(stop_token, std::chrono::minutes(1))) {
1147 std::scoped_lock l{m_save_needed_mutex}; 1157 std::scoped_lock l{m_save_needed_mutex};
1148 if (!std::exchange(m_save_needed, false)) { 1158 if (!std::exchange(m_save_needed, false)) {
diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h
index 3785d93d8..853f76fce 100644
--- a/src/core/hle/service/set/set_sys.h
+++ b/src/core/hle/service/set/set_sys.h
@@ -98,6 +98,7 @@ private:
98 void GetSettingsItemValue(HLERequestContext& ctx); 98 void GetSettingsItemValue(HLERequestContext& ctx);
99 void GetTvSettings(HLERequestContext& ctx); 99 void GetTvSettings(HLERequestContext& ctx);
100 void SetTvSettings(HLERequestContext& ctx); 100 void SetTvSettings(HLERequestContext& ctx);
101 void GetDebugModeFlag(HLERequestContext& ctx);
101 void GetQuestFlag(HLERequestContext& ctx); 102 void GetQuestFlag(HLERequestContext& ctx);
102 void GetDeviceTimeZoneLocationName(HLERequestContext& ctx); 103 void GetDeviceTimeZoneLocationName(HLERequestContext& ctx);
103 void SetDeviceTimeZoneLocationName(HLERequestContext& ctx); 104 void SetDeviceTimeZoneLocationName(HLERequestContext& ctx);
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 296ee6e89..1095dcf6c 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -29,8 +29,7 @@ ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {
29 29
30ServiceManager::~ServiceManager() { 30ServiceManager::~ServiceManager() {
31 for (auto& [name, port] : service_ports) { 31 for (auto& [name, port] : service_ports) {
32 port->GetClientPort().Close(); 32 port->Close();
33 port->GetServerPort().Close();
34 } 33 }
35 34
36 if (deferral_event) { 35 if (deferral_event) {
@@ -50,8 +49,8 @@ static Result ValidateServiceName(const std::string& name) {
50 return ResultSuccess; 49 return ResultSuccess;
51} 50}
52 51
53Result ServiceManager::RegisterService(std::string name, u32 max_sessions, 52Result ServiceManager::RegisterService(Kernel::KServerPort** out_server_port, std::string name,
54 SessionRequestHandlerFactory handler) { 53 u32 max_sessions, SessionRequestHandlerFactory handler) {
55 R_TRY(ValidateServiceName(name)); 54 R_TRY(ValidateServiceName(name));
56 55
57 std::scoped_lock lk{lock}; 56 std::scoped_lock lk{lock};
@@ -66,13 +65,17 @@ Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
66 // Register the port. 65 // Register the port.
67 Kernel::KPort::Register(kernel, port); 66 Kernel::KPort::Register(kernel, port);
68 67
69 service_ports.emplace(name, port); 68 service_ports.emplace(name, std::addressof(port->GetClientPort()));
70 registered_services.emplace(name, handler); 69 registered_services.emplace(name, handler);
71 if (deferral_event) { 70 if (deferral_event) {
72 deferral_event->Signal(); 71 deferral_event->Signal();
73 } 72 }
74 73
75 return ResultSuccess; 74 // Set our output.
75 *out_server_port = std::addressof(port->GetServerPort());
76
77 // We succeeded.
78 R_SUCCEED();
76} 79}
77 80
78Result ServiceManager::UnregisterService(const std::string& name) { 81Result ServiceManager::UnregisterService(const std::string& name) {
@@ -91,7 +94,8 @@ Result ServiceManager::UnregisterService(const std::string& name) {
91 return ResultSuccess; 94 return ResultSuccess;
92} 95}
93 96
94Result ServiceManager::GetServicePort(Kernel::KPort** out_port, const std::string& name) { 97Result ServiceManager::GetServicePort(Kernel::KClientPort** out_client_port,
98 const std::string& name) {
95 R_TRY(ValidateServiceName(name)); 99 R_TRY(ValidateServiceName(name));
96 100
97 std::scoped_lock lk{lock}; 101 std::scoped_lock lk{lock};
@@ -101,7 +105,7 @@ Result ServiceManager::GetServicePort(Kernel::KPort** out_port, const std::strin
101 return Service::SM::ResultNotRegistered; 105 return Service::SM::ResultNotRegistered;
102 } 106 }
103 107
104 *out_port = it->second; 108 *out_client_port = it->second;
105 return ResultSuccess; 109 return ResultSuccess;
106} 110}
107 111
@@ -172,8 +176,8 @@ Result SM::GetServiceImpl(Kernel::KClientSession** out_client_session, HLEReques
172 std::string name(PopServiceName(rp)); 176 std::string name(PopServiceName(rp));
173 177
174 // Find the named port. 178 // Find the named port.
175 Kernel::KPort* port{}; 179 Kernel::KClientPort* client_port{};
176 auto port_result = service_manager.GetServicePort(&port, name); 180 auto port_result = service_manager.GetServicePort(&client_port, name);
177 if (port_result == Service::SM::ResultInvalidServiceName) { 181 if (port_result == Service::SM::ResultInvalidServiceName) {
178 LOG_ERROR(Service_SM, "Invalid service name '{}'", name); 182 LOG_ERROR(Service_SM, "Invalid service name '{}'", name);
179 return Service::SM::ResultInvalidServiceName; 183 return Service::SM::ResultInvalidServiceName;
@@ -187,7 +191,7 @@ Result SM::GetServiceImpl(Kernel::KClientSession** out_client_session, HLEReques
187 191
188 // Create a new session. 192 // Create a new session.
189 Kernel::KClientSession* session{}; 193 Kernel::KClientSession* session{};
190 if (const auto result = port->GetClientPort().CreateSession(&session); result.IsError()) { 194 if (const auto result = client_port->CreateSession(&session); result.IsError()) {
191 LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw); 195 LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw);
192 return result; 196 return result;
193 } 197 }
@@ -221,7 +225,9 @@ void SM::RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_s
221 LOG_DEBUG(Service_SM, "called with name={}, max_session_count={}, is_light={}", name, 225 LOG_DEBUG(Service_SM, "called with name={}, max_session_count={}, is_light={}", name,
222 max_session_count, is_light); 226 max_session_count, is_light);
223 227
224 if (const auto result = service_manager.RegisterService(name, max_session_count, nullptr); 228 Kernel::KServerPort* server_port{};
229 if (const auto result = service_manager.RegisterService(std::addressof(server_port), name,
230 max_session_count, nullptr);
225 result.IsError()) { 231 result.IsError()) {
226 LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", result.raw); 232 LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", result.raw);
227 IPC::ResponseBuilder rb{ctx, 2}; 233 IPC::ResponseBuilder rb{ctx, 2};
@@ -229,13 +235,9 @@ void SM::RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_s
229 return; 235 return;
230 } 236 }
231 237
232 auto* port = Kernel::KPort::Create(kernel);
233 port->Initialize(ServerSessionCountMax, is_light, 0);
234 SCOPE_EXIT({ port->GetClientPort().Close(); });
235
236 IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; 238 IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
237 rb.Push(ResultSuccess); 239 rb.Push(ResultSuccess);
238 rb.PushMoveObjects(port->GetServerPort()); 240 rb.PushMoveObjects(server_port);
239} 241}
240 242
241void SM::UnregisterService(HLERequestContext& ctx) { 243void SM::UnregisterService(HLERequestContext& ctx) {
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index ff74f588a..4ae32a9c1 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -56,10 +56,10 @@ public:
56 explicit ServiceManager(Kernel::KernelCore& kernel_); 56 explicit ServiceManager(Kernel::KernelCore& kernel_);
57 ~ServiceManager(); 57 ~ServiceManager();
58 58
59 Result RegisterService(std::string name, u32 max_sessions, 59 Result RegisterService(Kernel::KServerPort** out_server_port, std::string name,
60 SessionRequestHandlerFactory handler_factory); 60 u32 max_sessions, SessionRequestHandlerFactory handler_factory);
61 Result UnregisterService(const std::string& name); 61 Result UnregisterService(const std::string& name);
62 Result GetServicePort(Kernel::KPort** out_port, const std::string& name); 62 Result GetServicePort(Kernel::KClientPort** out_client_port, const std::string& name);
63 63
64 template <Common::DerivedFrom<SessionRequestHandler> T> 64 template <Common::DerivedFrom<SessionRequestHandler> T>
65 std::shared_ptr<T> GetService(const std::string& service_name) const { 65 std::shared_ptr<T> GetService(const std::string& service_name) const {
@@ -84,7 +84,7 @@ private:
84 /// Map of registered services, retrieved using GetServicePort. 84 /// Map of registered services, retrieved using GetServicePort.
85 std::mutex lock; 85 std::mutex lock;
86 std::unordered_map<std::string, SessionRequestHandlerFactory> registered_services; 86 std::unordered_map<std::string, SessionRequestHandlerFactory> registered_services;
87 std::unordered_map<std::string, Kernel::KPort*> service_ports; 87 std::unordered_map<std::string, Kernel::KClientPort*> service_ports;
88 88
89 /// Kernel context 89 /// Kernel context
90 Kernel::KernelCore& kernel; 90 Kernel::KernelCore& kernel;
diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp
index 7dce28fe0..7f0fb91d0 100644
--- a/src/core/hle/service/sm/sm_controller.cpp
+++ b/src/core/hle/service/sm/sm_controller.cpp
@@ -28,7 +28,6 @@ void Controller::ConvertCurrentObjectToDomain(HLERequestContext& ctx) {
28void Controller::CloneCurrentObject(HLERequestContext& ctx) { 28void Controller::CloneCurrentObject(HLERequestContext& ctx) {
29 LOG_DEBUG(Service, "called"); 29 LOG_DEBUG(Service, "called");
30 30
31 auto& process = *ctx.GetThread().GetOwnerProcess();
32 auto session_manager = ctx.GetManager(); 31 auto session_manager = ctx.GetManager();
33 32
34 // FIXME: this is duplicated from the SVC, it should just call it instead 33 // FIXME: this is duplicated from the SVC, it should just call it instead
@@ -36,11 +35,11 @@ void Controller::CloneCurrentObject(HLERequestContext& ctx) {
36 35
37 // Reserve a new session from the process resource limit. 36 // Reserve a new session from the process resource limit.
38 Kernel::KScopedResourceReservation session_reservation( 37 Kernel::KScopedResourceReservation session_reservation(
39 &process, Kernel::LimitableResource::SessionCountMax); 38 Kernel::GetCurrentProcessPointer(kernel), Kernel::LimitableResource::SessionCountMax);
40 ASSERT(session_reservation.Succeeded()); 39 ASSERT(session_reservation.Succeeded());
41 40
42 // Create the session. 41 // Create the session.
43 Kernel::KSession* session = Kernel::KSession::Create(system.Kernel()); 42 Kernel::KSession* session = Kernel::KSession::Create(kernel);
44 ASSERT(session != nullptr); 43 ASSERT(session != nullptr);
45 44
46 // Initialize the session. 45 // Initialize the session.
@@ -50,7 +49,7 @@ void Controller::CloneCurrentObject(HLERequestContext& ctx) {
50 session_reservation.Commit(); 49 session_reservation.Commit();
51 50
52 // Register the session. 51 // Register the session.
53 Kernel::KSession::Register(system.Kernel(), session); 52 Kernel::KSession::Register(kernel, session);
54 53
55 // Register with server manager. 54 // Register with server manager.
56 session_manager->GetServerManager().RegisterSession(&session->GetServerSession(), 55 session_manager->GetServerManager().RegisterSession(&session->GetServerSession(),
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 60ee78e89..c9f8707b7 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -129,9 +129,10 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
129 } 129 }
130 metadata.Print(); 130 metadata.Print();
131 131
132 // Enable NCE only for programs with 39-bit address space. 132 // Enable NCE only for applications with 39-bit address space.
133 const bool is_39bit = 133 const bool is_39bit =
134 metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is39Bit; 134 metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is39Bit;
135 const bool is_application = metadata.GetPoolPartition() == FileSys::PoolPartition::Application;
135 Settings::SetNceEnabled(is_39bit); 136 Settings::SetNceEnabled(is_39bit);
136 137
137 const std::array static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", 138 const std::array static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2",
@@ -147,7 +148,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
147 148
148 const auto GetPatcher = [&](size_t i) -> Core::NCE::Patcher* { 149 const auto GetPatcher = [&](size_t i) -> Core::NCE::Patcher* {
149#ifdef HAS_NCE 150#ifdef HAS_NCE
150 if (Settings::IsNceEnabled()) { 151 if (is_application && Settings::IsNceEnabled()) {
151 return &module_patchers[i]; 152 return &module_patchers[i];
152 } 153 }
153#endif 154#endif
@@ -175,7 +176,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
175 176
176 // Enable direct memory mapping in case of NCE. 177 // Enable direct memory mapping in case of NCE.
177 const u64 fastmem_base = [&]() -> size_t { 178 const u64 fastmem_base = [&]() -> size_t {
178 if (Settings::IsNceEnabled()) { 179 if (is_application && Settings::IsNceEnabled()) {
179 auto& buffer = system.DeviceMemory().buffer; 180 auto& buffer = system.DeviceMemory().buffer;
180 buffer.EnableDirectMappedAddress(); 181 buffer.EnableDirectMappedAddress();
181 return reinterpret_cast<u64>(buffer.VirtualBasePointer()); 182 return reinterpret_cast<u64>(buffer.VirtualBasePointer());
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 169bf4c8c..8176a41be 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -10,6 +10,7 @@
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/atomic_ops.h" 11#include "common/atomic_ops.h"
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/heap_tracker.h"
13#include "common/logging/log.h" 14#include "common/logging/log.h"
14#include "common/page_table.h" 15#include "common/page_table.h"
15#include "common/scope_exit.h" 16#include "common/scope_exit.h"
@@ -45,11 +46,25 @@ struct Memory::Impl {
45 46
46 void SetCurrentPageTable(Kernel::KProcess& process) { 47 void SetCurrentPageTable(Kernel::KProcess& process) {
47 current_page_table = &process.GetPageTable().GetImpl(); 48 current_page_table = &process.GetPageTable().GetImpl();
48 current_page_table->fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer(); 49
50 if (std::addressof(process) == system.ApplicationProcess() &&
51 Settings::IsFastmemEnabled()) {
52 current_page_table->fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer();
53 } else {
54 current_page_table->fastmem_arena = nullptr;
55 }
56
57#ifdef __linux__
58 heap_tracker.emplace(system.DeviceMemory().buffer);
59 buffer = std::addressof(*heap_tracker);
60#else
61 buffer = std::addressof(system.DeviceMemory().buffer);
62#endif
49 } 63 }
50 64
51 void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, 65 void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
52 Common::PhysicalAddress target, Common::MemoryPermission perms) { 66 Common::PhysicalAddress target, Common::MemoryPermission perms,
67 bool separate_heap) {
53 ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size); 68 ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
54 ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base)); 69 ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base));
55 ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", 70 ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}",
@@ -57,20 +72,21 @@ struct Memory::Impl {
57 MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, target, 72 MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, target,
58 Common::PageType::Memory); 73 Common::PageType::Memory);
59 74
60 if (Settings::IsFastmemEnabled()) { 75 if (current_page_table->fastmem_arena) {
61 system.DeviceMemory().buffer.Map(GetInteger(base), 76 buffer->Map(GetInteger(base), GetInteger(target) - DramMemoryMap::Base, size, perms,
62 GetInteger(target) - DramMemoryMap::Base, size, perms); 77 separate_heap);
63 } 78 }
64 } 79 }
65 80
66 void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size) { 81 void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
82 bool separate_heap) {
67 ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size); 83 ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
68 ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base)); 84 ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base));
69 MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, 0, 85 MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, 0,
70 Common::PageType::Unmapped); 86 Common::PageType::Unmapped);
71 87
72 if (Settings::IsFastmemEnabled()) { 88 if (current_page_table->fastmem_arena) {
73 system.DeviceMemory().buffer.Unmap(GetInteger(base), size); 89 buffer->Unmap(GetInteger(base), size, separate_heap);
74 } 90 }
75 } 91 }
76 92
@@ -79,17 +95,7 @@ struct Memory::Impl {
79 ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size); 95 ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
80 ASSERT_MSG((vaddr & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", vaddr); 96 ASSERT_MSG((vaddr & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", vaddr);
81 97
82 if (!Settings::IsFastmemEnabled()) { 98 if (!current_page_table->fastmem_arena) {
83 return;
84 }
85
86 const bool is_r = True(perms & Common::MemoryPermission::Read);
87 const bool is_w = True(perms & Common::MemoryPermission::Write);
88 const bool is_x =
89 True(perms & Common::MemoryPermission::Execute) && Settings::IsNceEnabled();
90
91 if (!current_page_table) {
92 system.DeviceMemory().buffer.Protect(vaddr, size, is_r, is_w, is_x);
93 return; 99 return;
94 } 100 }
95 101
@@ -101,8 +107,7 @@ struct Memory::Impl {
101 switch (page_type) { 107 switch (page_type) {
102 case Common::PageType::RasterizerCachedMemory: 108 case Common::PageType::RasterizerCachedMemory:
103 if (protect_bytes > 0) { 109 if (protect_bytes > 0) {
104 system.DeviceMemory().buffer.Protect(protect_begin, protect_bytes, is_r, is_w, 110 buffer->Protect(protect_begin, protect_bytes, perms);
105 is_x);
106 protect_bytes = 0; 111 protect_bytes = 0;
107 } 112 }
108 break; 113 break;
@@ -115,7 +120,7 @@ struct Memory::Impl {
115 } 120 }
116 121
117 if (protect_bytes > 0) { 122 if (protect_bytes > 0) {
118 system.DeviceMemory().buffer.Protect(protect_begin, protect_bytes, is_r, is_w, is_x); 123 buffer->Protect(protect_begin, protect_bytes, perms);
119 } 124 }
120 } 125 }
121 126
@@ -239,7 +244,7 @@ struct Memory::Impl {
239 244
240 bool WalkBlock(const Common::ProcessAddress addr, const std::size_t size, auto on_unmapped, 245 bool WalkBlock(const Common::ProcessAddress addr, const std::size_t size, auto on_unmapped,
241 auto on_memory, auto on_rasterizer, auto increment) { 246 auto on_memory, auto on_rasterizer, auto increment) {
242 const auto& page_table = system.ApplicationProcess()->GetPageTable().GetImpl(); 247 const auto& page_table = *current_page_table;
243 std::size_t remaining_size = size; 248 std::size_t remaining_size = size;
244 std::size_t page_index = addr >> YUZU_PAGEBITS; 249 std::size_t page_index = addr >> YUZU_PAGEBITS;
245 std::size_t page_offset = addr & YUZU_PAGEMASK; 250 std::size_t page_offset = addr & YUZU_PAGEMASK;
@@ -484,8 +489,10 @@ struct Memory::Impl {
484 return; 489 return;
485 } 490 }
486 491
487 if (Settings::IsFastmemEnabled()) { 492 if (current_page_table->fastmem_arena) {
488 system.DeviceMemory().buffer.Protect(vaddr, size, !debug, !debug); 493 const auto perm{debug ? Common::MemoryPermission{}
494 : Common::MemoryPermission::ReadWrite};
495 buffer->Protect(vaddr, size, perm);
489 } 496 }
490 497
491 // Iterate over a contiguous CPU address space, marking/unmarking the region. 498 // Iterate over a contiguous CPU address space, marking/unmarking the region.
@@ -541,10 +548,15 @@ struct Memory::Impl {
541 return; 548 return;
542 } 549 }
543 550
544 if (Settings::IsFastmemEnabled()) { 551 if (current_page_table->fastmem_arena) {
545 const bool is_read_enable = 552 Common::MemoryPermission perm{};
546 !Settings::values.use_reactive_flushing.GetValue() || !cached; 553 if (!Settings::values.use_reactive_flushing.GetValue() || !cached) {
547 system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached); 554 perm |= Common::MemoryPermission::Read;
555 }
556 if (!cached) {
557 perm |= Common::MemoryPermission::Write;
558 }
559 buffer->Protect(vaddr, size, perm);
548 } 560 }
549 561
550 // Iterate over a contiguous CPU address space, which corresponds to the specified GPU 562 // Iterate over a contiguous CPU address space, which corresponds to the specified GPU
@@ -855,6 +867,13 @@ struct Memory::Impl {
855 std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{}; 867 std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{};
856 std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers; 868 std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers;
857 std::mutex sys_core_guard; 869 std::mutex sys_core_guard;
870
871 std::optional<Common::HeapTracker> heap_tracker;
872#ifdef __linux__
873 Common::HeapTracker* buffer{};
874#else
875 Common::HostMemory* buffer{};
876#endif
858}; 877};
859 878
860Memory::Memory(Core::System& system_) : system{system_} { 879Memory::Memory(Core::System& system_) : system{system_} {
@@ -872,12 +891,14 @@ void Memory::SetCurrentPageTable(Kernel::KProcess& process) {
872} 891}
873 892
874void Memory::MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, 893void Memory::MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
875 Common::PhysicalAddress target, Common::MemoryPermission perms) { 894 Common::PhysicalAddress target, Common::MemoryPermission perms,
876 impl->MapMemoryRegion(page_table, base, size, target, perms); 895 bool separate_heap) {
896 impl->MapMemoryRegion(page_table, base, size, target, perms, separate_heap);
877} 897}
878 898
879void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size) { 899void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
880 impl->UnmapRegion(page_table, base, size); 900 bool separate_heap) {
901 impl->UnmapRegion(page_table, base, size, separate_heap);
881} 902}
882 903
883void Memory::ProtectRegion(Common::PageTable& page_table, Common::ProcessAddress vaddr, u64 size, 904void Memory::ProtectRegion(Common::PageTable& page_table, Common::ProcessAddress vaddr, u64 size,
@@ -886,8 +907,7 @@ void Memory::ProtectRegion(Common::PageTable& page_table, Common::ProcessAddress
886} 907}
887 908
888bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const { 909bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const {
889 const Kernel::KProcess& process = *system.ApplicationProcess(); 910 const auto& page_table = *impl->current_page_table;
890 const auto& page_table = process.GetPageTable().GetImpl();
891 const size_t page = vaddr >> YUZU_PAGEBITS; 911 const size_t page = vaddr >> YUZU_PAGEBITS;
892 if (page >= page_table.pointers.size()) { 912 if (page >= page_table.pointers.size()) {
893 return false; 913 return false;
@@ -1048,7 +1068,9 @@ void Memory::FlushRegion(Common::ProcessAddress dest_addr, size_t size) {
1048} 1068}
1049 1069
1050bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) { 1070bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
1051 bool mapped = true; 1071 [[maybe_unused]] bool mapped = true;
1072 [[maybe_unused]] bool rasterizer = false;
1073
1052 u8* const ptr = impl->GetPointerImpl( 1074 u8* const ptr = impl->GetPointerImpl(
1053 GetInteger(vaddr), 1075 GetInteger(vaddr),
1054 [&] { 1076 [&] {
@@ -1056,8 +1078,26 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
1056 GetInteger(vaddr)); 1078 GetInteger(vaddr));
1057 mapped = false; 1079 mapped = false;
1058 }, 1080 },
1059 [&] { impl->system.GPU().InvalidateRegion(GetInteger(vaddr), size); }); 1081 [&] {
1082 impl->system.GPU().InvalidateRegion(GetInteger(vaddr), size);
1083 rasterizer = true;
1084 });
1085
1086#ifdef __linux__
1087 if (!rasterizer && mapped) {
1088 impl->buffer->DeferredMapSeparateHeap(GetInteger(vaddr));
1089 }
1090#endif
1091
1060 return mapped && ptr != nullptr; 1092 return mapped && ptr != nullptr;
1061} 1093}
1062 1094
1095bool Memory::InvalidateSeparateHeap(void* fault_address) {
1096#ifdef __linux__
1097 return impl->buffer->DeferredMapSeparateHeap(static_cast<u8*>(fault_address));
1098#else
1099 return false;
1100#endif
1101}
1102
1063} // namespace Core::Memory 1103} // namespace Core::Memory
diff --git a/src/core/memory.h b/src/core/memory.h
index c1879e78f..3e4d03f57 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -86,7 +86,8 @@ public:
86 * @param perms The permissions to map the memory with. 86 * @param perms The permissions to map the memory with.
87 */ 87 */
88 void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, 88 void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
89 Common::PhysicalAddress target, Common::MemoryPermission perms); 89 Common::PhysicalAddress target, Common::MemoryPermission perms,
90 bool separate_heap);
90 91
91 /** 92 /**
92 * Unmaps a region of the emulated process address space. 93 * Unmaps a region of the emulated process address space.
@@ -95,7 +96,8 @@ public:
95 * @param base The address to begin unmapping at. 96 * @param base The address to begin unmapping at.
96 * @param size The amount of bytes to unmap. 97 * @param size The amount of bytes to unmap.
97 */ 98 */
98 void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size); 99 void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
100 bool separate_heap);
99 101
100 /** 102 /**
101 * Protects a region of the emulated process address space with the new permissions. 103 * Protects a region of the emulated process address space with the new permissions.
@@ -486,6 +488,7 @@ public:
486 void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers); 488 void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers);
487 void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size); 489 void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size);
488 bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size); 490 bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size);
491 bool InvalidateSeparateHeap(void* fault_address);
489 void FlushRegion(Common::ProcessAddress dest_addr, size_t size); 492 void FlushRegion(Common::ProcessAddress dest_addr, size_t size);
490 493
491private: 494private:
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index ed023fcfe..89ebab08e 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -96,9 +96,9 @@ Id ImageType(EmitContext& ctx, const ImageDescriptor& desc, Id sampled_type) {
96} 96}
97 97
98Id DefineVariable(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin, 98Id DefineVariable(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin,
99 spv::StorageClass storage_class) { 99 spv::StorageClass storage_class, std::optional<Id> initializer = std::nullopt) {
100 const Id pointer_type{ctx.TypePointer(storage_class, type)}; 100 const Id pointer_type{ctx.TypePointer(storage_class, type)};
101 const Id id{ctx.AddGlobalVariable(pointer_type, storage_class)}; 101 const Id id{ctx.AddGlobalVariable(pointer_type, storage_class, initializer)};
102 if (builtin) { 102 if (builtin) {
103 ctx.Decorate(id, spv::Decoration::BuiltIn, *builtin); 103 ctx.Decorate(id, spv::Decoration::BuiltIn, *builtin);
104 } 104 }
@@ -144,11 +144,12 @@ Id DefineInput(EmitContext& ctx, Id type, bool per_invocation,
144} 144}
145 145
146Id DefineOutput(EmitContext& ctx, Id type, std::optional<u32> invocations, 146Id DefineOutput(EmitContext& ctx, Id type, std::optional<u32> invocations,
147 std::optional<spv::BuiltIn> builtin = std::nullopt) { 147 std::optional<spv::BuiltIn> builtin = std::nullopt,
148 std::optional<Id> initializer = std::nullopt) {
148 if (invocations && ctx.stage == Stage::TessellationControl) { 149 if (invocations && ctx.stage == Stage::TessellationControl) {
149 type = ctx.TypeArray(type, ctx.Const(*invocations)); 150 type = ctx.TypeArray(type, ctx.Const(*invocations));
150 } 151 }
151 return DefineVariable(ctx, type, builtin, spv::StorageClass::Output); 152 return DefineVariable(ctx, type, builtin, spv::StorageClass::Output, initializer);
152} 153}
153 154
154void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invocations) { 155void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invocations) {
@@ -811,10 +812,14 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
811 labels.push_back(OpLabel()); 812 labels.push_back(OpLabel());
812 } 813 }
813 if (info.stores.ClipDistances()) { 814 if (info.stores.ClipDistances()) {
814 literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance0) >> 2); 815 if (profile.max_user_clip_distances >= 4) {
815 labels.push_back(OpLabel()); 816 literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance0) >> 2);
816 literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance4) >> 2); 817 labels.push_back(OpLabel());
817 labels.push_back(OpLabel()); 818 }
819 if (profile.max_user_clip_distances >= 8) {
820 literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance4) >> 2);
821 labels.push_back(OpLabel());
822 }
818 } 823 }
819 OpSelectionMerge(end_block, spv::SelectionControlMask::MaskNone); 824 OpSelectionMerge(end_block, spv::SelectionControlMask::MaskNone);
820 OpSwitch(compare_index, default_label, literals, labels); 825 OpSwitch(compare_index, default_label, literals, labels);
@@ -843,17 +848,21 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
843 ++label_index; 848 ++label_index;
844 } 849 }
845 if (info.stores.ClipDistances()) { 850 if (info.stores.ClipDistances()) {
846 AddLabel(labels[label_index]); 851 if (profile.max_user_clip_distances >= 4) {
847 const Id pointer{OpAccessChain(output_f32, clip_distances, masked_index)}; 852 AddLabel(labels[label_index]);
848 OpStore(pointer, store_value); 853 const Id pointer{OpAccessChain(output_f32, clip_distances, masked_index)};
849 OpReturn(); 854 OpStore(pointer, store_value);
850 ++label_index; 855 OpReturn();
851 AddLabel(labels[label_index]); 856 ++label_index;
852 const Id fixed_index{OpIAdd(U32[1], masked_index, Const(4U))}; 857 }
853 const Id pointer2{OpAccessChain(output_f32, clip_distances, fixed_index)}; 858 if (profile.max_user_clip_distances >= 8) {
854 OpStore(pointer2, store_value); 859 AddLabel(labels[label_index]);
855 OpReturn(); 860 const Id fixed_index{OpIAdd(U32[1], masked_index, Const(4U))};
856 ++label_index; 861 const Id pointer{OpAccessChain(output_f32, clip_distances, fixed_index)};
862 OpStore(pointer, store_value);
863 OpReturn();
864 ++label_index;
865 }
857 } 866 }
858 AddLabel(end_block); 867 AddLabel(end_block);
859 OpUnreachable(); 868 OpUnreachable();
@@ -1532,9 +1541,16 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
1532 if (stage == Stage::Fragment) { 1541 if (stage == Stage::Fragment) {
1533 throw NotImplementedException("Storing ClipDistance in fragment stage"); 1542 throw NotImplementedException("Storing ClipDistance in fragment stage");
1534 } 1543 }
1535 const Id type{TypeArray( 1544 if (profile.max_user_clip_distances > 0) {
1536 F32[1], Const(std::min(info.used_clip_distances, profile.max_user_clip_distances)))}; 1545 const u32 used{std::min(profile.max_user_clip_distances, 8u)};
1537 clip_distances = DefineOutput(*this, type, invocations, spv::BuiltIn::ClipDistance); 1546 const std::array<Id, 8> zero{f32_zero_value, f32_zero_value, f32_zero_value,
1547 f32_zero_value, f32_zero_value, f32_zero_value,
1548 f32_zero_value, f32_zero_value};
1549 const Id type{TypeArray(F32[1], Const(used))};
1550 const Id initializer{ConstantComposite(type, std::span(zero).subspan(0, used))};
1551 clip_distances =
1552 DefineOutput(*this, type, invocations, spv::BuiltIn::ClipDistance, initializer);
1553 }
1538 } 1554 }
1539 if (info.stores[IR::Attribute::Layer] && 1555 if (info.stores[IR::Attribute::Layer] &&
1540 (profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) { 1556 (profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) {
diff --git a/src/tests/common/host_memory.cpp b/src/tests/common/host_memory.cpp
index 1a28e862b..cb040c942 100644
--- a/src/tests/common/host_memory.cpp
+++ b/src/tests/common/host_memory.cpp
@@ -12,6 +12,7 @@ using namespace Common::Literals;
12static constexpr size_t VIRTUAL_SIZE = 1ULL << 39; 12static constexpr size_t VIRTUAL_SIZE = 1ULL << 39;
13static constexpr size_t BACKING_SIZE = 4_GiB; 13static constexpr size_t BACKING_SIZE = 4_GiB;
14static constexpr auto PERMS = Common::MemoryPermission::ReadWrite; 14static constexpr auto PERMS = Common::MemoryPermission::ReadWrite;
15static constexpr auto HEAP = false;
15 16
16TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") { 17TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") {
17 { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); } 18 { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); }
@@ -20,7 +21,7 @@ TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") {
20 21
21TEST_CASE("HostMemory: Simple map", "[common]") { 22TEST_CASE("HostMemory: Simple map", "[common]") {
22 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 23 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
23 mem.Map(0x5000, 0x8000, 0x1000, PERMS); 24 mem.Map(0x5000, 0x8000, 0x1000, PERMS, HEAP);
24 25
25 volatile u8* const data = mem.VirtualBasePointer() + 0x5000; 26 volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
26 data[0] = 50; 27 data[0] = 50;
@@ -29,8 +30,8 @@ TEST_CASE("HostMemory: Simple map", "[common]") {
29 30
30TEST_CASE("HostMemory: Simple mirror map", "[common]") { 31TEST_CASE("HostMemory: Simple mirror map", "[common]") {
31 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 32 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
32 mem.Map(0x5000, 0x3000, 0x2000, PERMS); 33 mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
33 mem.Map(0x8000, 0x4000, 0x1000, PERMS); 34 mem.Map(0x8000, 0x4000, 0x1000, PERMS, HEAP);
34 35
35 volatile u8* const mirror_a = mem.VirtualBasePointer() + 0x5000; 36 volatile u8* const mirror_a = mem.VirtualBasePointer() + 0x5000;
36 volatile u8* const mirror_b = mem.VirtualBasePointer() + 0x8000; 37 volatile u8* const mirror_b = mem.VirtualBasePointer() + 0x8000;
@@ -40,116 +41,116 @@ TEST_CASE("HostMemory: Simple mirror map", "[common]") {
40 41
41TEST_CASE("HostMemory: Simple unmap", "[common]") { 42TEST_CASE("HostMemory: Simple unmap", "[common]") {
42 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 43 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
43 mem.Map(0x5000, 0x3000, 0x2000, PERMS); 44 mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
44 45
45 volatile u8* const data = mem.VirtualBasePointer() + 0x5000; 46 volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
46 data[75] = 50; 47 data[75] = 50;
47 REQUIRE(data[75] == 50); 48 REQUIRE(data[75] == 50);
48 49
49 mem.Unmap(0x5000, 0x2000); 50 mem.Unmap(0x5000, 0x2000, HEAP);
50} 51}
51 52
52TEST_CASE("HostMemory: Simple unmap and remap", "[common]") { 53TEST_CASE("HostMemory: Simple unmap and remap", "[common]") {
53 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 54 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
54 mem.Map(0x5000, 0x3000, 0x2000, PERMS); 55 mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
55 56
56 volatile u8* const data = mem.VirtualBasePointer() + 0x5000; 57 volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
57 data[0] = 50; 58 data[0] = 50;
58 REQUIRE(data[0] == 50); 59 REQUIRE(data[0] == 50);
59 60
60 mem.Unmap(0x5000, 0x2000); 61 mem.Unmap(0x5000, 0x2000, HEAP);
61 62
62 mem.Map(0x5000, 0x3000, 0x2000, PERMS); 63 mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
63 REQUIRE(data[0] == 50); 64 REQUIRE(data[0] == 50);
64 65
65 mem.Map(0x7000, 0x2000, 0x5000, PERMS); 66 mem.Map(0x7000, 0x2000, 0x5000, PERMS, HEAP);
66 REQUIRE(data[0x3000] == 50); 67 REQUIRE(data[0x3000] == 50);
67} 68}
68 69
69TEST_CASE("HostMemory: Nieche allocation", "[common]") { 70TEST_CASE("HostMemory: Nieche allocation", "[common]") {
70 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 71 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
71 mem.Map(0x0000, 0, 0x20000, PERMS); 72 mem.Map(0x0000, 0, 0x20000, PERMS, HEAP);
72 mem.Unmap(0x0000, 0x4000); 73 mem.Unmap(0x0000, 0x4000, HEAP);
73 mem.Map(0x1000, 0, 0x2000, PERMS); 74 mem.Map(0x1000, 0, 0x2000, PERMS, HEAP);
74 mem.Map(0x3000, 0, 0x1000, PERMS); 75 mem.Map(0x3000, 0, 0x1000, PERMS, HEAP);
75 mem.Map(0, 0, 0x1000, PERMS); 76 mem.Map(0, 0, 0x1000, PERMS, HEAP);
76} 77}
77 78
78TEST_CASE("HostMemory: Full unmap", "[common]") { 79TEST_CASE("HostMemory: Full unmap", "[common]") {
79 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 80 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
80 mem.Map(0x8000, 0, 0x4000, PERMS); 81 mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
81 mem.Unmap(0x8000, 0x4000); 82 mem.Unmap(0x8000, 0x4000, HEAP);
82 mem.Map(0x6000, 0, 0x16000, PERMS); 83 mem.Map(0x6000, 0, 0x16000, PERMS, HEAP);
83} 84}
84 85
85TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") { 86TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") {
86 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 87 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
87 mem.Map(0x0000, 0, 0x4000, PERMS); 88 mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
88 mem.Unmap(0x2000, 0x4000); 89 mem.Unmap(0x2000, 0x4000, HEAP);
89 mem.Map(0x2000, 0x80000, 0x4000, PERMS); 90 mem.Map(0x2000, 0x80000, 0x4000, PERMS, HEAP);
90} 91}
91 92
92TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") { 93TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") {
93 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 94 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
94 mem.Map(0x8000, 0, 0x4000, PERMS); 95 mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
95 mem.Unmap(0x6000, 0x4000); 96 mem.Unmap(0x6000, 0x4000, HEAP);
96 mem.Map(0x8000, 0, 0x2000, PERMS); 97 mem.Map(0x8000, 0, 0x2000, PERMS, HEAP);
97} 98}
98 99
99TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") { 100TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") {
100 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 101 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
101 mem.Map(0x0000, 0, 0x4000, PERMS); 102 mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
102 mem.Map(0x4000, 0, 0x1b000, PERMS); 103 mem.Map(0x4000, 0, 0x1b000, PERMS, HEAP);
103 mem.Unmap(0x3000, 0x1c000); 104 mem.Unmap(0x3000, 0x1c000, HEAP);
104 mem.Map(0x3000, 0, 0x20000, PERMS); 105 mem.Map(0x3000, 0, 0x20000, PERMS, HEAP);
105} 106}
106 107
107TEST_CASE("HostMemory: Unmap between placeholders", "[common]") { 108TEST_CASE("HostMemory: Unmap between placeholders", "[common]") {
108 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 109 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
109 mem.Map(0x0000, 0, 0x4000, PERMS); 110 mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
110 mem.Map(0x4000, 0, 0x4000, PERMS); 111 mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
111 mem.Unmap(0x2000, 0x4000); 112 mem.Unmap(0x2000, 0x4000, HEAP);
112 mem.Map(0x2000, 0, 0x4000, PERMS); 113 mem.Map(0x2000, 0, 0x4000, PERMS, HEAP);
113} 114}
114 115
115TEST_CASE("HostMemory: Unmap to origin", "[common]") { 116TEST_CASE("HostMemory: Unmap to origin", "[common]") {
116 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 117 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
117 mem.Map(0x4000, 0, 0x4000, PERMS); 118 mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
118 mem.Map(0x8000, 0, 0x4000, PERMS); 119 mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
119 mem.Unmap(0x4000, 0x4000); 120 mem.Unmap(0x4000, 0x4000, HEAP);
120 mem.Map(0, 0, 0x4000, PERMS); 121 mem.Map(0, 0, 0x4000, PERMS, HEAP);
121 mem.Map(0x4000, 0, 0x4000, PERMS); 122 mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
122} 123}
123 124
124TEST_CASE("HostMemory: Unmap to right", "[common]") { 125TEST_CASE("HostMemory: Unmap to right", "[common]") {
125 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 126 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
126 mem.Map(0x4000, 0, 0x4000, PERMS); 127 mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
127 mem.Map(0x8000, 0, 0x4000, PERMS); 128 mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
128 mem.Unmap(0x8000, 0x4000); 129 mem.Unmap(0x8000, 0x4000, HEAP);
129 mem.Map(0x8000, 0, 0x4000, PERMS); 130 mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
130} 131}
131 132
132TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") { 133TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") {
133 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 134 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
134 mem.Map(0x4000, 0x10000, 0x4000, PERMS); 135 mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
135 136
136 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; 137 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
137 ptr[0x1000] = 17; 138 ptr[0x1000] = 17;
138 139
139 mem.Unmap(0x6000, 0x2000); 140 mem.Unmap(0x6000, 0x2000, HEAP);
140 141
141 REQUIRE(ptr[0x1000] == 17); 142 REQUIRE(ptr[0x1000] == 17);
142} 143}
143 144
144TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") { 145TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
145 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 146 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
146 mem.Map(0x4000, 0x10000, 0x4000, PERMS); 147 mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
147 148
148 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; 149 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
149 ptr[0x3000] = 19; 150 ptr[0x3000] = 19;
150 ptr[0x3fff] = 12; 151 ptr[0x3fff] = 12;
151 152
152 mem.Unmap(0x4000, 0x2000); 153 mem.Unmap(0x4000, 0x2000, HEAP);
153 154
154 REQUIRE(ptr[0x3000] == 19); 155 REQUIRE(ptr[0x3000] == 19);
155 REQUIRE(ptr[0x3fff] == 12); 156 REQUIRE(ptr[0x3fff] == 12);
@@ -157,13 +158,13 @@ TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
157 158
158TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") { 159TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") {
159 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 160 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
160 mem.Map(0x4000, 0x10000, 0x4000, PERMS); 161 mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
161 162
162 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; 163 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
163 ptr[0x0000] = 19; 164 ptr[0x0000] = 19;
164 ptr[0x3fff] = 12; 165 ptr[0x3fff] = 12;
165 166
166 mem.Unmap(0x1000, 0x2000); 167 mem.Unmap(0x1000, 0x2000, HEAP);
167 168
168 REQUIRE(ptr[0x0000] == 19); 169 REQUIRE(ptr[0x0000] == 19);
169 REQUIRE(ptr[0x3fff] == 12); 170 REQUIRE(ptr[0x3fff] == 12);
@@ -171,14 +172,14 @@ TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") {
171 172
172TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common]") { 173TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common]") {
173 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 174 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
174 mem.Map(0x4000, 0x10000, 0x2000, PERMS); 175 mem.Map(0x4000, 0x10000, 0x2000, PERMS, HEAP);
175 mem.Map(0x6000, 0x20000, 0x2000, PERMS); 176 mem.Map(0x6000, 0x20000, 0x2000, PERMS, HEAP);
176 177
177 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; 178 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
178 ptr[0x0000] = 19; 179 ptr[0x0000] = 19;
179 ptr[0x3fff] = 12; 180 ptr[0x3fff] = 12;
180 181
181 mem.Unmap(0x5000, 0x2000); 182 mem.Unmap(0x5000, 0x2000, HEAP);
182 183
183 REQUIRE(ptr[0x0000] == 19); 184 REQUIRE(ptr[0x0000] == 19);
184 REQUIRE(ptr[0x3fff] == 12); 185 REQUIRE(ptr[0x3fff] == 12);
diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp
index 046c8085e..46e853e04 100644
--- a/src/video_core/macro/macro_hle.cpp
+++ b/src/video_core/macro/macro_hle.cpp
@@ -327,12 +327,13 @@ public:
327 explicit HLE_DrawIndirectByteCount(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {} 327 explicit HLE_DrawIndirectByteCount(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
328 328
329 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override { 329 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
330 const bool force = maxwell3d.Rasterizer().HasDrawTransformFeedback();
331
330 auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0xFFFFU); 332 auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0xFFFFU);
331 if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) { 333 if (!force && (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology))) {
332 Fallback(parameters); 334 Fallback(parameters);
333 return; 335 return;
334 } 336 }
335
336 auto& params = maxwell3d.draw_manager->GetIndirectParams(); 337 auto& params = maxwell3d.draw_manager->GetIndirectParams();
337 params.is_byte_count = true; 338 params.is_byte_count = true;
338 params.is_indexed = false; 339 params.is_indexed = false;
@@ -503,6 +504,8 @@ public:
503 maxwell3d.CallMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true); 504 maxwell3d.CallMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true);
504 maxwell3d.CallMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(inline_data)), 505 maxwell3d.CallMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(inline_data)),
505 regs.transform_feedback.controls[0].stride, true); 506 regs.transform_feedback.controls[0].stride, true);
507
508 maxwell3d.Rasterizer().RegisterTransformFeedback(regs.upload.dest.Address());
506 } 509 }
507}; 510};
508 511
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index af1469147..49224ca85 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -173,5 +173,13 @@ public:
173 virtual void BindChannel(Tegra::Control::ChannelState& channel) {} 173 virtual void BindChannel(Tegra::Control::ChannelState& channel) {}
174 174
175 virtual void ReleaseChannel(s32 channel_id) {} 175 virtual void ReleaseChannel(s32 channel_id) {}
176
177 /// Register the address as a Transform Feedback Object
178 virtual void RegisterTransformFeedback(GPUVAddr tfb_object_addr) {}
179
180 /// Returns true when the rasterizer has Draw Transform Feedback capabilities
181 virtual bool HasDrawTransformFeedback() {
182 return false;
183 }
176}; 184};
177} // namespace VideoCore 185} // namespace VideoCore
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index b787b6994..517ac14dd 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -376,4 +376,15 @@ void BufferCacheRuntime::BindImageBuffer(Buffer& buffer, u32 offset, u32 size, P
376 *image_handles++ = buffer.View(offset, size, format); 376 *image_handles++ = buffer.View(offset, size, format);
377} 377}
378 378
379void BufferCacheRuntime::BindTransformFeedbackObject(GPUVAddr tfb_object_addr) {
380 OGLTransformFeedback& tfb_object = tfb_objects[tfb_object_addr];
381 tfb_object.Create();
382 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfb_object.handle);
383}
384
385GLuint BufferCacheRuntime::GetTransformFeedbackObject(GPUVAddr tfb_object_addr) {
386 ASSERT(tfb_objects.contains(tfb_object_addr));
387 return tfb_objects[tfb_object_addr].handle;
388}
389
379} // namespace OpenGL 390} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index 1e8708f59..2c18de166 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -5,6 +5,7 @@
5 5
6#include <array> 6#include <array>
7#include <span> 7#include <span>
8#include <unordered_map>
8 9
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "video_core/buffer_cache/buffer_cache_base.h" 11#include "video_core/buffer_cache/buffer_cache_base.h"
@@ -121,6 +122,9 @@ public:
121 void BindImageBuffer(Buffer& buffer, u32 offset, u32 size, 122 void BindImageBuffer(Buffer& buffer, u32 offset, u32 size,
122 VideoCore::Surface::PixelFormat format); 123 VideoCore::Surface::PixelFormat format);
123 124
125 void BindTransformFeedbackObject(GPUVAddr tfb_object_addr);
126 GLuint GetTransformFeedbackObject(GPUVAddr tfb_object_addr);
127
124 u64 GetDeviceMemoryUsage() const; 128 u64 GetDeviceMemoryUsage() const;
125 129
126 void BindFastUniformBuffer(size_t stage, u32 binding_index, u32 size) { 130 void BindFastUniformBuffer(size_t stage, u32 binding_index, u32 size) {
@@ -233,6 +237,7 @@ private:
233 u32 index_buffer_offset = 0; 237 u32 index_buffer_offset = 0;
234 238
235 u64 device_access_memory; 239 u64 device_access_memory;
240 std::unordered_map<GPUVAddr, OGLTransformFeedback> tfb_objects;
236}; 241};
237 242
238struct BufferCacheParams { 243struct BufferCacheParams {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 339950d2e..7a5fad735 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -309,6 +309,13 @@ void RasterizerOpenGL::DrawIndirect() {
309 const auto& params = maxwell3d->draw_manager->GetIndirectParams(); 309 const auto& params = maxwell3d->draw_manager->GetIndirectParams();
310 buffer_cache.SetDrawIndirect(&params); 310 buffer_cache.SetDrawIndirect(&params);
311 PrepareDraw(params.is_indexed, [this, &params](GLenum primitive_mode) { 311 PrepareDraw(params.is_indexed, [this, &params](GLenum primitive_mode) {
312 if (params.is_byte_count) {
313 const GPUVAddr tfb_object_base_addr = params.indirect_start_address - 4U;
314 const GLuint tfb_object =
315 buffer_cache_runtime.GetTransformFeedbackObject(tfb_object_base_addr);
316 glDrawTransformFeedback(primitive_mode, tfb_object);
317 return;
318 }
312 const auto [buffer, offset] = buffer_cache.GetDrawIndirectBuffer(); 319 const auto [buffer, offset] = buffer_cache.GetDrawIndirectBuffer();
313 const GLvoid* const gl_offset = 320 const GLvoid* const gl_offset =
314 reinterpret_cast<const GLvoid*>(static_cast<uintptr_t>(offset)); 321 reinterpret_cast<const GLvoid*>(static_cast<uintptr_t>(offset));
@@ -1371,6 +1378,10 @@ void RasterizerOpenGL::ReleaseChannel(s32 channel_id) {
1371 query_cache.EraseChannel(channel_id); 1378 query_cache.EraseChannel(channel_id);
1372} 1379}
1373 1380
1381void RasterizerOpenGL::RegisterTransformFeedback(GPUVAddr tfb_object_addr) {
1382 buffer_cache_runtime.BindTransformFeedbackObject(tfb_object_addr);
1383}
1384
1374AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_, TextureCache& texture_cache_) 1385AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_, TextureCache& texture_cache_)
1375 : buffer_cache{buffer_cache_}, texture_cache{texture_cache_} {} 1386 : buffer_cache{buffer_cache_}, texture_cache{texture_cache_} {}
1376 1387
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index b79d7a70c..ce3460938 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -139,6 +139,12 @@ public:
139 139
140 void ReleaseChannel(s32 channel_id) override; 140 void ReleaseChannel(s32 channel_id) override;
141 141
142 void RegisterTransformFeedback(GPUVAddr tfb_object_addr) override;
143
144 bool HasDrawTransformFeedback() override {
145 return true;
146 }
147
142private: 148private:
143 static constexpr size_t MAX_TEXTURES = 192; 149 static constexpr size_t MAX_TEXTURES = 192;
144 static constexpr size_t MAX_IMAGES = 48; 150 static constexpr size_t MAX_IMAGES = 48;
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
index eae8fd110..1d2c9b70a 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -207,4 +207,21 @@ void OGLQuery::Release() {
207 handle = 0; 207 handle = 0;
208} 208}
209 209
210void OGLTransformFeedback::Create() {
211 if (handle != 0)
212 return;
213
214 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
215 glCreateTransformFeedbacks(1, &handle);
216}
217
218void OGLTransformFeedback::Release() {
219 if (handle == 0)
220 return;
221
222 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
223 glDeleteTransformFeedbacks(1, &handle);
224 handle = 0;
225}
226
210} // namespace OpenGL 227} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index 77362acd2..6ca8227bd 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -323,4 +323,31 @@ public:
323 GLuint handle = 0; 323 GLuint handle = 0;
324}; 324};
325 325
326class OGLTransformFeedback final {
327public:
328 YUZU_NON_COPYABLE(OGLTransformFeedback);
329
330 OGLTransformFeedback() = default;
331
332 OGLTransformFeedback(OGLTransformFeedback&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
333
334 ~OGLTransformFeedback() {
335 Release();
336 }
337
338 OGLTransformFeedback& operator=(OGLTransformFeedback&& o) noexcept {
339 Release();
340 handle = std::exchange(o.handle, 0);
341 return *this;
342 }
343
344 /// Creates a new internal OpenGL resource and stores the handle
345 void Create();
346
347 /// Deletes the internal OpenGL resource
348 void Release();
349
350 GLuint handle = 0;
351};
352
326} // namespace OpenGL 353} // namespace OpenGL
diff --git a/src/video_core/texture_cache/decode_bc.cpp b/src/video_core/texture_cache/decode_bc.cpp
index 3e26474a3..a018c6df4 100644
--- a/src/video_core/texture_cache/decode_bc.cpp
+++ b/src/video_core/texture_cache/decode_bc.cpp
@@ -60,66 +60,72 @@ u32 ConvertedBytesPerBlock(VideoCore::Surface::PixelFormat pixel_format) {
60} 60}
61 61
62template <auto decompress, PixelFormat pixel_format> 62template <auto decompress, PixelFormat pixel_format>
63void DecompressBlocks(std::span<const u8> input, std::span<u8> output, Extent3D extent, 63void DecompressBlocks(std::span<const u8> input, std::span<u8> output, BufferImageCopy& copy,
64 bool is_signed = false) { 64 bool is_signed = false) {
65 const u32 out_bpp = ConvertedBytesPerBlock(pixel_format); 65 const u32 out_bpp = ConvertedBytesPerBlock(pixel_format);
66 const u32 block_width = std::min(extent.width, BLOCK_SIZE); 66 const u32 block_size = BlockSize(pixel_format);
67 const u32 block_height = std::min(extent.height, BLOCK_SIZE); 67 const u32 width = copy.image_extent.width;
68 const u32 pitch = extent.width * out_bpp; 68 const u32 height = copy.image_extent.height * copy.image_subresource.num_layers;
69 const u32 depth = copy.image_extent.depth;
70 const u32 block_width = std::min(width, BLOCK_SIZE);
71 const u32 block_height = std::min(height, BLOCK_SIZE);
72 const u32 pitch = width * out_bpp;
69 size_t input_offset = 0; 73 size_t input_offset = 0;
70 size_t output_offset = 0; 74 size_t output_offset = 0;
71 for (u32 slice = 0; slice < extent.depth; ++slice) { 75 for (u32 slice = 0; slice < depth; ++slice) {
72 for (u32 y = 0; y < extent.height; y += block_height) { 76 for (u32 y = 0; y < height; y += block_height) {
73 size_t row_offset = 0; 77 size_t src_offset = input_offset;
74 for (u32 x = 0; x < extent.width; 78 size_t dst_offset = output_offset;
75 x += block_width, row_offset += block_width * out_bpp) { 79 for (u32 x = 0; x < width; x += block_width) {
76 const u8* src = input.data() + input_offset; 80 const u8* src = input.data() + src_offset;
77 u8* const dst = output.data() + output_offset + row_offset; 81 u8* const dst = output.data() + dst_offset;
78 if constexpr (IsSigned(pixel_format)) { 82 if constexpr (IsSigned(pixel_format)) {
79 decompress(src, dst, x, y, extent.width, extent.height, is_signed); 83 decompress(src, dst, x, y, width, height, is_signed);
80 } else { 84 } else {
81 decompress(src, dst, x, y, extent.width, extent.height); 85 decompress(src, dst, x, y, width, height);
82 } 86 }
83 input_offset += BlockSize(pixel_format); 87 src_offset += block_size;
88 dst_offset += block_width * out_bpp;
84 } 89 }
90 input_offset += copy.buffer_row_length * block_size / block_width;
85 output_offset += block_height * pitch; 91 output_offset += block_height * pitch;
86 } 92 }
87 } 93 }
88} 94}
89 95
90void DecompressBCn(std::span<const u8> input, std::span<u8> output, Extent3D extent, 96void DecompressBCn(std::span<const u8> input, std::span<u8> output, BufferImageCopy& copy,
91 VideoCore::Surface::PixelFormat pixel_format) { 97 VideoCore::Surface::PixelFormat pixel_format) {
92 switch (pixel_format) { 98 switch (pixel_format) {
93 case PixelFormat::BC1_RGBA_UNORM: 99 case PixelFormat::BC1_RGBA_UNORM:
94 case PixelFormat::BC1_RGBA_SRGB: 100 case PixelFormat::BC1_RGBA_SRGB:
95 DecompressBlocks<bcn::DecodeBc1, PixelFormat::BC1_RGBA_UNORM>(input, output, extent); 101 DecompressBlocks<bcn::DecodeBc1, PixelFormat::BC1_RGBA_UNORM>(input, output, copy);
96 break; 102 break;
97 case PixelFormat::BC2_UNORM: 103 case PixelFormat::BC2_UNORM:
98 case PixelFormat::BC2_SRGB: 104 case PixelFormat::BC2_SRGB:
99 DecompressBlocks<bcn::DecodeBc2, PixelFormat::BC2_UNORM>(input, output, extent); 105 DecompressBlocks<bcn::DecodeBc2, PixelFormat::BC2_UNORM>(input, output, copy);
100 break; 106 break;
101 case PixelFormat::BC3_UNORM: 107 case PixelFormat::BC3_UNORM:
102 case PixelFormat::BC3_SRGB: 108 case PixelFormat::BC3_SRGB:
103 DecompressBlocks<bcn::DecodeBc3, PixelFormat::BC3_UNORM>(input, output, extent); 109 DecompressBlocks<bcn::DecodeBc3, PixelFormat::BC3_UNORM>(input, output, copy);
104 break; 110 break;
105 case PixelFormat::BC4_SNORM: 111 case PixelFormat::BC4_SNORM:
106 case PixelFormat::BC4_UNORM: 112 case PixelFormat::BC4_UNORM:
107 DecompressBlocks<bcn::DecodeBc4, PixelFormat::BC4_UNORM>( 113 DecompressBlocks<bcn::DecodeBc4, PixelFormat::BC4_UNORM>(
108 input, output, extent, pixel_format == PixelFormat::BC4_SNORM); 114 input, output, copy, pixel_format == PixelFormat::BC4_SNORM);
109 break; 115 break;
110 case PixelFormat::BC5_SNORM: 116 case PixelFormat::BC5_SNORM:
111 case PixelFormat::BC5_UNORM: 117 case PixelFormat::BC5_UNORM:
112 DecompressBlocks<bcn::DecodeBc5, PixelFormat::BC5_UNORM>( 118 DecompressBlocks<bcn::DecodeBc5, PixelFormat::BC5_UNORM>(
113 input, output, extent, pixel_format == PixelFormat::BC5_SNORM); 119 input, output, copy, pixel_format == PixelFormat::BC5_SNORM);
114 break; 120 break;
115 case PixelFormat::BC6H_SFLOAT: 121 case PixelFormat::BC6H_SFLOAT:
116 case PixelFormat::BC6H_UFLOAT: 122 case PixelFormat::BC6H_UFLOAT:
117 DecompressBlocks<bcn::DecodeBc6, PixelFormat::BC6H_UFLOAT>( 123 DecompressBlocks<bcn::DecodeBc6, PixelFormat::BC6H_UFLOAT>(
118 input, output, extent, pixel_format == PixelFormat::BC6H_SFLOAT); 124 input, output, copy, pixel_format == PixelFormat::BC6H_SFLOAT);
119 break; 125 break;
120 case PixelFormat::BC7_SRGB: 126 case PixelFormat::BC7_SRGB:
121 case PixelFormat::BC7_UNORM: 127 case PixelFormat::BC7_UNORM:
122 DecompressBlocks<bcn::DecodeBc7, PixelFormat::BC7_UNORM>(input, output, extent); 128 DecompressBlocks<bcn::DecodeBc7, PixelFormat::BC7_UNORM>(input, output, copy);
123 break; 129 break;
124 default: 130 default:
125 LOG_WARNING(HW_GPU, "Unimplemented BCn decompression {}", pixel_format); 131 LOG_WARNING(HW_GPU, "Unimplemented BCn decompression {}", pixel_format);
diff --git a/src/video_core/texture_cache/decode_bc.h b/src/video_core/texture_cache/decode_bc.h
index 41d1ec0a3..4e3b9b8ac 100644
--- a/src/video_core/texture_cache/decode_bc.h
+++ b/src/video_core/texture_cache/decode_bc.h
@@ -13,7 +13,7 @@ namespace VideoCommon {
13 13
14[[nodiscard]] u32 ConvertedBytesPerBlock(VideoCore::Surface::PixelFormat pixel_format); 14[[nodiscard]] u32 ConvertedBytesPerBlock(VideoCore::Surface::PixelFormat pixel_format);
15 15
16void DecompressBCn(std::span<const u8> input, std::span<u8> output, Extent3D extent, 16void DecompressBCn(std::span<const u8> input, std::span<u8> output, BufferImageCopy& copy,
17 VideoCore::Surface::PixelFormat pixel_format); 17 VideoCore::Surface::PixelFormat pixel_format);
18 18
19} // namespace VideoCommon 19} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index 15596c925..fcf70068e 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -837,6 +837,7 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
837 std::span<u8> output) { 837 std::span<u8> output) {
838 const size_t guest_size_bytes = input.size_bytes(); 838 const size_t guest_size_bytes = input.size_bytes();
839 const u32 bpp_log2 = BytesPerBlockLog2(info.format); 839 const u32 bpp_log2 = BytesPerBlockLog2(info.format);
840 const Extent2D tile_size = DefaultBlockSize(info.format);
840 const Extent3D size = info.size; 841 const Extent3D size = info.size;
841 842
842 if (info.type == ImageType::Linear) { 843 if (info.type == ImageType::Linear) {
@@ -847,7 +848,7 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
847 return {{ 848 return {{
848 .buffer_offset = 0, 849 .buffer_offset = 0,
849 .buffer_size = guest_size_bytes, 850 .buffer_size = guest_size_bytes,
850 .buffer_row_length = info.pitch >> bpp_log2, 851 .buffer_row_length = info.pitch * tile_size.width >> bpp_log2,
851 .buffer_image_height = size.height, 852 .buffer_image_height = size.height,
852 .image_subresource = 853 .image_subresource =
853 { 854 {
@@ -862,7 +863,6 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
862 const LevelInfo level_info = MakeLevelInfo(info); 863 const LevelInfo level_info = MakeLevelInfo(info);
863 const s32 num_layers = info.resources.layers; 864 const s32 num_layers = info.resources.layers;
864 const s32 num_levels = info.resources.levels; 865 const s32 num_levels = info.resources.levels;
865 const Extent2D tile_size = DefaultBlockSize(info.format);
866 const std::array level_sizes = CalculateLevelSizes(level_info, num_levels); 866 const std::array level_sizes = CalculateLevelSizes(level_info, num_levels);
867 const Extent2D gob = GobSize(bpp_log2, info.block.height, info.tile_width_spacing); 867 const Extent2D gob = GobSize(bpp_log2, info.block.height, info.tile_width_spacing);
868 const u32 layer_size = CalculateLevelBytes(level_sizes, num_levels); 868 const u32 layer_size = CalculateLevelBytes(level_sizes, num_levels);
@@ -926,8 +926,6 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
926 926
927 const auto input_offset = input.subspan(copy.buffer_offset); 927 const auto input_offset = input.subspan(copy.buffer_offset);
928 copy.buffer_offset = output_offset; 928 copy.buffer_offset = output_offset;
929 copy.buffer_row_length = mip_size.width;
930 copy.buffer_image_height = mip_size.height;
931 929
932 const auto recompression_setting = Settings::values.astc_recompression.GetValue(); 930 const auto recompression_setting = Settings::values.astc_recompression.GetValue();
933 const bool astc = IsPixelFormatASTC(info.format); 931 const bool astc = IsPixelFormatASTC(info.format);
@@ -972,16 +970,14 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
972 bpp_div; 970 bpp_div;
973 output_offset += static_cast<u32>(copy.buffer_size); 971 output_offset += static_cast<u32>(copy.buffer_size);
974 } else { 972 } else {
975 const Extent3D image_extent{ 973 DecompressBCn(input_offset, output.subspan(output_offset), copy, info.format);
976 .width = copy.image_extent.width,
977 .height = copy.image_extent.height * copy.image_subresource.num_layers,
978 .depth = copy.image_extent.depth,
979 };
980 DecompressBCn(input_offset, output.subspan(output_offset), image_extent, info.format);
981 output_offset += copy.image_extent.width * copy.image_extent.height * 974 output_offset += copy.image_extent.width * copy.image_extent.height *
982 copy.image_subresource.num_layers * 975 copy.image_subresource.num_layers *
983 ConvertedBytesPerBlock(info.format); 976 ConvertedBytesPerBlock(info.format);
984 } 977 }
978
979 copy.buffer_row_length = mip_size.width;
980 copy.buffer_image_height = mip_size.height;
985 } 981 }
986} 982}
987 983
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index a6fbca69e..727bbd98d 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -755,10 +755,10 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags
755 // The wanted format is not supported by hardware, search for alternatives 755 // The wanted format is not supported by hardware, search for alternatives
756 const VkFormat* alternatives = GetFormatAlternatives(wanted_format); 756 const VkFormat* alternatives = GetFormatAlternatives(wanted_format);
757 if (alternatives == nullptr) { 757 if (alternatives == nullptr) {
758 ASSERT_MSG(false, 758 LOG_ERROR(Render_Vulkan,
759 "Format={} with usage={} and type={} has no defined alternatives and host " 759 "Format={} with usage={} and type={} has no defined alternatives and host "
760 "hardware does not support it", 760 "hardware does not support it",
761 wanted_format, wanted_usage, format_type); 761 wanted_format, wanted_usage, format_type);
762 return wanted_format; 762 return wanted_format;
763 } 763 }
764 764
@@ -774,10 +774,10 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags
774 } 774 }
775 775
776 // No alternatives found, panic 776 // No alternatives found, panic
777 ASSERT_MSG(false, 777 LOG_ERROR(Render_Vulkan,
778 "Format={} with usage={} and type={} is not supported by the host hardware and " 778 "Format={} with usage={} and type={} is not supported by the host hardware and "
779 "doesn't support any of the alternatives", 779 "doesn't support any of the alternatives",
780 wanted_format, wanted_usage, format_type); 780 wanted_format, wanted_usage, format_type);
781 return wanted_format; 781 return wanted_format;
782} 782}
783 783
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 2f78b8af0..074aed964 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -246,7 +246,9 @@ void SetObjectName(const DeviceDispatch* dld, VkDevice device, T handle, VkObjec
246 .objectHandle = reinterpret_cast<u64>(handle), 246 .objectHandle = reinterpret_cast<u64>(handle),
247 .pObjectName = name, 247 .pObjectName = name,
248 }; 248 };
249 Check(dld->vkSetDebugUtilsObjectNameEXT(device, &name_info)); 249 if (dld->vkSetDebugUtilsObjectNameEXT) {
250 Check(dld->vkSetDebugUtilsObjectNameEXT(device, &name_info));
251 }
250} 252}
251 253
252} // Anonymous namespace 254} // Anonymous namespace
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 059fcf041..c789c1e59 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -5342,6 +5342,10 @@ int main(int argc, char* argv[]) {
5342 if (QString::fromLocal8Bit(qgetenv("DISPLAY")).isEmpty()) { 5342 if (QString::fromLocal8Bit(qgetenv("DISPLAY")).isEmpty()) {
5343 qputenv("DISPLAY", ":0"); 5343 qputenv("DISPLAY", ":0");
5344 } 5344 }
5345
5346 // Fix the Wayland appId. This needs to match the name of the .desktop file without the .desktop
5347 // suffix.
5348 QGuiApplication::setDesktopFileName(QStringLiteral("org.yuzu_emu.yuzu"));
5345#endif 5349#endif
5346 5350
5347 SetHighDPIAttributes(); 5351 SetHighDPIAttributes();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 530e445f9..366e806d5 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -168,14 +168,6 @@ class GMainWindow : public QMainWindow {
168 /// Max number of recently loaded items to keep track of 168 /// Max number of recently loaded items to keep track of
169 static const int max_recent_files_item = 10; 169 static const int max_recent_files_item = 10;
170 170
171 // TODO: Make use of this!
172 enum {
173 UI_IDLE,
174 UI_EMU_BOOTING,
175 UI_EMU_RUNNING,
176 UI_EMU_STOPPING,
177 };
178
179 enum { 171 enum {
180 CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, 172 CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES,
181 CREATE_SHORTCUT_MSGBOX_SUCCESS, 173 CREATE_SHORTCUT_MSGBOX_SUCCESS,
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 5153cdb79..1a35d471c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -20,7 +20,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Co
20 : input_subsystem{input_subsystem_}, system{system_} { 20 : input_subsystem{input_subsystem_}, system{system_} {
21 input_subsystem->Initialize(); 21 input_subsystem->Initialize();
22 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) { 22 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) {
23 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); 23 LOG_CRITICAL(Frontend, "Failed to initialize SDL2: {}, Exiting...", SDL_GetError());
24 exit(1); 24 exit(1);
25 } 25 }
26 SDL_SetMainReady(); 26 SDL_SetMainReady();
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
index 9ed47d453..8b916f05c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -28,7 +28,8 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste
28 SDL_SysWMinfo wm; 28 SDL_SysWMinfo wm;
29 SDL_VERSION(&wm.version); 29 SDL_VERSION(&wm.version);
30 if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) { 30 if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) {
31 LOG_CRITICAL(Frontend, "Failed to get information from the window manager"); 31 LOG_CRITICAL(Frontend, "Failed to get information from the window manager: {}",
32 SDL_GetError());
32 std::exit(EXIT_FAILURE); 33 std::exit(EXIT_FAILURE);
33 } 34 }
34 35