diff options
Diffstat (limited to 'src')
97 files changed, 2482 insertions, 776 deletions
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 | ||
| 9 | void assert_fail_impl() { | 10 | void 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 | |||
| 10 | namespace Common { | ||
| 11 | |||
| 12 | namespace { | ||
| 13 | |||
| 14 | s64 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 | |||
| 31 | HeapTracker::HeapTracker(Common::HostMemory& buffer) | ||
| 32 | : m_buffer(buffer), m_max_resident_map_count(GetMaxPermissibleResidentMapCount()) {} | ||
| 33 | HeapTracker::~HeapTracker() = default; | ||
| 34 | |||
| 35 | void 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 | |||
| 65 | void 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 | |||
| 103 | void 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 | |||
| 160 | bool 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 | |||
| 168 | bool 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 | |||
| 205 | void 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 | |||
| 230 | void 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 | |||
| 237 | void 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 | |||
| 273 | HeapTracker::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 | |||
| 14 | namespace Common { | ||
| 15 | |||
| 16 | struct 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 | |||
| 27 | struct 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 | |||
| 39 | struct 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 | |||
| 51 | class HeapTracker { | ||
| 52 | public: | ||
| 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 | |||
| 67 | private: | ||
| 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 | |||
| 79 | private: | ||
| 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 | |||
| 87 | private: | ||
| 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; | |||
| 679 | HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default; | 679 | HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default; |
| 680 | 680 | ||
| 681 | void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, | 681 | void 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 | ||
| 694 | void HostMemory::Unmap(size_t virtual_offset, size_t length) { | 694 | void 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 | ||
| 704 | void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write, | 704 | void 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 | |||
| 67 | private: | 72 | private: |
| 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 | ||
| 329 | void Stop() { | ||
| 330 | Impl::Stop(); | ||
| 331 | } | ||
| 332 | |||
| 316 | void DisableLoggingInTests() { | 333 | void 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 | ||
| 15 | void Start(); | 15 | void Start(); |
| 16 | 16 | ||
| 17 | /// Explicitly stops the logger thread and flushes the buffers | ||
| 18 | void Stop(); | ||
| 19 | |||
| 17 | void DisableLoggingInTests(); | 20 | void 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 | ||
| 979 | if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) | 979 | if (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 | ||
| 10 | namespace Core { | 10 | namespace Core { |
| 11 | 11 | ||
| 12 | void ArmInterface::LogBacktrace(const Kernel::KProcess* process) const { | 12 | void 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 | ||
| 82 | void SymbolicateBacktrace(const Kernel::KProcess* process, std::vector<BacktraceEntry>& out) { | 82 | void 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 | ||
| 121 | std::vector<BacktraceEntry> GetAArch64Backtrace(const Kernel::KProcess* process, | 121 | std::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 | ||
| 147 | std::vector<BacktraceEntry> GetAArch32Backtrace(const Kernel::KProcess* process, | 147 | std::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 | ||
| 175 | std::optional<std::string> GetThreadName(const Kernel::KThread* thread) { | 175 | std::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 | ||
| 251 | Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process) { | 251 | Loader::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 | ||
| 315 | Kernel::KProcessAddress FindMainModuleEntrypoint(const Kernel::KProcess* process) { | 315 | Kernel::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 | ||
| 340 | std::vector<BacktraceEntry> GetBacktraceFromContext(const Kernel::KProcess* process, | 340 | std::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); | |||
| 14 | std::string_view GetThreadWaitReason(const Kernel::KThread* thread); | 14 | std::string_view GetThreadWaitReason(const Kernel::KThread* thread); |
| 15 | std::string GetThreadState(const Kernel::KThread* thread); | 15 | std::string GetThreadState(const Kernel::KThread* thread); |
| 16 | 16 | ||
| 17 | Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process); | 17 | Loader::AppLoader::Modules FindModules(Kernel::KProcess* process); |
| 18 | Kernel::KProcessAddress GetModuleEnd(const Kernel::KProcess* process, Kernel::KProcessAddress base); | 18 | Kernel::KProcessAddress GetModuleEnd(const Kernel::KProcess* process, Kernel::KProcessAddress base); |
| 19 | Kernel::KProcessAddress FindMainModuleEntrypoint(const Kernel::KProcess* process); | 19 | Kernel::KProcessAddress FindMainModuleEntrypoint(Kernel::KProcess* process); |
| 20 | 20 | ||
| 21 | void InvalidateInstructionCacheRange(const Kernel::KProcess* process, u64 address, u64 size); | 21 | void 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 | ||
| 31 | std::vector<BacktraceEntry> GetBacktraceFromContext(const Kernel::KProcess* process, | 31 | std::vector<BacktraceEntry> GetBacktraceFromContext(Kernel::KProcess* process, |
| 32 | const Kernel::Svc::ThreadContext& ctx); | 32 | const Kernel::Svc::ThreadContext& ctx); |
| 33 | std::vector<BacktraceEntry> GetBacktrace(const Kernel::KThread* thread); | 33 | std::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 | |||
| 12 | namespace Core { | ||
| 13 | |||
| 14 | namespace { | ||
| 15 | |||
| 16 | thread_local Core::Memory::Memory* g_current_memory{}; | ||
| 17 | std::once_flag g_registered{}; | ||
| 18 | struct sigaction g_old_segv {}; | ||
| 19 | |||
| 20 | void 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 | |||
| 30 | ScopedJitExecution::ScopedJitExecution(Kernel::KProcess* process) { | ||
| 31 | g_current_memory = std::addressof(process->GetMemory()); | ||
| 32 | } | ||
| 33 | |||
| 34 | ScopedJitExecution::~ScopedJitExecution() { | ||
| 35 | g_current_memory = nullptr; | ||
| 36 | } | ||
| 37 | |||
| 38 | void 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 | |||
| 31 | class ScopedJitExecution { | ||
| 32 | public: | ||
| 33 | explicit ScopedJitExecution(Kernel::KProcess* process); | ||
| 34 | ~ScopedJitExecution(); | ||
| 35 | static void RegisterHandler(); | ||
| 36 | }; | ||
| 37 | |||
| 38 | #else | ||
| 39 | |||
| 40 | class ScopedJitExecution { | ||
| 41 | public: | ||
| 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 | ||
| 16 | class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { | 16 | class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { |
| 17 | public: | 17 | public: |
| 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 | ||
| 333 | HaltReason ArmDynarmic32::RunThread(Kernel::KThread* thread) { | 333 | HaltReason 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 | ||
| 338 | HaltReason ArmDynarmic32::StepThread(Kernel::KThread* thread) { | 340 | HaltReason 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 | ||
| 373 | ArmDynarmic32::ArmDynarmic32(System& system, bool uses_wall_clock, const Kernel::KProcess* process, | 377 | ArmDynarmic32::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 | ||
| 382 | ArmDynarmic32::~ArmDynarmic32() = default; | 387 | ArmDynarmic32::~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 | ||
| 21 | class ArmDynarmic32 final : public ArmInterface { | 21 | class ArmDynarmic32 final : public ArmInterface { |
| 22 | public: | 22 | public: |
| 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 | ||
| 16 | class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { | 16 | class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { |
| 17 | public: | 17 | public: |
| 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 | ||
| 364 | HaltReason ArmDynarmic64::RunThread(Kernel::KThread* thread) { | 364 | HaltReason 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 | ||
| 369 | HaltReason ArmDynarmic64::StepThread(Kernel::KThread* thread) { | 371 | HaltReason 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 | ||
| 402 | ArmDynarmic64::ArmDynarmic64(System& system, bool uses_wall_clock, const Kernel::KProcess* process, | 406 | ArmDynarmic64::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 | ||
| 411 | ArmDynarmic64::~ArmDynarmic64() = default; | 416 | ArmDynarmic64::~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 | ||
| 26 | class ArmDynarmic64 final : public ArmInterface { | 26 | class ArmDynarmic64 final : public ArmInterface { |
| 27 | public: | 27 | public: |
| 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 | ||
| 131 | struct System::Impl { | 130 | struct 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 | ||
| 655 | Core::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. | ||
| 663 | const 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 | |||
| 670 | size_t System::GetCurrentHostThreadID() const { | 649 | size_t System::GetCurrentHostThreadID() const { |
| 671 | return impl->kernel.GetCurrentHostThreadID(); | 650 | return impl->kernel.GetCurrentHostThreadID(); |
| 672 | } | 651 | } |
| 673 | 652 | ||
| 674 | void System::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) { | 653 | void 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 | ||
| 680 | PerfStatsResults System::GetAndResetPerfStats() { | 657 | PerfStatsResults 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 | ||
| 726 | ExclusiveMonitor& System::Monitor() { | ||
| 727 | return impl->kernel.GetExclusiveMonitor(); | ||
| 728 | } | ||
| 729 | |||
| 730 | const ExclusiveMonitor& System::Monitor() const { | ||
| 731 | return impl->kernel.GetExclusiveMonitor(); | ||
| 732 | } | ||
| 733 | |||
| 734 | Memory::Memory& System::ApplicationMemory() { | 703 | Memory::Memory& System::ApplicationMemory() { |
| 735 | return impl->memory; | 704 | return impl->kernel.ApplicationProcess()->GetMemory(); |
| 736 | } | 705 | } |
| 737 | 706 | ||
| 738 | const Core::Memory::Memory& System::ApplicationMemory() const { | 707 | const Core::Memory::Memory& System::ApplicationMemory() const { |
| 739 | return impl->memory; | 708 | return impl->kernel.ApplicationProcess()->GetMemory(); |
| 740 | } | 709 | } |
| 741 | 710 | ||
| 742 | Tegra::GPU& System::GPU() { | 711 | Tegra::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; | |||
| 116 | class Debugger; | 116 | class Debugger; |
| 117 | class DeviceMemory; | 117 | class DeviceMemory; |
| 118 | class ExclusiveMonitor; | 118 | class ExclusiveMonitor; |
| 119 | class GPUDirtyMemoryManager; | ||
| 120 | class PerfStats; | 119 | class PerfStats; |
| 121 | class Reporter; | 120 | class Reporter; |
| 122 | class SpeedLimiter; | 121 | class 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 | ||
| 169 | PoolPartition ProgramMetadata::GetPoolPartition() const { | ||
| 170 | return acid_header.pool_partition; | ||
| 171 | } | ||
| 172 | |||
| 169 | const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const { | 173 | const 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 | ||
| 37 | enum 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 | ||
| 29 | bool DecrementIfLessThan(Core::System& system, s32* out, KProcessAddress address, s32 value) { | 30 | bool 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 | ||
| 69 | bool UpdateIfEqual(Core::System& system, s32* out, KProcessAddress address, s32 value, | 70 | bool 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 @@ | |||
| 8 | namespace Kernel { | 8 | namespace Kernel { |
| 9 | 9 | ||
| 10 | void KAutoObjectWithListContainer::Register(KAutoObjectWithList* obj) { | 10 | void 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 | ||
| 16 | void KAutoObjectWithListContainer::Unregister(KAutoObjectWithList* obj) { | 17 | void 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 | ||
| 22 | size_t KAutoObjectWithListContainer::GetOwnedCount(KProcess* owner) { | 24 | size_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 | ||
| 12 | namespace Kernel { | 12 | namespace 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 | ||
| 58 | private: | 33 | private: |
| 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 | ||
| 31 | bool UpdateLockAtomic(Core::System& system, u32* out, KProcessAddress address, u32 if_zero, | 31 | bool 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 | |||
| 434 | void KPageTableBase::Finalize() { | 434 | void 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 | ||
| 1095 | Result KProcess::GetThreadList(s32* out_num_threads, KProcessAddress out_thread_ids, | 1103 | Result 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) {} | |||
| 1128 | KProcess::KProcess(KernelCore& kernel) | 1135 | KProcess::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()} {} | ||
| 1132 | KProcess::~KProcess() = default; | 1140 | KProcess::~KProcess() = default; |
| 1133 | 1141 | ||
| 1134 | Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, | 1142 | Result 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 | ||
| 1237 | void KProcess::InitializeInterfaces() { | 1249 | void 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 | ||
| 1308 | Core::Memory::Memory& KProcess::GetMemory() const { | 1321 | void 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 | ||
| 21 | namespace Kernel { | 23 | namespace 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 | ||
| 130 | private: | 135 | private: |
| 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 | ||
| 507 | public: | 520 | public: |
| 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 | ||
| 30 | namespace { | 31 | namespace { |
| 31 | 32 | ||
| 33 | constexpr inline size_t PointerTransferBufferAlignment = 0x10; | ||
| 34 | constexpr inline size_t ReceiveListDataSize = | ||
| 35 | MessageBuffer::MessageHeader::ReceiveListCountType_CountMax * | ||
| 36 | MessageBuffer::ReceiveListEntry::GetDataSize() / sizeof(u32); | ||
| 37 | |||
| 38 | using ThreadQueueImplForKServerSessionRequest = KThreadQueue; | ||
| 39 | |||
| 40 | class ReceiveList { | ||
| 41 | public: | ||
| 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 | |||
| 151 | private: | ||
| 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 | |||
| 32 | template <bool MoveHandleAllowed> | 158 | template <bool MoveHandleAllowed> |
| 33 | Result ProcessMessageSpecialData(KProcess& dst_process, KProcess& src_process, KThread& src_thread, | 159 | Result 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 | ||
| 240 | Result 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 | |||
| 293 | constexpr 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 | |||
| 312 | constexpr 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 | |||
| 113 | void CleanupSpecialData(KProcess& dst_process, u32* dst_msg_ptr, size_t dst_buffer_size) { | 336 | void 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 | 370 | Result 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 | ||
| 149 | using 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 | ||
| 151 | KServerSession::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 | ||
| 154 | KServerSession::~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 | ||
| 156 | void 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 | ||
| 164 | void KServerSession::OnClientClosed() { | 418 | Result 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; | 449 | Result 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 | |||
| 477 | Result 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 | |||
| 488 | Result 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 | |||
| 540 | Result 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. | 767 | Result 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. | 811 | Result 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 | ||
| 257 | bool KServerSession::IsSignaled() const { | 859 | Result 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 | ||
| 269 | Result KServerSession::OnRequest(KSessionRequest* request) { | 1067 | void 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 | |||
| 1079 | KServerSession::KServerSession(KernelCore& kernel) | ||
| 1080 | : KSynchronizationObject{kernel}, m_lock{m_kernel} {} | ||
| 1081 | |||
| 1082 | KServerSession::~KServerSession() = default; | ||
| 1083 | |||
| 1084 | void KServerSession::Destroy() { | ||
| 1085 | m_parent->OnServerClosed(); | ||
| 1086 | |||
| 1087 | this->CleanupRequests(); | ||
| 1088 | |||
| 1089 | m_parent->Close(); | ||
| 1090 | } | ||
| 1091 | |||
| 1092 | Result 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 | ||
| 306 | Result KServerSession::SendReply(bool is_hle) { | 1217 | Result 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 | ||
| 413 | Result KServerSession::ReceiveRequest(std::shared_ptr<Service::HLERequestContext>* out_context, | 1321 | Result 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) { | 1358 | bool 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 | ||
| 500 | void KServerSession::CleanupRequests() { | 1370 | void 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 | ||
| 1439 | void 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 | ||
| 62 | private: | 69 | private: |
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 | ||
| 1424 | Core::Memory::Memory& GetCurrentMemory(KernelCore& kernel) { | 1424 | Core::Memory::Memory& GetCurrentMemory(KernelCore& kernel) { |
| 1425 | // TODO: per-process memory | 1425 | return GetCurrentProcess(kernel).GetMemory(); |
| 1426 | return kernel.System().ApplicationMemory(); | ||
| 1427 | } | 1426 | } |
| 1428 | 1427 | ||
| 1429 | KScopedDisableDispatch::~KScopedDisableDispatch() { | 1428 | KScopedDisableDispatch::~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 | ||
| 885 | KScopedAutoObject<KThread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const { | ||
| 886 | return impl->global_handle_table->GetObject<KThread>(handle); | ||
| 887 | } | ||
| 888 | |||
| 889 | void KernelCore::AppendNewProcess(KProcess* process) { | 871 | void 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 | ||
| 962 | Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() { | ||
| 963 | return *impl->exclusive_monitor; | ||
| 964 | } | ||
| 965 | |||
| 966 | const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const { | ||
| 967 | return *impl->exclusive_monitor; | ||
| 968 | } | ||
| 969 | |||
| 970 | KAutoObjectWithListContainer& KernelCore::ObjectListContainer() { | 944 | KAutoObjectWithListContainer& 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 | ||
| 1033 | KHandleTable& KernelCore::GlobalHandleTable() { | ||
| 1034 | return *impl->global_handle_table; | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | const KHandleTable& KernelCore::GlobalHandleTable() const { | ||
| 1038 | return *impl->global_handle_table; | ||
| 1039 | } | ||
| 1040 | |||
| 1041 | void KernelCore::RegisterCoreThread(std::size_t core_id) { | 1007 | void 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}; | |||
| 38 | constexpr Result ResultReservedUsed{ErrorModule::Kernel, 126}; | 38 | constexpr Result ResultReservedUsed{ErrorModule::Kernel, 126}; |
| 39 | constexpr Result ResultPortClosed{ErrorModule::Kernel, 131}; | 39 | constexpr Result ResultPortClosed{ErrorModule::Kernel, 131}; |
| 40 | constexpr Result ResultLimitReached{ErrorModule::Kernel, 132}; | 40 | constexpr Result ResultLimitReached{ErrorModule::Kernel, 132}; |
| 41 | constexpr Result ResultReceiveListBroken{ErrorModule::Kernel, 258}; | ||
| 41 | constexpr Result ResultOutOfAddressSpace{ErrorModule::Kernel, 259}; | 42 | constexpr Result ResultOutOfAddressSpace{ErrorModule::Kernel, 259}; |
| 43 | constexpr Result ResultMessageTooLarge{ErrorModule::Kernel, 260}; | ||
| 42 | constexpr Result ResultInvalidId{ErrorModule::Kernel, 519}; | 44 | constexpr 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 | ||
| 147 | HLERequestContext::~HLERequestContext() = default; | 147 | HLERequestContext::~HLERequestContext() = default; |
| 148 | 148 | ||
| 149 | void HLERequestContext::ParseCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf, | 149 | void 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 | ||
| 273 | Result HLERequestContext::PopulateFromIncomingCommandBuffer(Kernel::KProcess& process, | 270 | Result 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 | ||
| 287 | Result HLERequestContext::WriteToOutgoingCommandBuffer(Kernel::KThread& requesting_thread) { | 285 | Result 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 | ||
| 22 | union Result; | 23 | union 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: | |||
| 378 | private: | 388 | private: |
| 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 | ||
| 24 | class IJitEnvironment final : public ServiceFramework<IJitEnvironment> { | 24 | class IJitEnvironment final : public ServiceFramework<IJitEnvironment> { |
| 25 | public: | 25 | public: |
| 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 | ||
| 345 | private: | ||
| 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 | |||
| 418 | private: | ||
| 419 | std::mt19937_64 generate_random{}; | ||
| 401 | }; | 420 | }; |
| 402 | 421 | ||
| 403 | void LoopProcess(Core::System& system) { | 422 | void 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 | |||
| 6 | namespace Service::JIT { | ||
| 7 | |||
| 8 | Result 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 | |||
| 45 | void 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 | |||
| 10 | namespace Service::JIT { | ||
| 11 | |||
| 12 | class CodeMemory { | ||
| 13 | public: | ||
| 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 | |||
| 29 | public: | ||
| 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 | |||
| 42 | private: | ||
| 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 | ||
| 510 | void 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 | |||
| 510 | void SET_SYS::GetQuestFlag(HLERequestContext& ctx) { | 518 | void 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 | ||
| 1145 | void SET_SYS::StoreSettingsThreadFunc(std::stop_token stop_token) { | 1153 | void 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 | ||
| 30 | ServiceManager::~ServiceManager() { | 30 | ServiceManager::~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 | ||
| 53 | Result ServiceManager::RegisterService(std::string name, u32 max_sessions, | 52 | Result 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 | ||
| 78 | Result ServiceManager::UnregisterService(const std::string& name) { | 81 | Result 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 | ||
| 94 | Result ServiceManager::GetServicePort(Kernel::KPort** out_port, const std::string& name) { | 97 | Result 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 | ||
| 241 | void SM::UnregisterService(HLERequestContext& ctx) { | 243 | void 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) { | |||
| 28 | void Controller::CloneCurrentObject(HLERequestContext& ctx) { | 28 | void 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 | ||
| 860 | Memory::Memory(Core::System& system_) : system{system_} { | 879 | Memory::Memory(Core::System& system_) : system{system_} { |
| @@ -872,12 +891,14 @@ void Memory::SetCurrentPageTable(Kernel::KProcess& process) { | |||
| 872 | } | 891 | } |
| 873 | 892 | ||
| 874 | void Memory::MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, | 893 | void 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 | ||
| 879 | void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size) { | 899 | void 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 | ||
| 883 | void Memory::ProtectRegion(Common::PageTable& page_table, Common::ProcessAddress vaddr, u64 size, | 904 | void 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 | ||
| 888 | bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const { | 909 | bool 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 | ||
| 1050 | bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) { | 1070 | bool 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 | ||
| 1095 | bool 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 | ||
| 491 | private: | 494 | private: |
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 | ||
| 98 | Id DefineVariable(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin, | 98 | Id 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 | ||
| 146 | Id DefineOutput(EmitContext& ctx, Id type, std::optional<u32> invocations, | 146 | Id 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 | ||
| 154 | void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invocations) { | 155 | void 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; | |||
| 12 | static constexpr size_t VIRTUAL_SIZE = 1ULL << 39; | 12 | static constexpr size_t VIRTUAL_SIZE = 1ULL << 39; |
| 13 | static constexpr size_t BACKING_SIZE = 4_GiB; | 13 | static constexpr size_t BACKING_SIZE = 4_GiB; |
| 14 | static constexpr auto PERMS = Common::MemoryPermission::ReadWrite; | 14 | static constexpr auto PERMS = Common::MemoryPermission::ReadWrite; |
| 15 | static constexpr auto HEAP = false; | ||
| 15 | 16 | ||
| 16 | TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") { | 17 | TEST_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 | ||
| 21 | TEST_CASE("HostMemory: Simple map", "[common]") { | 22 | TEST_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 | ||
| 30 | TEST_CASE("HostMemory: Simple mirror map", "[common]") { | 31 | TEST_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 | ||
| 41 | TEST_CASE("HostMemory: Simple unmap", "[common]") { | 42 | TEST_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 | ||
| 52 | TEST_CASE("HostMemory: Simple unmap and remap", "[common]") { | 53 | TEST_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 | ||
| 69 | TEST_CASE("HostMemory: Nieche allocation", "[common]") { | 70 | TEST_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 | ||
| 78 | TEST_CASE("HostMemory: Full unmap", "[common]") { | 79 | TEST_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 | ||
| 85 | TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") { | 86 | TEST_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 | ||
| 92 | TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") { | 93 | TEST_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 | ||
| 99 | TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") { | 100 | TEST_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 | ||
| 107 | TEST_CASE("HostMemory: Unmap between placeholders", "[common]") { | 108 | TEST_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 | ||
| 115 | TEST_CASE("HostMemory: Unmap to origin", "[common]") { | 116 | TEST_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 | ||
| 124 | TEST_CASE("HostMemory: Unmap to right", "[common]") { | 125 | TEST_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 | ||
| 132 | TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") { | 133 | TEST_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 | ||
| 144 | TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") { | 145 | TEST_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 | ||
| 158 | TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") { | 159 | TEST_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 | ||
| 172 | TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common]") { | 173 | TEST_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 | ||
| 379 | void 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 | |||
| 385 | GLuint 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 | ||
| 238 | struct BufferCacheParams { | 243 | struct 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(¶ms); | 310 | buffer_cache.SetDrawIndirect(¶ms); |
| 311 | PrepareDraw(params.is_indexed, [this, ¶ms](GLenum primitive_mode) { | 311 | PrepareDraw(params.is_indexed, [this, ¶ms](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 | ||
| 1381 | void RasterizerOpenGL::RegisterTransformFeedback(GPUVAddr tfb_object_addr) { | ||
| 1382 | buffer_cache_runtime.BindTransformFeedbackObject(tfb_object_addr); | ||
| 1383 | } | ||
| 1384 | |||
| 1374 | AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_, TextureCache& texture_cache_) | 1385 | AccelerateDMA::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 | |||
| 142 | private: | 148 | private: |
| 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 | ||
| 210 | void OGLTransformFeedback::Create() { | ||
| 211 | if (handle != 0) | ||
| 212 | return; | ||
| 213 | |||
| 214 | MICROPROFILE_SCOPE(OpenGL_ResourceCreation); | ||
| 215 | glCreateTransformFeedbacks(1, &handle); | ||
| 216 | } | ||
| 217 | |||
| 218 | void 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 | ||
| 326 | class OGLTransformFeedback final { | ||
| 327 | public: | ||
| 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 | ||
| 62 | template <auto decompress, PixelFormat pixel_format> | 62 | template <auto decompress, PixelFormat pixel_format> |
| 63 | void DecompressBlocks(std::span<const u8> input, std::span<u8> output, Extent3D extent, | 63 | void 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 | ||
| 90 | void DecompressBCn(std::span<const u8> input, std::span<u8> output, Extent3D extent, | 96 | void 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 | ||
| 16 | void DecompressBCn(std::span<const u8> input, std::span<u8> output, Extent3D extent, | 16 | void 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 | ||