summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/app/src/main/AndroidManifest.xml2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt114
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeSetting.kt5
-rw-r--r--src/android/app/src/main/jni/native.cpp2
-rw-r--r--src/android/app/src/main/res/values/strings.xml4
-rw-r--r--src/audio_core/device/device_session.cpp6
-rw-r--r--src/audio_core/renderer/command/data_source/decode.cpp21
-rw-r--r--src/audio_core/renderer/command/effect/aux_.cpp82
-rw-r--r--src/common/demangle.cpp2
-rw-r--r--src/common/detached_tasks.cpp4
-rw-r--r--src/common/page_table.cpp1
-rw-r--r--src/common/page_table.h1
-rw-r--r--src/common/scratch_buffer.h17
-rw-r--r--src/common/settings.cpp5
-rw-r--r--src/common/socket_types.h17
-rw-r--r--src/common/time_zone.cpp47
-rw-r--r--src/core/CMakeLists.txt19
-rw-r--r--src/core/arm/arm_interface.cpp2
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp6
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h6
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp6
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h6
-rw-r--r--src/core/arm/dynarmic/dynarmic_exclusive_monitor.h5
-rw-r--r--src/core/core.cpp8
-rw-r--r--src/core/core.h3
-rw-r--r--src/core/core_timing.cpp3
-rw-r--r--src/core/core_timing.h2
-rw-r--r--src/core/debugger/gdbstub.cpp4
-rw-r--r--src/core/file_sys/content_archive.cpp39
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp17
-rw-r--r--src/core/hle/kernel/k_page_table.h41
-rw-r--r--src/core/hle/kernel/k_process.cpp2
-rw-r--r--src/core/hle/kernel/k_process.h10
-rw-r--r--src/core/hle/kernel/k_server_session.cpp165
-rw-r--r--src/core/hle/kernel/k_shared_memory.cpp6
-rw-r--r--src/core/hle/kernel/k_thread.cpp4
-rw-r--r--src/core/hle/kernel/k_thread_local_page.cpp10
-rw-r--r--src/core/hle/kernel/kernel.cpp6
-rw-r--r--src/core/hle/kernel/message_buffer.h612
-rw-r--r--src/core/hle/kernel/physical_core.cpp8
-rw-r--r--src/core/hle/kernel/svc/svc_cache.cpp2
-rw-r--r--src/core/hle/kernel/svc/svc_code_memory.cpp14
-rw-r--r--src/core/hle/kernel/svc/svc_device_address_space.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_info.cpp16
-rw-r--r--src/core/hle/kernel/svc/svc_memory.cpp33
-rw-r--r--src/core/hle/kernel/svc/svc_physical_memory.cpp14
-rw-r--r--src/core/hle/kernel/svc/svc_process.cpp4
-rw-r--r--src/core/hle/kernel/svc/svc_process_memory.cpp34
-rw-r--r--src/core/hle/kernel/svc/svc_query_memory.cpp2
-rw-r--r--src/core/hle/kernel/svc/svc_shared_memory.cpp4
-rw-r--r--src/core/hle/kernel/svc/svc_thread.cpp2
-rw-r--r--src/core/hle/kernel/svc/svc_transfer_memory.cpp2
-rw-r--r--src/core/hle/service/am/am.cpp4
-rw-r--r--src/core/hle/service/hle_ipc.cpp32
-rw-r--r--src/core/hle/service/ldr/ldr.cpp20
-rw-r--r--src/core/hle/service/nfc/common/amiibo_crypto.cpp2
-rw-r--r--src/core/hle/service/nfc/common/amiibo_crypto.h2
-rw-r--r--src/core/hle/service/nfc/common/device.cpp3
-rw-r--r--src/core/hle/service/nfc/common/device_manager.cpp135
-rw-r--r--src/core/hle/service/nfc/common/device_manager.h34
-rw-r--r--src/core/hle/service/nfc/nfc_interface.cpp18
-rw-r--r--src/core/hle/service/nfc/nfc_result.h3
-rw-r--r--src/core/hle/service/nifm/nifm.cpp1
-rw-r--r--src/core/hle/service/nifm/nifm.h7
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp4
-rw-r--r--src/core/hle/service/sockets/bsd.cpp120
-rw-r--r--src/core/hle/service/sockets/bsd.h13
-rw-r--r--src/core/hle/service/sockets/nsd.cpp75
-rw-r--r--src/core/hle/service/sockets/nsd.h5
-rw-r--r--src/core/hle/service/sockets/sfdnsres.cpp388
-rw-r--r--src/core/hle/service/sockets/sfdnsres.h3
-rw-r--r--src/core/hle/service/sockets/sockets.h33
-rw-r--r--src/core/hle/service/sockets/sockets_translate.cpp114
-rw-r--r--src/core/hle/service/sockets/sockets_translate.h17
-rw-r--r--src/core/hle/service/ssl/ssl.cpp353
-rw-r--r--src/core/hle/service/ssl/ssl_backend.h45
-rw-r--r--src/core/hle/service/ssl/ssl_backend_none.cpp16
-rw-r--r--src/core/hle/service/ssl/ssl_backend_openssl.cpp351
-rw-r--r--src/core/hle/service/ssl/ssl_backend_schannel.cpp544
-rw-r--r--src/core/hle/service/ssl/ssl_backend_securetransport.cpp222
-rw-r--r--src/core/internal_network/network.cpp286
-rw-r--r--src/core/internal_network/network.h36
-rw-r--r--src/core/internal_network/socket_proxy.cpp23
-rw-r--r--src/core/internal_network/socket_proxy.h12
-rw-r--r--src/core/internal_network/sockets.h19
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp2
-rw-r--r--src/core/loader/kip.cpp2
-rw-r--r--src/core/loader/loader.h2
-rw-r--r--src/core/loader/nro.cpp2
-rw-r--r--src/core/loader/nso.cpp2
-rw-r--r--src/core/memory.cpp62
-rw-r--r--src/core/memory.h212
-rw-r--r--src/core/memory/cheat_engine.cpp2
-rw-r--r--src/core/reporter.cpp4
-rw-r--r--src/input_common/drivers/sdl_driver.cpp46
-rw-r--r--src/input_common/drivers/sdl_driver.h7
-rw-r--r--src/video_core/CMakeLists.txt6
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h17
-rw-r--r--src/video_core/buffer_cache/buffer_cache_base.h2
-rw-r--r--src/video_core/dma_pusher.cpp26
-rw-r--r--src/video_core/engines/engine_upload.cpp28
-rw-r--r--src/video_core/engines/kepler_compute.cpp1
-rw-r--r--src/video_core/engines/maxwell_3d.cpp4
-rw-r--r--src/video_core/engines/maxwell_dma.cpp87
-rw-r--r--src/video_core/engines/sw_blitter/blitter.cpp29
-rw-r--r--src/video_core/memory_manager.cpp30
-rw-r--r--src/video_core/memory_manager.h18
-rw-r--r--src/video_core/renderer_base.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp15
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp33
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp12
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.cpp11
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp6
-rw-r--r--src/video_core/texture_cache/texture_cache.h24
-rw-r--r--src/video_core/texture_cache/util.cpp26
-rw-r--r--src/video_core/texture_cache/util.h3
-rw-r--r--src/video_core/vulkan_common/vma.cpp8
-rw-r--r--src/web_service/announce_room_json.cpp10
-rw-r--r--src/yuzu/game_list_worker.cpp2
121 files changed, 4199 insertions, 963 deletions
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml
index 742685fb0..6184f3eb6 100644
--- a/src/android/app/src/main/AndroidManifest.xml
+++ b/src/android/app/src/main/AndroidManifest.xml
@@ -22,7 +22,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
22 android:label="@string/app_name_suffixed" 22 android:label="@string/app_name_suffixed"
23 android:icon="@drawable/ic_launcher" 23 android:icon="@drawable/ic_launcher"
24 android:allowBackup="true" 24 android:allowBackup="true"
25 android:hasFragileUserData="true" 25 android:hasFragileUserData="false"
26 android:supportsRtl="true" 26 android:supportsRtl="true"
27 android:isGame="true" 27 android:isGame="true"
28 android:localeConfig="@xml/locales_config" 28 android:localeConfig="@xml/locales_config"
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
index d3df3bc81..aadc445f9 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
@@ -12,6 +12,7 @@ import androidx.core.content.res.ResourcesCompat
12import androidx.recyclerview.widget.RecyclerView 12import androidx.recyclerview.widget.RecyclerView
13import org.yuzu.yuzu_emu.R 13import org.yuzu.yuzu_emu.R
14import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding 14import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding
15import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
15import org.yuzu.yuzu_emu.model.HomeSetting 16import org.yuzu.yuzu_emu.model.HomeSetting
16 17
17class HomeSettingAdapter(private val activity: AppCompatActivity, var options: List<HomeSetting>) : 18class HomeSettingAdapter(private val activity: AppCompatActivity, var options: List<HomeSetting>) :
@@ -34,7 +35,14 @@ class HomeSettingAdapter(private val activity: AppCompatActivity, var options: L
34 35
35 override fun onClick(view: View) { 36 override fun onClick(view: View) {
36 val holder = view.tag as HomeOptionViewHolder 37 val holder = view.tag as HomeOptionViewHolder
37 holder.option.onClick.invoke() 38 if (holder.option.isEnabled.invoke()) {
39 holder.option.onClick.invoke()
40 } else {
41 MessageDialogFragment.newInstance(
42 holder.option.disabledTitleId,
43 holder.option.disabledMessageId
44 ).show(activity.supportFragmentManager, MessageDialogFragment.TAG)
45 }
38 } 46 }
39 47
40 inner class HomeOptionViewHolder(val binding: CardHomeOptionBinding) : 48 inner class HomeOptionViewHolder(val binding: CardHomeOptionBinding) :
@@ -65,6 +73,12 @@ class HomeSettingAdapter(private val activity: AppCompatActivity, var options: L
65 R.drawable.premium_background 73 R.drawable.premium_background
66 ) 74 )
67 } 75 }
76
77 if (!option.isEnabled.invoke()) {
78 binding.optionTitle.alpha = 0.5f
79 binding.optionDescription.alpha = 0.5f
80 binding.optionIcon.alpha = 0.5f
81 }
68 } 82 }
69 } 83 }
70} 84}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
index 5a36ffad4..c001af892 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
@@ -73,102 +73,113 @@ class HomeSettingsFragment : Fragment() {
73 HomeSetting( 73 HomeSetting(
74 R.string.advanced_settings, 74 R.string.advanced_settings,
75 R.string.settings_description, 75 R.string.settings_description,
76 R.drawable.ic_settings 76 R.drawable.ic_settings,
77 ) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") } 77 { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") }
78 )
78 ) 79 )
79 add( 80 add(
80 HomeSetting( 81 HomeSetting(
81 R.string.open_user_folder, 82 R.string.open_user_folder,
82 R.string.open_user_folder_description, 83 R.string.open_user_folder_description,
83 R.drawable.ic_folder_open 84 R.drawable.ic_folder_open,
84 ) { openFileManager() } 85 { openFileManager() }
86 )
85 ) 87 )
86 add( 88 add(
87 HomeSetting( 89 HomeSetting(
88 R.string.preferences_theme, 90 R.string.preferences_theme,
89 R.string.theme_and_color_description, 91 R.string.theme_and_color_description,
90 R.drawable.ic_palette 92 R.drawable.ic_palette,
91 ) { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") } 93 { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") }
94 )
92 ) 95 )
93 96 add(
94 if (GpuDriverHelper.supportsCustomDriverLoading()) { 97 HomeSetting(
95 add( 98 R.string.install_gpu_driver,
96 HomeSetting( 99 R.string.install_gpu_driver_description,
97 R.string.install_gpu_driver, 100 R.drawable.ic_exit,
98 R.string.install_gpu_driver_description, 101 { driverInstaller() },
99 R.drawable.ic_exit 102 { GpuDriverHelper.supportsCustomDriverLoading() },
100 ) { driverInstaller() } 103 R.string.custom_driver_not_supported,
104 R.string.custom_driver_not_supported_description
101 ) 105 )
102 } 106 )
103
104 add( 107 add(
105 HomeSetting( 108 HomeSetting(
106 R.string.install_amiibo_keys, 109 R.string.install_amiibo_keys,
107 R.string.install_amiibo_keys_description, 110 R.string.install_amiibo_keys_description,
108 R.drawable.ic_nfc 111 R.drawable.ic_nfc,
109 ) { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) } 112 { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) }
113 )
110 ) 114 )
111 add( 115 add(
112 HomeSetting( 116 HomeSetting(
113 R.string.install_game_content, 117 R.string.install_game_content,
114 R.string.install_game_content_description, 118 R.string.install_game_content_description,
115 R.drawable.ic_system_update_alt 119 R.drawable.ic_system_update_alt,
116 ) { mainActivity.installGameUpdate.launch(arrayOf("*/*")) } 120 { mainActivity.installGameUpdate.launch(arrayOf("*/*")) }
121 )
117 ) 122 )
118 add( 123 add(
119 HomeSetting( 124 HomeSetting(
120 R.string.select_games_folder, 125 R.string.select_games_folder,
121 R.string.select_games_folder_description, 126 R.string.select_games_folder_description,
122 R.drawable.ic_add 127 R.drawable.ic_add,
123 ) { 128 {
124 mainActivity.getGamesDirectory.launch( 129 mainActivity.getGamesDirectory.launch(
125 Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data 130 Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data
126 ) 131 )
127 } 132 }
133 )
128 ) 134 )
129 add( 135 add(
130 HomeSetting( 136 HomeSetting(
131 R.string.manage_save_data, 137 R.string.manage_save_data,
132 R.string.import_export_saves_description, 138 R.string.import_export_saves_description,
133 R.drawable.ic_save 139 R.drawable.ic_save,
134 ) { 140 {
135 ImportExportSavesFragment().show( 141 ImportExportSavesFragment().show(
136 parentFragmentManager, 142 parentFragmentManager,
137 ImportExportSavesFragment.TAG 143 ImportExportSavesFragment.TAG
138 ) 144 )
139 } 145 }
146 )
140 ) 147 )
141 add( 148 add(
142 HomeSetting( 149 HomeSetting(
143 R.string.install_prod_keys, 150 R.string.install_prod_keys,
144 R.string.install_prod_keys_description, 151 R.string.install_prod_keys_description,
145 R.drawable.ic_unlock 152 R.drawable.ic_unlock,
146 ) { mainActivity.getProdKey.launch(arrayOf("*/*")) } 153 { mainActivity.getProdKey.launch(arrayOf("*/*")) }
154 )
147 ) 155 )
148 add( 156 add(
149 HomeSetting( 157 HomeSetting(
150 R.string.install_firmware, 158 R.string.install_firmware,
151 R.string.install_firmware_description, 159 R.string.install_firmware_description,
152 R.drawable.ic_firmware 160 R.drawable.ic_firmware,
153 ) { mainActivity.getFirmware.launch(arrayOf("application/zip")) } 161 { mainActivity.getFirmware.launch(arrayOf("application/zip")) }
162 )
154 ) 163 )
155 add( 164 add(
156 HomeSetting( 165 HomeSetting(
157 R.string.share_log, 166 R.string.share_log,
158 R.string.share_log_description, 167 R.string.share_log_description,
159 R.drawable.ic_log 168 R.drawable.ic_log,
160 ) { shareLog() } 169 { shareLog() }
170 )
161 ) 171 )
162 add( 172 add(
163 HomeSetting( 173 HomeSetting(
164 R.string.about, 174 R.string.about,
165 R.string.about_description, 175 R.string.about_description,
166 R.drawable.ic_info_outline 176 R.drawable.ic_info_outline,
167 ) { 177 {
168 exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) 178 exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
169 parentFragmentManager.primaryNavigationFragment?.findNavController() 179 parentFragmentManager.primaryNavigationFragment?.findNavController()
170 ?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment) 180 ?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment)
171 } 181 }
182 )
172 ) 183 )
173 } 184 }
174 185
@@ -178,12 +189,13 @@ class HomeSettingsFragment : Fragment() {
178 HomeSetting( 189 HomeSetting(
179 R.string.get_early_access, 190 R.string.get_early_access,
180 R.string.get_early_access_description, 191 R.string.get_early_access_description,
181 R.drawable.ic_diamond 192 R.drawable.ic_diamond,
182 ) { 193 {
183 exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) 194 exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
184 parentFragmentManager.primaryNavigationFragment?.findNavController() 195 parentFragmentManager.primaryNavigationFragment?.findNavController()
185 ?.navigate(R.id.action_homeSettingsFragment_to_earlyAccessFragment) 196 ?.navigate(R.id.action_homeSettingsFragment_to_earlyAccessFragment)
186 } 197 }
198 )
187 ) 199 )
188 } 200 }
189 201
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeSetting.kt
index 7049f2fa5..522d07c37 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeSetting.kt
@@ -7,5 +7,8 @@ data class HomeSetting(
7 val titleId: Int, 7 val titleId: Int,
8 val descriptionId: Int, 8 val descriptionId: Int,
9 val iconId: Int, 9 val iconId: Int,
10 val onClick: () -> Unit 10 val onClick: () -> Unit,
11 val isEnabled: () -> Boolean = { true },
12 val disabledTitleId: Int = 0,
13 val disabledMessageId: Int = 0
11) 14)
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 8bc6a4a04..c23b2f19e 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -449,7 +449,7 @@ private:
449 loader->ReadTitle(entry.title); 449 loader->ReadTitle(entry.title);
450 loader->ReadIcon(entry.icon); 450 loader->ReadIcon(entry.icon);
451 if (loader->GetFileType() == Loader::FileType::NRO) { 451 if (loader->GetFileType() == Loader::FileType::NRO) {
452 jauto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get()); 452 jauto loader_nro = reinterpret_cast<Loader::AppLoader_NRO*>(loader.get());
453 entry.isHomebrew = loader_nro->IsHomebrew(); 453 entry.isHomebrew = loader_nro->IsHomebrew();
454 } else { 454 } else {
455 entry.isHomebrew = false; 455 entry.isHomebrew = false;
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index b3c737979..bfdebd35b 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -113,6 +113,8 @@
113 <string name="install_game_content_success_install">%1$d installed successfully</string> 113 <string name="install_game_content_success_install">%1$d installed successfully</string>
114 <string name="install_game_content_success_overwrite">%1$d overwritten successfully</string> 114 <string name="install_game_content_success_overwrite">%1$d overwritten successfully</string>
115 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> 115 <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
116 <string name="custom_driver_not_supported">Custom drivers not supported</string>
117 <string name="custom_driver_not_supported_description">Custom driver loading isn\'t currently supported for this device.\nCheck this option again in the future to see if support was added!</string>
116 118
117 <!-- About screen strings --> 119 <!-- About screen strings -->
118 <string name="gaia_is_not_real">Gaia isn\'t real</string> 120 <string name="gaia_is_not_real">Gaia isn\'t real</string>
@@ -230,7 +232,7 @@
230 232
231 <!-- ROM loading errors --> 233 <!-- ROM loading errors -->
232 <string name="loader_error_encrypted">Your ROM is encrypted</string> 234 <string name="loader_error_encrypted">Your ROM is encrypted</string>
233 <string name="loader_error_encrypted_roms_description"><![CDATA[Please follow the guides to redump your <a href="https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games">game cartidges</a> or <a href="https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop">installed titles</a>.]]></string> 235 <string name="loader_error_encrypted_roms_description"><![CDATA[Please follow the guides to redump your <a href="https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards">game cartidges</a> or <a href="https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop">installed titles</a>.]]></string>
234 <string name="loader_error_encrypted_keys_description"><![CDATA[Please ensure your <a href="https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys">prod.keys</a> file is installed so that games can be decrypted.]]></string> 236 <string name="loader_error_encrypted_keys_description"><![CDATA[Please ensure your <a href="https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys">prod.keys</a> file is installed so that games can be decrypted.]]></string>
235 <string name="loader_error_video_core">An error occurred initializing the video core</string> 237 <string name="loader_error_video_core">An error occurred initializing the video core</string>
236 <string name="loader_error_video_core_description">This is usually caused by an incompatible GPU driver. Installing a custom GPU driver may resolve this problem.</string> 238 <string name="loader_error_video_core_description">This is usually caused by an incompatible GPU driver. Installing a custom GPU driver may resolve this problem.</string>
diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp
index 86811fcb8..c41d9d1ea 100644
--- a/src/audio_core/device/device_session.cpp
+++ b/src/audio_core/device/device_session.cpp
@@ -92,9 +92,9 @@ void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) {
92 if (type == Sink::StreamType::In) { 92 if (type == Sink::StreamType::In) {
93 stream->AppendBuffer(new_buffer, tmp_samples); 93 stream->AppendBuffer(new_buffer, tmp_samples);
94 } else { 94 } else {
95 system.ApplicationMemory().ReadBlockUnsafe(buffer.samples, tmp_samples.data(), 95 Core::Memory::CpuGuestMemory<s16, Core::Memory::GuestMemoryFlags::UnsafeRead> samples(
96 buffer.size); 96 system.ApplicationMemory(), buffer.samples, buffer.size / sizeof(s16));
97 stream->AppendBuffer(new_buffer, tmp_samples); 97 stream->AppendBuffer(new_buffer, samples);
98 } 98 }
99 } 99 }
100} 100}
diff --git a/src/audio_core/renderer/command/data_source/decode.cpp b/src/audio_core/renderer/command/data_source/decode.cpp
index f45933203..257aa866e 100644
--- a/src/audio_core/renderer/command/data_source/decode.cpp
+++ b/src/audio_core/renderer/command/data_source/decode.cpp
@@ -28,7 +28,6 @@ constexpr std::array<u8, 3> PitchBySrcQuality = {4, 8, 4};
28template <typename T> 28template <typename T>
29static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, 29static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
30 const DecodeArg& req) { 30 const DecodeArg& req) {
31 std::array<T, TempBufferSize> tmp_samples{};
32 constexpr s32 min{std::numeric_limits<s16>::min()}; 31 constexpr s32 min{std::numeric_limits<s16>::min()};
33 constexpr s32 max{std::numeric_limits<s16>::max()}; 32 constexpr s32 max{std::numeric_limits<s16>::max()};
34 33
@@ -49,19 +48,18 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
49 const VAddr source{req.buffer + 48 const VAddr source{req.buffer +
50 (((req.start_offset + req.offset) * channel_count) * sizeof(T))}; 49 (((req.start_offset + req.offset) * channel_count) * sizeof(T))};
51 const u64 size{channel_count * samples_to_decode}; 50 const u64 size{channel_count * samples_to_decode};
52 const u64 size_bytes{size * sizeof(T)};
53
54 memory.ReadBlockUnsafe(source, tmp_samples.data(), size_bytes);
55 51
52 Core::Memory::CpuGuestMemory<T, Core::Memory::GuestMemoryFlags::UnsafeRead> samples(
53 memory, source, size);
56 if constexpr (std::is_floating_point_v<T>) { 54 if constexpr (std::is_floating_point_v<T>) {
57 for (u32 i = 0; i < samples_to_decode; i++) { 55 for (u32 i = 0; i < samples_to_decode; i++) {
58 auto sample{static_cast<s32>(tmp_samples[i * channel_count + req.target_channel] * 56 auto sample{static_cast<s32>(samples[i * channel_count + req.target_channel] *
59 std::numeric_limits<s16>::max())}; 57 std::numeric_limits<s16>::max())};
60 out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max)); 58 out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max));
61 } 59 }
62 } else { 60 } else {
63 for (u32 i = 0; i < samples_to_decode; i++) { 61 for (u32 i = 0; i < samples_to_decode; i++) {
64 out_buffer[i] = tmp_samples[i * channel_count + req.target_channel]; 62 out_buffer[i] = samples[i * channel_count + req.target_channel];
65 } 63 }
66 } 64 }
67 } break; 65 } break;
@@ -74,16 +72,17 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
74 } 72 }
75 73
76 const VAddr source{req.buffer + ((req.start_offset + req.offset) * sizeof(T))}; 74 const VAddr source{req.buffer + ((req.start_offset + req.offset) * sizeof(T))};
77 memory.ReadBlockUnsafe(source, tmp_samples.data(), samples_to_decode * sizeof(T)); 75 Core::Memory::CpuGuestMemory<T, Core::Memory::GuestMemoryFlags::UnsafeRead> samples(
76 memory, source, samples_to_decode);
78 77
79 if constexpr (std::is_floating_point_v<T>) { 78 if constexpr (std::is_floating_point_v<T>) {
80 for (u32 i = 0; i < samples_to_decode; i++) { 79 for (u32 i = 0; i < samples_to_decode; i++) {
81 auto sample{static_cast<s32>(tmp_samples[i * channel_count + req.target_channel] * 80 auto sample{static_cast<s32>(samples[i * channel_count + req.target_channel] *
82 std::numeric_limits<s16>::max())}; 81 std::numeric_limits<s16>::max())};
83 out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max)); 82 out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max));
84 } 83 }
85 } else { 84 } else {
86 std::memcpy(out_buffer.data(), tmp_samples.data(), samples_to_decode * sizeof(s16)); 85 std::memcpy(out_buffer.data(), samples.data(), samples_to_decode * sizeof(s16));
87 } 86 }
88 break; 87 break;
89 } 88 }
@@ -101,7 +100,6 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
101 */ 100 */
102static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, 101static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
103 const DecodeArg& req) { 102 const DecodeArg& req) {
104 std::array<u8, TempBufferSize> wavebuffer{};
105 constexpr u32 SamplesPerFrame{14}; 103 constexpr u32 SamplesPerFrame{14};
106 constexpr u32 NibblesPerFrame{16}; 104 constexpr u32 NibblesPerFrame{16};
107 105
@@ -139,7 +137,8 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
139 } 137 }
140 138
141 const auto size{std::max((samples_to_process / 8U) * SamplesPerFrame, 8U)}; 139 const auto size{std::max((samples_to_process / 8U) * SamplesPerFrame, 8U)};
142 memory.ReadBlockUnsafe(req.buffer + position_in_frame / 2, wavebuffer.data(), size); 140 Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> wavebuffer(
141 memory, req.buffer + position_in_frame / 2, size);
143 142
144 auto context{req.adpcm_context}; 143 auto context{req.adpcm_context};
145 auto header{context->header}; 144 auto header{context->header};
diff --git a/src/audio_core/renderer/command/effect/aux_.cpp b/src/audio_core/renderer/command/effect/aux_.cpp
index c5650effa..a3e12b3e7 100644
--- a/src/audio_core/renderer/command/effect/aux_.cpp
+++ b/src/audio_core/renderer/command/effect/aux_.cpp
@@ -21,23 +21,13 @@ static void ResetAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr aux_in
21 } 21 }
22 22
23 AuxInfo::AuxInfoDsp info{}; 23 AuxInfo::AuxInfoDsp info{};
24 auto info_ptr{&info}; 24 memory.ReadBlockUnsafe(aux_info, &info, sizeof(AuxInfo::AuxInfoDsp));
25 bool host_safe{(aux_info & Core::Memory::YUZU_PAGEMASK) <=
26 (Core::Memory::YUZU_PAGESIZE - sizeof(AuxInfo::AuxInfoDsp))};
27 25
28 if (host_safe) [[likely]] { 26 info.read_offset = 0;
29 info_ptr = memory.GetPointer<AuxInfo::AuxInfoDsp>(aux_info); 27 info.write_offset = 0;
30 } else { 28 info.total_sample_count = 0;
31 memory.ReadBlockUnsafe(aux_info, info_ptr, sizeof(AuxInfo::AuxInfoDsp));
32 }
33 29
34 info_ptr->read_offset = 0; 30 memory.WriteBlockUnsafe(aux_info, &info, sizeof(AuxInfo::AuxInfoDsp));
35 info_ptr->write_offset = 0;
36 info_ptr->total_sample_count = 0;
37
38 if (!host_safe) [[unlikely]] {
39 memory.WriteBlockUnsafe(aux_info, info_ptr, sizeof(AuxInfo::AuxInfoDsp));
40 }
41} 31}
42 32
43/** 33/**
@@ -86,17 +76,9 @@ static u32 WriteAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr send_info_,
86 } 76 }
87 77
88 AuxInfo::AuxInfoDsp send_info{}; 78 AuxInfo::AuxInfoDsp send_info{};
89 auto send_ptr = &send_info; 79 memory.ReadBlockUnsafe(send_info_, &send_info, sizeof(AuxInfo::AuxInfoDsp));
90 bool host_safe = (send_info_ & Core::Memory::YUZU_PAGEMASK) <=
91 (Core::Memory::YUZU_PAGESIZE - sizeof(AuxInfo::AuxInfoDsp));
92
93 if (host_safe) [[likely]] {
94 send_ptr = memory.GetPointer<AuxInfo::AuxInfoDsp>(send_info_);
95 } else {
96 memory.ReadBlockUnsafe(send_info_, send_ptr, sizeof(AuxInfo::AuxInfoDsp));
97 }
98 80
99 u32 target_write_offset{send_ptr->write_offset + write_offset}; 81 u32 target_write_offset{send_info.write_offset + write_offset};
100 if (target_write_offset > count_max) { 82 if (target_write_offset > count_max) {
101 return 0; 83 return 0;
102 } 84 }
@@ -105,15 +87,9 @@ static u32 WriteAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr send_info_,
105 u32 read_pos{0}; 87 u32 read_pos{0};
106 while (write_count > 0) { 88 while (write_count > 0) {
107 u32 to_write{std::min(count_max - target_write_offset, write_count)}; 89 u32 to_write{std::min(count_max - target_write_offset, write_count)};
108 const auto write_addr = send_buffer + target_write_offset * sizeof(s32); 90 if (to_write > 0) {
109 bool write_safe{(write_addr & Core::Memory::YUZU_PAGEMASK) <= 91 const auto write_addr = send_buffer + target_write_offset * sizeof(s32);
110 (Core::Memory::YUZU_PAGESIZE - (write_addr + to_write * sizeof(s32)))}; 92 memory.WriteBlockUnsafe(write_addr, &input[read_pos], to_write * sizeof(s32));
111 if (write_safe) [[likely]] {
112 auto ptr = memory.GetPointer(write_addr);
113 std::memcpy(ptr, &input[read_pos], to_write * sizeof(s32));
114 } else {
115 memory.WriteBlockUnsafe(send_buffer + target_write_offset * sizeof(s32),
116 &input[read_pos], to_write * sizeof(s32));
117 } 93 }
118 target_write_offset = (target_write_offset + to_write) % count_max; 94 target_write_offset = (target_write_offset + to_write) % count_max;
119 write_count -= to_write; 95 write_count -= to_write;
@@ -121,13 +97,10 @@ static u32 WriteAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr send_info_,
121 } 97 }
122 98
123 if (update_count) { 99 if (update_count) {
124 send_ptr->write_offset = (send_ptr->write_offset + update_count) % count_max; 100 send_info.write_offset = (send_info.write_offset + update_count) % count_max;
125 }
126
127 if (!host_safe) [[unlikely]] {
128 memory.WriteBlockUnsafe(send_info_, send_ptr, sizeof(AuxInfo::AuxInfoDsp));
129 } 101 }
130 102
103 memory.WriteBlockUnsafe(send_info_, &send_info, sizeof(AuxInfo::AuxInfoDsp));
131 return write_count_; 104 return write_count_;
132} 105}
133 106
@@ -174,17 +147,9 @@ static u32 ReadAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr return_info_,
174 } 147 }
175 148
176 AuxInfo::AuxInfoDsp return_info{}; 149 AuxInfo::AuxInfoDsp return_info{};
177 auto return_ptr = &return_info; 150 memory.ReadBlockUnsafe(return_info_, &return_info, sizeof(AuxInfo::AuxInfoDsp));
178 bool host_safe = (return_info_ & Core::Memory::YUZU_PAGEMASK) <=
179 (Core::Memory::YUZU_PAGESIZE - sizeof(AuxInfo::AuxInfoDsp));
180 151
181 if (host_safe) [[likely]] { 152 u32 target_read_offset{return_info.read_offset + read_offset};
182 return_ptr = memory.GetPointer<AuxInfo::AuxInfoDsp>(return_info_);
183 } else {
184 memory.ReadBlockUnsafe(return_info_, return_ptr, sizeof(AuxInfo::AuxInfoDsp));
185 }
186
187 u32 target_read_offset{return_ptr->read_offset + read_offset};
188 if (target_read_offset > count_max) { 153 if (target_read_offset > count_max) {
189 return 0; 154 return 0;
190 } 155 }
@@ -193,15 +158,9 @@ static u32 ReadAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr return_info_,
193 u32 write_pos{0}; 158 u32 write_pos{0};
194 while (read_count > 0) { 159 while (read_count > 0) {
195 u32 to_read{std::min(count_max - target_read_offset, read_count)}; 160 u32 to_read{std::min(count_max - target_read_offset, read_count)};
196 const auto read_addr = return_buffer + target_read_offset * sizeof(s32); 161 if (to_read > 0) {
197 bool read_safe{(read_addr & Core::Memory::YUZU_PAGEMASK) <= 162 const auto read_addr = return_buffer + target_read_offset * sizeof(s32);
198 (Core::Memory::YUZU_PAGESIZE - (read_addr + to_read * sizeof(s32)))}; 163 memory.ReadBlockUnsafe(read_addr, &output[write_pos], to_read * sizeof(s32));
199 if (read_safe) [[likely]] {
200 auto ptr = memory.GetPointer(read_addr);
201 std::memcpy(&output[write_pos], ptr, to_read * sizeof(s32));
202 } else {
203 memory.ReadBlockUnsafe(return_buffer + target_read_offset * sizeof(s32),
204 &output[write_pos], to_read * sizeof(s32));
205 } 164 }
206 target_read_offset = (target_read_offset + to_read) % count_max; 165 target_read_offset = (target_read_offset + to_read) % count_max;
207 read_count -= to_read; 166 read_count -= to_read;
@@ -209,13 +168,10 @@ static u32 ReadAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr return_info_,
209 } 168 }
210 169
211 if (update_count) { 170 if (update_count) {
212 return_ptr->read_offset = (return_ptr->read_offset + update_count) % count_max; 171 return_info.read_offset = (return_info.read_offset + update_count) % count_max;
213 }
214
215 if (!host_safe) [[unlikely]] {
216 memory.WriteBlockUnsafe(return_info_, return_ptr, sizeof(AuxInfo::AuxInfoDsp));
217 } 172 }
218 173
174 memory.WriteBlockUnsafe(return_info_, &return_info, sizeof(AuxInfo::AuxInfoDsp));
219 return read_count_; 175 return read_count_;
220} 176}
221 177
diff --git a/src/common/demangle.cpp b/src/common/demangle.cpp
index 3310faf86..6e117cb41 100644
--- a/src/common/demangle.cpp
+++ b/src/common/demangle.cpp
@@ -23,7 +23,7 @@ std::string DemangleSymbol(const std::string& mangled) {
23 SCOPE_EXIT({ std::free(demangled); }); 23 SCOPE_EXIT({ std::free(demangled); });
24 24
25 if (is_itanium(mangled)) { 25 if (is_itanium(mangled)) {
26 demangled = llvm::itaniumDemangle(mangled.c_str(), nullptr, nullptr, nullptr); 26 demangled = llvm::itaniumDemangle(mangled.c_str());
27 } 27 }
28 28
29 if (!demangled) { 29 if (!demangled) {
diff --git a/src/common/detached_tasks.cpp b/src/common/detached_tasks.cpp
index da64848da..f2ed795cc 100644
--- a/src/common/detached_tasks.cpp
+++ b/src/common/detached_tasks.cpp
@@ -30,8 +30,8 @@ DetachedTasks::~DetachedTasks() {
30void DetachedTasks::AddTask(std::function<void()> task) { 30void DetachedTasks::AddTask(std::function<void()> task) {
31 std::unique_lock lock{instance->mutex}; 31 std::unique_lock lock{instance->mutex};
32 ++instance->count; 32 ++instance->count;
33 std::thread([task{std::move(task)}]() { 33 std::thread([task_{std::move(task)}]() {
34 task(); 34 task_();
35 std::unique_lock thread_lock{instance->mutex}; 35 std::unique_lock thread_lock{instance->mutex};
36 --instance->count; 36 --instance->count;
37 std::notify_all_at_thread_exit(instance->cv, std::move(thread_lock)); 37 std::notify_all_at_thread_exit(instance->cv, std::move(thread_lock));
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp
index b744b68ce..4b1690269 100644
--- a/src/common/page_table.cpp
+++ b/src/common/page_table.cpp
@@ -66,6 +66,7 @@ void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page
66 << (address_space_width_in_bits - page_size_in_bits)}; 66 << (address_space_width_in_bits - page_size_in_bits)};
67 pointers.resize(num_page_table_entries); 67 pointers.resize(num_page_table_entries);
68 backing_addr.resize(num_page_table_entries); 68 backing_addr.resize(num_page_table_entries);
69 blocks.resize(num_page_table_entries);
69 current_address_space_width_in_bits = address_space_width_in_bits; 70 current_address_space_width_in_bits = address_space_width_in_bits;
70 page_size = 1ULL << page_size_in_bits; 71 page_size = 1ULL << page_size_in_bits;
71} 72}
diff --git a/src/common/page_table.h b/src/common/page_table.h
index 1ad3a9f8b..fec8378f3 100644
--- a/src/common/page_table.h
+++ b/src/common/page_table.h
@@ -122,6 +122,7 @@ struct PageTable {
122 * corresponding attribute element is of type `Memory`. 122 * corresponding attribute element is of type `Memory`.
123 */ 123 */
124 VirtualBuffer<PageInfo> pointers; 124 VirtualBuffer<PageInfo> pointers;
125 VirtualBuffer<u64> blocks;
125 126
126 VirtualBuffer<u64> backing_addr; 127 VirtualBuffer<u64> backing_addr;
127 128
diff --git a/src/common/scratch_buffer.h b/src/common/scratch_buffer.h
index d5961b020..2a98cda53 100644
--- a/src/common/scratch_buffer.h
+++ b/src/common/scratch_buffer.h
@@ -40,8 +40,21 @@ public:
40 ~ScratchBuffer() = default; 40 ~ScratchBuffer() = default;
41 ScratchBuffer(const ScratchBuffer&) = delete; 41 ScratchBuffer(const ScratchBuffer&) = delete;
42 ScratchBuffer& operator=(const ScratchBuffer&) = delete; 42 ScratchBuffer& operator=(const ScratchBuffer&) = delete;
43 ScratchBuffer(ScratchBuffer&&) = default; 43
44 ScratchBuffer& operator=(ScratchBuffer&&) = default; 44 ScratchBuffer(ScratchBuffer&& other) noexcept {
45 swap(other);
46 other.last_requested_size = 0;
47 other.buffer_capacity = 0;
48 other.buffer.reset();
49 }
50
51 ScratchBuffer& operator=(ScratchBuffer&& other) noexcept {
52 swap(other);
53 other.last_requested_size = 0;
54 other.buffer_capacity = 0;
55 other.buffer.reset();
56 return *this;
57 }
45 58
46 /// This will only grow the buffer's capacity if size is greater than the current capacity. 59 /// This will only grow the buffer's capacity if size is greater than the current capacity.
47 /// The previously held data will remain intact. 60 /// The previously held data will remain intact.
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 6cbbea1b2..d4e55f988 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -26,9 +26,10 @@ std::string GetTimeZoneString() {
26 26
27 std::string location_name; 27 std::string location_name;
28 if (time_zone_index == 0) { // Auto 28 if (time_zone_index == 0) { // Auto
29#if __cpp_lib_chrono >= 201907L 29#if __cpp_lib_chrono >= 201907L && !defined(MINGW)
30 const struct std::chrono::tzdb& time_zone_data = std::chrono::get_tzdb(); 30 // Disabled for MinGW -- tzdb always returns Etc/UTC
31 try { 31 try {
32 const struct std::chrono::tzdb& time_zone_data = std::chrono::get_tzdb();
32 const std::chrono::time_zone* current_zone = time_zone_data.current_zone(); 33 const std::chrono::time_zone* current_zone = time_zone_data.current_zone();
33 std::string_view current_zone_name = current_zone->name(); 34 std::string_view current_zone_name = current_zone->name();
34 location_name = current_zone_name; 35 location_name = current_zone_name;
diff --git a/src/common/socket_types.h b/src/common/socket_types.h
index 0a801a443..63824a5c4 100644
--- a/src/common/socket_types.h
+++ b/src/common/socket_types.h
@@ -3,17 +3,22 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <optional>
7#include <string>
8
6#include "common/common_types.h" 9#include "common/common_types.h"
7 10
8namespace Network { 11namespace Network {
9 12
10/// Address families 13/// Address families
11enum class Domain : u8 { 14enum class Domain : u8 {
12 INET, ///< Address family for IPv4 15 Unspecified, ///< Represents 0, used in getaddrinfo hints
16 INET, ///< Address family for IPv4
13}; 17};
14 18
15/// Socket types 19/// Socket types
16enum class Type { 20enum class Type {
21 Unspecified, ///< Represents 0, used in getaddrinfo hints
17 STREAM, 22 STREAM,
18 DGRAM, 23 DGRAM,
19 RAW, 24 RAW,
@@ -22,6 +27,7 @@ enum class Type {
22 27
23/// Protocol values for sockets 28/// Protocol values for sockets
24enum class Protocol : u8 { 29enum class Protocol : u8 {
30 Unspecified, ///< Represents 0, usable in various places
25 ICMP, 31 ICMP,
26 TCP, 32 TCP,
27 UDP, 33 UDP,
@@ -48,4 +54,13 @@ constexpr u32 FLAG_MSG_PEEK = 0x2;
48constexpr u32 FLAG_MSG_DONTWAIT = 0x80; 54constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
49constexpr u32 FLAG_O_NONBLOCK = 0x800; 55constexpr u32 FLAG_O_NONBLOCK = 0x800;
50 56
57/// Cross-platform addrinfo structure
58struct AddrInfo {
59 Domain family;
60 Type socket_type;
61 Protocol protocol;
62 SockAddrIn addr;
63 std::optional<std::string> canon_name;
64};
65
51} // namespace Network 66} // namespace Network
diff --git a/src/common/time_zone.cpp b/src/common/time_zone.cpp
index d8d7896c6..69e728a9d 100644
--- a/src/common/time_zone.cpp
+++ b/src/common/time_zone.cpp
@@ -4,13 +4,13 @@
4#include <chrono> 4#include <chrono>
5#include <exception> 5#include <exception>
6#include <iomanip> 6#include <iomanip>
7#include <map>
7#include <sstream> 8#include <sstream>
8#include <stdexcept> 9#include <stdexcept>
9#include <fmt/chrono.h> 10#include <fmt/chrono.h>
10#include <fmt/core.h> 11#include <fmt/core.h>
11 12
12#include "common/logging/log.h" 13#include "common/logging/log.h"
13#include "common/settings.h"
14#include "common/time_zone.h" 14#include "common/time_zone.h"
15 15
16namespace Common::TimeZone { 16namespace Common::TimeZone {
@@ -33,32 +33,29 @@ std::string GetDefaultTimeZone() {
33 return "GMT"; 33 return "GMT";
34} 34}
35 35
36static std::string GetOsTimeZoneOffset() { 36// Results are not comparable to seconds since Epoch
37 const std::time_t t{std::time(nullptr)}; 37static std::time_t TmSpecToSeconds(const struct std::tm& spec) {
38 const std::tm tm{*std::localtime(&t)}; 38 const int year = spec.tm_year - 1; // Years up to now
39 39 const int leap_years = year / 4 - year / 100;
40 return fmt::format("{:%z}", tm); 40 std::time_t cumulative = spec.tm_year;
41} 41 cumulative = cumulative * 365 + leap_years + spec.tm_yday; // Years to days
42 42 cumulative = cumulative * 24 + spec.tm_hour; // Days to hours
43static int ConvertOsTimeZoneOffsetToInt(const std::string& timezone) { 43 cumulative = cumulative * 60 + spec.tm_min; // Hours to minutes
44 try { 44 cumulative = cumulative * 60 + spec.tm_sec; // Minutes to seconds
45 return std::stoi(timezone); 45 return cumulative;
46 } catch (const std::invalid_argument&) {
47 LOG_CRITICAL(Common, "invalid_argument with {}!", timezone);
48 return 0;
49 } catch (const std::out_of_range&) {
50 LOG_CRITICAL(Common, "out_of_range with {}!", timezone);
51 return 0;
52 }
53} 46}
54 47
55std::chrono::seconds GetCurrentOffsetSeconds() { 48std::chrono::seconds GetCurrentOffsetSeconds() {
56 const int offset{ConvertOsTimeZoneOffsetToInt(GetOsTimeZoneOffset())}; 49 const std::time_t t{std::time(nullptr)};
50 const std::tm local{*std::localtime(&t)};
51 const std::tm gmt{*std::gmtime(&t)};
57 52
58 int seconds{(offset / 100) * 60 * 60}; // Convert hour component to seconds 53 // gmt_seconds is a different offset than time(nullptr)
59 seconds += (offset % 100) * 60; // Convert minute component to seconds 54 const auto gmt_seconds = TmSpecToSeconds(gmt);
55 const auto local_seconds = TmSpecToSeconds(local);
56 const auto seconds_offset = local_seconds - gmt_seconds;
60 57
61 return std::chrono::seconds{seconds}; 58 return std::chrono::seconds{seconds_offset};
62} 59}
63 60
64// Key is [Hours * 100 + Minutes], multiplied by 100 if DST 61// Key is [Hours * 100 + Minutes], multiplied by 100 if DST
@@ -71,11 +68,6 @@ const static std::map<s64, const char*> off_timezones = {
71}; 68};
72 69
73std::string FindSystemTimeZone() { 70std::string FindSystemTimeZone() {
74#if defined(MINGW)
75 // MinGW has broken strftime -- https://sourceforge.net/p/mingw-w64/bugs/793/
76 // e.g. fmt::format("{:%z}") -- returns "Eastern Daylight Time" when it should be "-0400"
77 return timezones[0];
78#else
79 const s64 seconds = static_cast<s64>(GetCurrentOffsetSeconds().count()); 71 const s64 seconds = static_cast<s64>(GetCurrentOffsetSeconds().count());
80 72
81 const s64 minutes = seconds / 60; 73 const s64 minutes = seconds / 60;
@@ -97,7 +89,6 @@ std::string FindSystemTimeZone() {
97 } 89 }
98 } 90 }
99 return fmt::format("Etc/GMT{:s}{:d}", hours > 0 ? "-" : "+", std::abs(hours)); 91 return fmt::format("Etc/GMT{:s}{:d}", hours > 0 ? "-" : "+", std::abs(hours));
100#endif
101} 92}
102 93
103} // namespace Common::TimeZone 94} // namespace Common::TimeZone
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 3655b8478..4b7395be8 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -285,6 +285,7 @@ add_library(core STATIC
285 hle/kernel/kernel.cpp 285 hle/kernel/kernel.cpp
286 hle/kernel/kernel.h 286 hle/kernel/kernel.h
287 hle/kernel/memory_types.h 287 hle/kernel/memory_types.h
288 hle/kernel/message_buffer.h
288 hle/kernel/physical_core.cpp 289 hle/kernel/physical_core.cpp
289 hle/kernel/physical_core.h 290 hle/kernel/physical_core.h
290 hle/kernel/physical_memory.h 291 hle/kernel/physical_memory.h
@@ -722,6 +723,7 @@ add_library(core STATIC
722 hle/service/spl/spl_types.h 723 hle/service/spl/spl_types.h
723 hle/service/ssl/ssl.cpp 724 hle/service/ssl/ssl.cpp
724 hle/service/ssl/ssl.h 725 hle/service/ssl/ssl.h
726 hle/service/ssl/ssl_backend.h
725 hle/service/time/clock_types.h 727 hle/service/time/clock_types.h
726 hle/service/time/ephemeral_network_system_clock_context_writer.h 728 hle/service/time/ephemeral_network_system_clock_context_writer.h
727 hle/service/time/ephemeral_network_system_clock_core.h 729 hle/service/time/ephemeral_network_system_clock_core.h
@@ -863,6 +865,23 @@ if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
863 target_link_libraries(core PRIVATE dynarmic::dynarmic) 865 target_link_libraries(core PRIVATE dynarmic::dynarmic)
864endif() 866endif()
865 867
868if(ENABLE_OPENSSL)
869 target_sources(core PRIVATE
870 hle/service/ssl/ssl_backend_openssl.cpp)
871 target_link_libraries(core PRIVATE OpenSSL::SSL)
872elseif (APPLE)
873 target_sources(core PRIVATE
874 hle/service/ssl/ssl_backend_securetransport.cpp)
875 target_link_libraries(core PRIVATE "-framework Security")
876elseif (WIN32)
877 target_sources(core PRIVATE
878 hle/service/ssl/ssl_backend_schannel.cpp)
879 target_link_libraries(core PRIVATE crypt32 secur32)
880else()
881 target_sources(core PRIVATE
882 hle/service/ssl/ssl_backend_none.cpp)
883endif()
884
866if (YUZU_USE_PRECOMPILED_HEADERS) 885if (YUZU_USE_PRECOMPILED_HEADERS)
867 target_precompile_headers(core PRIVATE precompiled_headers.h) 886 target_precompile_headers(core PRIVATE precompiled_headers.h)
868endif() 887endif()
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index beaea64b3..aa0eb9791 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -185,7 +185,7 @@ void ARM_Interface::Run() {
185 // Notify the debugger and go to sleep if a breakpoint was hit, 185 // Notify the debugger and go to sleep if a breakpoint was hit,
186 // or if the thread is unable to continue for any reason. 186 // or if the thread is unable to continue for any reason.
187 if (True(hr & HaltReason::InstructionBreakpoint) || True(hr & HaltReason::PrefetchAbort)) { 187 if (True(hr & HaltReason::InstructionBreakpoint) || True(hr & HaltReason::PrefetchAbort)) {
188 if (!True(hr & HaltReason::InstructionBreakpoint)) { 188 if (!True(hr & HaltReason::PrefetchAbort)) {
189 RewindBreakpointInstruction(); 189 RewindBreakpointInstruction();
190 } 190 }
191 if (system.DebuggerEnabled()) { 191 if (system.DebuggerEnabled()) {
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 3b82fb73c..c97158a71 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -346,11 +346,11 @@ void ARM_Dynarmic_32::RewindBreakpointInstruction() {
346} 346}
347 347
348ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, bool uses_wall_clock_, 348ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, bool uses_wall_clock_,
349 ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_) 349 DynarmicExclusiveMonitor& exclusive_monitor_,
350 std::size_t core_index_)
350 : ARM_Interface{system_, uses_wall_clock_}, cb(std::make_unique<DynarmicCallbacks32>(*this)), 351 : ARM_Interface{system_, uses_wall_clock_}, cb(std::make_unique<DynarmicCallbacks32>(*this)),
351 cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_}, 352 cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_},
352 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, 353 exclusive_monitor{exclusive_monitor_}, null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {}
353 null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {}
354 354
355ARM_Dynarmic_32::~ARM_Dynarmic_32() = default; 355ARM_Dynarmic_32::~ARM_Dynarmic_32() = default;
356 356
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index a990845cb..92fb3f836 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -12,7 +12,7 @@
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/hash.h" 13#include "common/hash.h"
14#include "core/arm/arm_interface.h" 14#include "core/arm/arm_interface.h"
15#include "core/arm/exclusive_monitor.h" 15#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
16 16
17namespace Core::Memory { 17namespace Core::Memory {
18class Memory; 18class Memory;
@@ -28,8 +28,8 @@ class System;
28 28
29class ARM_Dynarmic_32 final : public ARM_Interface { 29class ARM_Dynarmic_32 final : public ARM_Interface {
30public: 30public:
31 ARM_Dynarmic_32(System& system_, bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, 31 ARM_Dynarmic_32(System& system_, bool uses_wall_clock_,
32 std::size_t core_index_); 32 DynarmicExclusiveMonitor& exclusive_monitor_, std::size_t core_index_);
33 ~ARM_Dynarmic_32() override; 33 ~ARM_Dynarmic_32() override;
34 34
35 void SetPC(u64 pc) override; 35 void SetPC(u64 pc) override;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index bb97ed5bc..791d466ca 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -405,11 +405,11 @@ void ARM_Dynarmic_64::RewindBreakpointInstruction() {
405} 405}
406 406
407ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, bool uses_wall_clock_, 407ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, bool uses_wall_clock_,
408 ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_) 408 DynarmicExclusiveMonitor& exclusive_monitor_,
409 std::size_t core_index_)
409 : ARM_Interface{system_, uses_wall_clock_}, 410 : ARM_Interface{system_, uses_wall_clock_},
410 cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_}, 411 cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_},
411 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, 412 exclusive_monitor{exclusive_monitor_}, null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {}
412 null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {}
413 413
414ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; 414ARM_Dynarmic_64::~ARM_Dynarmic_64() = default;
415 415
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index af2aa1f1c..2b88a08e2 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -11,7 +11,7 @@
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/hash.h" 12#include "common/hash.h"
13#include "core/arm/arm_interface.h" 13#include "core/arm/arm_interface.h"
14#include "core/arm/exclusive_monitor.h" 14#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
15 15
16namespace Core::Memory { 16namespace Core::Memory {
17class Memory; 17class Memory;
@@ -25,8 +25,8 @@ class System;
25 25
26class ARM_Dynarmic_64 final : public ARM_Interface { 26class ARM_Dynarmic_64 final : public ARM_Interface {
27public: 27public:
28 ARM_Dynarmic_64(System& system_, bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, 28 ARM_Dynarmic_64(System& system_, bool uses_wall_clock_,
29 std::size_t core_index_); 29 DynarmicExclusiveMonitor& exclusive_monitor_, std::size_t core_index_);
30 ~ARM_Dynarmic_64() override; 30 ~ARM_Dynarmic_64() override;
31 31
32 void SetPC(u64 pc) override; 32 void SetPC(u64 pc) override;
diff --git a/src/core/arm/dynarmic/dynarmic_exclusive_monitor.h b/src/core/arm/dynarmic/dynarmic_exclusive_monitor.h
index 57e6dd0d0..fbfcd8d95 100644
--- a/src/core/arm/dynarmic/dynarmic_exclusive_monitor.h
+++ b/src/core/arm/dynarmic/dynarmic_exclusive_monitor.h
@@ -6,8 +6,6 @@
6#include <dynarmic/interface/exclusive_monitor.h> 6#include <dynarmic/interface/exclusive_monitor.h>
7 7
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/arm/dynarmic/arm_dynarmic_32.h"
10#include "core/arm/dynarmic/arm_dynarmic_64.h"
11#include "core/arm/exclusive_monitor.h" 9#include "core/arm/exclusive_monitor.h"
12 10
13namespace Core::Memory { 11namespace Core::Memory {
@@ -16,6 +14,9 @@ class Memory;
16 14
17namespace Core { 15namespace Core {
18 16
17class ARM_Dynarmic_32;
18class ARM_Dynarmic_64;
19
19class DynarmicExclusiveMonitor final : public ExclusiveMonitor { 20class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
20public: 21public:
21 explicit DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count_); 22 explicit DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count_);
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 9e3eb3795..48233d7c8 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -880,6 +880,14 @@ const FileSys::ContentProvider& System::GetContentProvider() const {
880 return *impl->content_provider; 880 return *impl->content_provider;
881} 881}
882 882
883FileSys::ContentProviderUnion& System::GetContentProviderUnion() {
884 return *impl->content_provider;
885}
886
887const FileSys::ContentProviderUnion& System::GetContentProviderUnion() const {
888 return *impl->content_provider;
889}
890
883Service::FileSystem::FileSystemController& System::GetFileSystemController() { 891Service::FileSystem::FileSystemController& System::GetFileSystemController() {
884 return impl->fs_controller; 892 return impl->fs_controller;
885} 893}
diff --git a/src/core/core.h b/src/core/core.h
index 14b2f7785..c70ea1965 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -381,6 +381,9 @@ public:
381 [[nodiscard]] FileSys::ContentProvider& GetContentProvider(); 381 [[nodiscard]] FileSys::ContentProvider& GetContentProvider();
382 [[nodiscard]] const FileSys::ContentProvider& GetContentProvider() const; 382 [[nodiscard]] const FileSys::ContentProvider& GetContentProvider() const;
383 383
384 [[nodiscard]] FileSys::ContentProviderUnion& GetContentProviderUnion();
385 [[nodiscard]] const FileSys::ContentProviderUnion& GetContentProviderUnion() const;
386
384 [[nodiscard]] Service::FileSystem::FileSystemController& GetFileSystemController(); 387 [[nodiscard]] Service::FileSystem::FileSystemController& GetFileSystemController();
385 [[nodiscard]] const Service::FileSystem::FileSystemController& GetFileSystemController() const; 388 [[nodiscard]] const Service::FileSystem::FileSystemController& GetFileSystemController() const;
386 389
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index e6112a3c9..b98a0cb33 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -70,7 +70,7 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
70 -> std::optional<std::chrono::nanoseconds> { return std::nullopt; }; 70 -> std::optional<std::chrono::nanoseconds> { return std::nullopt; };
71 ev_lost = CreateEvent("_lost_event", empty_timed_callback); 71 ev_lost = CreateEvent("_lost_event", empty_timed_callback);
72 if (is_multicore) { 72 if (is_multicore) {
73 timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this)); 73 timer_thread = std::make_unique<std::jthread>(ThreadEntry, std::ref(*this));
74 } 74 }
75} 75}
76 76
@@ -255,7 +255,6 @@ void CoreTiming::ThreadLoop() {
255#ifdef _WIN32 255#ifdef _WIN32
256 while (!paused && !event.IsSet() && wait_time > 0) { 256 while (!paused && !event.IsSet() && wait_time > 0) {
257 wait_time = *next_time - GetGlobalTimeNs().count(); 257 wait_time = *next_time - GetGlobalTimeNs().count();
258
259 if (wait_time >= timer_resolution_ns) { 258 if (wait_time >= timer_resolution_ns) {
260 Common::Windows::SleepForOneTick(); 259 Common::Windows::SleepForOneTick();
261 } else { 260 } else {
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 5bca1c78d..c20e906fb 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -163,7 +163,7 @@ private:
163 Common::Event pause_event{}; 163 Common::Event pause_event{};
164 std::mutex basic_lock; 164 std::mutex basic_lock;
165 std::mutex advance_lock; 165 std::mutex advance_lock;
166 std::unique_ptr<std::thread> timer_thread; 166 std::unique_ptr<std::jthread> timer_thread;
167 std::atomic<bool> paused{}; 167 std::atomic<bool> paused{};
168 std::atomic<bool> paused_set{}; 168 std::atomic<bool> paused_set{};
169 std::atomic<bool> wait_set{}; 169 std::atomic<bool> wait_set{};
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
index e2a13bbd2..da6078372 100644
--- a/src/core/debugger/gdbstub.cpp
+++ b/src/core/debugger/gdbstub.cpp
@@ -556,7 +556,7 @@ void GDBStub::HandleQuery(std::string_view command) {
556 } else { 556 } else {
557 SendReply(fmt::format( 557 SendReply(fmt::format(
558 "TextSeg={:x}", 558 "TextSeg={:x}",
559 GetInteger(system.ApplicationProcess()->PageTable().GetCodeRegionStart()))); 559 GetInteger(system.ApplicationProcess()->GetPageTable().GetCodeRegionStart())));
560 } 560 }
561 } else if (command.starts_with("Xfer:libraries:read::")) { 561 } else if (command.starts_with("Xfer:libraries:read::")) {
562 Loader::AppLoader::Modules modules; 562 Loader::AppLoader::Modules modules;
@@ -731,7 +731,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
731 std::string reply; 731 std::string reply;
732 732
733 auto* process = system.ApplicationProcess(); 733 auto* process = system.ApplicationProcess();
734 auto& page_table = process->PageTable(); 734 auto& page_table = process->GetPageTable();
735 735
736 const char* commands = "Commands:\n" 736 const char* commands = "Commands:\n"
737 " get fastmem\n" 737 " get fastmem\n"
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 50303fe42..06efab46d 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -57,11 +57,34 @@ struct NCASectionHeaderBlock {
57}; 57};
58static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size."); 58static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size.");
59 59
60struct NCABucketInfo {
61 u64 table_offset;
62 u64 table_size;
63 std::array<u8, 0x10> table_header;
64};
65static_assert(sizeof(NCABucketInfo) == 0x20, "NCABucketInfo has incorrect size.");
66
67struct NCASparseInfo {
68 NCABucketInfo bucket;
69 u64 physical_offset;
70 u16 generation;
71 INSERT_PADDING_BYTES_NOINIT(0x6);
72};
73static_assert(sizeof(NCASparseInfo) == 0x30, "NCASparseInfo has incorrect size.");
74
75struct NCACompressionInfo {
76 NCABucketInfo bucket;
77 INSERT_PADDING_BYTES_NOINIT(0x8);
78};
79static_assert(sizeof(NCACompressionInfo) == 0x28, "NCACompressionInfo has incorrect size.");
80
60struct NCASectionRaw { 81struct NCASectionRaw {
61 NCASectionHeaderBlock header; 82 NCASectionHeaderBlock header;
62 std::array<u8, 0x138> block_data; 83 std::array<u8, 0x138> block_data;
63 std::array<u8, 0x8> section_ctr; 84 std::array<u8, 0x8> section_ctr;
64 INSERT_PADDING_BYTES_NOINIT(0xB8); 85 NCASparseInfo sparse_info;
86 NCACompressionInfo compression_info;
87 INSERT_PADDING_BYTES_NOINIT(0x60);
65}; 88};
66static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size."); 89static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size.");
67 90
@@ -225,6 +248,20 @@ bool NCA::ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_b
225 for (std::size_t i = 0; i < sections.size(); ++i) { 248 for (std::size_t i = 0; i < sections.size(); ++i) {
226 const auto& section = sections[i]; 249 const auto& section = sections[i];
227 250
251 if (section.raw.sparse_info.bucket.table_offset != 0 &&
252 section.raw.sparse_info.bucket.table_size != 0) {
253 LOG_ERROR(Loader, "Sparse NCAs are not supported.");
254 status = Loader::ResultStatus::ErrorSparseNCA;
255 return false;
256 }
257
258 if (section.raw.compression_info.bucket.table_offset != 0 &&
259 section.raw.compression_info.bucket.table_size != 0) {
260 LOG_ERROR(Loader, "Compressed NCAs are not supported.");
261 status = Loader::ResultStatus::ErrorCompressedNCA;
262 return false;
263 }
264
228 if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) { 265 if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
229 if (!ReadRomFSSection(section, header.section_tables[i], bktr_base_ivfc_offset)) { 266 if (!ReadRomFSSection(section, header.section_tables[i], bktr_base_ivfc_offset)) {
230 return false; 267 return false;
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
index 3583bee44..7454be55c 100644
--- a/src/core/hle/kernel/k_code_memory.cpp
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -25,7 +25,7 @@ Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, KProcessAddres
25 m_owner = GetCurrentProcessPointer(m_kernel); 25 m_owner = GetCurrentProcessPointer(m_kernel);
26 26
27 // Get the owner page table. 27 // Get the owner page table.
28 auto& page_table = m_owner->PageTable(); 28 auto& page_table = m_owner->GetPageTable();
29 29
30 // Construct the page group. 30 // Construct the page group.
31 m_page_group.emplace(m_kernel, page_table.GetBlockInfoManager()); 31 m_page_group.emplace(m_kernel, page_table.GetBlockInfoManager());
@@ -53,7 +53,7 @@ void KCodeMemory::Finalize() {
53 // Unlock. 53 // Unlock.
54 if (!m_is_mapped && !m_is_owner_mapped) { 54 if (!m_is_mapped && !m_is_owner_mapped) {
55 const size_t size = m_page_group->GetNumPages() * PageSize; 55 const size_t size = m_page_group->GetNumPages() * PageSize;
56 m_owner->PageTable().UnlockForCodeMemory(m_address, size, *m_page_group); 56 m_owner->GetPageTable().UnlockForCodeMemory(m_address, size, *m_page_group);
57 } 57 }
58 58
59 // Close the page group. 59 // Close the page group.
@@ -75,7 +75,7 @@ Result KCodeMemory::Map(KProcessAddress address, size_t size) {
75 R_UNLESS(!m_is_mapped, ResultInvalidState); 75 R_UNLESS(!m_is_mapped, ResultInvalidState);
76 76
77 // Map the memory. 77 // Map the memory.
78 R_TRY(GetCurrentProcess(m_kernel).PageTable().MapPageGroup( 78 R_TRY(GetCurrentProcess(m_kernel).GetPageTable().MapPageGroup(
79 address, *m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite)); 79 address, *m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite));
80 80
81 // Mark ourselves as mapped. 81 // Mark ourselves as mapped.
@@ -92,8 +92,8 @@ Result KCodeMemory::Unmap(KProcessAddress address, size_t size) {
92 KScopedLightLock lk(m_lock); 92 KScopedLightLock lk(m_lock);
93 93
94 // Unmap the memory. 94 // Unmap the memory.
95 R_TRY(GetCurrentProcess(m_kernel).PageTable().UnmapPageGroup(address, *m_page_group, 95 R_TRY(GetCurrentProcess(m_kernel).GetPageTable().UnmapPageGroup(address, *m_page_group,
96 KMemoryState::CodeOut)); 96 KMemoryState::CodeOut));
97 97
98 // Mark ourselves as unmapped. 98 // Mark ourselves as unmapped.
99 m_is_mapped = false; 99 m_is_mapped = false;
@@ -126,8 +126,8 @@ Result KCodeMemory::MapToOwner(KProcessAddress address, size_t size, Svc::Memory
126 } 126 }
127 127
128 // Map the memory. 128 // Map the memory.
129 R_TRY(m_owner->PageTable().MapPageGroup(address, *m_page_group, KMemoryState::GeneratedCode, 129 R_TRY(m_owner->GetPageTable().MapPageGroup(address, *m_page_group, KMemoryState::GeneratedCode,
130 k_perm)); 130 k_perm));
131 131
132 // Mark ourselves as mapped. 132 // Mark ourselves as mapped.
133 m_is_owner_mapped = true; 133 m_is_owner_mapped = true;
@@ -143,7 +143,8 @@ Result KCodeMemory::UnmapFromOwner(KProcessAddress address, size_t size) {
143 KScopedLightLock lk(m_lock); 143 KScopedLightLock lk(m_lock);
144 144
145 // Unmap the memory. 145 // Unmap the memory.
146 R_TRY(m_owner->PageTable().UnmapPageGroup(address, *m_page_group, KMemoryState::GeneratedCode)); 146 R_TRY(m_owner->GetPageTable().UnmapPageGroup(address, *m_page_group,
147 KMemoryState::GeneratedCode));
147 148
148 // Mark ourselves as unmapped. 149 // Mark ourselves as unmapped.
149 m_is_owner_mapped = false; 150 m_is_owner_mapped = false;
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 022d15f35..b9e8c6042 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -388,39 +388,6 @@ public:
388 constexpr size_t GetHeapSize() const { 388 constexpr size_t GetHeapSize() const {
389 return m_current_heap_end - m_heap_region_start; 389 return m_current_heap_end - m_heap_region_start;
390 } 390 }
391 constexpr bool IsInsideAddressSpace(KProcessAddress address, size_t size) const {
392 return m_address_space_start <= address && address + size - 1 <= m_address_space_end - 1;
393 }
394 constexpr bool IsOutsideAliasRegion(KProcessAddress address, size_t size) const {
395 return m_alias_region_start > address || address + size - 1 > m_alias_region_end - 1;
396 }
397 constexpr bool IsOutsideStackRegion(KProcessAddress address, size_t size) const {
398 return m_stack_region_start > address || address + size - 1 > m_stack_region_end - 1;
399 }
400 constexpr bool IsInvalidRegion(KProcessAddress address, size_t size) const {
401 return address + size - 1 > GetAliasCodeRegionStart() + GetAliasCodeRegionSize() - 1;
402 }
403 constexpr bool IsInsideHeapRegion(KProcessAddress address, size_t size) const {
404 return address + size > m_heap_region_start && m_heap_region_end > address;
405 }
406 constexpr bool IsInsideAliasRegion(KProcessAddress address, size_t size) const {
407 return address + size > m_alias_region_start && m_alias_region_end > address;
408 }
409 constexpr bool IsOutsideASLRRegion(KProcessAddress address, size_t size) const {
410 if (IsInvalidRegion(address, size)) {
411 return true;
412 }
413 if (IsInsideHeapRegion(address, size)) {
414 return true;
415 }
416 if (IsInsideAliasRegion(address, size)) {
417 return true;
418 }
419 return {};
420 }
421 constexpr bool IsInsideASLRRegion(KProcessAddress address, size_t size) const {
422 return !IsOutsideASLRRegion(address, size);
423 }
424 constexpr size_t GetNumGuardPages() const { 391 constexpr size_t GetNumGuardPages() const {
425 return IsKernel() ? 1 : 4; 392 return IsKernel() ? 1 : 4;
426 } 393 }
@@ -436,6 +403,14 @@ public:
436 return m_address_space_start <= addr && addr < addr + size && 403 return m_address_space_start <= addr && addr < addr + size &&
437 addr + size - 1 <= m_address_space_end - 1; 404 addr + size - 1 <= m_address_space_end - 1;
438 } 405 }
406 constexpr bool IsInAliasRegion(KProcessAddress addr, size_t size) const {
407 return this->Contains(addr, size) && m_alias_region_start <= addr &&
408 addr + size - 1 <= m_alias_region_end - 1;
409 }
410 constexpr bool IsInHeapRegion(KProcessAddress addr, size_t size) const {
411 return this->Contains(addr, size) && m_heap_region_start <= addr &&
412 addr + size - 1 <= m_heap_region_end - 1;
413 }
439 414
440public: 415public:
441 static KVirtualAddress GetLinearMappedVirtualAddress(const KMemoryLayout& layout, 416 static KVirtualAddress GetLinearMappedVirtualAddress(const KMemoryLayout& layout,
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index efe86ad27..44c7cb22f 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -38,7 +38,7 @@ namespace {
38 */ 38 */
39void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority, 39void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority,
40 KProcessAddress stack_top) { 40 KProcessAddress stack_top) {
41 const KProcessAddress entry_point = owner_process.PageTable().GetCodeRegionStart(); 41 const KProcessAddress entry_point = owner_process.GetPageTable().GetCodeRegionStart();
42 ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::ThreadCountMax, 1)); 42 ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::ThreadCountMax, 1));
43 43
44 KThread* thread = KThread::Create(system.Kernel()); 44 KThread* thread = KThread::Create(system.Kernel());
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index 925981d06..c9b37e138 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -110,16 +110,6 @@ public:
110 ProcessType type, KResourceLimit* res_limit); 110 ProcessType type, KResourceLimit* res_limit);
111 111
112 /// Gets a reference to the process' page table. 112 /// Gets a reference to the process' page table.
113 KPageTable& PageTable() {
114 return m_page_table;
115 }
116
117 /// Gets const a reference to the process' page table.
118 const KPageTable& PageTable() const {
119 return m_page_table;
120 }
121
122 /// Gets a reference to the process' page table.
123 KPageTable& GetPageTable() { 113 KPageTable& GetPageTable() {
124 return m_page_table; 114 return m_page_table;
125 } 115 }
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index c66aff501..c64ceb530 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -20,12 +20,132 @@
20#include "core/hle/kernel/k_thread.h" 20#include "core/hle/kernel/k_thread.h"
21#include "core/hle/kernel/k_thread_queue.h" 21#include "core/hle/kernel/k_thread_queue.h"
22#include "core/hle/kernel/kernel.h" 22#include "core/hle/kernel/kernel.h"
23#include "core/hle/kernel/message_buffer.h"
23#include "core/hle/service/hle_ipc.h" 24#include "core/hle/service/hle_ipc.h"
24#include "core/hle/service/ipc_helpers.h" 25#include "core/hle/service/ipc_helpers.h"
25#include "core/memory.h" 26#include "core/memory.h"
26 27
27namespace Kernel { 28namespace Kernel {
28 29
30namespace {
31
32template <bool MoveHandleAllowed>
33Result ProcessMessageSpecialData(KProcess& dst_process, KProcess& src_process, KThread& src_thread,
34 MessageBuffer& dst_msg, const MessageBuffer& src_msg,
35 MessageBuffer::SpecialHeader& src_special_header) {
36 // Copy the special header to the destination.
37 s32 offset = dst_msg.Set(src_special_header);
38
39 // Copy the process ID.
40 if (src_special_header.GetHasProcessId()) {
41 offset = dst_msg.SetProcessId(offset, src_process.GetProcessId());
42 }
43
44 // Prepare to process handles.
45 auto& dst_handle_table = dst_process.GetHandleTable();
46 auto& src_handle_table = src_process.GetHandleTable();
47 Result result = ResultSuccess;
48
49 // Process copy handles.
50 for (auto i = 0; i < src_special_header.GetCopyHandleCount(); ++i) {
51 // Get the handles.
52 const Handle src_handle = src_msg.GetHandle(offset);
53 Handle dst_handle = Svc::InvalidHandle;
54
55 // If we're in a success state, try to move the handle to the new table.
56 if (R_SUCCEEDED(result) && src_handle != Svc::InvalidHandle) {
57 KScopedAutoObject obj =
58 src_handle_table.GetObjectForIpc(src_handle, std::addressof(src_thread));
59 if (obj.IsNotNull()) {
60 Result add_result =
61 dst_handle_table.Add(std::addressof(dst_handle), obj.GetPointerUnsafe());
62 if (R_FAILED(add_result)) {
63 result = add_result;
64 dst_handle = Svc::InvalidHandle;
65 }
66 } else {
67 result = ResultInvalidHandle;
68 }
69 }
70
71 // Set the handle.
72 offset = dst_msg.SetHandle(offset, dst_handle);
73 }
74
75 // Process move handles.
76 if constexpr (MoveHandleAllowed) {
77 for (auto i = 0; i < src_special_header.GetMoveHandleCount(); ++i) {
78 // Get the handles.
79 const Handle src_handle = src_msg.GetHandle(offset);
80 Handle dst_handle = Svc::InvalidHandle;
81
82 // Whether or not we've succeeded, we need to remove the handles from the source table.
83 if (src_handle != Svc::InvalidHandle) {
84 if (R_SUCCEEDED(result)) {
85 KScopedAutoObject obj =
86 src_handle_table.GetObjectForIpcWithoutPseudoHandle(src_handle);
87 if (obj.IsNotNull()) {
88 Result add_result = dst_handle_table.Add(std::addressof(dst_handle),
89 obj.GetPointerUnsafe());
90
91 src_handle_table.Remove(src_handle);
92
93 if (R_FAILED(add_result)) {
94 result = add_result;
95 dst_handle = Svc::InvalidHandle;
96 }
97 } else {
98 result = ResultInvalidHandle;
99 }
100 } else {
101 src_handle_table.Remove(src_handle);
102 }
103 }
104
105 // Set the handle.
106 offset = dst_msg.SetHandle(offset, dst_handle);
107 }
108 }
109
110 R_RETURN(result);
111}
112
113void CleanupSpecialData(KProcess& dst_process, u32* dst_msg_ptr, size_t dst_buffer_size) {
114 // Parse the message.
115 const MessageBuffer dst_msg(dst_msg_ptr, dst_buffer_size);
116 const MessageBuffer::MessageHeader dst_header(dst_msg);
117 const MessageBuffer::SpecialHeader dst_special_header(dst_msg, dst_header);
118
119 // Check that the size is big enough.
120 if (MessageBuffer::GetMessageBufferSize(dst_header, dst_special_header) > dst_buffer_size) {
121 return;
122 }
123
124 // Set the special header.
125 int offset = dst_msg.Set(dst_special_header);
126
127 // Clear the process id, if needed.
128 if (dst_special_header.GetHasProcessId()) {
129 offset = dst_msg.SetProcessId(offset, 0);
130 }
131
132 // Clear handles, as relevant.
133 auto& dst_handle_table = dst_process.GetHandleTable();
134 for (auto i = 0;
135 i < (dst_special_header.GetCopyHandleCount() + dst_special_header.GetMoveHandleCount());
136 ++i) {
137 const Handle handle = dst_msg.GetHandle(offset);
138
139 if (handle != Svc::InvalidHandle) {
140 dst_handle_table.Remove(handle);
141 }
142
143 offset = dst_msg.SetHandle(offset, Svc::InvalidHandle);
144 }
145}
146
147} // namespace
148
29using ThreadQueueImplForKServerSessionRequest = KThreadQueue; 149using ThreadQueueImplForKServerSessionRequest = KThreadQueue;
30 150
31KServerSession::KServerSession(KernelCore& kernel) 151KServerSession::KServerSession(KernelCore& kernel)
@@ -223,12 +343,27 @@ Result KServerSession::SendReply(bool is_hle) {
223 // the reply has already been written in this case. 343 // the reply has already been written in this case.
224 } else { 344 } else {
225 Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()}; 345 Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()};
226 KThread* server_thread{GetCurrentThreadPointer(m_kernel)}; 346 KThread* server_thread = GetCurrentThreadPointer(m_kernel);
347 KProcess& src_process = *client_thread->GetOwnerProcess();
348 KProcess& dst_process = *server_thread->GetOwnerProcess();
227 UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); 349 UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
228 350
229 auto* src_msg_buffer = memory.GetPointer(server_thread->GetTlsAddress()); 351 auto* src_msg_buffer = memory.GetPointer<u32>(server_thread->GetTlsAddress());
230 auto* dst_msg_buffer = memory.GetPointer(client_message); 352 auto* dst_msg_buffer = memory.GetPointer<u32>(client_message);
231 std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); 353 std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
354
355 // Translate special header ad-hoc.
356 MessageBuffer src_msg(src_msg_buffer, client_buffer_size);
357 MessageBuffer::MessageHeader src_header(src_msg);
358 MessageBuffer::SpecialHeader src_special_header(src_msg, src_header);
359 if (src_header.GetHasSpecialHeader()) {
360 MessageBuffer dst_msg(dst_msg_buffer, client_buffer_size);
361 result = ProcessMessageSpecialData<true>(dst_process, src_process, *server_thread,
362 dst_msg, src_msg, src_special_header);
363 if (R_FAILED(result)) {
364 CleanupSpecialData(dst_process, dst_msg_buffer, client_buffer_size);
365 }
366 }
232 } 367 }
233 } else { 368 } else {
234 result = ResultSessionClosed; 369 result = ResultSessionClosed;
@@ -330,12 +465,28 @@ Result KServerSession::ReceiveRequest(std::shared_ptr<Service::HLERequestContext
330 ->PopulateFromIncomingCommandBuffer(client_thread->GetOwnerProcess()->GetHandleTable(), 465 ->PopulateFromIncomingCommandBuffer(client_thread->GetOwnerProcess()->GetHandleTable(),
331 cmd_buf); 466 cmd_buf);
332 } else { 467 } else {
333 KThread* server_thread{GetCurrentThreadPointer(m_kernel)}; 468 KThread* server_thread = GetCurrentThreadPointer(m_kernel);
334 UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); 469 KProcess& src_process = *client_thread->GetOwnerProcess();
470 KProcess& dst_process = *server_thread->GetOwnerProcess();
471 UNIMPLEMENTED_IF(client_thread->GetOwnerProcess() != server_thread->GetOwnerProcess());
335 472
336 auto* src_msg_buffer = memory.GetPointer(client_message); 473 auto* src_msg_buffer = memory.GetPointer<u32>(client_message);
337 auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTlsAddress()); 474 auto* dst_msg_buffer = memory.GetPointer<u32>(server_thread->GetTlsAddress());
338 std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); 475 std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
476
477 // Translate special header ad-hoc.
478 // TODO: fix this mess
479 MessageBuffer src_msg(src_msg_buffer, client_buffer_size);
480 MessageBuffer::MessageHeader src_header(src_msg);
481 MessageBuffer::SpecialHeader src_special_header(src_msg, src_header);
482 if (src_header.GetHasSpecialHeader()) {
483 MessageBuffer dst_msg(dst_msg_buffer, client_buffer_size);
484 Result res = ProcessMessageSpecialData<false>(dst_process, src_process, *client_thread,
485 dst_msg, src_msg, src_special_header);
486 if (R_FAILED(res)) {
487 CleanupSpecialData(dst_process, dst_msg_buffer, client_buffer_size);
488 }
489 }
339 } 490 }
340 491
341 // We succeeded. 492 // We succeeded.
diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp
index efb5699de..f713968f6 100644
--- a/src/core/hle/kernel/k_shared_memory.cpp
+++ b/src/core/hle/kernel/k_shared_memory.cpp
@@ -90,8 +90,8 @@ Result KSharedMemory::Map(KProcess& target_process, KProcessAddress address, std
90 R_UNLESS(map_perm == test_perm, ResultInvalidNewMemoryPermission); 90 R_UNLESS(map_perm == test_perm, ResultInvalidNewMemoryPermission);
91 } 91 }
92 92
93 R_RETURN(target_process.PageTable().MapPageGroup(address, *m_page_group, KMemoryState::Shared, 93 R_RETURN(target_process.GetPageTable().MapPageGroup(
94 ConvertToKMemoryPermission(map_perm))); 94 address, *m_page_group, KMemoryState::Shared, ConvertToKMemoryPermission(map_perm)));
95} 95}
96 96
97Result KSharedMemory::Unmap(KProcess& target_process, KProcessAddress address, 97Result KSharedMemory::Unmap(KProcess& target_process, KProcessAddress address,
@@ -100,7 +100,7 @@ Result KSharedMemory::Unmap(KProcess& target_process, KProcessAddress address,
100 R_UNLESS(m_size == unmap_size, ResultInvalidSize); 100 R_UNLESS(m_size == unmap_size, ResultInvalidSize);
101 101
102 R_RETURN( 102 R_RETURN(
103 target_process.PageTable().UnmapPageGroup(address, *m_page_group, KMemoryState::Shared)); 103 target_process.GetPageTable().UnmapPageGroup(address, *m_page_group, KMemoryState::Shared));
104} 104}
105 105
106} // namespace Kernel 106} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index adb6ec581..d88909889 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -302,12 +302,12 @@ Result KThread::InitializeServiceThread(Core::System& system, KThread* thread,
302 std::function<void()>&& func, s32 prio, s32 virt_core, 302 std::function<void()>&& func, s32 prio, s32 virt_core,
303 KProcess* owner) { 303 KProcess* owner) {
304 system.Kernel().GlobalSchedulerContext().AddThread(thread); 304 system.Kernel().GlobalSchedulerContext().AddThread(thread);
305 std::function<void()> func2{[&system, func{std::move(func)}] { 305 std::function<void()> func2{[&system, func_{std::move(func)}] {
306 // Similar to UserModeThreadStarter. 306 // Similar to UserModeThreadStarter.
307 system.Kernel().CurrentScheduler()->OnThreadStart(); 307 system.Kernel().CurrentScheduler()->OnThreadStart();
308 308
309 // Run the guest function. 309 // Run the guest function.
310 func(); 310 func_();
311 311
312 // Exit. 312 // Exit.
313 Svc::ExitThread(system); 313 Svc::ExitThread(system);
diff --git a/src/core/hle/kernel/k_thread_local_page.cpp b/src/core/hle/kernel/k_thread_local_page.cpp
index b4a1e3cdb..2c45b4232 100644
--- a/src/core/hle/kernel/k_thread_local_page.cpp
+++ b/src/core/hle/kernel/k_thread_local_page.cpp
@@ -25,9 +25,9 @@ Result KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) {
25 25
26 // Map the address in. 26 // Map the address in.
27 const auto phys_addr = kernel.System().DeviceMemory().GetPhysicalAddr(page_buf); 27 const auto phys_addr = kernel.System().DeviceMemory().GetPhysicalAddr(page_buf);
28 R_TRY(m_owner->PageTable().MapPages(std::addressof(m_virt_addr), 1, PageSize, phys_addr, 28 R_TRY(m_owner->GetPageTable().MapPages(std::addressof(m_virt_addr), 1, PageSize, phys_addr,
29 KMemoryState::ThreadLocal, 29 KMemoryState::ThreadLocal,
30 KMemoryPermission::UserReadWrite)); 30 KMemoryPermission::UserReadWrite));
31 31
32 // We succeeded. 32 // We succeeded.
33 page_buf_guard.Cancel(); 33 page_buf_guard.Cancel();
@@ -37,11 +37,11 @@ Result KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) {
37 37
38Result KThreadLocalPage::Finalize() { 38Result KThreadLocalPage::Finalize() {
39 // Get the physical address of the page. 39 // Get the physical address of the page.
40 const KPhysicalAddress phys_addr = m_owner->PageTable().GetPhysicalAddr(m_virt_addr); 40 const KPhysicalAddress phys_addr = m_owner->GetPageTable().GetPhysicalAddr(m_virt_addr);
41 ASSERT(phys_addr); 41 ASSERT(phys_addr);
42 42
43 // Unmap the page. 43 // Unmap the page.
44 R_TRY(m_owner->PageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal)); 44 R_TRY(m_owner->GetPageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal));
45 45
46 // Free the page. 46 // Free the page.
47 KPageBuffer::Free(*m_kernel, KPageBuffer::FromPhysicalAddress(m_kernel->System(), phys_addr)); 47 KPageBuffer::Free(*m_kernel, KPageBuffer::FromPhysicalAddress(m_kernel->System(), phys_addr));
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index f33600ca5..ebe7582c6 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -1089,15 +1089,15 @@ static std::jthread RunHostThreadFunc(KernelCore& kernel, KProcess* process,
1089 KThread::Register(kernel, thread); 1089 KThread::Register(kernel, thread);
1090 1090
1091 return std::jthread( 1091 return std::jthread(
1092 [&kernel, thread, thread_name{std::move(thread_name)}, func{std::move(func)}] { 1092 [&kernel, thread, thread_name_{std::move(thread_name)}, func_{std::move(func)}] {
1093 // Set the thread name. 1093 // Set the thread name.
1094 Common::SetCurrentThreadName(thread_name.c_str()); 1094 Common::SetCurrentThreadName(thread_name_.c_str());
1095 1095
1096 // Set the thread as current. 1096 // Set the thread as current.
1097 kernel.RegisterHostThread(thread); 1097 kernel.RegisterHostThread(thread);
1098 1098
1099 // Run the callback. 1099 // Run the callback.
1100 func(); 1100 func_();
1101 1101
1102 // Close the thread. 1102 // Close the thread.
1103 // This will free the process if it is the last reference. 1103 // This will free the process if it is the last reference.
diff --git a/src/core/hle/kernel/message_buffer.h b/src/core/hle/kernel/message_buffer.h
new file mode 100644
index 000000000..75b275310
--- /dev/null
+++ b/src/core/hle/kernel/message_buffer.h
@@ -0,0 +1,612 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/alignment.h"
7#include "common/bit_field.h"
8#include "core/hle/kernel/k_thread.h"
9
10namespace Kernel {
11
12constexpr inline size_t MessageBufferSize = 0x100;
13
14class MessageBuffer {
15public:
16 class MessageHeader {
17 private:
18 static constexpr inline u64 NullTag = 0;
19
20 public:
21 enum class ReceiveListCountType : u32 {
22 None = 0,
23 ToMessageBuffer = 1,
24 ToSingleBuffer = 2,
25
26 CountOffset = 2,
27 CountMax = 13,
28 };
29
30 private:
31 union {
32 std::array<u32, 2> raw;
33
34 struct {
35 // Define fields for the first header word.
36 union {
37 BitField<0, 16, u16> tag;
38 BitField<16, 4, u32> pointer_count;
39 BitField<20, 4, u32> send_count;
40 BitField<24, 4, u32> receive_count;
41 BitField<28, 4, u32> exchange_count;
42 };
43
44 // Define fields for the second header word.
45 union {
46 BitField<0, 10, u32> raw_count;
47 BitField<10, 4, ReceiveListCountType> receive_list_count;
48 BitField<14, 6, u32> reserved0;
49 BitField<20, 11, u32> receive_list_offset;
50 BitField<31, 1, u32> has_special_header;
51 };
52 };
53 } m_header;
54
55 public:
56 constexpr MessageHeader() : m_header{} {}
57
58 constexpr MessageHeader(u16 tag, bool special, s32 ptr, s32 send, s32 recv, s32 exch,
59 s32 raw, ReceiveListCountType recv_list)
60 : m_header{} {
61 m_header.raw[0] = 0;
62 m_header.raw[1] = 0;
63
64 m_header.tag.Assign(tag);
65 m_header.pointer_count.Assign(ptr);
66 m_header.send_count.Assign(send);
67 m_header.receive_count.Assign(recv);
68 m_header.exchange_count.Assign(exch);
69
70 m_header.raw_count.Assign(raw);
71 m_header.receive_list_count.Assign(recv_list);
72 m_header.has_special_header.Assign(special);
73 }
74
75 explicit MessageHeader(const MessageBuffer& buf) : m_header{} {
76 buf.Get(0, m_header.raw.data(), 2);
77 }
78
79 explicit MessageHeader(const u32* msg) : m_header{{msg[0], msg[1]}} {}
80
81 constexpr u16 GetTag() const {
82 return m_header.tag;
83 }
84
85 constexpr s32 GetPointerCount() const {
86 return m_header.pointer_count;
87 }
88
89 constexpr s32 GetSendCount() const {
90 return m_header.send_count;
91 }
92
93 constexpr s32 GetReceiveCount() const {
94 return m_header.receive_count;
95 }
96
97 constexpr s32 GetExchangeCount() const {
98 return m_header.exchange_count;
99 }
100
101 constexpr s32 GetMapAliasCount() const {
102 return this->GetSendCount() + this->GetReceiveCount() + this->GetExchangeCount();
103 }
104
105 constexpr s32 GetRawCount() const {
106 return m_header.raw_count;
107 }
108
109 constexpr ReceiveListCountType GetReceiveListCount() const {
110 return m_header.receive_list_count;
111 }
112
113 constexpr s32 GetReceiveListOffset() const {
114 return m_header.receive_list_offset;
115 }
116
117 constexpr bool GetHasSpecialHeader() const {
118 return m_header.has_special_header.Value() != 0;
119 }
120
121 constexpr void SetReceiveListCount(ReceiveListCountType recv_list) {
122 m_header.receive_list_count.Assign(recv_list);
123 }
124
125 constexpr const u32* GetData() const {
126 return m_header.raw.data();
127 }
128
129 static constexpr size_t GetDataSize() {
130 return sizeof(m_header);
131 }
132 };
133
134 class SpecialHeader {
135 private:
136 union {
137 std::array<u32, 1> raw;
138
139 // Define fields for the header word.
140 BitField<0, 1, u32> has_process_id;
141 BitField<1, 4, u32> copy_handle_count;
142 BitField<5, 4, u32> move_handle_count;
143 } m_header;
144 bool m_has_header;
145
146 public:
147 constexpr explicit SpecialHeader(bool pid, s32 copy, s32 move)
148 : m_header{}, m_has_header(true) {
149 m_header.has_process_id.Assign(pid);
150 m_header.copy_handle_count.Assign(copy);
151 m_header.move_handle_count.Assign(move);
152 }
153
154 constexpr explicit SpecialHeader(bool pid, s32 copy, s32 move, bool _has_header)
155 : m_header{}, m_has_header(_has_header) {
156 m_header.has_process_id.Assign(pid);
157 m_header.copy_handle_count.Assign(copy);
158 m_header.move_handle_count.Assign(move);
159 }
160
161 explicit SpecialHeader(const MessageBuffer& buf, const MessageHeader& hdr)
162 : m_header{}, m_has_header(hdr.GetHasSpecialHeader()) {
163 if (m_has_header) {
164 buf.Get(static_cast<s32>(MessageHeader::GetDataSize() / sizeof(u32)),
165 m_header.raw.data(), sizeof(m_header) / sizeof(u32));
166 }
167 }
168
169 constexpr bool GetHasProcessId() const {
170 return m_header.has_process_id.Value() != 0;
171 }
172
173 constexpr s32 GetCopyHandleCount() const {
174 return m_header.copy_handle_count;
175 }
176
177 constexpr s32 GetMoveHandleCount() const {
178 return m_header.move_handle_count;
179 }
180
181 constexpr const u32* GetHeader() const {
182 return m_header.raw.data();
183 }
184
185 constexpr size_t GetHeaderSize() const {
186 if (m_has_header) {
187 return sizeof(m_header);
188 } else {
189 return 0;
190 }
191 }
192
193 constexpr size_t GetDataSize() const {
194 if (m_has_header) {
195 return (this->GetHasProcessId() ? sizeof(u64) : 0) +
196 (this->GetCopyHandleCount() * sizeof(Handle)) +
197 (this->GetMoveHandleCount() * sizeof(Handle));
198 } else {
199 return 0;
200 }
201 }
202 };
203
204 class MapAliasDescriptor {
205 public:
206 enum class Attribute : u32 {
207 Ipc = 0,
208 NonSecureIpc = 1,
209 NonDeviceIpc = 3,
210 };
211
212 private:
213 static constexpr u32 SizeLowCount = 32;
214 static constexpr u32 SizeHighCount = 4;
215 static constexpr u32 AddressLowCount = 32;
216 static constexpr u32 AddressMidCount = 4;
217
218 constexpr u32 GetAddressMid(u64 address) {
219 return static_cast<u32>(address >> AddressLowCount) & ((1U << AddressMidCount) - 1);
220 }
221
222 constexpr u32 GetAddressHigh(u64 address) {
223 return static_cast<u32>(address >> (AddressLowCount + AddressMidCount));
224 }
225
226 private:
227 union {
228 std::array<u32, 3> raw;
229
230 struct {
231 // Define fields for the first two words.
232 u32 size_low;
233 u32 address_low;
234
235 // Define fields for the packed descriptor word.
236 union {
237 BitField<0, 2, Attribute> attributes;
238 BitField<2, 3, u32> address_high;
239 BitField<5, 19, u32> reserved;
240 BitField<24, 4, u32> size_high;
241 BitField<28, 4, u32> address_mid;
242 };
243 };
244 } m_data;
245
246 public:
247 constexpr MapAliasDescriptor() : m_data{} {}
248
249 MapAliasDescriptor(const void* buffer, size_t _size, Attribute attr = Attribute::Ipc)
250 : m_data{} {
251 const u64 address = reinterpret_cast<u64>(buffer);
252 const u64 size = static_cast<u64>(_size);
253 m_data.size_low = static_cast<u32>(size);
254 m_data.address_low = static_cast<u32>(address);
255 m_data.attributes.Assign(attr);
256 m_data.address_mid.Assign(GetAddressMid(address));
257 m_data.size_high.Assign(static_cast<u32>(size >> SizeLowCount));
258 m_data.address_high.Assign(GetAddressHigh(address));
259 }
260
261 MapAliasDescriptor(const MessageBuffer& buf, s32 index) : m_data{} {
262 buf.Get(index, m_data.raw.data(), 3);
263 }
264
265 constexpr uintptr_t GetAddress() const {
266 return (static_cast<u64>((m_data.address_high << AddressMidCount) | m_data.address_mid)
267 << AddressLowCount) |
268 m_data.address_low;
269 }
270
271 constexpr uintptr_t GetSize() const {
272 return (static_cast<u64>(m_data.size_high) << SizeLowCount) | m_data.size_low;
273 }
274
275 constexpr Attribute GetAttribute() const {
276 return m_data.attributes;
277 }
278
279 constexpr const u32* GetData() const {
280 return m_data.raw.data();
281 }
282
283 static constexpr size_t GetDataSize() {
284 return sizeof(m_data);
285 }
286 };
287
288 class PointerDescriptor {
289 private:
290 static constexpr u32 AddressLowCount = 32;
291 static constexpr u32 AddressMidCount = 4;
292
293 constexpr u32 GetAddressMid(u64 address) {
294 return static_cast<u32>(address >> AddressLowCount) & ((1u << AddressMidCount) - 1);
295 }
296
297 constexpr u32 GetAddressHigh(u64 address) {
298 return static_cast<u32>(address >> (AddressLowCount + AddressMidCount));
299 }
300
301 private:
302 union {
303 std::array<u32, 2> raw;
304
305 struct {
306 // Define fields for the packed descriptor word.
307 union {
308 BitField<0, 4, u32> index;
309 BitField<4, 2, u32> reserved0;
310 BitField<6, 3, u32> address_high;
311 BitField<9, 3, u32> reserved1;
312 BitField<12, 4, u32> address_mid;
313 BitField<16, 16, u32> size;
314 };
315
316 // Define fields for the second word.
317 u32 address_low;
318 };
319 } m_data;
320
321 public:
322 constexpr PointerDescriptor() : m_data{} {}
323
324 PointerDescriptor(const void* buffer, size_t size, s32 index) : m_data{} {
325 const u64 address = reinterpret_cast<u64>(buffer);
326
327 m_data.index.Assign(index);
328 m_data.address_high.Assign(GetAddressHigh(address));
329 m_data.address_mid.Assign(GetAddressMid(address));
330 m_data.size.Assign(static_cast<u32>(size));
331
332 m_data.address_low = static_cast<u32>(address);
333 }
334
335 PointerDescriptor(const MessageBuffer& buf, s32 index) : m_data{} {
336 buf.Get(index, m_data.raw.data(), 2);
337 }
338
339 constexpr s32 GetIndex() const {
340 return m_data.index;
341 }
342
343 constexpr uintptr_t GetAddress() const {
344 return (static_cast<u64>((m_data.address_high << AddressMidCount) | m_data.address_mid)
345 << AddressLowCount) |
346 m_data.address_low;
347 }
348
349 constexpr size_t GetSize() const {
350 return m_data.size;
351 }
352
353 constexpr const u32* GetData() const {
354 return m_data.raw.data();
355 }
356
357 static constexpr size_t GetDataSize() {
358 return sizeof(m_data);
359 }
360 };
361
362 class ReceiveListEntry {
363 private:
364 static constexpr u32 AddressLowCount = 32;
365
366 constexpr u32 GetAddressHigh(u64 address) {
367 return static_cast<u32>(address >> (AddressLowCount));
368 }
369
370 private:
371 union {
372 std::array<u32, 2> raw;
373
374 struct {
375 // Define fields for the first word.
376 u32 address_low;
377
378 // Define fields for the packed descriptor word.
379 union {
380 BitField<0, 7, u32> address_high;
381 BitField<7, 9, u32> reserved;
382 BitField<16, 16, u32> size;
383 };
384 };
385 } m_data;
386
387 public:
388 constexpr ReceiveListEntry() : m_data{} {}
389
390 ReceiveListEntry(const void* buffer, size_t size) : m_data{} {
391 const u64 address = reinterpret_cast<u64>(buffer);
392
393 m_data.address_low = static_cast<u32>(address);
394
395 m_data.address_high.Assign(GetAddressHigh(address));
396 m_data.size.Assign(static_cast<u32>(size));
397 }
398
399 ReceiveListEntry(u32 a, u32 b) : m_data{{a, b}} {}
400
401 constexpr uintptr_t GetAddress() const {
402 return (static_cast<u64>(m_data.address_high) << AddressLowCount) | m_data.address_low;
403 }
404
405 constexpr size_t GetSize() const {
406 return m_data.size;
407 }
408
409 constexpr const u32* GetData() const {
410 return m_data.raw.data();
411 }
412
413 static constexpr size_t GetDataSize() {
414 return sizeof(m_data);
415 }
416 };
417
418private:
419 u32* m_buffer;
420 size_t m_size;
421
422public:
423 constexpr MessageBuffer(u32* b, size_t sz) : m_buffer(b), m_size(sz) {}
424 constexpr explicit MessageBuffer(u32* b) : m_buffer(b), m_size(MessageBufferSize) {}
425
426 constexpr void* GetBufferForDebug() const {
427 return m_buffer;
428 }
429
430 constexpr size_t GetBufferSize() const {
431 return m_size;
432 }
433
434 void Get(s32 index, u32* dst, size_t count) const {
435 // Ensure that this doesn't get re-ordered.
436 std::atomic_thread_fence(std::memory_order_seq_cst);
437
438 // Get the words.
439 static_assert(sizeof(*dst) == sizeof(*m_buffer));
440
441 memcpy(dst, m_buffer + index, count * sizeof(*dst));
442 }
443
444 s32 Set(s32 index, u32* src, size_t count) const {
445 // Ensure that this doesn't get re-ordered.
446 std::atomic_thread_fence(std::memory_order_seq_cst);
447
448 // Set the words.
449 memcpy(m_buffer + index, src, count * sizeof(*src));
450
451 // Ensure that this doesn't get re-ordered.
452 std::atomic_thread_fence(std::memory_order_seq_cst);
453
454 return static_cast<s32>(index + count);
455 }
456
457 template <typename T>
458 const T& GetRaw(s32 index) const {
459 return *reinterpret_cast<const T*>(m_buffer + index);
460 }
461
462 template <typename T>
463 s32 SetRaw(s32 index, const T& val) const {
464 *reinterpret_cast<const T*>(m_buffer + index) = val;
465 return index + (Common::AlignUp(sizeof(val), sizeof(*m_buffer)) / sizeof(*m_buffer));
466 }
467
468 void GetRawArray(s32 index, void* dst, size_t len) const {
469 memcpy(dst, m_buffer + index, len);
470 }
471
472 void SetRawArray(s32 index, const void* src, size_t len) const {
473 memcpy(m_buffer + index, src, len);
474 }
475
476 void SetNull() const {
477 this->Set(MessageHeader());
478 }
479
480 s32 Set(const MessageHeader& hdr) const {
481 memcpy(m_buffer, hdr.GetData(), hdr.GetDataSize());
482 return static_cast<s32>(hdr.GetDataSize() / sizeof(*m_buffer));
483 }
484
485 s32 Set(const SpecialHeader& spc) const {
486 const s32 index = static_cast<s32>(MessageHeader::GetDataSize() / sizeof(*m_buffer));
487 memcpy(m_buffer + index, spc.GetHeader(), spc.GetHeaderSize());
488 return static_cast<s32>(index + (spc.GetHeaderSize() / sizeof(*m_buffer)));
489 }
490
491 s32 SetHandle(s32 index, const Handle& hnd) const {
492 memcpy(m_buffer + index, std::addressof(hnd), sizeof(hnd));
493 return static_cast<s32>(index + (sizeof(hnd) / sizeof(*m_buffer)));
494 }
495
496 s32 SetProcessId(s32 index, const u64 pid) const {
497 memcpy(m_buffer + index, std::addressof(pid), sizeof(pid));
498 return static_cast<s32>(index + (sizeof(pid) / sizeof(*m_buffer)));
499 }
500
501 s32 Set(s32 index, const MapAliasDescriptor& desc) const {
502 memcpy(m_buffer + index, desc.GetData(), desc.GetDataSize());
503 return static_cast<s32>(index + (desc.GetDataSize() / sizeof(*m_buffer)));
504 }
505
506 s32 Set(s32 index, const PointerDescriptor& desc) const {
507 memcpy(m_buffer + index, desc.GetData(), desc.GetDataSize());
508 return static_cast<s32>(index + (desc.GetDataSize() / sizeof(*m_buffer)));
509 }
510
511 s32 Set(s32 index, const ReceiveListEntry& desc) const {
512 memcpy(m_buffer + index, desc.GetData(), desc.GetDataSize());
513 return static_cast<s32>(index + (desc.GetDataSize() / sizeof(*m_buffer)));
514 }
515
516 s32 Set(s32 index, const u32 val) const {
517 memcpy(m_buffer + index, std::addressof(val), sizeof(val));
518 return static_cast<s32>(index + (sizeof(val) / sizeof(*m_buffer)));
519 }
520
521 Result GetAsyncResult() const {
522 MessageHeader hdr(m_buffer);
523 MessageHeader null{};
524 if (memcmp(hdr.GetData(), null.GetData(), MessageHeader::GetDataSize()) != 0) [[unlikely]] {
525 R_SUCCEED();
526 }
527 return Result(m_buffer[MessageHeader::GetDataSize() / sizeof(*m_buffer)]);
528 }
529
530 void SetAsyncResult(Result res) const {
531 const s32 index = this->Set(MessageHeader());
532 const auto value = res.raw;
533 memcpy(m_buffer + index, std::addressof(value), sizeof(value));
534 }
535
536 u32 Get32(s32 index) const {
537 return m_buffer[index];
538 }
539
540 u64 Get64(s32 index) const {
541 u64 value;
542 memcpy(std::addressof(value), m_buffer + index, sizeof(value));
543 return value;
544 }
545
546 u64 GetProcessId(s32 index) const {
547 return this->Get64(index);
548 }
549
550 Handle GetHandle(s32 index) const {
551 static_assert(sizeof(Handle) == sizeof(*m_buffer));
552 return Handle(m_buffer[index]);
553 }
554
555 static constexpr s32 GetSpecialDataIndex(const MessageHeader& hdr, const SpecialHeader& spc) {
556 return static_cast<s32>((MessageHeader::GetDataSize() / sizeof(u32)) +
557 (spc.GetHeaderSize() / sizeof(u32)));
558 }
559
560 static constexpr s32 GetPointerDescriptorIndex(const MessageHeader& hdr,
561 const SpecialHeader& spc) {
562 return static_cast<s32>(GetSpecialDataIndex(hdr, spc) + (spc.GetDataSize() / sizeof(u32)));
563 }
564
565 static constexpr s32 GetMapAliasDescriptorIndex(const MessageHeader& hdr,
566 const SpecialHeader& spc) {
567 return GetPointerDescriptorIndex(hdr, spc) +
568 static_cast<s32>(hdr.GetPointerCount() * PointerDescriptor::GetDataSize() /
569 sizeof(u32));
570 }
571
572 static constexpr s32 GetRawDataIndex(const MessageHeader& hdr, const SpecialHeader& spc) {
573 return GetMapAliasDescriptorIndex(hdr, spc) +
574 static_cast<s32>(hdr.GetMapAliasCount() * MapAliasDescriptor::GetDataSize() /
575 sizeof(u32));
576 }
577
578 static constexpr s32 GetReceiveListIndex(const MessageHeader& hdr, const SpecialHeader& spc) {
579 if (const s32 recv_list_index = hdr.GetReceiveListOffset()) {
580 return recv_list_index;
581 } else {
582 return GetRawDataIndex(hdr, spc) + hdr.GetRawCount();
583 }
584 }
585
586 static constexpr size_t GetMessageBufferSize(const MessageHeader& hdr,
587 const SpecialHeader& spc) {
588 // Get the size of the plain message.
589 size_t msg_size = GetReceiveListIndex(hdr, spc) * sizeof(u32);
590
591 // Add the size of the receive list.
592 const auto count = hdr.GetReceiveListCount();
593 switch (count) {
594 case MessageHeader::ReceiveListCountType::None:
595 break;
596 case MessageHeader::ReceiveListCountType::ToMessageBuffer:
597 break;
598 case MessageHeader::ReceiveListCountType::ToSingleBuffer:
599 msg_size += ReceiveListEntry::GetDataSize();
600 break;
601 default:
602 msg_size += (static_cast<s32>(count) -
603 static_cast<s32>(MessageHeader::ReceiveListCountType::CountOffset)) *
604 ReceiveListEntry::GetDataSize();
605 break;
606 }
607
608 return msg_size;
609 }
610};
611
612} // namespace Kernel
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
index 2e0c36129..5ee869fa2 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -17,7 +17,9 @@ PhysicalCore::PhysicalCore(std::size_t core_index, Core::System& system, KSchedu
17 // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager. 17 // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.
18 auto& kernel = system.Kernel(); 18 auto& kernel = system.Kernel();
19 m_arm_interface = std::make_unique<Core::ARM_Dynarmic_64>( 19 m_arm_interface = std::make_unique<Core::ARM_Dynarmic_64>(
20 system, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), m_core_index); 20 system, kernel.IsMulticore(),
21 reinterpret_cast<Core::DynarmicExclusiveMonitor&>(kernel.GetExclusiveMonitor()),
22 m_core_index);
21#else 23#else
22#error Platform not supported yet. 24#error Platform not supported yet.
23#endif 25#endif
@@ -31,7 +33,9 @@ void PhysicalCore::Initialize(bool is_64_bit) {
31 if (!is_64_bit) { 33 if (!is_64_bit) {
32 // We already initialized a 64-bit core, replace with a 32-bit one. 34 // We already initialized a 64-bit core, replace with a 32-bit one.
33 m_arm_interface = std::make_unique<Core::ARM_Dynarmic_32>( 35 m_arm_interface = std::make_unique<Core::ARM_Dynarmic_32>(
34 m_system, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), m_core_index); 36 m_system, kernel.IsMulticore(),
37 reinterpret_cast<Core::DynarmicExclusiveMonitor&>(kernel.GetExclusiveMonitor()),
38 m_core_index);
35 } 39 }
36#else 40#else
37#error Platform not supported yet. 41#error Platform not supported yet.
diff --git a/src/core/hle/kernel/svc/svc_cache.cpp b/src/core/hle/kernel/svc/svc_cache.cpp
index 082942dab..c2c8be10f 100644
--- a/src/core/hle/kernel/svc/svc_cache.cpp
+++ b/src/core/hle/kernel/svc/svc_cache.cpp
@@ -42,7 +42,7 @@ Result FlushProcessDataCache(Core::System& system, Handle process_handle, u64 ad
42 R_UNLESS(process.IsNotNull(), ResultInvalidHandle); 42 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
43 43
44 // Verify the region is within range. 44 // Verify the region is within range.
45 auto& page_table = process->PageTable(); 45 auto& page_table = process->GetPageTable();
46 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); 46 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
47 47
48 // Perform the operation. 48 // Perform the operation.
diff --git a/src/core/hle/kernel/svc/svc_code_memory.cpp b/src/core/hle/kernel/svc/svc_code_memory.cpp
index 687baff82..bae4cb0cd 100644
--- a/src/core/hle/kernel/svc/svc_code_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_code_memory.cpp
@@ -48,7 +48,7 @@ Result CreateCodeMemory(Core::System& system, Handle* out, u64 address, uint64_t
48 SCOPE_EXIT({ code_mem->Close(); }); 48 SCOPE_EXIT({ code_mem->Close(); });
49 49
50 // Verify that the region is in range. 50 // Verify that the region is in range.
51 R_UNLESS(GetCurrentProcess(system.Kernel()).PageTable().Contains(address, size), 51 R_UNLESS(GetCurrentProcess(system.Kernel()).GetPageTable().Contains(address, size),
52 ResultInvalidCurrentMemory); 52 ResultInvalidCurrentMemory);
53 53
54 // Initialize the code memory. 54 // Initialize the code memory.
@@ -92,7 +92,7 @@ Result ControlCodeMemory(Core::System& system, Handle code_memory_handle,
92 case CodeMemoryOperation::Map: { 92 case CodeMemoryOperation::Map: {
93 // Check that the region is in range. 93 // Check that the region is in range.
94 R_UNLESS(GetCurrentProcess(system.Kernel()) 94 R_UNLESS(GetCurrentProcess(system.Kernel())
95 .PageTable() 95 .GetPageTable()
96 .CanContain(address, size, KMemoryState::CodeOut), 96 .CanContain(address, size, KMemoryState::CodeOut),
97 ResultInvalidMemoryRegion); 97 ResultInvalidMemoryRegion);
98 98
@@ -105,7 +105,7 @@ Result ControlCodeMemory(Core::System& system, Handle code_memory_handle,
105 case CodeMemoryOperation::Unmap: { 105 case CodeMemoryOperation::Unmap: {
106 // Check that the region is in range. 106 // Check that the region is in range.
107 R_UNLESS(GetCurrentProcess(system.Kernel()) 107 R_UNLESS(GetCurrentProcess(system.Kernel())
108 .PageTable() 108 .GetPageTable()
109 .CanContain(address, size, KMemoryState::CodeOut), 109 .CanContain(address, size, KMemoryState::CodeOut),
110 ResultInvalidMemoryRegion); 110 ResultInvalidMemoryRegion);
111 111
@@ -117,8 +117,8 @@ Result ControlCodeMemory(Core::System& system, Handle code_memory_handle,
117 } break; 117 } break;
118 case CodeMemoryOperation::MapToOwner: { 118 case CodeMemoryOperation::MapToOwner: {
119 // Check that the region is in range. 119 // Check that the region is in range.
120 R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size, 120 R_UNLESS(code_mem->GetOwner()->GetPageTable().CanContain(address, size,
121 KMemoryState::GeneratedCode), 121 KMemoryState::GeneratedCode),
122 ResultInvalidMemoryRegion); 122 ResultInvalidMemoryRegion);
123 123
124 // Check the memory permission. 124 // Check the memory permission.
@@ -129,8 +129,8 @@ Result ControlCodeMemory(Core::System& system, Handle code_memory_handle,
129 } break; 129 } break;
130 case CodeMemoryOperation::UnmapFromOwner: { 130 case CodeMemoryOperation::UnmapFromOwner: {
131 // Check that the region is in range. 131 // Check that the region is in range.
132 R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size, 132 R_UNLESS(code_mem->GetOwner()->GetPageTable().CanContain(address, size,
133 KMemoryState::GeneratedCode), 133 KMemoryState::GeneratedCode),
134 ResultInvalidMemoryRegion); 134 ResultInvalidMemoryRegion);
135 135
136 // Check the memory permission. 136 // Check the memory permission.
diff --git a/src/core/hle/kernel/svc/svc_device_address_space.cpp b/src/core/hle/kernel/svc/svc_device_address_space.cpp
index ec3143e67..42add9473 100644
--- a/src/core/hle/kernel/svc/svc_device_address_space.cpp
+++ b/src/core/hle/kernel/svc/svc_device_address_space.cpp
@@ -107,7 +107,7 @@ Result MapDeviceAddressSpaceByForce(Core::System& system, Handle das_handle, Han
107 R_UNLESS(process.IsNotNull(), ResultInvalidHandle); 107 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
108 108
109 // Validate that the process address is within range. 109 // Validate that the process address is within range.
110 auto& page_table = process->PageTable(); 110 auto& page_table = process->GetPageTable();
111 R_UNLESS(page_table.Contains(process_address, size), ResultInvalidCurrentMemory); 111 R_UNLESS(page_table.Contains(process_address, size), ResultInvalidCurrentMemory);
112 112
113 // Map. 113 // Map.
@@ -148,7 +148,7 @@ Result MapDeviceAddressSpaceAligned(Core::System& system, Handle das_handle, Han
148 R_UNLESS(process.IsNotNull(), ResultInvalidHandle); 148 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
149 149
150 // Validate that the process address is within range. 150 // Validate that the process address is within range.
151 auto& page_table = process->PageTable(); 151 auto& page_table = process->GetPageTable();
152 R_UNLESS(page_table.Contains(process_address, size), ResultInvalidCurrentMemory); 152 R_UNLESS(page_table.Contains(process_address, size), ResultInvalidCurrentMemory);
153 153
154 // Map. 154 // Map.
@@ -180,7 +180,7 @@ Result UnmapDeviceAddressSpace(Core::System& system, Handle das_handle, Handle p
180 R_UNLESS(process.IsNotNull(), ResultInvalidHandle); 180 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
181 181
182 // Validate that the process address is within range. 182 // Validate that the process address is within range.
183 auto& page_table = process->PageTable(); 183 auto& page_table = process->GetPageTable();
184 R_UNLESS(page_table.Contains(process_address, size), ResultInvalidCurrentMemory); 184 R_UNLESS(page_table.Contains(process_address, size), ResultInvalidCurrentMemory);
185 185
186 R_RETURN(das->Unmap(std::addressof(page_table), process_address, size, device_address)); 186 R_RETURN(das->Unmap(std::addressof(page_table), process_address, size, device_address));
diff --git a/src/core/hle/kernel/svc/svc_info.cpp b/src/core/hle/kernel/svc/svc_info.cpp
index 445cdd87b..f99964028 100644
--- a/src/core/hle/kernel/svc/svc_info.cpp
+++ b/src/core/hle/kernel/svc/svc_info.cpp
@@ -54,35 +54,35 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
54 R_SUCCEED(); 54 R_SUCCEED();
55 55
56 case InfoType::AliasRegionAddress: 56 case InfoType::AliasRegionAddress:
57 *result = GetInteger(process->PageTable().GetAliasRegionStart()); 57 *result = GetInteger(process->GetPageTable().GetAliasRegionStart());
58 R_SUCCEED(); 58 R_SUCCEED();
59 59
60 case InfoType::AliasRegionSize: 60 case InfoType::AliasRegionSize:
61 *result = process->PageTable().GetAliasRegionSize(); 61 *result = process->GetPageTable().GetAliasRegionSize();
62 R_SUCCEED(); 62 R_SUCCEED();
63 63
64 case InfoType::HeapRegionAddress: 64 case InfoType::HeapRegionAddress:
65 *result = GetInteger(process->PageTable().GetHeapRegionStart()); 65 *result = GetInteger(process->GetPageTable().GetHeapRegionStart());
66 R_SUCCEED(); 66 R_SUCCEED();
67 67
68 case InfoType::HeapRegionSize: 68 case InfoType::HeapRegionSize:
69 *result = process->PageTable().GetHeapRegionSize(); 69 *result = process->GetPageTable().GetHeapRegionSize();
70 R_SUCCEED(); 70 R_SUCCEED();
71 71
72 case InfoType::AslrRegionAddress: 72 case InfoType::AslrRegionAddress:
73 *result = GetInteger(process->PageTable().GetAliasCodeRegionStart()); 73 *result = GetInteger(process->GetPageTable().GetAliasCodeRegionStart());
74 R_SUCCEED(); 74 R_SUCCEED();
75 75
76 case InfoType::AslrRegionSize: 76 case InfoType::AslrRegionSize:
77 *result = process->PageTable().GetAliasCodeRegionSize(); 77 *result = process->GetPageTable().GetAliasCodeRegionSize();
78 R_SUCCEED(); 78 R_SUCCEED();
79 79
80 case InfoType::StackRegionAddress: 80 case InfoType::StackRegionAddress:
81 *result = GetInteger(process->PageTable().GetStackRegionStart()); 81 *result = GetInteger(process->GetPageTable().GetStackRegionStart());
82 R_SUCCEED(); 82 R_SUCCEED();
83 83
84 case InfoType::StackRegionSize: 84 case InfoType::StackRegionSize:
85 *result = process->PageTable().GetStackRegionSize(); 85 *result = process->GetPageTable().GetStackRegionSize();
86 R_SUCCEED(); 86 R_SUCCEED();
87 87
88 case InfoType::TotalMemorySize: 88 case InfoType::TotalMemorySize:
diff --git a/src/core/hle/kernel/svc/svc_memory.cpp b/src/core/hle/kernel/svc/svc_memory.cpp
index 5dcb7f045..2cab74127 100644
--- a/src/core/hle/kernel/svc/svc_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_memory.cpp
@@ -63,36 +63,13 @@ Result MapUnmapMemorySanityChecks(const KPageTable& manager, u64 dst_addr, u64 s
63 R_THROW(ResultInvalidCurrentMemory); 63 R_THROW(ResultInvalidCurrentMemory);
64 } 64 }
65 65
66 if (!manager.IsInsideAddressSpace(src_addr, size)) { 66 if (!manager.Contains(src_addr, size)) {
67 LOG_ERROR(Kernel_SVC, 67 LOG_ERROR(Kernel_SVC,
68 "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", 68 "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}",
69 src_addr, size); 69 src_addr, size);
70 R_THROW(ResultInvalidCurrentMemory); 70 R_THROW(ResultInvalidCurrentMemory);
71 } 71 }
72 72
73 if (manager.IsOutsideStackRegion(dst_addr, size)) {
74 LOG_ERROR(Kernel_SVC,
75 "Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}",
76 dst_addr, size);
77 R_THROW(ResultInvalidMemoryRegion);
78 }
79
80 if (manager.IsInsideHeapRegion(dst_addr, size)) {
81 LOG_ERROR(Kernel_SVC,
82 "Destination does not fit within the heap region, addr=0x{:016X}, "
83 "size=0x{:016X}",
84 dst_addr, size);
85 R_THROW(ResultInvalidMemoryRegion);
86 }
87
88 if (manager.IsInsideAliasRegion(dst_addr, size)) {
89 LOG_ERROR(Kernel_SVC,
90 "Destination does not fit within the map region, addr=0x{:016X}, "
91 "size=0x{:016X}",
92 dst_addr, size);
93 R_THROW(ResultInvalidMemoryRegion);
94 }
95
96 R_SUCCEED(); 73 R_SUCCEED();
97} 74}
98 75
@@ -112,7 +89,7 @@ Result SetMemoryPermission(Core::System& system, u64 address, u64 size, MemoryPe
112 R_UNLESS(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission); 89 R_UNLESS(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission);
113 90
114 // Validate that the region is in range for the current process. 91 // Validate that the region is in range for the current process.
115 auto& page_table = GetCurrentProcess(system.Kernel()).PageTable(); 92 auto& page_table = GetCurrentProcess(system.Kernel()).GetPageTable();
116 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); 93 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
117 94
118 // Set the memory attribute. 95 // Set the memory attribute.
@@ -136,7 +113,7 @@ Result SetMemoryAttribute(Core::System& system, u64 address, u64 size, u32 mask,
136 R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination); 113 R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination);
137 114
138 // Validate that the region is in range for the current process. 115 // Validate that the region is in range for the current process.
139 auto& page_table{GetCurrentProcess(system.Kernel()).PageTable()}; 116 auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()};
140 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); 117 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
141 118
142 // Set the memory attribute. 119 // Set the memory attribute.
@@ -148,7 +125,7 @@ Result MapMemory(Core::System& system, u64 dst_addr, u64 src_addr, u64 size) {
148 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, 125 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
149 src_addr, size); 126 src_addr, size);
150 127
151 auto& page_table{GetCurrentProcess(system.Kernel()).PageTable()}; 128 auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()};
152 129
153 if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; 130 if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
154 result.IsError()) { 131 result.IsError()) {
@@ -163,7 +140,7 @@ Result UnmapMemory(Core::System& system, u64 dst_addr, u64 src_addr, u64 size) {
163 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, 140 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
164 src_addr, size); 141 src_addr, size);
165 142
166 auto& page_table{GetCurrentProcess(system.Kernel()).PageTable()}; 143 auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()};
167 144
168 if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; 145 if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
169 result.IsError()) { 146 result.IsError()) {
diff --git a/src/core/hle/kernel/svc/svc_physical_memory.cpp b/src/core/hle/kernel/svc/svc_physical_memory.cpp
index c2fbfb59a..d3545f232 100644
--- a/src/core/hle/kernel/svc/svc_physical_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_physical_memory.cpp
@@ -16,7 +16,7 @@ Result SetHeapSize(Core::System& system, u64* out_address, u64 size) {
16 R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize); 16 R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize);
17 17
18 // Set the heap size. 18 // Set the heap size.
19 R_RETURN(GetCurrentProcess(system.Kernel()).PageTable().SetHeapSize(out_address, size)); 19 R_RETURN(GetCurrentProcess(system.Kernel()).GetPageTable().SetHeapSize(out_address, size));
20} 20}
21 21
22/// Maps memory at a desired address 22/// Maps memory at a desired address
@@ -44,21 +44,21 @@ Result MapPhysicalMemory(Core::System& system, u64 addr, u64 size) {
44 } 44 }
45 45
46 KProcess* const current_process{GetCurrentProcessPointer(system.Kernel())}; 46 KProcess* const current_process{GetCurrentProcessPointer(system.Kernel())};
47 auto& page_table{current_process->PageTable()}; 47 auto& page_table{current_process->GetPageTable()};
48 48
49 if (current_process->GetSystemResourceSize() == 0) { 49 if (current_process->GetSystemResourceSize() == 0) {
50 LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); 50 LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
51 R_THROW(ResultInvalidState); 51 R_THROW(ResultInvalidState);
52 } 52 }
53 53
54 if (!page_table.IsInsideAddressSpace(addr, size)) { 54 if (!page_table.Contains(addr, size)) {
55 LOG_ERROR(Kernel_SVC, 55 LOG_ERROR(Kernel_SVC,
56 "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, 56 "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
57 size); 57 size);
58 R_THROW(ResultInvalidMemoryRegion); 58 R_THROW(ResultInvalidMemoryRegion);
59 } 59 }
60 60
61 if (page_table.IsOutsideAliasRegion(addr, size)) { 61 if (!page_table.IsInAliasRegion(addr, size)) {
62 LOG_ERROR(Kernel_SVC, 62 LOG_ERROR(Kernel_SVC,
63 "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr, 63 "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
64 size); 64 size);
@@ -93,21 +93,21 @@ Result UnmapPhysicalMemory(Core::System& system, u64 addr, u64 size) {
93 } 93 }
94 94
95 KProcess* const current_process{GetCurrentProcessPointer(system.Kernel())}; 95 KProcess* const current_process{GetCurrentProcessPointer(system.Kernel())};
96 auto& page_table{current_process->PageTable()}; 96 auto& page_table{current_process->GetPageTable()};
97 97
98 if (current_process->GetSystemResourceSize() == 0) { 98 if (current_process->GetSystemResourceSize() == 0) {
99 LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); 99 LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
100 R_THROW(ResultInvalidState); 100 R_THROW(ResultInvalidState);
101 } 101 }
102 102
103 if (!page_table.IsInsideAddressSpace(addr, size)) { 103 if (!page_table.Contains(addr, size)) {
104 LOG_ERROR(Kernel_SVC, 104 LOG_ERROR(Kernel_SVC,
105 "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, 105 "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
106 size); 106 size);
107 R_THROW(ResultInvalidMemoryRegion); 107 R_THROW(ResultInvalidMemoryRegion);
108 } 108 }
109 109
110 if (page_table.IsOutsideAliasRegion(addr, size)) { 110 if (!page_table.IsInAliasRegion(addr, size)) {
111 LOG_ERROR(Kernel_SVC, 111 LOG_ERROR(Kernel_SVC,
112 "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr, 112 "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
113 size); 113 size);
diff --git a/src/core/hle/kernel/svc/svc_process.cpp b/src/core/hle/kernel/svc/svc_process.cpp
index 619ed16a3..caa8bee9a 100644
--- a/src/core/hle/kernel/svc/svc_process.cpp
+++ b/src/core/hle/kernel/svc/svc_process.cpp
@@ -66,8 +66,8 @@ Result GetProcessList(Core::System& system, s32* out_num_processes, u64 out_proc
66 auto& kernel = system.Kernel(); 66 auto& kernel = system.Kernel();
67 const auto total_copy_size = out_process_ids_size * sizeof(u64); 67 const auto total_copy_size = out_process_ids_size * sizeof(u64);
68 68
69 if (out_process_ids_size > 0 && !GetCurrentProcess(kernel).PageTable().IsInsideAddressSpace( 69 if (out_process_ids_size > 0 &&
70 out_process_ids, total_copy_size)) { 70 !GetCurrentProcess(kernel).GetPageTable().Contains(out_process_ids, total_copy_size)) {
71 LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}", 71 LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
72 out_process_ids, out_process_ids + total_copy_size); 72 out_process_ids, out_process_ids + total_copy_size);
73 R_THROW(ResultInvalidCurrentMemory); 73 R_THROW(ResultInvalidCurrentMemory);
diff --git a/src/core/hle/kernel/svc/svc_process_memory.cpp b/src/core/hle/kernel/svc/svc_process_memory.cpp
index aee0f2f36..07cd48175 100644
--- a/src/core/hle/kernel/svc/svc_process_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_process_memory.cpp
@@ -49,7 +49,7 @@ Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, u
49 R_UNLESS(process.IsNotNull(), ResultInvalidHandle); 49 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
50 50
51 // Validate that the address is in range. 51 // Validate that the address is in range.
52 auto& page_table = process->PageTable(); 52 auto& page_table = process->GetPageTable();
53 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); 53 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
54 54
55 // Set the memory permission. 55 // Set the memory permission.
@@ -77,8 +77,8 @@ Result MapProcessMemory(Core::System& system, u64 dst_address, Handle process_ha
77 R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); 77 R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
78 78
79 // Get the page tables. 79 // Get the page tables.
80 auto& dst_pt = dst_process->PageTable(); 80 auto& dst_pt = dst_process->GetPageTable();
81 auto& src_pt = src_process->PageTable(); 81 auto& src_pt = src_process->GetPageTable();
82 82
83 // Validate that the mapping is in range. 83 // Validate that the mapping is in range.
84 R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory); 84 R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
@@ -118,8 +118,8 @@ Result UnmapProcessMemory(Core::System& system, u64 dst_address, Handle process_
118 R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); 118 R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
119 119
120 // Get the page tables. 120 // Get the page tables.
121 auto& dst_pt = dst_process->PageTable(); 121 auto& dst_pt = dst_process->GetPageTable();
122 auto& src_pt = src_process->PageTable(); 122 auto& src_pt = src_process->GetPageTable();
123 123
124 // Validate that the mapping is in range. 124 // Validate that the mapping is in range.
125 R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory); 125 R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
@@ -178,8 +178,8 @@ Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst
178 R_THROW(ResultInvalidHandle); 178 R_THROW(ResultInvalidHandle);
179 } 179 }
180 180
181 auto& page_table = process->PageTable(); 181 auto& page_table = process->GetPageTable();
182 if (!page_table.IsInsideAddressSpace(src_address, size)) { 182 if (!page_table.Contains(src_address, size)) {
183 LOG_ERROR(Kernel_SVC, 183 LOG_ERROR(Kernel_SVC,
184 "Source address range is not within the address space (src_address=0x{:016X}, " 184 "Source address range is not within the address space (src_address=0x{:016X}, "
185 "size=0x{:016X}).", 185 "size=0x{:016X}).",
@@ -187,14 +187,6 @@ Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst
187 R_THROW(ResultInvalidCurrentMemory); 187 R_THROW(ResultInvalidCurrentMemory);
188 } 188 }
189 189
190 if (!page_table.IsInsideASLRRegion(dst_address, size)) {
191 LOG_ERROR(Kernel_SVC,
192 "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
193 "size=0x{:016X}).",
194 dst_address, size);
195 R_THROW(ResultInvalidMemoryRegion);
196 }
197
198 R_RETURN(page_table.MapCodeMemory(dst_address, src_address, size)); 190 R_RETURN(page_table.MapCodeMemory(dst_address, src_address, size));
199} 191}
200 192
@@ -246,8 +238,8 @@ Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 d
246 R_THROW(ResultInvalidHandle); 238 R_THROW(ResultInvalidHandle);
247 } 239 }
248 240
249 auto& page_table = process->PageTable(); 241 auto& page_table = process->GetPageTable();
250 if (!page_table.IsInsideAddressSpace(src_address, size)) { 242 if (!page_table.Contains(src_address, size)) {
251 LOG_ERROR(Kernel_SVC, 243 LOG_ERROR(Kernel_SVC,
252 "Source address range is not within the address space (src_address=0x{:016X}, " 244 "Source address range is not within the address space (src_address=0x{:016X}, "
253 "size=0x{:016X}).", 245 "size=0x{:016X}).",
@@ -255,14 +247,6 @@ Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 d
255 R_THROW(ResultInvalidCurrentMemory); 247 R_THROW(ResultInvalidCurrentMemory);
256 } 248 }
257 249
258 if (!page_table.IsInsideASLRRegion(dst_address, size)) {
259 LOG_ERROR(Kernel_SVC,
260 "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
261 "size=0x{:016X}).",
262 dst_address, size);
263 R_THROW(ResultInvalidMemoryRegion);
264 }
265
266 R_RETURN(page_table.UnmapCodeMemory(dst_address, src_address, size, 250 R_RETURN(page_table.UnmapCodeMemory(dst_address, src_address, size,
267 KPageTable::ICacheInvalidationStrategy::InvalidateAll)); 251 KPageTable::ICacheInvalidationStrategy::InvalidateAll));
268} 252}
diff --git a/src/core/hle/kernel/svc/svc_query_memory.cpp b/src/core/hle/kernel/svc/svc_query_memory.cpp
index 4d9fcd25f..51af06e97 100644
--- a/src/core/hle/kernel/svc/svc_query_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_query_memory.cpp
@@ -31,7 +31,7 @@ Result QueryProcessMemory(Core::System& system, uint64_t out_memory_info, PageIn
31 } 31 }
32 32
33 auto& current_memory{GetCurrentMemory(system.Kernel())}; 33 auto& current_memory{GetCurrentMemory(system.Kernel())};
34 const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()}; 34 const auto memory_info{process->GetPageTable().QueryInfo(address).GetSvcMemoryInfo()};
35 35
36 current_memory.WriteBlock(out_memory_info, std::addressof(memory_info), sizeof(memory_info)); 36 current_memory.WriteBlock(out_memory_info, std::addressof(memory_info), sizeof(memory_info));
37 37
diff --git a/src/core/hle/kernel/svc/svc_shared_memory.cpp b/src/core/hle/kernel/svc/svc_shared_memory.cpp
index a698596aa..012b1ae2b 100644
--- a/src/core/hle/kernel/svc/svc_shared_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_shared_memory.cpp
@@ -43,7 +43,7 @@ Result MapSharedMemory(Core::System& system, Handle shmem_handle, u64 address, u
43 43
44 // Get the current process. 44 // Get the current process.
45 auto& process = GetCurrentProcess(system.Kernel()); 45 auto& process = GetCurrentProcess(system.Kernel());
46 auto& page_table = process.PageTable(); 46 auto& page_table = process.GetPageTable();
47 47
48 // Get the shared memory. 48 // Get the shared memory.
49 KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle); 49 KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
@@ -73,7 +73,7 @@ Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, u64 address,
73 73
74 // Get the current process. 74 // Get the current process.
75 auto& process = GetCurrentProcess(system.Kernel()); 75 auto& process = GetCurrentProcess(system.Kernel());
76 auto& page_table = process.PageTable(); 76 auto& page_table = process.GetPageTable();
77 77
78 // Get the shared memory. 78 // Get the shared memory.
79 KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle); 79 KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
diff --git a/src/core/hle/kernel/svc/svc_thread.cpp b/src/core/hle/kernel/svc/svc_thread.cpp
index 36b94e6bf..92bcea72b 100644
--- a/src/core/hle/kernel/svc/svc_thread.cpp
+++ b/src/core/hle/kernel/svc/svc_thread.cpp
@@ -236,7 +236,7 @@ Result GetThreadList(Core::System& system, s32* out_num_threads, u64 out_thread_
236 const auto total_copy_size = out_thread_ids_size * sizeof(u64); 236 const auto total_copy_size = out_thread_ids_size * sizeof(u64);
237 237
238 if (out_thread_ids_size > 0 && 238 if (out_thread_ids_size > 0 &&
239 !current_process->PageTable().IsInsideAddressSpace(out_thread_ids, total_copy_size)) { 239 !current_process->GetPageTable().Contains(out_thread_ids, total_copy_size)) {
240 LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}", 240 LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
241 out_thread_ids, out_thread_ids + total_copy_size); 241 out_thread_ids, out_thread_ids + total_copy_size);
242 R_THROW(ResultInvalidCurrentMemory); 242 R_THROW(ResultInvalidCurrentMemory);
diff --git a/src/core/hle/kernel/svc/svc_transfer_memory.cpp b/src/core/hle/kernel/svc/svc_transfer_memory.cpp
index 82d469a37..7d94e7f09 100644
--- a/src/core/hle/kernel/svc/svc_transfer_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_transfer_memory.cpp
@@ -55,7 +55,7 @@ Result CreateTransferMemory(Core::System& system, Handle* out, u64 address, u64
55 SCOPE_EXIT({ trmem->Close(); }); 55 SCOPE_EXIT({ trmem->Close(); });
56 56
57 // Ensure that the region is in range. 57 // Ensure that the region is in range.
58 R_UNLESS(process.PageTable().Contains(address, size), ResultInvalidCurrentMemory); 58 R_UNLESS(process.GetPageTable().Contains(address, size), ResultInvalidCurrentMemory);
59 59
60 // Initialize the transfer memory. 60 // Initialize the transfer memory.
61 R_TRY(trmem->Initialize(address, size, map_perm)); 61 R_TRY(trmem->Initialize(address, size, map_perm));
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index a2375508a..4f400d341 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -506,8 +506,8 @@ void ISelfController::SetHandlesRequestToDisplay(HLERequestContext& ctx) {
506void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) { 506void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) {
507 IPC::RequestParser rp{ctx}; 507 IPC::RequestParser rp{ctx};
508 idle_time_detection_extension = rp.Pop<u32>(); 508 idle_time_detection_extension = rp.Pop<u32>();
509 LOG_WARNING(Service_AM, "(STUBBED) called idle_time_detection_extension={}", 509 LOG_DEBUG(Service_AM, "(STUBBED) called idle_time_detection_extension={}",
510 idle_time_detection_extension); 510 idle_time_detection_extension);
511 511
512 IPC::ResponseBuilder rb{ctx, 2}; 512 IPC::ResponseBuilder rb{ctx, 2};
513 rb.Push(ResultSuccess); 513 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/hle_ipc.cpp b/src/core/hle/service/hle_ipc.cpp
index 2290df705..f6a1e54f2 100644
--- a/src/core/hle/service/hle_ipc.cpp
+++ b/src/core/hle/service/hle_ipc.cpp
@@ -329,8 +329,22 @@ std::vector<u8> HLERequestContext::ReadBufferCopy(std::size_t buffer_index) cons
329} 329}
330 330
331std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { 331std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
332 static thread_local std::array<Common::ScratchBuffer<u8>, 2> read_buffer_a; 332 static thread_local std::array read_buffer_a{
333 static thread_local std::array<Common::ScratchBuffer<u8>, 2> read_buffer_x; 333 Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
334 Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
335 };
336 static thread_local std::array read_buffer_data_a{
337 Common::ScratchBuffer<u8>(),
338 Common::ScratchBuffer<u8>(),
339 };
340 static thread_local std::array read_buffer_x{
341 Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
342 Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
343 };
344 static thread_local std::array read_buffer_data_x{
345 Common::ScratchBuffer<u8>(),
346 Common::ScratchBuffer<u8>(),
347 };
334 348
335 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && 349 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
336 BufferDescriptorA()[buffer_index].Size()}; 350 BufferDescriptorA()[buffer_index].Size()};
@@ -339,19 +353,17 @@ std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) cons
339 BufferDescriptorA().size() > buffer_index, { return {}; }, 353 BufferDescriptorA().size() > buffer_index, { return {}; },
340 "BufferDescriptorA invalid buffer_index {}", buffer_index); 354 "BufferDescriptorA invalid buffer_index {}", buffer_index);
341 auto& read_buffer = read_buffer_a[buffer_index]; 355 auto& read_buffer = read_buffer_a[buffer_index];
342 read_buffer.resize_destructive(BufferDescriptorA()[buffer_index].Size()); 356 return read_buffer.Read(BufferDescriptorA()[buffer_index].Address(),
343 memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), read_buffer.data(), 357 BufferDescriptorA()[buffer_index].Size(),
344 read_buffer.size()); 358 &read_buffer_data_a[buffer_index]);
345 return read_buffer;
346 } else { 359 } else {
347 ASSERT_OR_EXECUTE_MSG( 360 ASSERT_OR_EXECUTE_MSG(
348 BufferDescriptorX().size() > buffer_index, { return {}; }, 361 BufferDescriptorX().size() > buffer_index, { return {}; },
349 "BufferDescriptorX invalid buffer_index {}", buffer_index); 362 "BufferDescriptorX invalid buffer_index {}", buffer_index);
350 auto& read_buffer = read_buffer_x[buffer_index]; 363 auto& read_buffer = read_buffer_x[buffer_index];
351 read_buffer.resize_destructive(BufferDescriptorX()[buffer_index].Size()); 364 return read_buffer.Read(BufferDescriptorX()[buffer_index].Address(),
352 memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), read_buffer.data(), 365 BufferDescriptorX()[buffer_index].Size(),
353 read_buffer.size()); 366 &read_buffer_data_x[buffer_index]);
354 return read_buffer;
355 } 367 }
356} 368}
357 369
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index c42489ff9..055c0a2db 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -318,15 +318,15 @@ public:
318 return false; 318 return false;
319 } 319 }
320 320
321 if (!page_table.IsInsideAddressSpace(out_addr, size)) { 321 if (!page_table.Contains(out_addr, size)) {
322 return false; 322 return false;
323 } 323 }
324 324
325 if (page_table.IsInsideHeapRegion(out_addr, size)) { 325 if (page_table.IsInHeapRegion(out_addr, size)) {
326 return false; 326 return false;
327 } 327 }
328 328
329 if (page_table.IsInsideAliasRegion(out_addr, size)) { 329 if (page_table.IsInAliasRegion(out_addr, size)) {
330 return false; 330 return false;
331 } 331 }
332 332
@@ -358,7 +358,7 @@ public:
358 } 358 }
359 359
360 ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr base_addr, u64 size) { 360 ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr base_addr, u64 size) {
361 auto& page_table{process->PageTable()}; 361 auto& page_table{process->GetPageTable()};
362 VAddr addr{}; 362 VAddr addr{};
363 363
364 for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { 364 for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
@@ -382,7 +382,7 @@ public:
382 ResultVal<VAddr> MapNro(Kernel::KProcess* process, VAddr nro_addr, std::size_t nro_size, 382 ResultVal<VAddr> MapNro(Kernel::KProcess* process, VAddr nro_addr, std::size_t nro_size,
383 VAddr bss_addr, std::size_t bss_size, std::size_t size) { 383 VAddr bss_addr, std::size_t bss_size, std::size_t size) {
384 for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { 384 for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
385 auto& page_table{process->PageTable()}; 385 auto& page_table{process->GetPageTable()};
386 VAddr addr{}; 386 VAddr addr{};
387 387
388 CASCADE_RESULT(addr, MapProcessCodeMemory(process, nro_addr, nro_size)); 388 CASCADE_RESULT(addr, MapProcessCodeMemory(process, nro_addr, nro_size));
@@ -437,12 +437,12 @@ public:
437 CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start, 437 CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start,
438 nro_header.segment_headers[DATA_INDEX].memory_size); 438 nro_header.segment_headers[DATA_INDEX].memory_size);
439 439
440 CASCADE_CODE(process->PageTable().SetProcessMemoryPermission( 440 CASCADE_CODE(process->GetPageTable().SetProcessMemoryPermission(
441 text_start, ro_start - text_start, Kernel::Svc::MemoryPermission::ReadExecute)); 441 text_start, ro_start - text_start, Kernel::Svc::MemoryPermission::ReadExecute));
442 CASCADE_CODE(process->PageTable().SetProcessMemoryPermission( 442 CASCADE_CODE(process->GetPageTable().SetProcessMemoryPermission(
443 ro_start, data_start - ro_start, Kernel::Svc::MemoryPermission::Read)); 443 ro_start, data_start - ro_start, Kernel::Svc::MemoryPermission::Read));
444 444
445 return process->PageTable().SetProcessMemoryPermission( 445 return process->GetPageTable().SetProcessMemoryPermission(
446 data_start, bss_end_addr - data_start, Kernel::Svc::MemoryPermission::ReadWrite); 446 data_start, bss_end_addr - data_start, Kernel::Svc::MemoryPermission::ReadWrite);
447 } 447 }
448 448
@@ -571,7 +571,7 @@ public:
571 571
572 Result UnmapNro(const NROInfo& info) { 572 Result UnmapNro(const NROInfo& info) {
573 // Each region must be unmapped separately to validate memory state 573 // Each region must be unmapped separately to validate memory state
574 auto& page_table{system.ApplicationProcess()->PageTable()}; 574 auto& page_table{system.ApplicationProcess()->GetPageTable()};
575 575
576 if (info.bss_size != 0) { 576 if (info.bss_size != 0) {
577 CASCADE_CODE(page_table.UnmapCodeMemory( 577 CASCADE_CODE(page_table.UnmapCodeMemory(
@@ -643,7 +643,7 @@ public:
643 643
644 initialized = true; 644 initialized = true;
645 current_map_addr = 645 current_map_addr =
646 GetInteger(system.ApplicationProcess()->PageTable().GetAliasCodeRegionStart()); 646 GetInteger(system.ApplicationProcess()->GetPageTable().GetAliasCodeRegionStart());
647 647
648 IPC::ResponseBuilder rb{ctx, 2}; 648 IPC::ResponseBuilder rb{ctx, 2};
649 rb.Push(ResultSuccess); 649 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/nfc/common/amiibo_crypto.cpp b/src/core/hle/service/nfc/common/amiibo_crypto.cpp
index bc232c334..9556e9193 100644
--- a/src/core/hle/service/nfc/common/amiibo_crypto.cpp
+++ b/src/core/hle/service/nfc/common/amiibo_crypto.cpp
@@ -180,7 +180,7 @@ std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed
180} 180}
181 181
182void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key, 182void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key,
183 const std::vector<u8>& seed) { 183 std::span<const u8> seed) {
184 // Initialize context 184 // Initialize context
185 ctx.used = false; 185 ctx.used = false;
186 ctx.counter = 0; 186 ctx.counter = 0;
diff --git a/src/core/hle/service/nfc/common/amiibo_crypto.h b/src/core/hle/service/nfc/common/amiibo_crypto.h
index 6a3e0841e..2cc0e4d51 100644
--- a/src/core/hle/service/nfc/common/amiibo_crypto.h
+++ b/src/core/hle/service/nfc/common/amiibo_crypto.h
@@ -75,7 +75,7 @@ std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed
75 75
76// Initializes mbedtls context 76// Initializes mbedtls context
77void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key, 77void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key,
78 const std::vector<u8>& seed); 78 std::span<const u8> seed);
79 79
80// Feeds data to mbedtls context to generate the derived key 80// Feeds data to mbedtls context to generate the derived key
81void CryptoStep(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, DrgbOutput& output); 81void CryptoStep(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, DrgbOutput& output);
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp
index 2d633b03f..49446bc42 100644
--- a/src/core/hle/service/nfc/common/device.cpp
+++ b/src/core/hle/service/nfc/common/device.cpp
@@ -34,8 +34,6 @@
34#include "core/hle/service/nfc/mifare_result.h" 34#include "core/hle/service/nfc/mifare_result.h"
35#include "core/hle/service/nfc/nfc_result.h" 35#include "core/hle/service/nfc/nfc_result.h"
36#include "core/hle/service/time/time_manager.h" 36#include "core/hle/service/time/time_manager.h"
37#include "core/hle/service/time/time_zone_content_manager.h"
38#include "core/hle/service/time/time_zone_types.h"
39 37
40namespace Service::NFC { 38namespace Service::NFC {
41NfcDevice::NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, 39NfcDevice::NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
@@ -1486,6 +1484,7 @@ DeviceState NfcDevice::GetCurrentState() const {
1486} 1484}
1487 1485
1488Result NfcDevice::GetNpadId(Core::HID::NpadIdType& out_npad_id) const { 1486Result NfcDevice::GetNpadId(Core::HID::NpadIdType& out_npad_id) const {
1487 // TODO: This should get the npad id from nn::hid::system::GetXcdHandleForNpadWithNfc
1489 out_npad_id = npad_id; 1488 out_npad_id = npad_id;
1490 return ResultSuccess; 1489 return ResultSuccess;
1491} 1490}
diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp
index 562f3a28e..a71d26157 100644
--- a/src/core/hle/service/nfc/common/device_manager.cpp
+++ b/src/core/hle/service/nfc/common/device_manager.cpp
@@ -1,6 +1,8 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include <algorithm>
5
4#include "common/logging/log.h" 6#include "common/logging/log.h"
5#include "core/core.h" 7#include "core/core.h"
6#include "core/hid/hid_types.h" 8#include "core/hid/hid_types.h"
@@ -10,6 +12,7 @@
10#include "core/hle/service/nfc/common/device_manager.h" 12#include "core/hle/service/nfc/common/device_manager.h"
11#include "core/hle/service/nfc/nfc_result.h" 13#include "core/hle/service/nfc/nfc_result.h"
12#include "core/hle/service/time/clock_types.h" 14#include "core/hle/service/time/clock_types.h"
15#include "core/hle/service/time/time_manager.h"
13 16
14namespace Service::NFC { 17namespace Service::NFC {
15 18
@@ -51,22 +54,53 @@ Result DeviceManager::Finalize() {
51 return ResultSuccess; 54 return ResultSuccess;
52} 55}
53 56
54Result DeviceManager::ListDevices(std::vector<u64>& nfp_devices, 57Result DeviceManager::ListDevices(std::vector<u64>& nfp_devices, std::size_t max_allowed_devices,
55 std::size_t max_allowed_devices) const { 58 bool skip_fatal_errors) const {
59 std::scoped_lock lock{mutex};
60 if (max_allowed_devices < 1) {
61 return ResultInvalidArgument;
62 }
63
64 Result result = IsNfcParameterSet();
65 if (result.IsError()) {
66 return result;
67 }
68
69 result = IsNfcEnabled();
70 if (result.IsError()) {
71 return result;
72 }
73
74 result = IsNfcInitialized();
75 if (result.IsError()) {
76 return result;
77 }
78
56 for (auto& device : devices) { 79 for (auto& device : devices) {
57 if (nfp_devices.size() >= max_allowed_devices) { 80 if (nfp_devices.size() >= max_allowed_devices) {
58 continue; 81 continue;
59 } 82 }
60 if (device->GetCurrentState() != DeviceState::Unavailable) { 83 if (skip_fatal_errors) {
61 nfp_devices.push_back(device->GetHandle()); 84 constexpr u64 MinimumRecoveryTime = 60;
85 auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()};
86 const u64 elapsed_time = standard_steady_clock.GetCurrentTimePoint(system).time_point -
87 time_since_last_error;
88
89 if (time_since_last_error != 0 && elapsed_time < MinimumRecoveryTime) {
90 continue;
91 }
62 } 92 }
93 if (device->GetCurrentState() == DeviceState::Unavailable) {
94 continue;
95 }
96 nfp_devices.push_back(device->GetHandle());
63 } 97 }
64 98
65 if (nfp_devices.empty()) { 99 if (nfp_devices.empty()) {
66 return ResultDeviceNotFound; 100 return ResultDeviceNotFound;
67 } 101 }
68 102
69 return ResultSuccess; 103 return result;
70} 104}
71 105
72DeviceState DeviceManager::GetDeviceState(u64 device_handle) const { 106DeviceState DeviceManager::GetDeviceState(u64 device_handle) const {
@@ -79,10 +113,10 @@ DeviceState DeviceManager::GetDeviceState(u64 device_handle) const {
79 return device->GetCurrentState(); 113 return device->GetCurrentState();
80 } 114 }
81 115
82 return DeviceState::Unavailable; 116 return DeviceState::Finalized;
83} 117}
84 118
85Result DeviceManager::GetNpadId(u64 device_handle, Core::HID::NpadIdType& npad_id) const { 119Result DeviceManager::GetNpadId(u64 device_handle, Core::HID::NpadIdType& npad_id) {
86 std::scoped_lock lock{mutex}; 120 std::scoped_lock lock{mutex};
87 121
88 std::shared_ptr<NfcDevice> device = nullptr; 122 std::shared_ptr<NfcDevice> device = nullptr;
@@ -128,7 +162,7 @@ Result DeviceManager::StopDetection(u64 device_handle) {
128 return result; 162 return result;
129} 163}
130 164
131Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info) const { 165Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info) {
132 std::scoped_lock lock{mutex}; 166 std::scoped_lock lock{mutex};
133 167
134 std::shared_ptr<NfcDevice> device = nullptr; 168 std::shared_ptr<NfcDevice> device = nullptr;
@@ -142,24 +176,46 @@ Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info) const {
142 return result; 176 return result;
143} 177}
144 178
145Kernel::KReadableEvent& DeviceManager::AttachActivateEvent(u64 device_handle) const { 179Result DeviceManager::AttachActivateEvent(Kernel::KReadableEvent** out_event,
146 std::scoped_lock lock{mutex}; 180 u64 device_handle) const {
147 181 std::vector<u64> nfp_devices;
148 std::shared_ptr<NfcDevice> device = nullptr; 182 std::shared_ptr<NfcDevice> device = nullptr;
149 GetDeviceFromHandle(device_handle, device, false); 183 Result result = ListDevices(nfp_devices, 9, false);
150 184
151 // TODO: Return proper error code on failure 185 if (result.IsSuccess()) {
152 return device->GetActivateEvent(); 186 result = CheckHandleOnList(device_handle, nfp_devices);
153} 187 }
154 188
155Kernel::KReadableEvent& DeviceManager::AttachDeactivateEvent(u64 device_handle) const { 189 if (result.IsSuccess()) {
156 std::scoped_lock lock{mutex}; 190 result = GetDeviceFromHandle(device_handle, device, false);
191 }
192
193 if (result.IsSuccess()) {
194 *out_event = &device->GetActivateEvent();
195 }
196
197 return result;
198}
157 199
200Result DeviceManager::AttachDeactivateEvent(Kernel::KReadableEvent** out_event,
201 u64 device_handle) const {
202 std::vector<u64> nfp_devices;
158 std::shared_ptr<NfcDevice> device = nullptr; 203 std::shared_ptr<NfcDevice> device = nullptr;
159 GetDeviceFromHandle(device_handle, device, false); 204 Result result = ListDevices(nfp_devices, 9, false);
160 205
161 // TODO: Return proper error code on failure 206 if (result.IsSuccess()) {
162 return device->GetDeactivateEvent(); 207 result = CheckHandleOnList(device_handle, nfp_devices);
208 }
209
210 if (result.IsSuccess()) {
211 result = GetDeviceFromHandle(device_handle, device, false);
212 }
213
214 if (result.IsSuccess()) {
215 *out_event = &device->GetDeactivateEvent();
216 }
217
218 return result;
163} 219}
164 220
165Result DeviceManager::ReadMifare(u64 device_handle, 221Result DeviceManager::ReadMifare(u64 device_handle,
@@ -253,7 +309,7 @@ Result DeviceManager::OpenApplicationArea(u64 device_handle, u32 access_id) {
253 return result; 309 return result;
254} 310}
255 311
256Result DeviceManager::GetApplicationArea(u64 device_handle, std::span<u8> data) const { 312Result DeviceManager::GetApplicationArea(u64 device_handle, std::span<u8> data) {
257 std::scoped_lock lock{mutex}; 313 std::scoped_lock lock{mutex};
258 314
259 std::shared_ptr<NfcDevice> device = nullptr; 315 std::shared_ptr<NfcDevice> device = nullptr;
@@ -324,7 +380,7 @@ Result DeviceManager::CreateApplicationArea(u64 device_handle, u32 access_id,
324 return result; 380 return result;
325} 381}
326 382
327Result DeviceManager::GetRegisterInfo(u64 device_handle, NFP::RegisterInfo& register_info) const { 383Result DeviceManager::GetRegisterInfo(u64 device_handle, NFP::RegisterInfo& register_info) {
328 std::scoped_lock lock{mutex}; 384 std::scoped_lock lock{mutex};
329 385
330 std::shared_ptr<NfcDevice> device = nullptr; 386 std::shared_ptr<NfcDevice> device = nullptr;
@@ -338,7 +394,7 @@ Result DeviceManager::GetRegisterInfo(u64 device_handle, NFP::RegisterInfo& regi
338 return result; 394 return result;
339} 395}
340 396
341Result DeviceManager::GetCommonInfo(u64 device_handle, NFP::CommonInfo& common_info) const { 397Result DeviceManager::GetCommonInfo(u64 device_handle, NFP::CommonInfo& common_info) {
342 std::scoped_lock lock{mutex}; 398 std::scoped_lock lock{mutex};
343 399
344 std::shared_ptr<NfcDevice> device = nullptr; 400 std::shared_ptr<NfcDevice> device = nullptr;
@@ -352,7 +408,7 @@ Result DeviceManager::GetCommonInfo(u64 device_handle, NFP::CommonInfo& common_i
352 return result; 408 return result;
353} 409}
354 410
355Result DeviceManager::GetModelInfo(u64 device_handle, NFP::ModelInfo& model_info) const { 411Result DeviceManager::GetModelInfo(u64 device_handle, NFP::ModelInfo& model_info) {
356 std::scoped_lock lock{mutex}; 412 std::scoped_lock lock{mutex};
357 413
358 std::shared_ptr<NfcDevice> device = nullptr; 414 std::shared_ptr<NfcDevice> device = nullptr;
@@ -399,7 +455,7 @@ Result DeviceManager::Format(u64 device_handle) {
399 return result; 455 return result;
400} 456}
401 457
402Result DeviceManager::GetAdminInfo(u64 device_handle, NFP::AdminInfo& admin_info) const { 458Result DeviceManager::GetAdminInfo(u64 device_handle, NFP::AdminInfo& admin_info) {
403 std::scoped_lock lock{mutex}; 459 std::scoped_lock lock{mutex};
404 460
405 std::shared_ptr<NfcDevice> device = nullptr; 461 std::shared_ptr<NfcDevice> device = nullptr;
@@ -414,7 +470,7 @@ Result DeviceManager::GetAdminInfo(u64 device_handle, NFP::AdminInfo& admin_info
414} 470}
415 471
416Result DeviceManager::GetRegisterInfoPrivate(u64 device_handle, 472Result DeviceManager::GetRegisterInfoPrivate(u64 device_handle,
417 NFP::RegisterInfoPrivate& register_info) const { 473 NFP::RegisterInfoPrivate& register_info) {
418 std::scoped_lock lock{mutex}; 474 std::scoped_lock lock{mutex};
419 475
420 std::shared_ptr<NfcDevice> device = nullptr; 476 std::shared_ptr<NfcDevice> device = nullptr;
@@ -471,7 +527,7 @@ Result DeviceManager::DeleteApplicationArea(u64 device_handle) {
471 return result; 527 return result;
472} 528}
473 529
474Result DeviceManager::ExistsApplicationArea(u64 device_handle, bool& has_application_area) const { 530Result DeviceManager::ExistsApplicationArea(u64 device_handle, bool& has_application_area) {
475 std::scoped_lock lock{mutex}; 531 std::scoped_lock lock{mutex};
476 532
477 std::shared_ptr<NfcDevice> device = nullptr; 533 std::shared_ptr<NfcDevice> device = nullptr;
@@ -485,7 +541,7 @@ Result DeviceManager::ExistsApplicationArea(u64 device_handle, bool& has_applica
485 return result; 541 return result;
486} 542}
487 543
488Result DeviceManager::GetAll(u64 device_handle, NFP::NfpData& nfp_data) const { 544Result DeviceManager::GetAll(u64 device_handle, NFP::NfpData& nfp_data) {
489 std::scoped_lock lock{mutex}; 545 std::scoped_lock lock{mutex};
490 546
491 std::shared_ptr<NfcDevice> device = nullptr; 547 std::shared_ptr<NfcDevice> device = nullptr;
@@ -541,7 +597,7 @@ Result DeviceManager::BreakTag(u64 device_handle, NFP::BreakType break_type) {
541 return result; 597 return result;
542} 598}
543 599
544Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) const { 600Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) {
545 std::scoped_lock lock{mutex}; 601 std::scoped_lock lock{mutex};
546 602
547 std::shared_ptr<NfcDevice> device = nullptr; 603 std::shared_ptr<NfcDevice> device = nullptr;
@@ -593,6 +649,19 @@ Result DeviceManager::WriteNtf(u64 device_handle, NFP::WriteType, std::span<cons
593 return result; 649 return result;
594} 650}
595 651
652Result DeviceManager::CheckHandleOnList(u64 device_handle,
653 const std::span<const u64> device_list) const {
654 if (device_list.size() < 1) {
655 return ResultDeviceNotFound;
656 }
657
658 if (std::find(device_list.begin(), device_list.end(), device_handle) != device_list.end()) {
659 return ResultSuccess;
660 }
661
662 return ResultDeviceNotFound;
663}
664
596Result DeviceManager::GetDeviceFromHandle(u64 handle, std::shared_ptr<NfcDevice>& nfc_device, 665Result DeviceManager::GetDeviceFromHandle(u64 handle, std::shared_ptr<NfcDevice>& nfc_device,
597 bool check_state) const { 666 bool check_state) const {
598 if (check_state) { 667 if (check_state) {
@@ -647,7 +716,7 @@ Result DeviceManager::GetDeviceHandle(u64 handle, std::shared_ptr<NfcDevice>& de
647} 716}
648 717
649Result DeviceManager::VerifyDeviceResult(std::shared_ptr<NfcDevice> device, 718Result DeviceManager::VerifyDeviceResult(std::shared_ptr<NfcDevice> device,
650 Result operation_result) const { 719 Result operation_result) {
651 if (operation_result.IsSuccess()) { 720 if (operation_result.IsSuccess()) {
652 return operation_result; 721 return operation_result;
653 } 722 }
@@ -669,6 +738,12 @@ Result DeviceManager::VerifyDeviceResult(std::shared_ptr<NfcDevice> device,
669 return device_state; 738 return device_state;
670 } 739 }
671 740
741 if (operation_result == ResultUnknown112 || operation_result == ResultUnknown114 ||
742 operation_result == ResultUnknown115) {
743 auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()};
744 time_since_last_error = standard_steady_clock.GetCurrentTimePoint(system).time_point;
745 }
746
672 return operation_result; 747 return operation_result;
673} 748}
674 749
diff --git a/src/core/hle/service/nfc/common/device_manager.h b/src/core/hle/service/nfc/common/device_manager.h
index c61ba0cf3..c9f038e32 100644
--- a/src/core/hle/service/nfc/common/device_manager.h
+++ b/src/core/hle/service/nfc/common/device_manager.h
@@ -27,15 +27,16 @@ public:
27 // Nfc device manager 27 // Nfc device manager
28 Result Initialize(); 28 Result Initialize();
29 Result Finalize(); 29 Result Finalize();
30 Result ListDevices(std::vector<u64>& nfp_devices, std::size_t max_allowed_devices) const; 30 Result ListDevices(std::vector<u64>& nfp_devices, std::size_t max_allowed_devices,
31 bool skip_fatal_errors) const;
31 DeviceState GetDeviceState(u64 device_handle) const; 32 DeviceState GetDeviceState(u64 device_handle) const;
32 Result GetNpadId(u64 device_handle, Core::HID::NpadIdType& npad_id) const; 33 Result GetNpadId(u64 device_handle, Core::HID::NpadIdType& npad_id);
33 Kernel::KReadableEvent& AttachAvailabilityChangeEvent() const; 34 Kernel::KReadableEvent& AttachAvailabilityChangeEvent() const;
34 Result StartDetection(u64 device_handle, NfcProtocol tag_protocol); 35 Result StartDetection(u64 device_handle, NfcProtocol tag_protocol);
35 Result StopDetection(u64 device_handle); 36 Result StopDetection(u64 device_handle);
36 Result GetTagInfo(u64 device_handle, NFP::TagInfo& tag_info) const; 37 Result GetTagInfo(u64 device_handle, NFP::TagInfo& tag_info);
37 Kernel::KReadableEvent& AttachActivateEvent(u64 device_handle) const; 38 Result AttachActivateEvent(Kernel::KReadableEvent** event, u64 device_handle) const;
38 Kernel::KReadableEvent& AttachDeactivateEvent(u64 device_handle) const; 39 Result AttachDeactivateEvent(Kernel::KReadableEvent** event, u64 device_handle) const;
39 Result ReadMifare(u64 device_handle, 40 Result ReadMifare(u64 device_handle,
40 const std::span<const MifareReadBlockParameter> read_parameters, 41 const std::span<const MifareReadBlockParameter> read_parameters,
41 std::span<MifareReadBlockData> read_data); 42 std::span<MifareReadBlockData> read_data);
@@ -48,28 +49,28 @@ public:
48 Result Mount(u64 device_handle, NFP::ModelType model_type, NFP::MountTarget mount_target); 49 Result Mount(u64 device_handle, NFP::ModelType model_type, NFP::MountTarget mount_target);
49 Result Unmount(u64 device_handle); 50 Result Unmount(u64 device_handle);
50 Result OpenApplicationArea(u64 device_handle, u32 access_id); 51 Result OpenApplicationArea(u64 device_handle, u32 access_id);
51 Result GetApplicationArea(u64 device_handle, std::span<u8> data) const; 52 Result GetApplicationArea(u64 device_handle, std::span<u8> data);
52 Result SetApplicationArea(u64 device_handle, std::span<const u8> data); 53 Result SetApplicationArea(u64 device_handle, std::span<const u8> data);
53 Result Flush(u64 device_handle); 54 Result Flush(u64 device_handle);
54 Result Restore(u64 device_handle); 55 Result Restore(u64 device_handle);
55 Result CreateApplicationArea(u64 device_handle, u32 access_id, std::span<const u8> data); 56 Result CreateApplicationArea(u64 device_handle, u32 access_id, std::span<const u8> data);
56 Result GetRegisterInfo(u64 device_handle, NFP::RegisterInfo& register_info) const; 57 Result GetRegisterInfo(u64 device_handle, NFP::RegisterInfo& register_info);
57 Result GetCommonInfo(u64 device_handle, NFP::CommonInfo& common_info) const; 58 Result GetCommonInfo(u64 device_handle, NFP::CommonInfo& common_info);
58 Result GetModelInfo(u64 device_handle, NFP::ModelInfo& model_info) const; 59 Result GetModelInfo(u64 device_handle, NFP::ModelInfo& model_info);
59 u32 GetApplicationAreaSize() const; 60 u32 GetApplicationAreaSize() const;
60 Result RecreateApplicationArea(u64 device_handle, u32 access_id, std::span<const u8> data); 61 Result RecreateApplicationArea(u64 device_handle, u32 access_id, std::span<const u8> data);
61 Result Format(u64 device_handle); 62 Result Format(u64 device_handle);
62 Result GetAdminInfo(u64 device_handle, NFP::AdminInfo& admin_info) const; 63 Result GetAdminInfo(u64 device_handle, NFP::AdminInfo& admin_info);
63 Result GetRegisterInfoPrivate(u64 device_handle, NFP::RegisterInfoPrivate& register_info) const; 64 Result GetRegisterInfoPrivate(u64 device_handle, NFP::RegisterInfoPrivate& register_info);
64 Result SetRegisterInfoPrivate(u64 device_handle, const NFP::RegisterInfoPrivate& register_info); 65 Result SetRegisterInfoPrivate(u64 device_handle, const NFP::RegisterInfoPrivate& register_info);
65 Result DeleteRegisterInfo(u64 device_handle); 66 Result DeleteRegisterInfo(u64 device_handle);
66 Result DeleteApplicationArea(u64 device_handle); 67 Result DeleteApplicationArea(u64 device_handle);
67 Result ExistsApplicationArea(u64 device_handle, bool& has_application_area) const; 68 Result ExistsApplicationArea(u64 device_handle, bool& has_application_area);
68 Result GetAll(u64 device_handle, NFP::NfpData& nfp_data) const; 69 Result GetAll(u64 device_handle, NFP::NfpData& nfp_data);
69 Result SetAll(u64 device_handle, const NFP::NfpData& nfp_data); 70 Result SetAll(u64 device_handle, const NFP::NfpData& nfp_data);
70 Result FlushDebug(u64 device_handle); 71 Result FlushDebug(u64 device_handle);
71 Result BreakTag(u64 device_handle, NFP::BreakType break_type); 72 Result BreakTag(u64 device_handle, NFP::BreakType break_type);
72 Result ReadBackupData(u64 device_handle, std::span<u8> data) const; 73 Result ReadBackupData(u64 device_handle, std::span<u8> data);
73 Result WriteBackupData(u64 device_handle, std::span<const u8> data); 74 Result WriteBackupData(u64 device_handle, std::span<const u8> data);
74 Result WriteNtf(u64 device_handle, NFP::WriteType, std::span<const u8> data); 75 Result WriteNtf(u64 device_handle, NFP::WriteType, std::span<const u8> data);
75 76
@@ -78,17 +79,20 @@ private:
78 Result IsNfcParameterSet() const; 79 Result IsNfcParameterSet() const;
79 Result IsNfcInitialized() const; 80 Result IsNfcInitialized() const;
80 81
82 Result CheckHandleOnList(u64 device_handle, std::span<const u64> device_list) const;
83
81 Result GetDeviceFromHandle(u64 handle, std::shared_ptr<NfcDevice>& device, 84 Result GetDeviceFromHandle(u64 handle, std::shared_ptr<NfcDevice>& device,
82 bool check_state) const; 85 bool check_state) const;
83 86
84 Result GetDeviceHandle(u64 handle, std::shared_ptr<NfcDevice>& device) const; 87 Result GetDeviceHandle(u64 handle, std::shared_ptr<NfcDevice>& device) const;
85 Result VerifyDeviceResult(std::shared_ptr<NfcDevice> device, Result operation_result) const; 88 Result VerifyDeviceResult(std::shared_ptr<NfcDevice> device, Result operation_result);
86 Result CheckDeviceState(std::shared_ptr<NfcDevice> device) const; 89 Result CheckDeviceState(std::shared_ptr<NfcDevice> device) const;
87 90
88 std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle); 91 std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle);
89 const std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle) const; 92 const std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle) const;
90 93
91 bool is_initialized = false; 94 bool is_initialized = false;
95 u64 time_since_last_error = 0;
92 mutable std::mutex mutex; 96 mutable std::mutex mutex;
93 std::array<std::shared_ptr<NfcDevice>, 10> devices{}; 97 std::array<std::shared_ptr<NfcDevice>, 10> devices{};
94 98
diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp
index e7ca7582e..179c7ba2c 100644
--- a/src/core/hle/service/nfc/nfc_interface.cpp
+++ b/src/core/hle/service/nfc/nfc_interface.cpp
@@ -79,7 +79,7 @@ void NfcInterface::ListDevices(HLERequestContext& ctx) {
79 const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>(); 79 const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
80 LOG_DEBUG(Service_NFC, "called"); 80 LOG_DEBUG(Service_NFC, "called");
81 81
82 auto result = GetManager()->ListDevices(nfp_devices, max_allowed_devices); 82 auto result = GetManager()->ListDevices(nfp_devices, max_allowed_devices, true);
83 result = TranslateResultToServiceError(result); 83 result = TranslateResultToServiceError(result);
84 84
85 if (result.IsError()) { 85 if (result.IsError()) {
@@ -190,9 +190,13 @@ void NfcInterface::AttachActivateEvent(HLERequestContext& ctx) {
190 const auto device_handle{rp.Pop<u64>()}; 190 const auto device_handle{rp.Pop<u64>()};
191 LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); 191 LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
192 192
193 Kernel::KReadableEvent* out_event = nullptr;
194 auto result = GetManager()->AttachActivateEvent(&out_event, device_handle);
195 result = TranslateResultToServiceError(result);
196
193 IPC::ResponseBuilder rb{ctx, 2, 1}; 197 IPC::ResponseBuilder rb{ctx, 2, 1};
194 rb.Push(ResultSuccess); 198 rb.Push(result);
195 rb.PushCopyObjects(GetManager()->AttachActivateEvent(device_handle)); 199 rb.PushCopyObjects(out_event);
196} 200}
197 201
198void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) { 202void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) {
@@ -200,9 +204,13 @@ void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) {
200 const auto device_handle{rp.Pop<u64>()}; 204 const auto device_handle{rp.Pop<u64>()};
201 LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); 205 LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
202 206
207 Kernel::KReadableEvent* out_event = nullptr;
208 auto result = GetManager()->AttachDeactivateEvent(&out_event, device_handle);
209 result = TranslateResultToServiceError(result);
210
203 IPC::ResponseBuilder rb{ctx, 2, 1}; 211 IPC::ResponseBuilder rb{ctx, 2, 1};
204 rb.Push(ResultSuccess); 212 rb.Push(result);
205 rb.PushCopyObjects(GetManager()->AttachDeactivateEvent(device_handle)); 213 rb.PushCopyObjects(out_event);
206} 214}
207 215
208void NfcInterface::ReadMifare(HLERequestContext& ctx) { 216void NfcInterface::ReadMifare(HLERequestContext& ctx) {
diff --git a/src/core/hle/service/nfc/nfc_result.h b/src/core/hle/service/nfc/nfc_result.h
index 715c0e80c..464b5fd69 100644
--- a/src/core/hle/service/nfc/nfc_result.h
+++ b/src/core/hle/service/nfc/nfc_result.h
@@ -17,7 +17,10 @@ constexpr Result ResultNfcNotInitialized(ErrorModule::NFC, 77);
17constexpr Result ResultNfcDisabled(ErrorModule::NFC, 80); 17constexpr Result ResultNfcDisabled(ErrorModule::NFC, 80);
18constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFC, 88); 18constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFC, 88);
19constexpr Result ResultTagRemoved(ErrorModule::NFC, 97); 19constexpr Result ResultTagRemoved(ErrorModule::NFC, 97);
20constexpr Result ResultUnknown112(ErrorModule::NFC, 112);
20constexpr Result ResultUnableToAccessBackupFile(ErrorModule::NFC, 113); 21constexpr Result ResultUnableToAccessBackupFile(ErrorModule::NFC, 113);
22constexpr Result ResultUnknown114(ErrorModule::NFC, 114);
23constexpr Result ResultUnknown115(ErrorModule::NFC, 115);
21constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFC, 120); 24constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFC, 120);
22constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFC, 128); 25constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFC, 128);
23constexpr Result ResultCorruptedDataWithBackup(ErrorModule::NFC, 136); 26constexpr Result ResultCorruptedDataWithBackup(ErrorModule::NFC, 136);
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 91d42853e..21b06d10b 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -7,6 +7,7 @@
7#include "core/hle/service/kernel_helpers.h" 7#include "core/hle/service/kernel_helpers.h"
8#include "core/hle/service/nifm/nifm.h" 8#include "core/hle/service/nifm/nifm.h"
9#include "core/hle/service/server_manager.h" 9#include "core/hle/service/server_manager.h"
10#include "network/network.h"
10 11
11namespace { 12namespace {
12 13
diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h
index 9b20e6823..ae99c4695 100644
--- a/src/core/hle/service/nifm/nifm.h
+++ b/src/core/hle/service/nifm/nifm.h
@@ -4,14 +4,15 @@
4#pragma once 4#pragma once
5 5
6#include "core/hle/service/service.h" 6#include "core/hle/service/service.h"
7#include "network/network.h"
8#include "network/room.h"
9#include "network/room_member.h"
10 7
11namespace Core { 8namespace Core {
12class System; 9class System;
13} 10}
14 11
12namespace Network {
13class RoomNetwork;
14}
15
15namespace Service::NIFM { 16namespace Service::NIFM {
16 17
17void LoopProcess(Core::System& system); 18void LoopProcess(Core::System& system);
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index e7f7e273b..968eaa175 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -128,7 +128,7 @@ NvResult nvmap::IocAlloc(std::span<const u8> input, std::span<u8> output) {
128 } 128 }
129 bool is_out_io{}; 129 bool is_out_io{};
130 ASSERT(system.ApplicationProcess() 130 ASSERT(system.ApplicationProcess()
131 ->PageTable() 131 ->GetPageTable()
132 .LockForMapDeviceAddressSpace(&is_out_io, handle_description->address, 132 .LockForMapDeviceAddressSpace(&is_out_io, handle_description->address,
133 handle_description->size, 133 handle_description->size,
134 Kernel::KMemoryPermission::None, true, false) 134 Kernel::KMemoryPermission::None, true, false)
@@ -255,7 +255,7 @@ NvResult nvmap::IocFree(std::span<const u8> input, std::span<u8> output) {
255 if (auto freeInfo{file.FreeHandle(params.handle, false)}) { 255 if (auto freeInfo{file.FreeHandle(params.handle, false)}) {
256 if (freeInfo->can_unlock) { 256 if (freeInfo->can_unlock) {
257 ASSERT(system.ApplicationProcess() 257 ASSERT(system.ApplicationProcess()
258 ->PageTable() 258 ->GetPageTable()
259 .UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size) 259 .UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size)
260 .IsSuccess()); 260 .IsSuccess());
261 } 261 }
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index bce45d321..e63b0a357 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -20,6 +20,9 @@
20#include "core/internal_network/sockets.h" 20#include "core/internal_network/sockets.h"
21#include "network/network.h" 21#include "network/network.h"
22 22
23using Common::Expected;
24using Common::Unexpected;
25
23namespace Service::Sockets { 26namespace Service::Sockets {
24 27
25namespace { 28namespace {
@@ -265,16 +268,19 @@ void BSD::GetSockOpt(HLERequestContext& ctx) {
265 const u32 level = rp.Pop<u32>(); 268 const u32 level = rp.Pop<u32>();
266 const auto optname = static_cast<OptName>(rp.Pop<u32>()); 269 const auto optname = static_cast<OptName>(rp.Pop<u32>());
267 270
268 LOG_WARNING(Service, "(STUBBED) called. fd={} level={} optname=0x{:x}", fd, level, optname);
269
270 std::vector<u8> optval(ctx.GetWriteBufferSize()); 271 std::vector<u8> optval(ctx.GetWriteBufferSize());
271 272
273 LOG_DEBUG(Service, "called. fd={} level={} optname=0x{:x} len=0x{:x}", fd, level, optname,
274 optval.size());
275
276 const Errno err = GetSockOptImpl(fd, level, optname, optval);
277
272 ctx.WriteBuffer(optval); 278 ctx.WriteBuffer(optval);
273 279
274 IPC::ResponseBuilder rb{ctx, 5}; 280 IPC::ResponseBuilder rb{ctx, 5};
275 rb.Push(ResultSuccess); 281 rb.Push(ResultSuccess);
276 rb.Push<s32>(-1); 282 rb.Push<s32>(err == Errno::SUCCESS ? 0 : -1);
277 rb.PushEnum(Errno::NOTCONN); 283 rb.PushEnum(err);
278 rb.Push<u32>(static_cast<u32>(optval.size())); 284 rb.Push<u32>(static_cast<u32>(optval.size()));
279} 285}
280 286
@@ -436,6 +442,31 @@ void BSD::Close(HLERequestContext& ctx) {
436 BuildErrnoResponse(ctx, CloseImpl(fd)); 442 BuildErrnoResponse(ctx, CloseImpl(fd));
437} 443}
438 444
445void BSD::DuplicateSocket(HLERequestContext& ctx) {
446 struct InputParameters {
447 s32 fd;
448 u64 reserved;
449 };
450 static_assert(sizeof(InputParameters) == 0x10);
451
452 struct OutputParameters {
453 s32 ret;
454 Errno bsd_errno;
455 };
456 static_assert(sizeof(OutputParameters) == 0x8);
457
458 IPC::RequestParser rp{ctx};
459 auto input = rp.PopRaw<InputParameters>();
460
461 Expected<s32, Errno> res = DuplicateSocketImpl(input.fd);
462 IPC::ResponseBuilder rb{ctx, 4};
463 rb.Push(ResultSuccess);
464 rb.PushRaw(OutputParameters{
465 .ret = res.value_or(0),
466 .bsd_errno = res ? Errno::SUCCESS : res.error(),
467 });
468}
469
439void BSD::EventFd(HLERequestContext& ctx) { 470void BSD::EventFd(HLERequestContext& ctx) {
440 IPC::RequestParser rp{ctx}; 471 IPC::RequestParser rp{ctx};
441 const u64 initval = rp.Pop<u64>(); 472 const u64 initval = rp.Pop<u64>();
@@ -477,12 +508,12 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco
477 508
478 auto room_member = room_network.GetRoomMember().lock(); 509 auto room_member = room_network.GetRoomMember().lock();
479 if (room_member && room_member->IsConnected()) { 510 if (room_member && room_member->IsConnected()) {
480 descriptor.socket = std::make_unique<Network::ProxySocket>(room_network); 511 descriptor.socket = std::make_shared<Network::ProxySocket>(room_network);
481 } else { 512 } else {
482 descriptor.socket = std::make_unique<Network::Socket>(); 513 descriptor.socket = std::make_shared<Network::Socket>();
483 } 514 }
484 515
485 descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol)); 516 descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(protocol));
486 descriptor.is_connection_based = IsConnectionBased(type); 517 descriptor.is_connection_based = IsConnectionBased(type);
487 518
488 return {fd, Errno::SUCCESS}; 519 return {fd, Errno::SUCCESS};
@@ -538,7 +569,7 @@ std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<con
538 std::transform(fds.begin(), fds.end(), host_pollfds.begin(), [this](PollFD pollfd) { 569 std::transform(fds.begin(), fds.end(), host_pollfds.begin(), [this](PollFD pollfd) {
539 Network::PollFD result; 570 Network::PollFD result;
540 result.socket = file_descriptors[pollfd.fd]->socket.get(); 571 result.socket = file_descriptors[pollfd.fd]->socket.get();
541 result.events = TranslatePollEventsToHost(pollfd.events); 572 result.events = Translate(pollfd.events);
542 result.revents = Network::PollEvents{}; 573 result.revents = Network::PollEvents{};
543 return result; 574 return result;
544 }); 575 });
@@ -547,7 +578,7 @@ std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<con
547 578
548 const size_t num = host_pollfds.size(); 579 const size_t num = host_pollfds.size();
549 for (size_t i = 0; i < num; ++i) { 580 for (size_t i = 0; i < num; ++i) {
550 fds[i].revents = TranslatePollEventsToGuest(host_pollfds[i].revents); 581 fds[i].revents = Translate(host_pollfds[i].revents);
551 } 582 }
552 std::memcpy(write_buffer.data(), fds.data(), length); 583 std::memcpy(write_buffer.data(), fds.data(), length);
553 584
@@ -617,7 +648,8 @@ Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
617 } 648 }
618 const SockAddrIn guest_addrin = Translate(addr_in); 649 const SockAddrIn guest_addrin = Translate(addr_in);
619 650
620 ASSERT(write_buffer.size() == sizeof(guest_addrin)); 651 ASSERT(write_buffer.size() >= sizeof(guest_addrin));
652 write_buffer.resize(sizeof(guest_addrin));
621 std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); 653 std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin));
622 return Translate(bsd_errno); 654 return Translate(bsd_errno);
623} 655}
@@ -633,7 +665,8 @@ Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
633 } 665 }
634 const SockAddrIn guest_addrin = Translate(addr_in); 666 const SockAddrIn guest_addrin = Translate(addr_in);
635 667
636 ASSERT(write_buffer.size() == sizeof(guest_addrin)); 668 ASSERT(write_buffer.size() >= sizeof(guest_addrin));
669 write_buffer.resize(sizeof(guest_addrin));
637 std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); 670 std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin));
638 return Translate(bsd_errno); 671 return Translate(bsd_errno);
639} 672}
@@ -671,13 +704,47 @@ std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
671 } 704 }
672} 705}
673 706
674Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval) { 707Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval) {
675 UNIMPLEMENTED_IF(level != 0xffff); // SOL_SOCKET 708 if (!IsFileDescriptorValid(fd)) {
709 return Errno::BADF;
710 }
711
712 if (level != static_cast<u32>(SocketLevel::SOCKET)) {
713 UNIMPLEMENTED_MSG("Unknown getsockopt level");
714 return Errno::SUCCESS;
715 }
716
717 Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
718
719 switch (optname) {
720 case OptName::ERROR_: {
721 auto [pending_err, getsockopt_err] = socket->GetPendingError();
722 if (getsockopt_err == Network::Errno::SUCCESS) {
723 Errno translated_pending_err = Translate(pending_err);
724 ASSERT_OR_EXECUTE_MSG(
725 optval.size() == sizeof(Errno), { return Errno::INVAL; },
726 "Incorrect getsockopt option size");
727 optval.resize(sizeof(Errno));
728 memcpy(optval.data(), &translated_pending_err, sizeof(Errno));
729 }
730 return Translate(getsockopt_err);
731 }
732 default:
733 UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
734 return Errno::SUCCESS;
735 }
736}
676 737
738Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval) {
677 if (!IsFileDescriptorValid(fd)) { 739 if (!IsFileDescriptorValid(fd)) {
678 return Errno::BADF; 740 return Errno::BADF;
679 } 741 }
680 742
743 if (level != static_cast<u32>(SocketLevel::SOCKET)) {
744 UNIMPLEMENTED_MSG("Unknown setsockopt level");
745 return Errno::SUCCESS;
746 }
747
681 Network::SocketBase* const socket = file_descriptors[fd]->socket.get(); 748 Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
682 749
683 if (optname == OptName::LINGER) { 750 if (optname == OptName::LINGER) {
@@ -711,6 +778,9 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con
711 return Translate(socket->SetSndTimeo(value)); 778 return Translate(socket->SetSndTimeo(value));
712 case OptName::RCVTIMEO: 779 case OptName::RCVTIMEO:
713 return Translate(socket->SetRcvTimeo(value)); 780 return Translate(socket->SetRcvTimeo(value));
781 case OptName::NOSIGPIPE:
782 LOG_WARNING(Service, "(STUBBED) setting NOSIGPIPE to {}", value);
783 return Errno::SUCCESS;
714 default: 784 default:
715 UNIMPLEMENTED_MSG("Unimplemented optname={}", optname); 785 UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
716 return Errno::SUCCESS; 786 return Errno::SUCCESS;
@@ -841,6 +911,28 @@ Errno BSD::CloseImpl(s32 fd) {
841 return bsd_errno; 911 return bsd_errno;
842} 912}
843 913
914Expected<s32, Errno> BSD::DuplicateSocketImpl(s32 fd) {
915 if (!IsFileDescriptorValid(fd)) {
916 return Unexpected(Errno::BADF);
917 }
918
919 const s32 new_fd = FindFreeFileDescriptorHandle();
920 if (new_fd < 0) {
921 LOG_ERROR(Service, "No more file descriptors available");
922 return Unexpected(Errno::MFILE);
923 }
924
925 file_descriptors[new_fd] = file_descriptors[fd];
926 return new_fd;
927}
928
929std::optional<std::shared_ptr<Network::SocketBase>> BSD::GetSocket(s32 fd) {
930 if (!IsFileDescriptorValid(fd)) {
931 return std::nullopt;
932 }
933 return file_descriptors[fd]->socket;
934}
935
844s32 BSD::FindFreeFileDescriptorHandle() noexcept { 936s32 BSD::FindFreeFileDescriptorHandle() noexcept {
845 for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) { 937 for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) {
846 if (!file_descriptors[fd]) { 938 if (!file_descriptors[fd]) {
@@ -911,7 +1003,7 @@ BSD::BSD(Core::System& system_, const char* name)
911 {24, &BSD::Write, "Write"}, 1003 {24, &BSD::Write, "Write"},
912 {25, &BSD::Read, "Read"}, 1004 {25, &BSD::Read, "Read"},
913 {26, &BSD::Close, "Close"}, 1005 {26, &BSD::Close, "Close"},
914 {27, nullptr, "DuplicateSocket"}, 1006 {27, &BSD::DuplicateSocket, "DuplicateSocket"},
915 {28, nullptr, "GetResourceStatistics"}, 1007 {28, nullptr, "GetResourceStatistics"},
916 {29, nullptr, "RecvMMsg"}, 1008 {29, nullptr, "RecvMMsg"},
917 {30, nullptr, "SendMMsg"}, 1009 {30, nullptr, "SendMMsg"},
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index 30ae9c140..430edb97c 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -8,6 +8,7 @@
8#include <vector> 8#include <vector>
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/expected.h"
11#include "common/socket_types.h" 12#include "common/socket_types.h"
12#include "core/hle/service/service.h" 13#include "core/hle/service/service.h"
13#include "core/hle/service/sockets/sockets.h" 14#include "core/hle/service/sockets/sockets.h"
@@ -29,12 +30,19 @@ public:
29 explicit BSD(Core::System& system_, const char* name); 30 explicit BSD(Core::System& system_, const char* name);
30 ~BSD() override; 31 ~BSD() override;
31 32
33 // These methods are called from SSL; the first two are also called from
34 // this class for the corresponding IPC methods.
35 // On the real device, the SSL service makes IPC calls to this service.
36 Common::Expected<s32, Errno> DuplicateSocketImpl(s32 fd);
37 Errno CloseImpl(s32 fd);
38 std::optional<std::shared_ptr<Network::SocketBase>> GetSocket(s32 fd);
39
32private: 40private:
33 /// Maximum number of file descriptors 41 /// Maximum number of file descriptors
34 static constexpr size_t MAX_FD = 128; 42 static constexpr size_t MAX_FD = 128;
35 43
36 struct FileDescriptor { 44 struct FileDescriptor {
37 std::unique_ptr<Network::SocketBase> socket; 45 std::shared_ptr<Network::SocketBase> socket;
38 s32 flags = 0; 46 s32 flags = 0;
39 bool is_connection_based = false; 47 bool is_connection_based = false;
40 }; 48 };
@@ -138,6 +146,7 @@ private:
138 void Write(HLERequestContext& ctx); 146 void Write(HLERequestContext& ctx);
139 void Read(HLERequestContext& ctx); 147 void Read(HLERequestContext& ctx);
140 void Close(HLERequestContext& ctx); 148 void Close(HLERequestContext& ctx);
149 void DuplicateSocket(HLERequestContext& ctx);
141 void EventFd(HLERequestContext& ctx); 150 void EventFd(HLERequestContext& ctx);
142 151
143 template <typename Work> 152 template <typename Work>
@@ -153,6 +162,7 @@ private:
153 Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer); 162 Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
154 Errno ListenImpl(s32 fd, s32 backlog); 163 Errno ListenImpl(s32 fd, s32 backlog);
155 std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg); 164 std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg);
165 Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval);
156 Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval); 166 Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval);
157 Errno ShutdownImpl(s32 fd, s32 how); 167 Errno ShutdownImpl(s32 fd, s32 how);
158 std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message); 168 std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
@@ -161,7 +171,6 @@ private:
161 std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message); 171 std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message);
162 std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message, 172 std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
163 std::span<const u8> addr); 173 std::span<const u8> addr);
164 Errno CloseImpl(s32 fd);
165 174
166 s32 FindFreeFileDescriptorHandle() noexcept; 175 s32 FindFreeFileDescriptorHandle() noexcept;
167 bool IsFileDescriptorValid(s32 fd) const noexcept; 176 bool IsFileDescriptorValid(s32 fd) const noexcept;
diff --git a/src/core/hle/service/sockets/nsd.cpp b/src/core/hle/service/sockets/nsd.cpp
index 6491a73be..36c6cd05c 100644
--- a/src/core/hle/service/sockets/nsd.cpp
+++ b/src/core/hle/service/sockets/nsd.cpp
@@ -1,10 +1,24 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hle/service/ipc_helpers.h"
4#include "core/hle/service/sockets/nsd.h" 5#include "core/hle/service/sockets/nsd.h"
5 6
7#include "common/string_util.h"
8
6namespace Service::Sockets { 9namespace Service::Sockets {
7 10
11constexpr Result ResultOverflow{ErrorModule::NSD, 6};
12
13// This is nn::oe::ServerEnvironmentType
14enum class ServerEnvironmentType : u8 {
15 Dd,
16 Lp,
17 Sd,
18 Sp,
19 Dp,
20};
21
8NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} { 22NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} {
9 // clang-format off 23 // clang-format off
10 static const FunctionInfo functions[] = { 24 static const FunctionInfo functions[] = {
@@ -15,8 +29,8 @@ NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, na
15 {13, nullptr, "DeleteSettings"}, 29 {13, nullptr, "DeleteSettings"},
16 {14, nullptr, "ImportSettings"}, 30 {14, nullptr, "ImportSettings"},
17 {15, nullptr, "SetChangeEnvironmentIdentifierDisabled"}, 31 {15, nullptr, "SetChangeEnvironmentIdentifierDisabled"},
18 {20, nullptr, "Resolve"}, 32 {20, &NSD::Resolve, "Resolve"},
19 {21, nullptr, "ResolveEx"}, 33 {21, &NSD::ResolveEx, "ResolveEx"},
20 {30, nullptr, "GetNasServiceSetting"}, 34 {30, nullptr, "GetNasServiceSetting"},
21 {31, nullptr, "GetNasServiceSettingEx"}, 35 {31, nullptr, "GetNasServiceSettingEx"},
22 {40, nullptr, "GetNasRequestFqdn"}, 36 {40, nullptr, "GetNasRequestFqdn"},
@@ -31,7 +45,7 @@ NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, na
31 {62, nullptr, "DeleteSaveDataOfFsForTest"}, 45 {62, nullptr, "DeleteSaveDataOfFsForTest"},
32 {63, nullptr, "IsChangeEnvironmentIdentifierDisabled"}, 46 {63, nullptr, "IsChangeEnvironmentIdentifierDisabled"},
33 {64, nullptr, "SetWithoutDomainExchangeFqdns"}, 47 {64, nullptr, "SetWithoutDomainExchangeFqdns"},
34 {100, nullptr, "GetApplicationServerEnvironmentType"}, 48 {100, &NSD::GetApplicationServerEnvironmentType, "GetApplicationServerEnvironmentType"},
35 {101, nullptr, "SetApplicationServerEnvironmentType"}, 49 {101, nullptr, "SetApplicationServerEnvironmentType"},
36 {102, nullptr, "DeleteApplicationServerEnvironmentType"}, 50 {102, nullptr, "DeleteApplicationServerEnvironmentType"},
37 }; 51 };
@@ -40,6 +54,61 @@ NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, na
40 RegisterHandlers(functions); 54 RegisterHandlers(functions);
41} 55}
42 56
57static ResultVal<std::string> ResolveImpl(const std::string& fqdn_in) {
58 // The real implementation makes various substitutions.
59 // For now we just return the string as-is, which is good enough when not
60 // connecting to real Nintendo servers.
61 LOG_WARNING(Service, "(STUBBED) called, fqdn_in={}", fqdn_in);
62 return fqdn_in;
63}
64
65static Result ResolveCommon(const std::string& fqdn_in, std::array<char, 0x100>& fqdn_out) {
66 const auto res = ResolveImpl(fqdn_in);
67 if (res.Failed()) {
68 return res.Code();
69 }
70 if (res->size() >= fqdn_out.size()) {
71 return ResultOverflow;
72 }
73 std::memcpy(fqdn_out.data(), res->c_str(), res->size() + 1);
74 return ResultSuccess;
75}
76
77void NSD::Resolve(HLERequestContext& ctx) {
78 const std::string fqdn_in = Common::StringFromBuffer(ctx.ReadBuffer(0));
79
80 std::array<char, 0x100> fqdn_out{};
81 const Result res = ResolveCommon(fqdn_in, fqdn_out);
82
83 ctx.WriteBuffer(fqdn_out);
84 IPC::ResponseBuilder rb{ctx, 2};
85 rb.Push(res);
86}
87
88void NSD::ResolveEx(HLERequestContext& ctx) {
89 const std::string fqdn_in = Common::StringFromBuffer(ctx.ReadBuffer(0));
90
91 std::array<char, 0x100> fqdn_out;
92 const Result res = ResolveCommon(fqdn_in, fqdn_out);
93
94 if (res.IsError()) {
95 IPC::ResponseBuilder rb{ctx, 2};
96 rb.Push(res);
97 return;
98 }
99
100 ctx.WriteBuffer(fqdn_out);
101 IPC::ResponseBuilder rb{ctx, 4};
102 rb.Push(ResultSuccess);
103 rb.Push(ResultSuccess);
104}
105
106void NSD::GetApplicationServerEnvironmentType(HLERequestContext& ctx) {
107 IPC::ResponseBuilder rb{ctx, 3};
108 rb.Push(ResultSuccess);
109 rb.Push(static_cast<u32>(ServerEnvironmentType::Lp));
110}
111
43NSD::~NSD() = default; 112NSD::~NSD() = default;
44 113
45} // namespace Service::Sockets 114} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/nsd.h b/src/core/hle/service/sockets/nsd.h
index 5cc12b855..57760a0c8 100644
--- a/src/core/hle/service/sockets/nsd.h
+++ b/src/core/hle/service/sockets/nsd.h
@@ -15,6 +15,11 @@ class NSD final : public ServiceFramework<NSD> {
15public: 15public:
16 explicit NSD(Core::System& system_, const char* name); 16 explicit NSD(Core::System& system_, const char* name);
17 ~NSD() override; 17 ~NSD() override;
18
19private:
20 void Resolve(HLERequestContext& ctx);
21 void ResolveEx(HLERequestContext& ctx);
22 void GetApplicationServerEnvironmentType(HLERequestContext& ctx);
18}; 23};
19 24
20} // namespace Service::Sockets 25} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp
index 132dd5797..84cc79de8 100644
--- a/src/core/hle/service/sockets/sfdnsres.cpp
+++ b/src/core/hle/service/sockets/sfdnsres.cpp
@@ -10,27 +10,18 @@
10#include "core/core.h" 10#include "core/core.h"
11#include "core/hle/service/ipc_helpers.h" 11#include "core/hle/service/ipc_helpers.h"
12#include "core/hle/service/sockets/sfdnsres.h" 12#include "core/hle/service/sockets/sfdnsres.h"
13#include "core/hle/service/sockets/sockets.h"
14#include "core/hle/service/sockets/sockets_translate.h"
15#include "core/internal_network/network.h"
13#include "core/memory.h" 16#include "core/memory.h"
14 17
15#ifdef _WIN32
16#include <ws2tcpip.h>
17#elif YUZU_UNIX
18#include <arpa/inet.h>
19#include <netdb.h>
20#include <netinet/in.h>
21#include <sys/socket.h>
22#ifndef EAI_NODATA
23#define EAI_NODATA EAI_NONAME
24#endif
25#endif
26
27namespace Service::Sockets { 18namespace Service::Sockets {
28 19
29SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"} { 20SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"} {
30 static const FunctionInfo functions[] = { 21 static const FunctionInfo functions[] = {
31 {0, nullptr, "SetDnsAddressesPrivateRequest"}, 22 {0, nullptr, "SetDnsAddressesPrivateRequest"},
32 {1, nullptr, "GetDnsAddressPrivateRequest"}, 23 {1, nullptr, "GetDnsAddressPrivateRequest"},
33 {2, nullptr, "GetHostByNameRequest"}, 24 {2, &SFDNSRES::GetHostByNameRequest, "GetHostByNameRequest"},
34 {3, nullptr, "GetHostByAddrRequest"}, 25 {3, nullptr, "GetHostByAddrRequest"},
35 {4, nullptr, "GetHostStringErrorRequest"}, 26 {4, nullptr, "GetHostStringErrorRequest"},
36 {5, nullptr, "GetGaiStringErrorRequest"}, 27 {5, nullptr, "GetGaiStringErrorRequest"},
@@ -38,11 +29,11 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"
38 {7, nullptr, "GetNameInfoRequest"}, 29 {7, nullptr, "GetNameInfoRequest"},
39 {8, nullptr, "RequestCancelHandleRequest"}, 30 {8, nullptr, "RequestCancelHandleRequest"},
40 {9, nullptr, "CancelRequest"}, 31 {9, nullptr, "CancelRequest"},
41 {10, nullptr, "GetHostByNameRequestWithOptions"}, 32 {10, &SFDNSRES::GetHostByNameRequestWithOptions, "GetHostByNameRequestWithOptions"},
42 {11, nullptr, "GetHostByAddrRequestWithOptions"}, 33 {11, nullptr, "GetHostByAddrRequestWithOptions"},
43 {12, &SFDNSRES::GetAddrInfoRequestWithOptions, "GetAddrInfoRequestWithOptions"}, 34 {12, &SFDNSRES::GetAddrInfoRequestWithOptions, "GetAddrInfoRequestWithOptions"},
44 {13, nullptr, "GetNameInfoRequestWithOptions"}, 35 {13, nullptr, "GetNameInfoRequestWithOptions"},
45 {14, nullptr, "ResolverSetOptionRequest"}, 36 {14, &SFDNSRES::ResolverSetOptionRequest, "ResolverSetOptionRequest"},
46 {15, nullptr, "ResolverGetOptionRequest"}, 37 {15, nullptr, "ResolverGetOptionRequest"},
47 }; 38 };
48 RegisterHandlers(functions); 39 RegisterHandlers(functions);
@@ -59,188 +50,285 @@ enum class NetDbError : s32 {
59 NoData = 4, 50 NoData = 4,
60}; 51};
61 52
62static NetDbError AddrInfoErrorToNetDbError(s32 result) { 53static NetDbError GetAddrInfoErrorToNetDbError(GetAddrInfoError result) {
63 // Best effort guess to map errors 54 // These combinations have been verified on console (but are not
55 // exhaustive).
64 switch (result) { 56 switch (result) {
65 case 0: 57 case GetAddrInfoError::SUCCESS:
66 return NetDbError::Success; 58 return NetDbError::Success;
67 case EAI_AGAIN: 59 case GetAddrInfoError::AGAIN:
68 return NetDbError::TryAgain; 60 return NetDbError::TryAgain;
69 case EAI_NODATA: 61 case GetAddrInfoError::NODATA:
70 return NetDbError::NoData; 62 return NetDbError::HostNotFound;
63 case GetAddrInfoError::SERVICE:
64 return NetDbError::Success;
71 default: 65 default:
72 return NetDbError::HostNotFound; 66 return NetDbError::HostNotFound;
73 } 67 }
74} 68}
75 69
76static std::vector<u8> SerializeAddrInfo(const addrinfo* addrinfo, s32 result_code, 70static Errno GetAddrInfoErrorToErrno(GetAddrInfoError result) {
71 // These combinations have been verified on console (but are not
72 // exhaustive).
73 switch (result) {
74 case GetAddrInfoError::SUCCESS:
75 // Note: Sometimes a successful lookup sets errno to EADDRNOTAVAIL for
76 // some reason, but that doesn't seem useful to implement.
77 return Errno::SUCCESS;
78 case GetAddrInfoError::AGAIN:
79 return Errno::SUCCESS;
80 case GetAddrInfoError::NODATA:
81 return Errno::SUCCESS;
82 case GetAddrInfoError::SERVICE:
83 return Errno::INVAL;
84 default:
85 return Errno::SUCCESS;
86 }
87}
88
89template <typename T>
90static void Append(std::vector<u8>& vec, T t) {
91 const size_t offset = vec.size();
92 vec.resize(offset + sizeof(T));
93 std::memcpy(vec.data() + offset, &t, sizeof(T));
94}
95
96static void AppendNulTerminated(std::vector<u8>& vec, std::string_view str) {
97 const size_t offset = vec.size();
98 vec.resize(offset + str.size() + 1);
99 std::memmove(vec.data() + offset, str.data(), str.size());
100}
101
102// We implement gethostbyname using the host's getaddrinfo rather than the
103// host's gethostbyname, because it simplifies portability: e.g., getaddrinfo
104// behaves the same on Unix and Windows, unlike gethostbyname where Windows
105// doesn't implement h_errno.
106static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::AddrInfo>& vec,
107 std::string_view host) {
108
109 std::vector<u8> data;
110 // h_name: use the input hostname (append nul-terminated)
111 AppendNulTerminated(data, host);
112 // h_aliases: leave empty
113
114 Append<u32_be>(data, 0); // count of h_aliases
115 // (If the count were nonzero, the aliases would be appended as nul-terminated here.)
116 Append<u16_be>(data, static_cast<u16>(Domain::INET)); // h_addrtype
117 Append<u16_be>(data, sizeof(Network::IPv4Address)); // h_length
118 // h_addr_list:
119 size_t count = vec.size();
120 ASSERT(count <= UINT32_MAX);
121 Append<u32_be>(data, static_cast<uint32_t>(count));
122 for (const Network::AddrInfo& addrinfo : vec) {
123 // On the Switch, this is passed through htonl despite already being
124 // big-endian, so it ends up as little-endian.
125 Append<u32_le>(data, Network::IPv4AddressToInteger(addrinfo.addr.ip));
126
127 LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host,
128 Network::IPv4AddressToString(addrinfo.addr.ip));
129 }
130 return data;
131}
132
133static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) {
134 struct InputParameters {
135 u8 use_nsd_resolve;
136 u32 cancel_handle;
137 u64 process_id;
138 };
139 static_assert(sizeof(InputParameters) == 0x10);
140
141 IPC::RequestParser rp{ctx};
142 const auto parameters = rp.PopRaw<InputParameters>();
143
144 LOG_WARNING(
145 Service,
146 "called with ignored parameters: use_nsd_resolve={}, cancel_handle={}, process_id={}",
147 parameters.use_nsd_resolve, parameters.cancel_handle, parameters.process_id);
148
149 const auto host_buffer = ctx.ReadBuffer(0);
150 const std::string host = Common::StringFromBuffer(host_buffer);
151 // For now, ignore options, which are in input buffer 1 for GetHostByNameRequestWithOptions.
152
153 auto res = Network::GetAddressInfo(host, /*service*/ std::nullopt);
154 if (!res.has_value()) {
155 return {0, Translate(res.error())};
156 }
157
158 const std::vector<u8> data = SerializeAddrInfoAsHostEnt(res.value(), host);
159 const u32 data_size = static_cast<u32>(data.size());
160 ctx.WriteBuffer(data, 0);
161
162 return {data_size, GetAddrInfoError::SUCCESS};
163}
164
165void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
166 auto [data_size, emu_gai_err] = GetHostByNameRequestImpl(ctx);
167
168 struct OutputParameters {
169 NetDbError netdb_error;
170 Errno bsd_errno;
171 u32 data_size;
172 };
173 static_assert(sizeof(OutputParameters) == 0xc);
174
175 IPC::ResponseBuilder rb{ctx, 5};
176 rb.Push(ResultSuccess);
177 rb.PushRaw(OutputParameters{
178 .netdb_error = GetAddrInfoErrorToNetDbError(emu_gai_err),
179 .bsd_errno = GetAddrInfoErrorToErrno(emu_gai_err),
180 .data_size = data_size,
181 });
182}
183
184void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
185 auto [data_size, emu_gai_err] = GetHostByNameRequestImpl(ctx);
186
187 struct OutputParameters {
188 u32 data_size;
189 NetDbError netdb_error;
190 Errno bsd_errno;
191 };
192 static_assert(sizeof(OutputParameters) == 0xc);
193
194 IPC::ResponseBuilder rb{ctx, 5};
195 rb.Push(ResultSuccess);
196 rb.PushRaw(OutputParameters{
197 .data_size = data_size,
198 .netdb_error = GetAddrInfoErrorToNetDbError(emu_gai_err),
199 .bsd_errno = GetAddrInfoErrorToErrno(emu_gai_err),
200 });
201}
202
203static std::vector<u8> SerializeAddrInfo(const std::vector<Network::AddrInfo>& vec,
77 std::string_view host) { 204 std::string_view host) {
78 // Adapted from 205 // Adapted from
79 // https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190 206 // https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
80 std::vector<u8> data; 207 std::vector<u8> data;
81 208
82 auto* current = addrinfo; 209 for (const Network::AddrInfo& addrinfo : vec) {
83 while (current != nullptr) { 210 // serialized addrinfo:
84 struct SerializedResponseHeader { 211 Append<u32_be>(data, 0xBEEFCAFE); // magic
85 u32 magic; 212 Append<u32_be>(data, 0); // ai_flags
86 s32 flags; 213 Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.family))); // ai_family
87 s32 family; 214 Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.socket_type))); // ai_socktype
88 s32 socket_type; 215 Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.protocol))); // ai_protocol
89 s32 protocol; 216 Append<u32_be>(data, sizeof(SockAddrIn)); // ai_addrlen
90 u32 address_length; 217 // ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size
91 }; 218
92 static_assert(sizeof(SerializedResponseHeader) == 0x18, 219 // ai_addr:
93 "Response header size must be 0x18 bytes"); 220 Append<u16_be>(data, static_cast<u16>(Translate(addrinfo.addr.family))); // sin_family
94 221 // On the Switch, the following fields are passed through htonl despite
95 constexpr auto header_size = sizeof(SerializedResponseHeader); 222 // already being big-endian, so they end up as little-endian.
96 const auto addr_size = 223 Append<u16_le>(data, addrinfo.addr.portno); // sin_port
97 current->ai_addr && current->ai_addrlen > 0 ? current->ai_addrlen : 4; 224 Append<u32_le>(data, Network::IPv4AddressToInteger(addrinfo.addr.ip)); // sin_addr
98 const auto canonname_size = current->ai_canonname ? strlen(current->ai_canonname) + 1 : 1; 225 data.resize(data.size() + 8, 0); // sin_zero
99 226
100 const auto last_size = data.size(); 227 if (addrinfo.canon_name.has_value()) {
101 data.resize(last_size + header_size + addr_size + canonname_size); 228 AppendNulTerminated(data, *addrinfo.canon_name);
102
103 // Header in network byte order
104 SerializedResponseHeader header{};
105
106 constexpr auto HEADER_MAGIC = 0xBEEFCAFE;
107 header.magic = htonl(HEADER_MAGIC);
108 header.family = htonl(current->ai_family);
109 header.flags = htonl(current->ai_flags);
110 header.socket_type = htonl(current->ai_socktype);
111 header.protocol = htonl(current->ai_protocol);
112 header.address_length = current->ai_addr ? htonl((u32)current->ai_addrlen) : 0;
113
114 auto* header_ptr = data.data() + last_size;
115 std::memcpy(header_ptr, &header, header_size);
116
117 if (header.address_length == 0) {
118 std::memset(header_ptr + header_size, 0, 4);
119 } else {
120 switch (current->ai_family) {
121 case AF_INET: {
122 struct SockAddrIn {
123 s16 sin_family;
124 u16 sin_port;
125 u32 sin_addr;
126 u8 sin_zero[8];
127 };
128
129 SockAddrIn serialized_addr{};
130 const auto addr = *reinterpret_cast<sockaddr_in*>(current->ai_addr);
131 serialized_addr.sin_port = htons(addr.sin_port);
132 serialized_addr.sin_family = htons(addr.sin_family);
133 serialized_addr.sin_addr = htonl(addr.sin_addr.s_addr);
134 std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn));
135
136 char addr_string_buf[64]{};
137 inet_ntop(AF_INET, &addr.sin_addr, addr_string_buf, std::size(addr_string_buf));
138 LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host, addr_string_buf);
139 break;
140 }
141 case AF_INET6: {
142 struct SockAddrIn6 {
143 s16 sin6_family;
144 u16 sin6_port;
145 u32 sin6_flowinfo;
146 u8 sin6_addr[16];
147 u32 sin6_scope_id;
148 };
149
150 SockAddrIn6 serialized_addr{};
151 const auto addr = *reinterpret_cast<sockaddr_in6*>(current->ai_addr);
152 serialized_addr.sin6_family = htons(addr.sin6_family);
153 serialized_addr.sin6_port = htons(addr.sin6_port);
154 serialized_addr.sin6_flowinfo = htonl(addr.sin6_flowinfo);
155 serialized_addr.sin6_scope_id = htonl(addr.sin6_scope_id);
156 std::memcpy(serialized_addr.sin6_addr, &addr.sin6_addr,
157 sizeof(SockAddrIn6::sin6_addr));
158 std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn6));
159
160 char addr_string_buf[64]{};
161 inet_ntop(AF_INET6, &addr.sin6_addr, addr_string_buf, std::size(addr_string_buf));
162 LOG_INFO(Service, "Resolved host '{}' to IPv6 address {}", host, addr_string_buf);
163 break;
164 }
165 default:
166 std::memcpy(header_ptr + header_size, current->ai_addr, addr_size);
167 break;
168 }
169 }
170 if (current->ai_canonname) {
171 std::memcpy(header_ptr + addr_size, current->ai_canonname, canonname_size);
172 } else { 229 } else {
173 *(header_ptr + header_size + addr_size) = 0; 230 data.push_back(0);
174 } 231 }
175 232
176 current = current->ai_next; 233 LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host,
234 Network::IPv4AddressToString(addrinfo.addr.ip));
177 } 235 }
178 236
179 // 4-byte sentinel value 237 data.resize(data.size() + 4, 0); // 4-byte sentinel value
180 data.push_back(0);
181 data.push_back(0);
182 data.push_back(0);
183 data.push_back(0);
184 238
185 return data; 239 return data;
186} 240}
187 241
188static std::pair<u32, s32> GetAddrInfoRequestImpl(HLERequestContext& ctx) { 242static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) {
189 struct Parameters { 243 struct InputParameters {
190 u8 use_nsd_resolve; 244 u8 use_nsd_resolve;
191 u32 unknown; 245 u32 cancel_handle;
192 u64 process_id; 246 u64 process_id;
193 }; 247 };
248 static_assert(sizeof(InputParameters) == 0x10);
194 249
195 IPC::RequestParser rp{ctx}; 250 IPC::RequestParser rp{ctx};
196 const auto parameters = rp.PopRaw<Parameters>(); 251 const auto parameters = rp.PopRaw<InputParameters>();
252
253 LOG_WARNING(
254 Service,
255 "called with ignored parameters: use_nsd_resolve={}, cancel_handle={}, process_id={}",
256 parameters.use_nsd_resolve, parameters.cancel_handle, parameters.process_id);
197 257
198 LOG_WARNING(Service, 258 // TODO: If use_nsd_resolve is true, pass the name through NSD::Resolve
199 "called with ignored parameters: use_nsd_resolve={}, unknown={}, process_id={}", 259 // before looking up.
200 parameters.use_nsd_resolve, parameters.unknown, parameters.process_id);
201 260
202 const auto host_buffer = ctx.ReadBuffer(0); 261 const auto host_buffer = ctx.ReadBuffer(0);
203 const std::string host = Common::StringFromBuffer(host_buffer); 262 const std::string host = Common::StringFromBuffer(host_buffer);
204 263
205 const auto service_buffer = ctx.ReadBuffer(1); 264 std::optional<std::string> service = std::nullopt;
206 const std::string service = Common::StringFromBuffer(service_buffer); 265 if (ctx.CanReadBuffer(1)) {
207 266 const std::span<const u8> service_buffer = ctx.ReadBuffer(1);
208 addrinfo* addrinfo; 267 service = Common::StringFromBuffer(service_buffer);
209 // Pass null for hints. Serialized hints are also passed in a buffer, but are ignored for now 268 }
210 s32 result_code = getaddrinfo(host.c_str(), service.c_str(), nullptr, &addrinfo);
211 269
212 u32 data_size = 0; 270 // Serialized hints are also passed in a buffer, but are ignored for now.
213 if (result_code == 0 && addrinfo != nullptr) {
214 const std::vector<u8>& data = SerializeAddrInfo(addrinfo, result_code, host);
215 data_size = static_cast<u32>(data.size());
216 freeaddrinfo(addrinfo);
217 271
218 ctx.WriteBuffer(data, 0); 272 auto res = Network::GetAddressInfo(host, service);
273 if (!res.has_value()) {
274 return {0, Translate(res.error())};
219 } 275 }
220 276
221 return std::make_pair(data_size, result_code); 277 const std::vector<u8> data = SerializeAddrInfo(res.value(), host);
278 const u32 data_size = static_cast<u32>(data.size());
279 ctx.WriteBuffer(data, 0);
280
281 return {data_size, GetAddrInfoError::SUCCESS};
222} 282}
223 283
224void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) { 284void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
225 auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx); 285 auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx);
286
287 struct OutputParameters {
288 Errno bsd_errno;
289 GetAddrInfoError gai_error;
290 u32 data_size;
291 };
292 static_assert(sizeof(OutputParameters) == 0xc);
226 293
227 IPC::ResponseBuilder rb{ctx, 4}; 294 IPC::ResponseBuilder rb{ctx, 5};
228 rb.Push(ResultSuccess); 295 rb.Push(ResultSuccess);
229 rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode 296 rb.PushRaw(OutputParameters{
230 rb.Push(result_code); // errno 297 .bsd_errno = GetAddrInfoErrorToErrno(emu_gai_err),
231 rb.Push(data_size); // serialized size 298 .gai_error = emu_gai_err,
299 .data_size = data_size,
300 });
232} 301}
233 302
234void SFDNSRES::GetAddrInfoRequestWithOptions(HLERequestContext& ctx) { 303void SFDNSRES::GetAddrInfoRequestWithOptions(HLERequestContext& ctx) {
235 // Additional options are ignored 304 // Additional options are ignored
236 auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx); 305 auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx);
306
307 struct OutputParameters {
308 u32 data_size;
309 GetAddrInfoError gai_error;
310 NetDbError netdb_error;
311 Errno bsd_errno;
312 };
313 static_assert(sizeof(OutputParameters) == 0x10);
314
315 IPC::ResponseBuilder rb{ctx, 6};
316 rb.Push(ResultSuccess);
317 rb.PushRaw(OutputParameters{
318 .data_size = data_size,
319 .gai_error = emu_gai_err,
320 .netdb_error = GetAddrInfoErrorToNetDbError(emu_gai_err),
321 .bsd_errno = GetAddrInfoErrorToErrno(emu_gai_err),
322 });
323}
324
325void SFDNSRES::ResolverSetOptionRequest(HLERequestContext& ctx) {
326 LOG_WARNING(Service, "(STUBBED) called");
327
328 IPC::ResponseBuilder rb{ctx, 3};
237 329
238 IPC::ResponseBuilder rb{ctx, 5};
239 rb.Push(ResultSuccess); 330 rb.Push(ResultSuccess);
240 rb.Push(data_size); // serialized size 331 rb.Push<s32>(0); // bsd errno
241 rb.Push(result_code); // errno
242 rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode
243 rb.Push(0);
244} 332}
245 333
246} // namespace Service::Sockets 334} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/sfdnsres.h b/src/core/hle/service/sockets/sfdnsres.h
index 18e3cd60c..d99a9d560 100644
--- a/src/core/hle/service/sockets/sfdnsres.h
+++ b/src/core/hle/service/sockets/sfdnsres.h
@@ -17,8 +17,11 @@ public:
17 ~SFDNSRES() override; 17 ~SFDNSRES() override;
18 18
19private: 19private:
20 void GetHostByNameRequest(HLERequestContext& ctx);
21 void GetHostByNameRequestWithOptions(HLERequestContext& ctx);
20 void GetAddrInfoRequest(HLERequestContext& ctx); 22 void GetAddrInfoRequest(HLERequestContext& ctx);
21 void GetAddrInfoRequestWithOptions(HLERequestContext& ctx); 23 void GetAddrInfoRequestWithOptions(HLERequestContext& ctx);
24 void ResolverSetOptionRequest(HLERequestContext& ctx);
22}; 25};
23 26
24} // namespace Service::Sockets 27} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h
index acd2dae7b..77426c46e 100644
--- a/src/core/hle/service/sockets/sockets.h
+++ b/src/core/hle/service/sockets/sockets.h
@@ -22,13 +22,35 @@ enum class Errno : u32 {
22 CONNRESET = 104, 22 CONNRESET = 104,
23 NOTCONN = 107, 23 NOTCONN = 107,
24 TIMEDOUT = 110, 24 TIMEDOUT = 110,
25 INPROGRESS = 115,
26};
27
28enum class GetAddrInfoError : s32 {
29 SUCCESS = 0,
30 ADDRFAMILY = 1,
31 AGAIN = 2,
32 BADFLAGS = 3,
33 FAIL = 4,
34 FAMILY = 5,
35 MEMORY = 6,
36 NODATA = 7,
37 NONAME = 8,
38 SERVICE = 9,
39 SOCKTYPE = 10,
40 SYSTEM = 11,
41 BADHINTS = 12,
42 PROTOCOL = 13,
43 OVERFLOW_ = 14, // avoid name collision with Windows macro
44 OTHER = 15,
25}; 45};
26 46
27enum class Domain : u32 { 47enum class Domain : u32 {
48 Unspecified = 0,
28 INET = 2, 49 INET = 2,
29}; 50};
30 51
31enum class Type : u32 { 52enum class Type : u32 {
53 Unspecified = 0,
32 STREAM = 1, 54 STREAM = 1,
33 DGRAM = 2, 55 DGRAM = 2,
34 RAW = 3, 56 RAW = 3,
@@ -36,12 +58,16 @@ enum class Type : u32 {
36}; 58};
37 59
38enum class Protocol : u32 { 60enum class Protocol : u32 {
39 UNSPECIFIED = 0, 61 Unspecified = 0,
40 ICMP = 1, 62 ICMP = 1,
41 TCP = 6, 63 TCP = 6,
42 UDP = 17, 64 UDP = 17,
43}; 65};
44 66
67enum class SocketLevel : u32 {
68 SOCKET = 0xffff, // i.e. SOL_SOCKET
69};
70
45enum class OptName : u32 { 71enum class OptName : u32 {
46 REUSEADDR = 0x4, 72 REUSEADDR = 0x4,
47 KEEPALIVE = 0x8, 73 KEEPALIVE = 0x8,
@@ -51,6 +77,8 @@ enum class OptName : u32 {
51 RCVBUF = 0x1002, 77 RCVBUF = 0x1002,
52 SNDTIMEO = 0x1005, 78 SNDTIMEO = 0x1005,
53 RCVTIMEO = 0x1006, 79 RCVTIMEO = 0x1006,
80 ERROR_ = 0x1007, // avoid name collision with Windows macro
81 NOSIGPIPE = 0x800, // at least according to libnx
54}; 82};
55 83
56enum class ShutdownHow : s32 { 84enum class ShutdownHow : s32 {
@@ -80,6 +108,9 @@ enum class PollEvents : u16 {
80 Err = 1 << 3, 108 Err = 1 << 3,
81 Hup = 1 << 4, 109 Hup = 1 << 4,
82 Nval = 1 << 5, 110 Nval = 1 << 5,
111 RdNorm = 1 << 6,
112 RdBand = 1 << 7,
113 WrBand = 1 << 8,
83}; 114};
84 115
85DECLARE_ENUM_FLAG_OPERATORS(PollEvents); 116DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp
index 594e58f90..2f9a0e39c 100644
--- a/src/core/hle/service/sockets/sockets_translate.cpp
+++ b/src/core/hle/service/sockets/sockets_translate.cpp
@@ -29,6 +29,8 @@ Errno Translate(Network::Errno value) {
29 return Errno::TIMEDOUT; 29 return Errno::TIMEDOUT;
30 case Network::Errno::CONNRESET: 30 case Network::Errno::CONNRESET:
31 return Errno::CONNRESET; 31 return Errno::CONNRESET;
32 case Network::Errno::INPROGRESS:
33 return Errno::INPROGRESS;
32 default: 34 default:
33 UNIMPLEMENTED_MSG("Unimplemented errno={}", value); 35 UNIMPLEMENTED_MSG("Unimplemented errno={}", value);
34 return Errno::SUCCESS; 36 return Errno::SUCCESS;
@@ -39,8 +41,50 @@ std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value) {
39 return {value.first, Translate(value.second)}; 41 return {value.first, Translate(value.second)};
40} 42}
41 43
44GetAddrInfoError Translate(Network::GetAddrInfoError error) {
45 switch (error) {
46 case Network::GetAddrInfoError::SUCCESS:
47 return GetAddrInfoError::SUCCESS;
48 case Network::GetAddrInfoError::ADDRFAMILY:
49 return GetAddrInfoError::ADDRFAMILY;
50 case Network::GetAddrInfoError::AGAIN:
51 return GetAddrInfoError::AGAIN;
52 case Network::GetAddrInfoError::BADFLAGS:
53 return GetAddrInfoError::BADFLAGS;
54 case Network::GetAddrInfoError::FAIL:
55 return GetAddrInfoError::FAIL;
56 case Network::GetAddrInfoError::FAMILY:
57 return GetAddrInfoError::FAMILY;
58 case Network::GetAddrInfoError::MEMORY:
59 return GetAddrInfoError::MEMORY;
60 case Network::GetAddrInfoError::NODATA:
61 return GetAddrInfoError::NODATA;
62 case Network::GetAddrInfoError::NONAME:
63 return GetAddrInfoError::NONAME;
64 case Network::GetAddrInfoError::SERVICE:
65 return GetAddrInfoError::SERVICE;
66 case Network::GetAddrInfoError::SOCKTYPE:
67 return GetAddrInfoError::SOCKTYPE;
68 case Network::GetAddrInfoError::SYSTEM:
69 return GetAddrInfoError::SYSTEM;
70 case Network::GetAddrInfoError::BADHINTS:
71 return GetAddrInfoError::BADHINTS;
72 case Network::GetAddrInfoError::PROTOCOL:
73 return GetAddrInfoError::PROTOCOL;
74 case Network::GetAddrInfoError::OVERFLOW_:
75 return GetAddrInfoError::OVERFLOW_;
76 case Network::GetAddrInfoError::OTHER:
77 return GetAddrInfoError::OTHER;
78 default:
79 UNIMPLEMENTED_MSG("Unimplemented GetAddrInfoError={}", error);
80 return GetAddrInfoError::OTHER;
81 }
82}
83
42Network::Domain Translate(Domain domain) { 84Network::Domain Translate(Domain domain) {
43 switch (domain) { 85 switch (domain) {
86 case Domain::Unspecified:
87 return Network::Domain::Unspecified;
44 case Domain::INET: 88 case Domain::INET:
45 return Network::Domain::INET; 89 return Network::Domain::INET;
46 default: 90 default:
@@ -51,6 +95,8 @@ Network::Domain Translate(Domain domain) {
51 95
52Domain Translate(Network::Domain domain) { 96Domain Translate(Network::Domain domain) {
53 switch (domain) { 97 switch (domain) {
98 case Network::Domain::Unspecified:
99 return Domain::Unspecified;
54 case Network::Domain::INET: 100 case Network::Domain::INET:
55 return Domain::INET; 101 return Domain::INET;
56 default: 102 default:
@@ -61,39 +107,69 @@ Domain Translate(Network::Domain domain) {
61 107
62Network::Type Translate(Type type) { 108Network::Type Translate(Type type) {
63 switch (type) { 109 switch (type) {
110 case Type::Unspecified:
111 return Network::Type::Unspecified;
64 case Type::STREAM: 112 case Type::STREAM:
65 return Network::Type::STREAM; 113 return Network::Type::STREAM;
66 case Type::DGRAM: 114 case Type::DGRAM:
67 return Network::Type::DGRAM; 115 return Network::Type::DGRAM;
116 case Type::RAW:
117 return Network::Type::RAW;
118 case Type::SEQPACKET:
119 return Network::Type::SEQPACKET;
68 default: 120 default:
69 UNIMPLEMENTED_MSG("Unimplemented type={}", type); 121 UNIMPLEMENTED_MSG("Unimplemented type={}", type);
70 return Network::Type{}; 122 return Network::Type{};
71 } 123 }
72} 124}
73 125
74Network::Protocol Translate(Type type, Protocol protocol) { 126Type Translate(Network::Type type) {
127 switch (type) {
128 case Network::Type::Unspecified:
129 return Type::Unspecified;
130 case Network::Type::STREAM:
131 return Type::STREAM;
132 case Network::Type::DGRAM:
133 return Type::DGRAM;
134 case Network::Type::RAW:
135 return Type::RAW;
136 case Network::Type::SEQPACKET:
137 return Type::SEQPACKET;
138 default:
139 UNIMPLEMENTED_MSG("Unimplemented type={}", type);
140 return Type{};
141 }
142}
143
144Network::Protocol Translate(Protocol protocol) {
75 switch (protocol) { 145 switch (protocol) {
76 case Protocol::UNSPECIFIED: 146 case Protocol::Unspecified:
77 LOG_WARNING(Service, "Unspecified protocol, assuming protocol from type"); 147 return Network::Protocol::Unspecified;
78 switch (type) {
79 case Type::DGRAM:
80 return Network::Protocol::UDP;
81 case Type::STREAM:
82 return Network::Protocol::TCP;
83 default:
84 return Network::Protocol::TCP;
85 }
86 case Protocol::TCP: 148 case Protocol::TCP:
87 return Network::Protocol::TCP; 149 return Network::Protocol::TCP;
88 case Protocol::UDP: 150 case Protocol::UDP:
89 return Network::Protocol::UDP; 151 return Network::Protocol::UDP;
90 default: 152 default:
91 UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol); 153 UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
92 return Network::Protocol::TCP; 154 return Network::Protocol::Unspecified;
155 }
156}
157
158Protocol Translate(Network::Protocol protocol) {
159 switch (protocol) {
160 case Network::Protocol::Unspecified:
161 return Protocol::Unspecified;
162 case Network::Protocol::TCP:
163 return Protocol::TCP;
164 case Network::Protocol::UDP:
165 return Protocol::UDP;
166 default:
167 UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
168 return Protocol::Unspecified;
93 } 169 }
94} 170}
95 171
96Network::PollEvents TranslatePollEventsToHost(PollEvents flags) { 172Network::PollEvents Translate(PollEvents flags) {
97 Network::PollEvents result{}; 173 Network::PollEvents result{};
98 const auto translate = [&result, &flags](PollEvents from, Network::PollEvents to) { 174 const auto translate = [&result, &flags](PollEvents from, Network::PollEvents to) {
99 if (True(flags & from)) { 175 if (True(flags & from)) {
@@ -107,12 +183,15 @@ Network::PollEvents TranslatePollEventsToHost(PollEvents flags) {
107 translate(PollEvents::Err, Network::PollEvents::Err); 183 translate(PollEvents::Err, Network::PollEvents::Err);
108 translate(PollEvents::Hup, Network::PollEvents::Hup); 184 translate(PollEvents::Hup, Network::PollEvents::Hup);
109 translate(PollEvents::Nval, Network::PollEvents::Nval); 185 translate(PollEvents::Nval, Network::PollEvents::Nval);
186 translate(PollEvents::RdNorm, Network::PollEvents::RdNorm);
187 translate(PollEvents::RdBand, Network::PollEvents::RdBand);
188 translate(PollEvents::WrBand, Network::PollEvents::WrBand);
110 189
111 UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags); 190 UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags);
112 return result; 191 return result;
113} 192}
114 193
115PollEvents TranslatePollEventsToGuest(Network::PollEvents flags) { 194PollEvents Translate(Network::PollEvents flags) {
116 PollEvents result{}; 195 PollEvents result{};
117 const auto translate = [&result, &flags](Network::PollEvents from, PollEvents to) { 196 const auto translate = [&result, &flags](Network::PollEvents from, PollEvents to) {
118 if (True(flags & from)) { 197 if (True(flags & from)) {
@@ -127,13 +206,18 @@ PollEvents TranslatePollEventsToGuest(Network::PollEvents flags) {
127 translate(Network::PollEvents::Err, PollEvents::Err); 206 translate(Network::PollEvents::Err, PollEvents::Err);
128 translate(Network::PollEvents::Hup, PollEvents::Hup); 207 translate(Network::PollEvents::Hup, PollEvents::Hup);
129 translate(Network::PollEvents::Nval, PollEvents::Nval); 208 translate(Network::PollEvents::Nval, PollEvents::Nval);
209 translate(Network::PollEvents::RdNorm, PollEvents::RdNorm);
210 translate(Network::PollEvents::RdBand, PollEvents::RdBand);
211 translate(Network::PollEvents::WrBand, PollEvents::WrBand);
130 212
131 UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags); 213 UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags);
132 return result; 214 return result;
133} 215}
134 216
135Network::SockAddrIn Translate(SockAddrIn value) { 217Network::SockAddrIn Translate(SockAddrIn value) {
136 ASSERT(value.len == 0 || value.len == sizeof(value)); 218 // Note: 6 is incorrect, but can be passed by homebrew (because libnx sets
219 // sin_len to 6 when deserializing getaddrinfo results).
220 ASSERT(value.len == 0 || value.len == sizeof(value) || value.len == 6);
137 221
138 return { 222 return {
139 .family = Translate(static_cast<Domain>(value.family)), 223 .family = Translate(static_cast<Domain>(value.family)),
diff --git a/src/core/hle/service/sockets/sockets_translate.h b/src/core/hle/service/sockets/sockets_translate.h
index c93291d3e..694868b37 100644
--- a/src/core/hle/service/sockets/sockets_translate.h
+++ b/src/core/hle/service/sockets/sockets_translate.h
@@ -17,6 +17,9 @@ Errno Translate(Network::Errno value);
17/// Translate abstract return value errno pair to guest return value errno pair 17/// Translate abstract return value errno pair to guest return value errno pair
18std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value); 18std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value);
19 19
20/// Translate abstract getaddrinfo error to guest getaddrinfo error
21GetAddrInfoError Translate(Network::GetAddrInfoError value);
22
20/// Translate guest domain to abstract domain 23/// Translate guest domain to abstract domain
21Network::Domain Translate(Domain domain); 24Network::Domain Translate(Domain domain);
22 25
@@ -26,14 +29,20 @@ Domain Translate(Network::Domain domain);
26/// Translate guest type to abstract type 29/// Translate guest type to abstract type
27Network::Type Translate(Type type); 30Network::Type Translate(Type type);
28 31
32/// Translate abstract type to guest type
33Type Translate(Network::Type type);
34
29/// Translate guest protocol to abstract protocol 35/// Translate guest protocol to abstract protocol
30Network::Protocol Translate(Type type, Protocol protocol); 36Network::Protocol Translate(Protocol protocol);
31 37
32/// Translate abstract poll event flags to guest poll event flags 38/// Translate abstract protocol to guest protocol
33Network::PollEvents TranslatePollEventsToHost(PollEvents flags); 39Protocol Translate(Network::Protocol protocol);
34 40
35/// Translate guest poll event flags to abstract poll event flags 41/// Translate guest poll event flags to abstract poll event flags
36PollEvents TranslatePollEventsToGuest(Network::PollEvents flags); 42Network::PollEvents Translate(PollEvents flags);
43
44/// Translate abstract poll event flags to guest poll event flags
45PollEvents Translate(Network::PollEvents flags);
37 46
38/// Translate guest socket address structure to abstract socket address structure 47/// Translate guest socket address structure to abstract socket address structure
39Network::SockAddrIn Translate(SockAddrIn value); 48Network::SockAddrIn Translate(SockAddrIn value);
diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp
index 2b99dd7ac..9c96f9763 100644
--- a/src/core/hle/service/ssl/ssl.cpp
+++ b/src/core/hle/service/ssl/ssl.cpp
@@ -1,10 +1,18 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/string_util.h"
5
6#include "core/core.h"
4#include "core/hle/service/ipc_helpers.h" 7#include "core/hle/service/ipc_helpers.h"
5#include "core/hle/service/server_manager.h" 8#include "core/hle/service/server_manager.h"
6#include "core/hle/service/service.h" 9#include "core/hle/service/service.h"
10#include "core/hle/service/sm/sm.h"
11#include "core/hle/service/sockets/bsd.h"
7#include "core/hle/service/ssl/ssl.h" 12#include "core/hle/service/ssl/ssl.h"
13#include "core/hle/service/ssl/ssl_backend.h"
14#include "core/internal_network/network.h"
15#include "core/internal_network/sockets.h"
8 16
9namespace Service::SSL { 17namespace Service::SSL {
10 18
@@ -20,6 +28,18 @@ enum class ContextOption : u32 {
20 CrlImportDateCheckEnable = 1, 28 CrlImportDateCheckEnable = 1,
21}; 29};
22 30
31// This is nn::ssl::Connection::IoMode
32enum class IoMode : u32 {
33 Blocking = 1,
34 NonBlocking = 2,
35};
36
37// This is nn::ssl::sf::OptionType
38enum class OptionType : u32 {
39 DoNotCloseSocket = 0,
40 GetServerCertChain = 1,
41};
42
23// This is nn::ssl::sf::SslVersion 43// This is nn::ssl::sf::SslVersion
24struct SslVersion { 44struct SslVersion {
25 union { 45 union {
@@ -34,35 +54,42 @@ struct SslVersion {
34 }; 54 };
35}; 55};
36 56
57struct SslContextSharedData {
58 u32 connection_count = 0;
59};
60
37class ISslConnection final : public ServiceFramework<ISslConnection> { 61class ISslConnection final : public ServiceFramework<ISslConnection> {
38public: 62public:
39 explicit ISslConnection(Core::System& system_, SslVersion version) 63 explicit ISslConnection(Core::System& system_in, SslVersion ssl_version_in,
40 : ServiceFramework{system_, "ISslConnection"}, ssl_version{version} { 64 std::shared_ptr<SslContextSharedData>& shared_data_in,
65 std::unique_ptr<SSLConnectionBackend>&& backend_in)
66 : ServiceFramework{system_in, "ISslConnection"}, ssl_version{ssl_version_in},
67 shared_data{shared_data_in}, backend{std::move(backend_in)} {
41 // clang-format off 68 // clang-format off
42 static const FunctionInfo functions[] = { 69 static const FunctionInfo functions[] = {
43 {0, nullptr, "SetSocketDescriptor"}, 70 {0, &ISslConnection::SetSocketDescriptor, "SetSocketDescriptor"},
44 {1, nullptr, "SetHostName"}, 71 {1, &ISslConnection::SetHostName, "SetHostName"},
45 {2, nullptr, "SetVerifyOption"}, 72 {2, &ISslConnection::SetVerifyOption, "SetVerifyOption"},
46 {3, nullptr, "SetIoMode"}, 73 {3, &ISslConnection::SetIoMode, "SetIoMode"},
47 {4, nullptr, "GetSocketDescriptor"}, 74 {4, nullptr, "GetSocketDescriptor"},
48 {5, nullptr, "GetHostName"}, 75 {5, nullptr, "GetHostName"},
49 {6, nullptr, "GetVerifyOption"}, 76 {6, nullptr, "GetVerifyOption"},
50 {7, nullptr, "GetIoMode"}, 77 {7, nullptr, "GetIoMode"},
51 {8, nullptr, "DoHandshake"}, 78 {8, &ISslConnection::DoHandshake, "DoHandshake"},
52 {9, nullptr, "DoHandshakeGetServerCert"}, 79 {9, &ISslConnection::DoHandshakeGetServerCert, "DoHandshakeGetServerCert"},
53 {10, nullptr, "Read"}, 80 {10, &ISslConnection::Read, "Read"},
54 {11, nullptr, "Write"}, 81 {11, &ISslConnection::Write, "Write"},
55 {12, nullptr, "Pending"}, 82 {12, &ISslConnection::Pending, "Pending"},
56 {13, nullptr, "Peek"}, 83 {13, nullptr, "Peek"},
57 {14, nullptr, "Poll"}, 84 {14, nullptr, "Poll"},
58 {15, nullptr, "GetVerifyCertError"}, 85 {15, nullptr, "GetVerifyCertError"},
59 {16, nullptr, "GetNeededServerCertBufferSize"}, 86 {16, nullptr, "GetNeededServerCertBufferSize"},
60 {17, nullptr, "SetSessionCacheMode"}, 87 {17, &ISslConnection::SetSessionCacheMode, "SetSessionCacheMode"},
61 {18, nullptr, "GetSessionCacheMode"}, 88 {18, nullptr, "GetSessionCacheMode"},
62 {19, nullptr, "FlushSessionCache"}, 89 {19, nullptr, "FlushSessionCache"},
63 {20, nullptr, "SetRenegotiationMode"}, 90 {20, nullptr, "SetRenegotiationMode"},
64 {21, nullptr, "GetRenegotiationMode"}, 91 {21, nullptr, "GetRenegotiationMode"},
65 {22, nullptr, "SetOption"}, 92 {22, &ISslConnection::SetOption, "SetOption"},
66 {23, nullptr, "GetOption"}, 93 {23, nullptr, "GetOption"},
67 {24, nullptr, "GetVerifyCertErrors"}, 94 {24, nullptr, "GetVerifyCertErrors"},
68 {25, nullptr, "GetCipherInfo"}, 95 {25, nullptr, "GetCipherInfo"},
@@ -80,21 +107,299 @@ public:
80 // clang-format on 107 // clang-format on
81 108
82 RegisterHandlers(functions); 109 RegisterHandlers(functions);
110
111 shared_data->connection_count++;
112 }
113
114 ~ISslConnection() {
115 shared_data->connection_count--;
116 if (fd_to_close.has_value()) {
117 const s32 fd = *fd_to_close;
118 if (!do_not_close_socket) {
119 LOG_ERROR(Service_SSL,
120 "do_not_close_socket was changed after setting socket; is this right?");
121 } else {
122 auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD>("bsd:u");
123 if (bsd) {
124 auto err = bsd->CloseImpl(fd);
125 if (err != Service::Sockets::Errno::SUCCESS) {
126 LOG_ERROR(Service_SSL, "Failed to close duplicated socket: {}", err);
127 }
128 }
129 }
130 }
83 } 131 }
84 132
85private: 133private:
86 SslVersion ssl_version; 134 SslVersion ssl_version;
135 std::shared_ptr<SslContextSharedData> shared_data;
136 std::unique_ptr<SSLConnectionBackend> backend;
137 std::optional<int> fd_to_close;
138 bool do_not_close_socket = false;
139 bool get_server_cert_chain = false;
140 std::shared_ptr<Network::SocketBase> socket;
141 bool did_set_host_name = false;
142 bool did_handshake = false;
143
144 ResultVal<s32> SetSocketDescriptorImpl(s32 fd) {
145 LOG_DEBUG(Service_SSL, "called, fd={}", fd);
146 ASSERT(!did_handshake);
147 auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD>("bsd:u");
148 ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; });
149 s32 ret_fd;
150 // Based on https://switchbrew.org/wiki/SSL_services#SetSocketDescriptor
151 if (do_not_close_socket) {
152 auto res = bsd->DuplicateSocketImpl(fd);
153 if (!res.has_value()) {
154 LOG_ERROR(Service_SSL, "Failed to duplicate socket with fd {}", fd);
155 return ResultInvalidSocket;
156 }
157 fd = *res;
158 fd_to_close = fd;
159 ret_fd = fd;
160 } else {
161 ret_fd = -1;
162 }
163 std::optional<std::shared_ptr<Network::SocketBase>> sock = bsd->GetSocket(fd);
164 if (!sock.has_value()) {
165 LOG_ERROR(Service_SSL, "invalid socket fd {}", fd);
166 return ResultInvalidSocket;
167 }
168 socket = std::move(*sock);
169 backend->SetSocket(socket);
170 return ret_fd;
171 }
172
173 Result SetHostNameImpl(const std::string& hostname) {
174 LOG_DEBUG(Service_SSL, "called. hostname={}", hostname);
175 ASSERT(!did_handshake);
176 Result res = backend->SetHostName(hostname);
177 if (res == ResultSuccess) {
178 did_set_host_name = true;
179 }
180 return res;
181 }
182
183 Result SetVerifyOptionImpl(u32 option) {
184 ASSERT(!did_handshake);
185 LOG_WARNING(Service_SSL, "(STUBBED) called. option={}", option);
186 return ResultSuccess;
187 }
188
189 Result SetIoModeImpl(u32 input_mode) {
190 auto mode = static_cast<IoMode>(input_mode);
191 ASSERT(mode == IoMode::Blocking || mode == IoMode::NonBlocking);
192 ASSERT_OR_EXECUTE(socket, { return ResultNoSocket; });
193
194 const bool non_block = mode == IoMode::NonBlocking;
195 const Network::Errno error = socket->SetNonBlock(non_block);
196 if (error != Network::Errno::SUCCESS) {
197 LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block);
198 }
199 return ResultSuccess;
200 }
201
202 Result SetSessionCacheModeImpl(u32 mode) {
203 ASSERT(!did_handshake);
204 LOG_WARNING(Service_SSL, "(STUBBED) called. value={}", mode);
205 return ResultSuccess;
206 }
207
208 Result DoHandshakeImpl() {
209 ASSERT_OR_EXECUTE(!did_handshake && socket, { return ResultNoSocket; });
210 ASSERT_OR_EXECUTE_MSG(
211 did_set_host_name, { return ResultInternalError; },
212 "Expected SetHostName before DoHandshake");
213 Result res = backend->DoHandshake();
214 did_handshake = res.IsSuccess();
215 return res;
216 }
217
218 std::vector<u8> SerializeServerCerts(const std::vector<std::vector<u8>>& certs) {
219 struct Header {
220 u64 magic;
221 u32 count;
222 u32 pad;
223 };
224 struct EntryHeader {
225 u32 size;
226 u32 offset;
227 };
228 if (!get_server_cert_chain) {
229 // Just return the first one, unencoded.
230 ASSERT_OR_EXECUTE_MSG(
231 !certs.empty(), { return {}; }, "Should be at least one server cert");
232 return certs[0];
233 }
234 std::vector<u8> ret;
235 Header header{0x4E4D684374726543, static_cast<u32>(certs.size()), 0};
236 ret.insert(ret.end(), reinterpret_cast<u8*>(&header), reinterpret_cast<u8*>(&header + 1));
237 size_t data_offset = sizeof(Header) + certs.size() * sizeof(EntryHeader);
238 for (auto& cert : certs) {
239 EntryHeader entry_header{static_cast<u32>(cert.size()), static_cast<u32>(data_offset)};
240 data_offset += cert.size();
241 ret.insert(ret.end(), reinterpret_cast<u8*>(&entry_header),
242 reinterpret_cast<u8*>(&entry_header + 1));
243 }
244 for (auto& cert : certs) {
245 ret.insert(ret.end(), cert.begin(), cert.end());
246 }
247 return ret;
248 }
249
250 ResultVal<std::vector<u8>> ReadImpl(size_t size) {
251 ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; });
252 std::vector<u8> res(size);
253 ResultVal<size_t> actual = backend->Read(res);
254 if (actual.Failed()) {
255 return actual.Code();
256 }
257 res.resize(*actual);
258 return res;
259 }
260
261 ResultVal<size_t> WriteImpl(std::span<const u8> data) {
262 ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; });
263 return backend->Write(data);
264 }
265
266 ResultVal<s32> PendingImpl() {
267 LOG_WARNING(Service_SSL, "(STUBBED) called.");
268 return 0;
269 }
270
271 void SetSocketDescriptor(HLERequestContext& ctx) {
272 IPC::RequestParser rp{ctx};
273 const s32 fd = rp.Pop<s32>();
274 const ResultVal<s32> res = SetSocketDescriptorImpl(fd);
275 IPC::ResponseBuilder rb{ctx, 3};
276 rb.Push(res.Code());
277 rb.Push<s32>(res.ValueOr(-1));
278 }
279
280 void SetHostName(HLERequestContext& ctx) {
281 const std::string hostname = Common::StringFromBuffer(ctx.ReadBuffer());
282 const Result res = SetHostNameImpl(hostname);
283 IPC::ResponseBuilder rb{ctx, 2};
284 rb.Push(res);
285 }
286
287 void SetVerifyOption(HLERequestContext& ctx) {
288 IPC::RequestParser rp{ctx};
289 const u32 option = rp.Pop<u32>();
290 const Result res = SetVerifyOptionImpl(option);
291 IPC::ResponseBuilder rb{ctx, 2};
292 rb.Push(res);
293 }
294
295 void SetIoMode(HLERequestContext& ctx) {
296 IPC::RequestParser rp{ctx};
297 const u32 mode = rp.Pop<u32>();
298 const Result res = SetIoModeImpl(mode);
299 IPC::ResponseBuilder rb{ctx, 2};
300 rb.Push(res);
301 }
302
303 void DoHandshake(HLERequestContext& ctx) {
304 const Result res = DoHandshakeImpl();
305 IPC::ResponseBuilder rb{ctx, 2};
306 rb.Push(res);
307 }
308
309 void DoHandshakeGetServerCert(HLERequestContext& ctx) {
310 struct OutputParameters {
311 u32 certs_size;
312 u32 certs_count;
313 };
314 static_assert(sizeof(OutputParameters) == 0x8);
315
316 const Result res = DoHandshakeImpl();
317 OutputParameters out{};
318 if (res == ResultSuccess) {
319 auto certs = backend->GetServerCerts();
320 if (certs.Succeeded()) {
321 const std::vector<u8> certs_buf = SerializeServerCerts(*certs);
322 ctx.WriteBuffer(certs_buf);
323 out.certs_count = static_cast<u32>(certs->size());
324 out.certs_size = static_cast<u32>(certs_buf.size());
325 }
326 }
327 IPC::ResponseBuilder rb{ctx, 4};
328 rb.Push(res);
329 rb.PushRaw(out);
330 }
331
332 void Read(HLERequestContext& ctx) {
333 const ResultVal<std::vector<u8>> res = ReadImpl(ctx.GetWriteBufferSize());
334 IPC::ResponseBuilder rb{ctx, 3};
335 rb.Push(res.Code());
336 if (res.Succeeded()) {
337 rb.Push(static_cast<u32>(res->size()));
338 ctx.WriteBuffer(*res);
339 } else {
340 rb.Push(static_cast<u32>(0));
341 }
342 }
343
344 void Write(HLERequestContext& ctx) {
345 const ResultVal<size_t> res = WriteImpl(ctx.ReadBuffer());
346 IPC::ResponseBuilder rb{ctx, 3};
347 rb.Push(res.Code());
348 rb.Push(static_cast<u32>(res.ValueOr(0)));
349 }
350
351 void Pending(HLERequestContext& ctx) {
352 const ResultVal<s32> res = PendingImpl();
353 IPC::ResponseBuilder rb{ctx, 3};
354 rb.Push(res.Code());
355 rb.Push<s32>(res.ValueOr(0));
356 }
357
358 void SetSessionCacheMode(HLERequestContext& ctx) {
359 IPC::RequestParser rp{ctx};
360 const u32 mode = rp.Pop<u32>();
361 const Result res = SetSessionCacheModeImpl(mode);
362 IPC::ResponseBuilder rb{ctx, 2};
363 rb.Push(res);
364 }
365
366 void SetOption(HLERequestContext& ctx) {
367 struct Parameters {
368 OptionType option;
369 s32 value;
370 };
371 static_assert(sizeof(Parameters) == 0x8, "Parameters is an invalid size");
372
373 IPC::RequestParser rp{ctx};
374 const auto parameters = rp.PopRaw<Parameters>();
375
376 switch (parameters.option) {
377 case OptionType::DoNotCloseSocket:
378 do_not_close_socket = static_cast<bool>(parameters.value);
379 break;
380 case OptionType::GetServerCertChain:
381 get_server_cert_chain = static_cast<bool>(parameters.value);
382 break;
383 default:
384 LOG_WARNING(Service_SSL, "Unknown option={}, value={}", parameters.option,
385 parameters.value);
386 }
387
388 IPC::ResponseBuilder rb{ctx, 2};
389 rb.Push(ResultSuccess);
390 }
87}; 391};
88 392
89class ISslContext final : public ServiceFramework<ISslContext> { 393class ISslContext final : public ServiceFramework<ISslContext> {
90public: 394public:
91 explicit ISslContext(Core::System& system_, SslVersion version) 395 explicit ISslContext(Core::System& system_, SslVersion version)
92 : ServiceFramework{system_, "ISslContext"}, ssl_version{version} { 396 : ServiceFramework{system_, "ISslContext"}, ssl_version{version},
397 shared_data{std::make_shared<SslContextSharedData>()} {
93 static const FunctionInfo functions[] = { 398 static const FunctionInfo functions[] = {
94 {0, &ISslContext::SetOption, "SetOption"}, 399 {0, &ISslContext::SetOption, "SetOption"},
95 {1, nullptr, "GetOption"}, 400 {1, nullptr, "GetOption"},
96 {2, &ISslContext::CreateConnection, "CreateConnection"}, 401 {2, &ISslContext::CreateConnection, "CreateConnection"},
97 {3, nullptr, "GetConnectionCount"}, 402 {3, &ISslContext::GetConnectionCount, "GetConnectionCount"},
98 {4, &ISslContext::ImportServerPki, "ImportServerPki"}, 403 {4, &ISslContext::ImportServerPki, "ImportServerPki"},
99 {5, &ISslContext::ImportClientPki, "ImportClientPki"}, 404 {5, &ISslContext::ImportClientPki, "ImportClientPki"},
100 {6, nullptr, "RemoveServerPki"}, 405 {6, nullptr, "RemoveServerPki"},
@@ -111,6 +416,7 @@ public:
111 416
112private: 417private:
113 SslVersion ssl_version; 418 SslVersion ssl_version;
419 std::shared_ptr<SslContextSharedData> shared_data;
114 420
115 void SetOption(HLERequestContext& ctx) { 421 void SetOption(HLERequestContext& ctx) {
116 struct Parameters { 422 struct Parameters {
@@ -130,11 +436,24 @@ private:
130 } 436 }
131 437
132 void CreateConnection(HLERequestContext& ctx) { 438 void CreateConnection(HLERequestContext& ctx) {
133 LOG_WARNING(Service_SSL, "(STUBBED) called"); 439 LOG_WARNING(Service_SSL, "called");
440
441 auto backend_res = CreateSSLConnectionBackend();
134 442
135 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 443 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
444 rb.Push(backend_res.Code());
445 if (backend_res.Succeeded()) {
446 rb.PushIpcInterface<ISslConnection>(system, ssl_version, shared_data,
447 std::move(*backend_res));
448 }
449 }
450
451 void GetConnectionCount(HLERequestContext& ctx) {
452 LOG_DEBUG(Service_SSL, "connection_count={}", shared_data->connection_count);
453
454 IPC::ResponseBuilder rb{ctx, 3};
136 rb.Push(ResultSuccess); 455 rb.Push(ResultSuccess);
137 rb.PushIpcInterface<ISslConnection>(system, ssl_version); 456 rb.Push(shared_data->connection_count);
138 } 457 }
139 458
140 void ImportServerPki(HLERequestContext& ctx) { 459 void ImportServerPki(HLERequestContext& ctx) {
diff --git a/src/core/hle/service/ssl/ssl_backend.h b/src/core/hle/service/ssl/ssl_backend.h
new file mode 100644
index 000000000..409f4367c
--- /dev/null
+++ b/src/core/hle/service/ssl/ssl_backend.h
@@ -0,0 +1,45 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <span>
8#include <string>
9#include <vector>
10
11#include "common/common_types.h"
12
13#include "core/hle/result.h"
14
15namespace Network {
16class SocketBase;
17}
18
19namespace Service::SSL {
20
21constexpr Result ResultNoSocket{ErrorModule::SSLSrv, 103};
22constexpr Result ResultInvalidSocket{ErrorModule::SSLSrv, 106};
23constexpr Result ResultTimeout{ErrorModule::SSLSrv, 205};
24constexpr Result ResultInternalError{ErrorModule::SSLSrv, 999}; // made up
25
26// ResultWouldBlock is returned from Read and Write, and oddly, DoHandshake,
27// with no way in the latter case to distinguish whether the client should poll
28// for read or write. The one official client I've seen handles this by always
29// polling for read (with a timeout).
30constexpr Result ResultWouldBlock{ErrorModule::SSLSrv, 204};
31
32class SSLConnectionBackend {
33public:
34 virtual ~SSLConnectionBackend() {}
35 virtual void SetSocket(std::shared_ptr<Network::SocketBase> socket) = 0;
36 virtual Result SetHostName(const std::string& hostname) = 0;
37 virtual Result DoHandshake() = 0;
38 virtual ResultVal<size_t> Read(std::span<u8> data) = 0;
39 virtual ResultVal<size_t> Write(std::span<const u8> data) = 0;
40 virtual ResultVal<std::vector<std::vector<u8>>> GetServerCerts() = 0;
41};
42
43ResultVal<std::unique_ptr<SSLConnectionBackend>> CreateSSLConnectionBackend();
44
45} // namespace Service::SSL
diff --git a/src/core/hle/service/ssl/ssl_backend_none.cpp b/src/core/hle/service/ssl/ssl_backend_none.cpp
new file mode 100644
index 000000000..2f4f23c42
--- /dev/null
+++ b/src/core/hle/service/ssl/ssl_backend_none.cpp
@@ -0,0 +1,16 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5
6#include "core/hle/service/ssl/ssl_backend.h"
7
8namespace Service::SSL {
9
10ResultVal<std::unique_ptr<SSLConnectionBackend>> CreateSSLConnectionBackend() {
11 LOG_ERROR(Service_SSL,
12 "Can't create SSL connection because no SSL backend is available on this platform");
13 return ResultInternalError;
14}
15
16} // namespace Service::SSL
diff --git a/src/core/hle/service/ssl/ssl_backend_openssl.cpp b/src/core/hle/service/ssl/ssl_backend_openssl.cpp
new file mode 100644
index 000000000..6ca869dbf
--- /dev/null
+++ b/src/core/hle/service/ssl/ssl_backend_openssl.cpp
@@ -0,0 +1,351 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <mutex>
5
6#include <openssl/bio.h>
7#include <openssl/err.h>
8#include <openssl/ssl.h>
9#include <openssl/x509.h>
10
11#include "common/fs/file.h"
12#include "common/hex_util.h"
13#include "common/string_util.h"
14
15#include "core/hle/service/ssl/ssl_backend.h"
16#include "core/internal_network/network.h"
17#include "core/internal_network/sockets.h"
18
19using namespace Common::FS;
20
21namespace Service::SSL {
22
23// Import OpenSSL's `SSL` type into the namespace. This is needed because the
24// namespace is also named `SSL`.
25using ::SSL;
26
27namespace {
28
29std::once_flag one_time_init_flag;
30bool one_time_init_success = false;
31
32SSL_CTX* ssl_ctx;
33IOFile key_log_file; // only open if SSLKEYLOGFILE set in environment
34BIO_METHOD* bio_meth;
35
36Result CheckOpenSSLErrors();
37void OneTimeInit();
38void OneTimeInitLogFile();
39bool OneTimeInitBIO();
40
41} // namespace
42
43class SSLConnectionBackendOpenSSL final : public SSLConnectionBackend {
44public:
45 Result Init() {
46 std::call_once(one_time_init_flag, OneTimeInit);
47
48 if (!one_time_init_success) {
49 LOG_ERROR(Service_SSL,
50 "Can't create SSL connection because OpenSSL one-time initialization failed");
51 return ResultInternalError;
52 }
53
54 ssl = SSL_new(ssl_ctx);
55 if (!ssl) {
56 LOG_ERROR(Service_SSL, "SSL_new failed");
57 return CheckOpenSSLErrors();
58 }
59
60 SSL_set_connect_state(ssl);
61
62 bio = BIO_new(bio_meth);
63 if (!bio) {
64 LOG_ERROR(Service_SSL, "BIO_new failed");
65 return CheckOpenSSLErrors();
66 }
67
68 BIO_set_data(bio, this);
69 BIO_set_init(bio, 1);
70 SSL_set_bio(ssl, bio, bio);
71
72 return ResultSuccess;
73 }
74
75 void SetSocket(std::shared_ptr<Network::SocketBase> socket_in) override {
76 socket = std::move(socket_in);
77 }
78
79 Result SetHostName(const std::string& hostname) override {
80 if (!SSL_set1_host(ssl, hostname.c_str())) { // hostname for verification
81 LOG_ERROR(Service_SSL, "SSL_set1_host({}) failed", hostname);
82 return CheckOpenSSLErrors();
83 }
84 if (!SSL_set_tlsext_host_name(ssl, hostname.c_str())) { // hostname for SNI
85 LOG_ERROR(Service_SSL, "SSL_set_tlsext_host_name({}) failed", hostname);
86 return CheckOpenSSLErrors();
87 }
88 return ResultSuccess;
89 }
90
91 Result DoHandshake() override {
92 SSL_set_verify_result(ssl, X509_V_OK);
93 const int ret = SSL_do_handshake(ssl);
94 const long verify_result = SSL_get_verify_result(ssl);
95 if (verify_result != X509_V_OK) {
96 LOG_ERROR(Service_SSL, "SSL cert verification failed because: {}",
97 X509_verify_cert_error_string(verify_result));
98 return CheckOpenSSLErrors();
99 }
100 if (ret <= 0) {
101 const int ssl_err = SSL_get_error(ssl, ret);
102 if (ssl_err == SSL_ERROR_ZERO_RETURN ||
103 (ssl_err == SSL_ERROR_SYSCALL && got_read_eof)) {
104 LOG_ERROR(Service_SSL, "SSL handshake failed because server hung up");
105 return ResultInternalError;
106 }
107 }
108 return HandleReturn("SSL_do_handshake", 0, ret).Code();
109 }
110
111 ResultVal<size_t> Read(std::span<u8> data) override {
112 size_t actual;
113 const int ret = SSL_read_ex(ssl, data.data(), data.size(), &actual);
114 return HandleReturn("SSL_read_ex", actual, ret);
115 }
116
117 ResultVal<size_t> Write(std::span<const u8> data) override {
118 size_t actual;
119 const int ret = SSL_write_ex(ssl, data.data(), data.size(), &actual);
120 return HandleReturn("SSL_write_ex", actual, ret);
121 }
122
123 ResultVal<size_t> HandleReturn(const char* what, size_t actual, int ret) {
124 const int ssl_err = SSL_get_error(ssl, ret);
125 CheckOpenSSLErrors();
126 switch (ssl_err) {
127 case SSL_ERROR_NONE:
128 return actual;
129 case SSL_ERROR_ZERO_RETURN:
130 LOG_DEBUG(Service_SSL, "{} => SSL_ERROR_ZERO_RETURN", what);
131 // DoHandshake special-cases this, but for Read and Write:
132 return size_t(0);
133 case SSL_ERROR_WANT_READ:
134 LOG_DEBUG(Service_SSL, "{} => SSL_ERROR_WANT_READ", what);
135 return ResultWouldBlock;
136 case SSL_ERROR_WANT_WRITE:
137 LOG_DEBUG(Service_SSL, "{} => SSL_ERROR_WANT_WRITE", what);
138 return ResultWouldBlock;
139 default:
140 if (ssl_err == SSL_ERROR_SYSCALL && got_read_eof) {
141 LOG_DEBUG(Service_SSL, "{} => SSL_ERROR_SYSCALL because server hung up", what);
142 return size_t(0);
143 }
144 LOG_ERROR(Service_SSL, "{} => other SSL_get_error return value {}", what, ssl_err);
145 return ResultInternalError;
146 }
147 }
148
149 ResultVal<std::vector<std::vector<u8>>> GetServerCerts() override {
150 STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl);
151 if (!chain) {
152 LOG_ERROR(Service_SSL, "SSL_get_peer_cert_chain returned nullptr");
153 return ResultInternalError;
154 }
155 std::vector<std::vector<u8>> ret;
156 int count = sk_X509_num(chain);
157 ASSERT(count >= 0);
158 for (int i = 0; i < count; i++) {
159 X509* x509 = sk_X509_value(chain, i);
160 ASSERT_OR_EXECUTE(x509 != nullptr, { continue; });
161 unsigned char* buf = nullptr;
162 int len = i2d_X509(x509, &buf);
163 ASSERT_OR_EXECUTE(len >= 0 && buf, { continue; });
164 ret.emplace_back(buf, buf + len);
165 OPENSSL_free(buf);
166 }
167 return ret;
168 }
169
170 ~SSLConnectionBackendOpenSSL() {
171 // these are null-tolerant:
172 SSL_free(ssl);
173 BIO_free(bio);
174 }
175
176 static void KeyLogCallback(const SSL* ssl, const char* line) {
177 std::string str(line);
178 str.push_back('\n');
179 // Do this in a single WriteString for atomicity if multiple instances
180 // are running on different threads (though that can't currently
181 // happen).
182 if (key_log_file.WriteString(str) != str.size() || !key_log_file.Flush()) {
183 LOG_CRITICAL(Service_SSL, "Failed to write to SSLKEYLOGFILE");
184 }
185 LOG_DEBUG(Service_SSL, "Wrote to SSLKEYLOGFILE: {}", line);
186 }
187
188 static int WriteCallback(BIO* bio, const char* buf, size_t len, size_t* actual_p) {
189 auto self = static_cast<SSLConnectionBackendOpenSSL*>(BIO_get_data(bio));
190 ASSERT_OR_EXECUTE_MSG(
191 self->socket, { return 0; }, "OpenSSL asked to send but we have no socket");
192 BIO_clear_retry_flags(bio);
193 auto [actual, err] = self->socket->Send({reinterpret_cast<const u8*>(buf), len}, 0);
194 switch (err) {
195 case Network::Errno::SUCCESS:
196 *actual_p = actual;
197 return 1;
198 case Network::Errno::AGAIN:
199 BIO_set_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY);
200 return 0;
201 default:
202 LOG_ERROR(Service_SSL, "Socket send returned Network::Errno {}", err);
203 return -1;
204 }
205 }
206
207 static int ReadCallback(BIO* bio, char* buf, size_t len, size_t* actual_p) {
208 auto self = static_cast<SSLConnectionBackendOpenSSL*>(BIO_get_data(bio));
209 ASSERT_OR_EXECUTE_MSG(
210 self->socket, { return 0; }, "OpenSSL asked to recv but we have no socket");
211 BIO_clear_retry_flags(bio);
212 auto [actual, err] = self->socket->Recv(0, {reinterpret_cast<u8*>(buf), len});
213 switch (err) {
214 case Network::Errno::SUCCESS:
215 *actual_p = actual;
216 if (actual == 0) {
217 self->got_read_eof = true;
218 }
219 return actual ? 1 : 0;
220 case Network::Errno::AGAIN:
221 BIO_set_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY);
222 return 0;
223 default:
224 LOG_ERROR(Service_SSL, "Socket recv returned Network::Errno {}", err);
225 return -1;
226 }
227 }
228
229 static long CtrlCallback(BIO* bio, int cmd, long l_arg, void* p_arg) {
230 switch (cmd) {
231 case BIO_CTRL_FLUSH:
232 // Nothing to flush.
233 return 1;
234 case BIO_CTRL_PUSH:
235 case BIO_CTRL_POP:
236#ifdef BIO_CTRL_GET_KTLS_SEND
237 case BIO_CTRL_GET_KTLS_SEND:
238 case BIO_CTRL_GET_KTLS_RECV:
239#endif
240 // We don't support these operations, but don't bother logging them
241 // as they're nothing unusual.
242 return 0;
243 default:
244 LOG_DEBUG(Service_SSL, "OpenSSL BIO got ctrl({}, {}, {})", cmd, l_arg, p_arg);
245 return 0;
246 }
247 }
248
249 SSL* ssl = nullptr;
250 BIO* bio = nullptr;
251 bool got_read_eof = false;
252
253 std::shared_ptr<Network::SocketBase> socket;
254};
255
256ResultVal<std::unique_ptr<SSLConnectionBackend>> CreateSSLConnectionBackend() {
257 auto conn = std::make_unique<SSLConnectionBackendOpenSSL>();
258 const Result res = conn->Init();
259 if (res.IsFailure()) {
260 return res;
261 }
262 return conn;
263}
264
265namespace {
266
267Result CheckOpenSSLErrors() {
268 unsigned long rc;
269 const char* file;
270 int line;
271 const char* func;
272 const char* data;
273 int flags;
274#if OPENSSL_VERSION_NUMBER >= 0x30000000L
275 while ((rc = ERR_get_error_all(&file, &line, &func, &data, &flags)))
276#else
277 // Can't get function names from OpenSSL on this version, so use mine:
278 func = __func__;
279 while ((rc = ERR_get_error_line_data(&file, &line, &data, &flags)))
280#endif
281 {
282 std::string msg;
283 msg.resize(1024, '\0');
284 ERR_error_string_n(rc, msg.data(), msg.size());
285 msg.resize(strlen(msg.data()), '\0');
286 if (flags & ERR_TXT_STRING) {
287 msg.append(" | ");
288 msg.append(data);
289 }
290 Common::Log::FmtLogMessage(Common::Log::Class::Service_SSL, Common::Log::Level::Error,
291 Common::Log::TrimSourcePath(file), line, func, "OpenSSL: {}",
292 msg);
293 }
294 return ResultInternalError;
295}
296
297void OneTimeInit() {
298 ssl_ctx = SSL_CTX_new(TLS_client_method());
299 if (!ssl_ctx) {
300 LOG_ERROR(Service_SSL, "SSL_CTX_new failed");
301 CheckOpenSSLErrors();
302 return;
303 }
304
305 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, nullptr);
306
307 if (!SSL_CTX_set_default_verify_paths(ssl_ctx)) {
308 LOG_ERROR(Service_SSL, "SSL_CTX_set_default_verify_paths failed");
309 CheckOpenSSLErrors();
310 return;
311 }
312
313 OneTimeInitLogFile();
314
315 if (!OneTimeInitBIO()) {
316 return;
317 }
318
319 one_time_init_success = true;
320}
321
322void OneTimeInitLogFile() {
323 const char* logfile = getenv("SSLKEYLOGFILE");
324 if (logfile) {
325 key_log_file.Open(logfile, FileAccessMode::Append, FileType::TextFile,
326 FileShareFlag::ShareWriteOnly);
327 if (key_log_file.IsOpen()) {
328 SSL_CTX_set_keylog_callback(ssl_ctx, &SSLConnectionBackendOpenSSL::KeyLogCallback);
329 } else {
330 LOG_CRITICAL(Service_SSL,
331 "SSLKEYLOGFILE was set but file could not be opened; not logging keys!");
332 }
333 }
334}
335
336bool OneTimeInitBIO() {
337 bio_meth =
338 BIO_meth_new(BIO_get_new_index() | BIO_TYPE_SOURCE_SINK, "SSLConnectionBackendOpenSSL");
339 if (!bio_meth ||
340 !BIO_meth_set_write_ex(bio_meth, &SSLConnectionBackendOpenSSL::WriteCallback) ||
341 !BIO_meth_set_read_ex(bio_meth, &SSLConnectionBackendOpenSSL::ReadCallback) ||
342 !BIO_meth_set_ctrl(bio_meth, &SSLConnectionBackendOpenSSL::CtrlCallback)) {
343 LOG_ERROR(Service_SSL, "Failed to create BIO_METHOD");
344 return false;
345 }
346 return true;
347}
348
349} // namespace
350
351} // namespace Service::SSL
diff --git a/src/core/hle/service/ssl/ssl_backend_schannel.cpp b/src/core/hle/service/ssl/ssl_backend_schannel.cpp
new file mode 100644
index 000000000..d8074339a
--- /dev/null
+++ b/src/core/hle/service/ssl/ssl_backend_schannel.cpp
@@ -0,0 +1,544 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <mutex>
5
6#include "common/error.h"
7#include "common/fs/file.h"
8#include "common/hex_util.h"
9#include "common/string_util.h"
10
11#include "core/hle/service/ssl/ssl_backend.h"
12#include "core/internal_network/network.h"
13#include "core/internal_network/sockets.h"
14
15namespace {
16
17// These includes are inside the namespace to avoid a conflict on MinGW where
18// the headers define an enum containing Network and Service as enumerators
19// (which clash with the correspondingly named namespaces).
20#define SECURITY_WIN32
21#include <schnlsp.h>
22#include <security.h>
23#include <wincrypt.h>
24
25std::once_flag one_time_init_flag;
26bool one_time_init_success = false;
27
28SCHANNEL_CRED schannel_cred{};
29CredHandle cred_handle;
30
31static void OneTimeInit() {
32 schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
33 schannel_cred.dwFlags =
34 SCH_USE_STRONG_CRYPTO | // don't allow insecure protocols
35 SCH_CRED_AUTO_CRED_VALIDATION | // validate certs
36 SCH_CRED_NO_DEFAULT_CREDS; // don't automatically present a client certificate
37 // ^ I'm assuming that nobody would want to connect Yuzu to a
38 // service that requires some OS-provided corporate client
39 // certificate, and presenting one to some arbitrary server
40 // might be a privacy concern? Who knows, though.
41
42 const SECURITY_STATUS ret =
43 AcquireCredentialsHandle(nullptr, const_cast<LPTSTR>(UNISP_NAME), SECPKG_CRED_OUTBOUND,
44 nullptr, &schannel_cred, nullptr, nullptr, &cred_handle, nullptr);
45 if (ret != SEC_E_OK) {
46 // SECURITY_STATUS codes are a type of HRESULT and can be used with NativeErrorToString.
47 LOG_ERROR(Service_SSL, "AcquireCredentialsHandle failed: {}",
48 Common::NativeErrorToString(ret));
49 return;
50 }
51
52 if (getenv("SSLKEYLOGFILE")) {
53 LOG_CRITICAL(Service_SSL, "SSLKEYLOGFILE was set but Schannel does not support exporting "
54 "keys; not logging keys!");
55 // Not fatal.
56 }
57
58 one_time_init_success = true;
59}
60
61} // namespace
62
63namespace Service::SSL {
64
65class SSLConnectionBackendSchannel final : public SSLConnectionBackend {
66public:
67 Result Init() {
68 std::call_once(one_time_init_flag, OneTimeInit);
69
70 if (!one_time_init_success) {
71 LOG_ERROR(
72 Service_SSL,
73 "Can't create SSL connection because Schannel one-time initialization failed");
74 return ResultInternalError;
75 }
76
77 return ResultSuccess;
78 }
79
80 void SetSocket(std::shared_ptr<Network::SocketBase> socket_in) override {
81 socket = std::move(socket_in);
82 }
83
84 Result SetHostName(const std::string& hostname_in) override {
85 hostname = hostname_in;
86 return ResultSuccess;
87 }
88
89 Result DoHandshake() override {
90 while (1) {
91 Result r;
92 switch (handshake_state) {
93 case HandshakeState::Initial:
94 if ((r = FlushCiphertextWriteBuf()) != ResultSuccess ||
95 (r = CallInitializeSecurityContext()) != ResultSuccess) {
96 return r;
97 }
98 // CallInitializeSecurityContext updated `handshake_state`.
99 continue;
100 case HandshakeState::ContinueNeeded:
101 case HandshakeState::IncompleteMessage:
102 if ((r = FlushCiphertextWriteBuf()) != ResultSuccess ||
103 (r = FillCiphertextReadBuf()) != ResultSuccess) {
104 return r;
105 }
106 if (ciphertext_read_buf.empty()) {
107 LOG_ERROR(Service_SSL, "SSL handshake failed because server hung up");
108 return ResultInternalError;
109 }
110 if ((r = CallInitializeSecurityContext()) != ResultSuccess) {
111 return r;
112 }
113 // CallInitializeSecurityContext updated `handshake_state`.
114 continue;
115 case HandshakeState::DoneAfterFlush:
116 if ((r = FlushCiphertextWriteBuf()) != ResultSuccess) {
117 return r;
118 }
119 handshake_state = HandshakeState::Connected;
120 return ResultSuccess;
121 case HandshakeState::Connected:
122 LOG_ERROR(Service_SSL, "Called DoHandshake but we already handshook");
123 return ResultInternalError;
124 case HandshakeState::Error:
125 return ResultInternalError;
126 }
127 }
128 }
129
130 Result FillCiphertextReadBuf() {
131 const size_t fill_size = read_buf_fill_size ? read_buf_fill_size : 4096;
132 read_buf_fill_size = 0;
133 // This unnecessarily zeroes the buffer; oh well.
134 const size_t offset = ciphertext_read_buf.size();
135 ASSERT_OR_EXECUTE(offset + fill_size >= offset, { return ResultInternalError; });
136 ciphertext_read_buf.resize(offset + fill_size, 0);
137 const auto read_span = std::span(ciphertext_read_buf).subspan(offset, fill_size);
138 const auto [actual, err] = socket->Recv(0, read_span);
139 switch (err) {
140 case Network::Errno::SUCCESS:
141 ASSERT(static_cast<size_t>(actual) <= fill_size);
142 ciphertext_read_buf.resize(offset + actual);
143 return ResultSuccess;
144 case Network::Errno::AGAIN:
145 ciphertext_read_buf.resize(offset);
146 return ResultWouldBlock;
147 default:
148 ciphertext_read_buf.resize(offset);
149 LOG_ERROR(Service_SSL, "Socket recv returned Network::Errno {}", err);
150 return ResultInternalError;
151 }
152 }
153
154 // Returns success if the write buffer has been completely emptied.
155 Result FlushCiphertextWriteBuf() {
156 while (!ciphertext_write_buf.empty()) {
157 const auto [actual, err] = socket->Send(ciphertext_write_buf, 0);
158 switch (err) {
159 case Network::Errno::SUCCESS:
160 ASSERT(static_cast<size_t>(actual) <= ciphertext_write_buf.size());
161 ciphertext_write_buf.erase(ciphertext_write_buf.begin(),
162 ciphertext_write_buf.begin() + actual);
163 break;
164 case Network::Errno::AGAIN:
165 return ResultWouldBlock;
166 default:
167 LOG_ERROR(Service_SSL, "Socket send returned Network::Errno {}", err);
168 return ResultInternalError;
169 }
170 }
171 return ResultSuccess;
172 }
173
174 Result CallInitializeSecurityContext() {
175 const unsigned long req = ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_CONFIDENTIALITY |
176 ISC_REQ_INTEGRITY | ISC_REQ_REPLAY_DETECT |
177 ISC_REQ_SEQUENCE_DETECT | ISC_REQ_STREAM |
178 ISC_REQ_USE_SUPPLIED_CREDS;
179 unsigned long attr;
180 // https://learn.microsoft.com/en-us/windows/win32/secauthn/initializesecuritycontext--schannel
181 std::array<SecBuffer, 2> input_buffers{{
182 // only used if `initial_call_done`
183 {
184 // [0]
185 .cbBuffer = static_cast<unsigned long>(ciphertext_read_buf.size()),
186 .BufferType = SECBUFFER_TOKEN,
187 .pvBuffer = ciphertext_read_buf.data(),
188 },
189 {
190 // [1] (will be replaced by SECBUFFER_MISSING when SEC_E_INCOMPLETE_MESSAGE is
191 // returned, or SECBUFFER_EXTRA when SEC_E_CONTINUE_NEEDED is returned if the
192 // whole buffer wasn't used)
193 .cbBuffer = 0,
194 .BufferType = SECBUFFER_EMPTY,
195 .pvBuffer = nullptr,
196 },
197 }};
198 std::array<SecBuffer, 2> output_buffers{{
199 {
200 .cbBuffer = 0,
201 .BufferType = SECBUFFER_TOKEN,
202 .pvBuffer = nullptr,
203 }, // [0]
204 {
205 .cbBuffer = 0,
206 .BufferType = SECBUFFER_ALERT,
207 .pvBuffer = nullptr,
208 }, // [1]
209 }};
210 SecBufferDesc input_desc{
211 .ulVersion = SECBUFFER_VERSION,
212 .cBuffers = static_cast<unsigned long>(input_buffers.size()),
213 .pBuffers = input_buffers.data(),
214 };
215 SecBufferDesc output_desc{
216 .ulVersion = SECBUFFER_VERSION,
217 .cBuffers = static_cast<unsigned long>(output_buffers.size()),
218 .pBuffers = output_buffers.data(),
219 };
220 ASSERT_OR_EXECUTE_MSG(
221 input_buffers[0].cbBuffer == ciphertext_read_buf.size(),
222 { return ResultInternalError; }, "read buffer too large");
223
224 bool initial_call_done = handshake_state != HandshakeState::Initial;
225 if (initial_call_done) {
226 LOG_DEBUG(Service_SSL, "Passing {} bytes into InitializeSecurityContext",
227 ciphertext_read_buf.size());
228 }
229
230 const SECURITY_STATUS ret =
231 InitializeSecurityContextA(&cred_handle, initial_call_done ? &ctxt : nullptr,
232 // Caller ensured we have set a hostname:
233 const_cast<char*>(hostname.value().c_str()), req,
234 0, // Reserved1
235 0, // TargetDataRep not used with Schannel
236 initial_call_done ? &input_desc : nullptr,
237 0, // Reserved2
238 initial_call_done ? nullptr : &ctxt, &output_desc, &attr,
239 nullptr); // ptsExpiry
240
241 if (output_buffers[0].pvBuffer) {
242 const std::span span(static_cast<u8*>(output_buffers[0].pvBuffer),
243 output_buffers[0].cbBuffer);
244 ciphertext_write_buf.insert(ciphertext_write_buf.end(), span.begin(), span.end());
245 FreeContextBuffer(output_buffers[0].pvBuffer);
246 }
247
248 if (output_buffers[1].pvBuffer) {
249 const std::span span(static_cast<u8*>(output_buffers[1].pvBuffer),
250 output_buffers[1].cbBuffer);
251 // The documentation doesn't explain what format this data is in.
252 LOG_DEBUG(Service_SSL, "Got a {}-byte alert buffer: {}", span.size(),
253 Common::HexToString(span));
254 }
255
256 switch (ret) {
257 case SEC_I_CONTINUE_NEEDED:
258 LOG_DEBUG(Service_SSL, "InitializeSecurityContext => SEC_I_CONTINUE_NEEDED");
259 if (input_buffers[1].BufferType == SECBUFFER_EXTRA) {
260 LOG_DEBUG(Service_SSL, "EXTRA of size {}", input_buffers[1].cbBuffer);
261 ASSERT(input_buffers[1].cbBuffer <= ciphertext_read_buf.size());
262 ciphertext_read_buf.erase(ciphertext_read_buf.begin(),
263 ciphertext_read_buf.end() - input_buffers[1].cbBuffer);
264 } else {
265 ASSERT(input_buffers[1].BufferType == SECBUFFER_EMPTY);
266 ciphertext_read_buf.clear();
267 }
268 handshake_state = HandshakeState::ContinueNeeded;
269 return ResultSuccess;
270 case SEC_E_INCOMPLETE_MESSAGE:
271 LOG_DEBUG(Service_SSL, "InitializeSecurityContext => SEC_E_INCOMPLETE_MESSAGE");
272 ASSERT(input_buffers[1].BufferType == SECBUFFER_MISSING);
273 read_buf_fill_size = input_buffers[1].cbBuffer;
274 handshake_state = HandshakeState::IncompleteMessage;
275 return ResultSuccess;
276 case SEC_E_OK:
277 LOG_DEBUG(Service_SSL, "InitializeSecurityContext => SEC_E_OK");
278 ciphertext_read_buf.clear();
279 handshake_state = HandshakeState::DoneAfterFlush;
280 return GrabStreamSizes();
281 default:
282 LOG_ERROR(Service_SSL,
283 "InitializeSecurityContext failed (probably certificate/protocol issue): {}",
284 Common::NativeErrorToString(ret));
285 handshake_state = HandshakeState::Error;
286 return ResultInternalError;
287 }
288 }
289
290 Result GrabStreamSizes() {
291 const SECURITY_STATUS ret =
292 QueryContextAttributes(&ctxt, SECPKG_ATTR_STREAM_SIZES, &stream_sizes);
293 if (ret != SEC_E_OK) {
294 LOG_ERROR(Service_SSL, "QueryContextAttributes(SECPKG_ATTR_STREAM_SIZES) failed: {}",
295 Common::NativeErrorToString(ret));
296 handshake_state = HandshakeState::Error;
297 return ResultInternalError;
298 }
299 return ResultSuccess;
300 }
301
302 ResultVal<size_t> Read(std::span<u8> data) override {
303 if (handshake_state != HandshakeState::Connected) {
304 LOG_ERROR(Service_SSL, "Called Read but we did not successfully handshake");
305 return ResultInternalError;
306 }
307 if (data.size() == 0 || got_read_eof) {
308 return size_t(0);
309 }
310 while (1) {
311 if (!cleartext_read_buf.empty()) {
312 const size_t read_size = std::min(cleartext_read_buf.size(), data.size());
313 std::memcpy(data.data(), cleartext_read_buf.data(), read_size);
314 cleartext_read_buf.erase(cleartext_read_buf.begin(),
315 cleartext_read_buf.begin() + read_size);
316 return read_size;
317 }
318 if (!ciphertext_read_buf.empty()) {
319 SecBuffer empty{
320 .cbBuffer = 0,
321 .BufferType = SECBUFFER_EMPTY,
322 .pvBuffer = nullptr,
323 };
324 std::array<SecBuffer, 5> buffers{{
325 {
326 .cbBuffer = static_cast<unsigned long>(ciphertext_read_buf.size()),
327 .BufferType = SECBUFFER_DATA,
328 .pvBuffer = ciphertext_read_buf.data(),
329 },
330 empty,
331 empty,
332 empty,
333 }};
334 ASSERT_OR_EXECUTE_MSG(
335 buffers[0].cbBuffer == ciphertext_read_buf.size(),
336 { return ResultInternalError; }, "read buffer too large");
337 SecBufferDesc desc{
338 .ulVersion = SECBUFFER_VERSION,
339 .cBuffers = static_cast<unsigned long>(buffers.size()),
340 .pBuffers = buffers.data(),
341 };
342 SECURITY_STATUS ret =
343 DecryptMessage(&ctxt, &desc, /*MessageSeqNo*/ 0, /*pfQOP*/ nullptr);
344 switch (ret) {
345 case SEC_E_OK:
346 ASSERT_OR_EXECUTE(buffers[0].BufferType == SECBUFFER_STREAM_HEADER,
347 { return ResultInternalError; });
348 ASSERT_OR_EXECUTE(buffers[1].BufferType == SECBUFFER_DATA,
349 { return ResultInternalError; });
350 ASSERT_OR_EXECUTE(buffers[2].BufferType == SECBUFFER_STREAM_TRAILER,
351 { return ResultInternalError; });
352 cleartext_read_buf.assign(static_cast<u8*>(buffers[1].pvBuffer),
353 static_cast<u8*>(buffers[1].pvBuffer) +
354 buffers[1].cbBuffer);
355 if (buffers[3].BufferType == SECBUFFER_EXTRA) {
356 ASSERT(buffers[3].cbBuffer <= ciphertext_read_buf.size());
357 ciphertext_read_buf.erase(ciphertext_read_buf.begin(),
358 ciphertext_read_buf.end() - buffers[3].cbBuffer);
359 } else {
360 ASSERT(buffers[3].BufferType == SECBUFFER_EMPTY);
361 ciphertext_read_buf.clear();
362 }
363 continue;
364 case SEC_E_INCOMPLETE_MESSAGE:
365 break;
366 case SEC_I_CONTEXT_EXPIRED:
367 // Server hung up by sending close_notify.
368 got_read_eof = true;
369 return size_t(0);
370 default:
371 LOG_ERROR(Service_SSL, "DecryptMessage failed: {}",
372 Common::NativeErrorToString(ret));
373 return ResultInternalError;
374 }
375 }
376 const Result r = FillCiphertextReadBuf();
377 if (r != ResultSuccess) {
378 return r;
379 }
380 if (ciphertext_read_buf.empty()) {
381 got_read_eof = true;
382 return size_t(0);
383 }
384 }
385 }
386
387 ResultVal<size_t> Write(std::span<const u8> data) override {
388 if (handshake_state != HandshakeState::Connected) {
389 LOG_ERROR(Service_SSL, "Called Write but we did not successfully handshake");
390 return ResultInternalError;
391 }
392 if (data.size() == 0) {
393 return size_t(0);
394 }
395 data = data.subspan(0, std::min<size_t>(data.size(), stream_sizes.cbMaximumMessage));
396 if (!cleartext_write_buf.empty()) {
397 // Already in the middle of a write. It wouldn't make sense to not
398 // finish sending the entire buffer since TLS has
399 // header/MAC/padding/etc.
400 if (data.size() != cleartext_write_buf.size() ||
401 std::memcmp(data.data(), cleartext_write_buf.data(), data.size())) {
402 LOG_ERROR(Service_SSL, "Called Write but buffer does not match previous buffer");
403 return ResultInternalError;
404 }
405 return WriteAlreadyEncryptedData();
406 } else {
407 cleartext_write_buf.assign(data.begin(), data.end());
408 }
409
410 std::vector<u8> header_buf(stream_sizes.cbHeader, 0);
411 std::vector<u8> tmp_data_buf = cleartext_write_buf;
412 std::vector<u8> trailer_buf(stream_sizes.cbTrailer, 0);
413
414 std::array<SecBuffer, 3> buffers{{
415 {
416 .cbBuffer = stream_sizes.cbHeader,
417 .BufferType = SECBUFFER_STREAM_HEADER,
418 .pvBuffer = header_buf.data(),
419 },
420 {
421 .cbBuffer = static_cast<unsigned long>(tmp_data_buf.size()),
422 .BufferType = SECBUFFER_DATA,
423 .pvBuffer = tmp_data_buf.data(),
424 },
425 {
426 .cbBuffer = stream_sizes.cbTrailer,
427 .BufferType = SECBUFFER_STREAM_TRAILER,
428 .pvBuffer = trailer_buf.data(),
429 },
430 }};
431 ASSERT_OR_EXECUTE_MSG(
432 buffers[1].cbBuffer == tmp_data_buf.size(), { return ResultInternalError; },
433 "temp buffer too large");
434 SecBufferDesc desc{
435 .ulVersion = SECBUFFER_VERSION,
436 .cBuffers = static_cast<unsigned long>(buffers.size()),
437 .pBuffers = buffers.data(),
438 };
439
440 const SECURITY_STATUS ret = EncryptMessage(&ctxt, /*fQOP*/ 0, &desc, /*MessageSeqNo*/ 0);
441 if (ret != SEC_E_OK) {
442 LOG_ERROR(Service_SSL, "EncryptMessage failed: {}", Common::NativeErrorToString(ret));
443 return ResultInternalError;
444 }
445 ciphertext_write_buf.insert(ciphertext_write_buf.end(), header_buf.begin(),
446 header_buf.end());
447 ciphertext_write_buf.insert(ciphertext_write_buf.end(), tmp_data_buf.begin(),
448 tmp_data_buf.end());
449 ciphertext_write_buf.insert(ciphertext_write_buf.end(), trailer_buf.begin(),
450 trailer_buf.end());
451 return WriteAlreadyEncryptedData();
452 }
453
454 ResultVal<size_t> WriteAlreadyEncryptedData() {
455 const Result r = FlushCiphertextWriteBuf();
456 if (r != ResultSuccess) {
457 return r;
458 }
459 // write buf is empty
460 const size_t cleartext_bytes_written = cleartext_write_buf.size();
461 cleartext_write_buf.clear();
462 return cleartext_bytes_written;
463 }
464
465 ResultVal<std::vector<std::vector<u8>>> GetServerCerts() override {
466 PCCERT_CONTEXT returned_cert = nullptr;
467 const SECURITY_STATUS ret =
468 QueryContextAttributes(&ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &returned_cert);
469 if (ret != SEC_E_OK) {
470 LOG_ERROR(Service_SSL,
471 "QueryContextAttributes(SECPKG_ATTR_REMOTE_CERT_CONTEXT) failed: {}",
472 Common::NativeErrorToString(ret));
473 return ResultInternalError;
474 }
475 PCCERT_CONTEXT some_cert = nullptr;
476 std::vector<std::vector<u8>> certs;
477 while ((some_cert = CertEnumCertificatesInStore(returned_cert->hCertStore, some_cert))) {
478 certs.emplace_back(static_cast<u8*>(some_cert->pbCertEncoded),
479 static_cast<u8*>(some_cert->pbCertEncoded) +
480 some_cert->cbCertEncoded);
481 }
482 std::reverse(certs.begin(),
483 certs.end()); // Windows returns certs in reverse order from what we want
484 CertFreeCertificateContext(returned_cert);
485 return certs;
486 }
487
488 ~SSLConnectionBackendSchannel() {
489 if (handshake_state != HandshakeState::Initial) {
490 DeleteSecurityContext(&ctxt);
491 }
492 }
493
494 enum class HandshakeState {
495 // Haven't called anything yet.
496 Initial,
497 // `SEC_I_CONTINUE_NEEDED` was returned by
498 // `InitializeSecurityContext`; must finish sending data (if any) in
499 // the write buffer, then read at least one byte before calling
500 // `InitializeSecurityContext` again.
501 ContinueNeeded,
502 // `SEC_E_INCOMPLETE_MESSAGE` was returned by
503 // `InitializeSecurityContext`; hopefully the write buffer is empty;
504 // must read at least one byte before calling
505 // `InitializeSecurityContext` again.
506 IncompleteMessage,
507 // `SEC_E_OK` was returned by `InitializeSecurityContext`; must
508 // finish sending data in the write buffer before having `DoHandshake`
509 // report success.
510 DoneAfterFlush,
511 // We finished the above and are now connected. At this point, writing
512 // and reading are separate 'state machines' represented by the
513 // nonemptiness of the ciphertext and cleartext read and write buffers.
514 Connected,
515 // Another error was returned and we shouldn't allow initialization
516 // to continue.
517 Error,
518 } handshake_state = HandshakeState::Initial;
519
520 CtxtHandle ctxt;
521 SecPkgContext_StreamSizes stream_sizes;
522
523 std::shared_ptr<Network::SocketBase> socket;
524 std::optional<std::string> hostname;
525
526 std::vector<u8> ciphertext_read_buf;
527 std::vector<u8> ciphertext_write_buf;
528 std::vector<u8> cleartext_read_buf;
529 std::vector<u8> cleartext_write_buf;
530
531 bool got_read_eof = false;
532 size_t read_buf_fill_size = 0;
533};
534
535ResultVal<std::unique_ptr<SSLConnectionBackend>> CreateSSLConnectionBackend() {
536 auto conn = std::make_unique<SSLConnectionBackendSchannel>();
537 const Result res = conn->Init();
538 if (res.IsFailure()) {
539 return res;
540 }
541 return conn;
542}
543
544} // namespace Service::SSL
diff --git a/src/core/hle/service/ssl/ssl_backend_securetransport.cpp b/src/core/hle/service/ssl/ssl_backend_securetransport.cpp
new file mode 100644
index 000000000..b3083cbad
--- /dev/null
+++ b/src/core/hle/service/ssl/ssl_backend_securetransport.cpp
@@ -0,0 +1,222 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <mutex>
5
6// SecureTransport has been deprecated in its entirety in favor of
7// Network.framework, but that does not allow layering TLS on top of an
8// arbitrary socket.
9#if defined(__GNUC__) || defined(__clang__)
10#pragma GCC diagnostic push
11#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
12#include <Security/SecureTransport.h>
13#pragma GCC diagnostic pop
14#endif
15
16#include "core/hle/service/ssl/ssl_backend.h"
17#include "core/internal_network/network.h"
18#include "core/internal_network/sockets.h"
19
20namespace {
21
22template <typename T>
23struct CFReleaser {
24 T ptr;
25
26 YUZU_NON_COPYABLE(CFReleaser);
27 constexpr CFReleaser() : ptr(nullptr) {}
28 constexpr CFReleaser(T ptr) : ptr(ptr) {}
29 constexpr operator T() {
30 return ptr;
31 }
32 ~CFReleaser() {
33 if (ptr) {
34 CFRelease(ptr);
35 }
36 }
37};
38
39std::string CFStringToString(CFStringRef cfstr) {
40 CFReleaser<CFDataRef> cfdata(
41 CFStringCreateExternalRepresentation(nullptr, cfstr, kCFStringEncodingUTF8, 0));
42 ASSERT_OR_EXECUTE(cfdata, { return "???"; });
43 return std::string(reinterpret_cast<const char*>(CFDataGetBytePtr(cfdata)),
44 CFDataGetLength(cfdata));
45}
46
47std::string OSStatusToString(OSStatus status) {
48 CFReleaser<CFStringRef> cfstr(SecCopyErrorMessageString(status, nullptr));
49 if (!cfstr) {
50 return "[unknown error]";
51 }
52 return CFStringToString(cfstr);
53}
54
55} // namespace
56
57namespace Service::SSL {
58
59class SSLConnectionBackendSecureTransport final : public SSLConnectionBackend {
60public:
61 Result Init() {
62 static std::once_flag once_flag;
63 std::call_once(once_flag, []() {
64 if (getenv("SSLKEYLOGFILE")) {
65 LOG_CRITICAL(Service_SSL, "SSLKEYLOGFILE was set but SecureTransport does not "
66 "support exporting keys; not logging keys!");
67 // Not fatal.
68 }
69 });
70
71 context.ptr = SSLCreateContext(nullptr, kSSLClientSide, kSSLStreamType);
72 if (!context) {
73 LOG_ERROR(Service_SSL, "SSLCreateContext failed");
74 return ResultInternalError;
75 }
76
77 OSStatus status;
78 if ((status = SSLSetIOFuncs(context, ReadCallback, WriteCallback)) ||
79 (status = SSLSetConnection(context, this))) {
80 LOG_ERROR(Service_SSL, "SSLContext initialization failed: {}",
81 OSStatusToString(status));
82 return ResultInternalError;
83 }
84
85 return ResultSuccess;
86 }
87
88 void SetSocket(std::shared_ptr<Network::SocketBase> in_socket) override {
89 socket = std::move(in_socket);
90 }
91
92 Result SetHostName(const std::string& hostname) override {
93 OSStatus status = SSLSetPeerDomainName(context, hostname.c_str(), hostname.size());
94 if (status) {
95 LOG_ERROR(Service_SSL, "SSLSetPeerDomainName failed: {}", OSStatusToString(status));
96 return ResultInternalError;
97 }
98 return ResultSuccess;
99 }
100
101 Result DoHandshake() override {
102 OSStatus status = SSLHandshake(context);
103 return HandleReturn("SSLHandshake", 0, status).Code();
104 }
105
106 ResultVal<size_t> Read(std::span<u8> data) override {
107 size_t actual;
108 OSStatus status = SSLRead(context, data.data(), data.size(), &actual);
109 ;
110 return HandleReturn("SSLRead", actual, status);
111 }
112
113 ResultVal<size_t> Write(std::span<const u8> data) override {
114 size_t actual;
115 OSStatus status = SSLWrite(context, data.data(), data.size(), &actual);
116 ;
117 return HandleReturn("SSLWrite", actual, status);
118 }
119
120 ResultVal<size_t> HandleReturn(const char* what, size_t actual, OSStatus status) {
121 switch (status) {
122 case 0:
123 return actual;
124 case errSSLWouldBlock:
125 return ResultWouldBlock;
126 default: {
127 std::string reason;
128 if (got_read_eof) {
129 reason = "server hung up";
130 } else {
131 reason = OSStatusToString(status);
132 }
133 LOG_ERROR(Service_SSL, "{} failed: {}", what, reason);
134 return ResultInternalError;
135 }
136 }
137 }
138
139 ResultVal<std::vector<std::vector<u8>>> GetServerCerts() override {
140 CFReleaser<SecTrustRef> trust;
141 OSStatus status = SSLCopyPeerTrust(context, &trust.ptr);
142 if (status) {
143 LOG_ERROR(Service_SSL, "SSLCopyPeerTrust failed: {}", OSStatusToString(status));
144 return ResultInternalError;
145 }
146 std::vector<std::vector<u8>> ret;
147 for (CFIndex i = 0, count = SecTrustGetCertificateCount(trust); i < count; i++) {
148 SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, i);
149 CFReleaser<CFDataRef> data(SecCertificateCopyData(cert));
150 ASSERT_OR_EXECUTE(data, { return ResultInternalError; });
151 const u8* ptr = CFDataGetBytePtr(data);
152 ret.emplace_back(ptr, ptr + CFDataGetLength(data));
153 }
154 return ret;
155 }
156
157 static OSStatus ReadCallback(SSLConnectionRef connection, void* data, size_t* dataLength) {
158 return ReadOrWriteCallback(connection, data, dataLength, true);
159 }
160
161 static OSStatus WriteCallback(SSLConnectionRef connection, const void* data,
162 size_t* dataLength) {
163 return ReadOrWriteCallback(connection, const_cast<void*>(data), dataLength, false);
164 }
165
166 static OSStatus ReadOrWriteCallback(SSLConnectionRef connection, void* data, size_t* dataLength,
167 bool is_read) {
168 auto self =
169 static_cast<SSLConnectionBackendSecureTransport*>(const_cast<void*>(connection));
170 ASSERT_OR_EXECUTE_MSG(
171 self->socket, { return 0; }, "SecureTransport asked to {} but we have no socket",
172 is_read ? "read" : "write");
173
174 // SecureTransport callbacks (unlike OpenSSL BIO callbacks) are
175 // expected to read/write the full requested dataLength or return an
176 // error, so we have to add a loop ourselves.
177 size_t requested_len = *dataLength;
178 size_t offset = 0;
179 while (offset < requested_len) {
180 std::span cur(reinterpret_cast<u8*>(data) + offset, requested_len - offset);
181 auto [actual, err] = is_read ? self->socket->Recv(0, cur) : self->socket->Send(cur, 0);
182 LOG_CRITICAL(Service_SSL, "op={}, offset={} actual={}/{} err={}", is_read, offset,
183 actual, cur.size(), static_cast<s32>(err));
184 switch (err) {
185 case Network::Errno::SUCCESS:
186 offset += actual;
187 if (actual == 0) {
188 ASSERT(is_read);
189 self->got_read_eof = true;
190 return errSecEndOfData;
191 }
192 break;
193 case Network::Errno::AGAIN:
194 *dataLength = offset;
195 return errSSLWouldBlock;
196 default:
197 LOG_ERROR(Service_SSL, "Socket {} returned Network::Errno {}",
198 is_read ? "recv" : "send", err);
199 return errSecIO;
200 }
201 }
202 ASSERT(offset == requested_len);
203 return 0;
204 }
205
206private:
207 CFReleaser<SSLContextRef> context = nullptr;
208 bool got_read_eof = false;
209
210 std::shared_ptr<Network::SocketBase> socket;
211};
212
213ResultVal<std::unique_ptr<SSLConnectionBackend>> CreateSSLConnectionBackend() {
214 auto conn = std::make_unique<SSLConnectionBackendSecureTransport>();
215 const Result res = conn->Init();
216 if (res.IsFailure()) {
217 return res;
218 }
219 return conn;
220}
221
222} // namespace Service::SSL
diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp
index 75ac10a9c..28f89c599 100644
--- a/src/core/internal_network/network.cpp
+++ b/src/core/internal_network/network.cpp
@@ -27,6 +27,7 @@
27 27
28#include "common/assert.h" 28#include "common/assert.h"
29#include "common/common_types.h" 29#include "common/common_types.h"
30#include "common/expected.h"
30#include "common/logging/log.h" 31#include "common/logging/log.h"
31#include "common/settings.h" 32#include "common/settings.h"
32#include "core/internal_network/network.h" 33#include "core/internal_network/network.h"
@@ -97,6 +98,8 @@ bool EnableNonBlock(SOCKET fd, bool enable) {
97 98
98Errno TranslateNativeError(int e) { 99Errno TranslateNativeError(int e) {
99 switch (e) { 100 switch (e) {
101 case 0:
102 return Errno::SUCCESS;
100 case WSAEBADF: 103 case WSAEBADF:
101 return Errno::BADF; 104 return Errno::BADF;
102 case WSAEINVAL: 105 case WSAEINVAL:
@@ -121,6 +124,8 @@ Errno TranslateNativeError(int e) {
121 return Errno::MSGSIZE; 124 return Errno::MSGSIZE;
122 case WSAETIMEDOUT: 125 case WSAETIMEDOUT:
123 return Errno::TIMEDOUT; 126 return Errno::TIMEDOUT;
127 case WSAEINPROGRESS:
128 return Errno::INPROGRESS;
124 default: 129 default:
125 UNIMPLEMENTED_MSG("Unimplemented errno={}", e); 130 UNIMPLEMENTED_MSG("Unimplemented errno={}", e);
126 return Errno::OTHER; 131 return Errno::OTHER;
@@ -195,6 +200,8 @@ bool EnableNonBlock(int fd, bool enable) {
195 200
196Errno TranslateNativeError(int e) { 201Errno TranslateNativeError(int e) {
197 switch (e) { 202 switch (e) {
203 case 0:
204 return Errno::SUCCESS;
198 case EBADF: 205 case EBADF:
199 return Errno::BADF; 206 return Errno::BADF;
200 case EINVAL: 207 case EINVAL:
@@ -219,8 +226,10 @@ Errno TranslateNativeError(int e) {
219 return Errno::MSGSIZE; 226 return Errno::MSGSIZE;
220 case ETIMEDOUT: 227 case ETIMEDOUT:
221 return Errno::TIMEDOUT; 228 return Errno::TIMEDOUT;
229 case EINPROGRESS:
230 return Errno::INPROGRESS;
222 default: 231 default:
223 UNIMPLEMENTED_MSG("Unimplemented errno={}", e); 232 UNIMPLEMENTED_MSG("Unimplemented errno={} ({})", e, strerror(e));
224 return Errno::OTHER; 233 return Errno::OTHER;
225 } 234 }
226} 235}
@@ -234,15 +243,84 @@ Errno GetAndLogLastError() {
234 int e = errno; 243 int e = errno;
235#endif 244#endif
236 const Errno err = TranslateNativeError(e); 245 const Errno err = TranslateNativeError(e);
237 if (err == Errno::AGAIN || err == Errno::TIMEDOUT) { 246 if (err == Errno::AGAIN || err == Errno::TIMEDOUT || err == Errno::INPROGRESS) {
247 // These happen during normal operation, so only log them at debug level.
248 LOG_DEBUG(Network, "Socket operation error: {}", Common::NativeErrorToString(e));
238 return err; 249 return err;
239 } 250 }
240 LOG_ERROR(Network, "Socket operation error: {}", Common::NativeErrorToString(e)); 251 LOG_ERROR(Network, "Socket operation error: {}", Common::NativeErrorToString(e));
241 return err; 252 return err;
242} 253}
243 254
244int TranslateDomain(Domain domain) { 255GetAddrInfoError TranslateGetAddrInfoErrorFromNative(int gai_err) {
256 switch (gai_err) {
257 case 0:
258 return GetAddrInfoError::SUCCESS;
259#ifdef EAI_ADDRFAMILY
260 case EAI_ADDRFAMILY:
261 return GetAddrInfoError::ADDRFAMILY;
262#endif
263 case EAI_AGAIN:
264 return GetAddrInfoError::AGAIN;
265 case EAI_BADFLAGS:
266 return GetAddrInfoError::BADFLAGS;
267 case EAI_FAIL:
268 return GetAddrInfoError::FAIL;
269 case EAI_FAMILY:
270 return GetAddrInfoError::FAMILY;
271 case EAI_MEMORY:
272 return GetAddrInfoError::MEMORY;
273 case EAI_NONAME:
274 return GetAddrInfoError::NONAME;
275 case EAI_SERVICE:
276 return GetAddrInfoError::SERVICE;
277 case EAI_SOCKTYPE:
278 return GetAddrInfoError::SOCKTYPE;
279 // These codes may not be defined on all systems:
280#ifdef EAI_SYSTEM
281 case EAI_SYSTEM:
282 return GetAddrInfoError::SYSTEM;
283#endif
284#ifdef EAI_BADHINTS
285 case EAI_BADHINTS:
286 return GetAddrInfoError::BADHINTS;
287#endif
288#ifdef EAI_PROTOCOL
289 case EAI_PROTOCOL:
290 return GetAddrInfoError::PROTOCOL;
291#endif
292#ifdef EAI_OVERFLOW
293 case EAI_OVERFLOW:
294 return GetAddrInfoError::OVERFLOW_;
295#endif
296 default:
297#ifdef EAI_NODATA
298 // This can't be a case statement because it would create a duplicate
299 // case on Windows where EAI_NODATA is an alias for EAI_NONAME.
300 if (gai_err == EAI_NODATA) {
301 return GetAddrInfoError::NODATA;
302 }
303#endif
304 return GetAddrInfoError::OTHER;
305 }
306}
307
308Domain TranslateDomainFromNative(int domain) {
309 switch (domain) {
310 case 0:
311 return Domain::Unspecified;
312 case AF_INET:
313 return Domain::INET;
314 default:
315 UNIMPLEMENTED_MSG("Unhandled domain={}", domain);
316 return Domain::INET;
317 }
318}
319
320int TranslateDomainToNative(Domain domain) {
245 switch (domain) { 321 switch (domain) {
322 case Domain::Unspecified:
323 return 0;
246 case Domain::INET: 324 case Domain::INET:
247 return AF_INET; 325 return AF_INET;
248 default: 326 default:
@@ -251,20 +329,58 @@ int TranslateDomain(Domain domain) {
251 } 329 }
252} 330}
253 331
254int TranslateType(Type type) { 332Type TranslateTypeFromNative(int type) {
333 switch (type) {
334 case 0:
335 return Type::Unspecified;
336 case SOCK_STREAM:
337 return Type::STREAM;
338 case SOCK_DGRAM:
339 return Type::DGRAM;
340 case SOCK_RAW:
341 return Type::RAW;
342 case SOCK_SEQPACKET:
343 return Type::SEQPACKET;
344 default:
345 UNIMPLEMENTED_MSG("Unimplemented type={}", type);
346 return Type::STREAM;
347 }
348}
349
350int TranslateTypeToNative(Type type) {
255 switch (type) { 351 switch (type) {
352 case Type::Unspecified:
353 return 0;
256 case Type::STREAM: 354 case Type::STREAM:
257 return SOCK_STREAM; 355 return SOCK_STREAM;
258 case Type::DGRAM: 356 case Type::DGRAM:
259 return SOCK_DGRAM; 357 return SOCK_DGRAM;
358 case Type::RAW:
359 return SOCK_RAW;
260 default: 360 default:
261 UNIMPLEMENTED_MSG("Unimplemented type={}", type); 361 UNIMPLEMENTED_MSG("Unimplemented type={}", type);
262 return 0; 362 return 0;
263 } 363 }
264} 364}
265 365
266int TranslateProtocol(Protocol protocol) { 366Protocol TranslateProtocolFromNative(int protocol) {
367 switch (protocol) {
368 case 0:
369 return Protocol::Unspecified;
370 case IPPROTO_TCP:
371 return Protocol::TCP;
372 case IPPROTO_UDP:
373 return Protocol::UDP;
374 default:
375 UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
376 return Protocol::Unspecified;
377 }
378}
379
380int TranslateProtocolToNative(Protocol protocol) {
267 switch (protocol) { 381 switch (protocol) {
382 case Protocol::Unspecified:
383 return 0;
268 case Protocol::TCP: 384 case Protocol::TCP:
269 return IPPROTO_TCP; 385 return IPPROTO_TCP;
270 case Protocol::UDP: 386 case Protocol::UDP:
@@ -275,21 +391,10 @@ int TranslateProtocol(Protocol protocol) {
275 } 391 }
276} 392}
277 393
278SockAddrIn TranslateToSockAddrIn(sockaddr input_) { 394SockAddrIn TranslateToSockAddrIn(sockaddr_in input, size_t input_len) {
279 sockaddr_in input;
280 std::memcpy(&input, &input_, sizeof(input));
281
282 SockAddrIn result; 395 SockAddrIn result;
283 396
284 switch (input.sin_family) { 397 result.family = TranslateDomainFromNative(input.sin_family);
285 case AF_INET:
286 result.family = Domain::INET;
287 break;
288 default:
289 UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.sin_family);
290 result.family = Domain::INET;
291 break;
292 }
293 398
294 result.portno = ntohs(input.sin_port); 399 result.portno = ntohs(input.sin_port);
295 400
@@ -301,22 +406,33 @@ SockAddrIn TranslateToSockAddrIn(sockaddr input_) {
301short TranslatePollEvents(PollEvents events) { 406short TranslatePollEvents(PollEvents events) {
302 short result = 0; 407 short result = 0;
303 408
304 if (True(events & PollEvents::In)) { 409 const auto translate = [&result, &events](PollEvents guest, short host) {
305 events &= ~PollEvents::In; 410 if (True(events & guest)) {
306 result |= POLLIN; 411 events &= ~guest;
307 } 412 result |= host;
308 if (True(events & PollEvents::Pri)) { 413 }
309 events &= ~PollEvents::Pri; 414 };
415
416 translate(PollEvents::In, POLLIN);
417 translate(PollEvents::Pri, POLLPRI);
418 translate(PollEvents::Out, POLLOUT);
419 translate(PollEvents::Err, POLLERR);
420 translate(PollEvents::Hup, POLLHUP);
421 translate(PollEvents::Nval, POLLNVAL);
422 translate(PollEvents::RdNorm, POLLRDNORM);
423 translate(PollEvents::RdBand, POLLRDBAND);
424 translate(PollEvents::WrBand, POLLWRBAND);
425
310#ifdef _WIN32 426#ifdef _WIN32
311 LOG_WARNING(Service, "Winsock doesn't support POLLPRI"); 427 short allowed_events = POLLRDBAND | POLLRDNORM | POLLWRNORM;
312#else 428 // Unlike poll on other OSes, WSAPoll will complain if any other flags are set on input.
313 result |= POLLPRI; 429 if (result & ~allowed_events) {
430 LOG_DEBUG(Network,
431 "Removing WSAPoll input events 0x{:x} because Windows doesn't support them",
432 result & ~allowed_events);
433 }
434 result &= allowed_events;
314#endif 435#endif
315 }
316 if (True(events & PollEvents::Out)) {
317 events &= ~PollEvents::Out;
318 result |= POLLOUT;
319 }
320 436
321 UNIMPLEMENTED_IF_MSG((u16)events != 0, "Unhandled guest events=0x{:x}", (u16)events); 437 UNIMPLEMENTED_IF_MSG((u16)events != 0, "Unhandled guest events=0x{:x}", (u16)events);
322 438
@@ -337,6 +453,10 @@ PollEvents TranslatePollRevents(short revents) {
337 translate(POLLOUT, PollEvents::Out); 453 translate(POLLOUT, PollEvents::Out);
338 translate(POLLERR, PollEvents::Err); 454 translate(POLLERR, PollEvents::Err);
339 translate(POLLHUP, PollEvents::Hup); 455 translate(POLLHUP, PollEvents::Hup);
456 translate(POLLNVAL, PollEvents::Nval);
457 translate(POLLRDNORM, PollEvents::RdNorm);
458 translate(POLLRDBAND, PollEvents::RdBand);
459 translate(POLLWRBAND, PollEvents::WrBand);
340 460
341 UNIMPLEMENTED_IF_MSG(revents != 0, "Unhandled host revents=0x{:x}", revents); 461 UNIMPLEMENTED_IF_MSG(revents != 0, "Unhandled host revents=0x{:x}", revents);
342 462
@@ -360,12 +480,51 @@ std::optional<IPv4Address> GetHostIPv4Address() {
360 return {}; 480 return {};
361 } 481 }
362 482
363 std::array<char, 16> ip_addr = {};
364 ASSERT(inet_ntop(AF_INET, &network_interface->ip_address, ip_addr.data(), sizeof(ip_addr)) !=
365 nullptr);
366 return TranslateIPv4(network_interface->ip_address); 483 return TranslateIPv4(network_interface->ip_address);
367} 484}
368 485
486std::string IPv4AddressToString(IPv4Address ip_addr) {
487 std::array<char, INET_ADDRSTRLEN> buf = {};
488 ASSERT(inet_ntop(AF_INET, &ip_addr, buf.data(), sizeof(buf)) == buf.data());
489 return std::string(buf.data());
490}
491
492u32 IPv4AddressToInteger(IPv4Address ip_addr) {
493 return static_cast<u32>(ip_addr[0]) << 24 | static_cast<u32>(ip_addr[1]) << 16 |
494 static_cast<u32>(ip_addr[2]) << 8 | static_cast<u32>(ip_addr[3]);
495}
496
497Common::Expected<std::vector<AddrInfo>, GetAddrInfoError> GetAddressInfo(
498 const std::string& host, const std::optional<std::string>& service) {
499 addrinfo hints{};
500 hints.ai_family = AF_INET; // Switch only supports IPv4.
501 addrinfo* addrinfo;
502 s32 gai_err = getaddrinfo(host.c_str(), service.has_value() ? service->c_str() : nullptr,
503 &hints, &addrinfo);
504 if (gai_err != 0) {
505 return Common::Unexpected(TranslateGetAddrInfoErrorFromNative(gai_err));
506 }
507 std::vector<AddrInfo> ret;
508 for (auto* current = addrinfo; current; current = current->ai_next) {
509 // We should only get AF_INET results due to the hints value.
510 ASSERT_OR_EXECUTE(addrinfo->ai_family == AF_INET &&
511 addrinfo->ai_addrlen == sizeof(sockaddr_in),
512 continue;);
513
514 AddrInfo& out = ret.emplace_back();
515 out.family = TranslateDomainFromNative(current->ai_family);
516 out.socket_type = TranslateTypeFromNative(current->ai_socktype);
517 out.protocol = TranslateProtocolFromNative(current->ai_protocol);
518 out.addr = TranslateToSockAddrIn(*reinterpret_cast<sockaddr_in*>(current->ai_addr),
519 current->ai_addrlen);
520 if (current->ai_canonname != nullptr) {
521 out.canon_name = current->ai_canonname;
522 }
523 }
524 freeaddrinfo(addrinfo);
525 return ret;
526}
527
369std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { 528std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
370 const size_t num = pollfds.size(); 529 const size_t num = pollfds.size();
371 530
@@ -411,9 +570,21 @@ Socket::Socket(Socket&& rhs) noexcept {
411} 570}
412 571
413template <typename T> 572template <typename T>
414Errno Socket::SetSockOpt(SOCKET fd_, int option, T value) { 573std::pair<T, Errno> Socket::GetSockOpt(SOCKET fd_so, int option) {
574 T value{};
575 socklen_t len = sizeof(value);
576 const int result = getsockopt(fd_so, SOL_SOCKET, option, reinterpret_cast<char*>(&value), &len);
577 if (result != SOCKET_ERROR) {
578 ASSERT(len == sizeof(value));
579 return {value, Errno::SUCCESS};
580 }
581 return {value, GetAndLogLastError()};
582}
583
584template <typename T>
585Errno Socket::SetSockOpt(SOCKET fd_so, int option, T value) {
415 const int result = 586 const int result =
416 setsockopt(fd_, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value)); 587 setsockopt(fd_so, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value));
417 if (result != SOCKET_ERROR) { 588 if (result != SOCKET_ERROR) {
418 return Errno::SUCCESS; 589 return Errno::SUCCESS;
419 } 590 }
@@ -421,7 +592,8 @@ Errno Socket::SetSockOpt(SOCKET fd_, int option, T value) {
421} 592}
422 593
423Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { 594Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) {
424 fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol)); 595 fd = socket(TranslateDomainToNative(domain), TranslateTypeToNative(type),
596 TranslateProtocolToNative(protocol));
425 if (fd != INVALID_SOCKET) { 597 if (fd != INVALID_SOCKET) {
426 return Errno::SUCCESS; 598 return Errno::SUCCESS;
427 } 599 }
@@ -430,19 +602,17 @@ Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) {
430} 602}
431 603
432std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() { 604std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() {
433 sockaddr addr; 605 sockaddr_in addr;
434 socklen_t addrlen = sizeof(addr); 606 socklen_t addrlen = sizeof(addr);
435 const SOCKET new_socket = accept(fd, &addr, &addrlen); 607 const SOCKET new_socket = accept(fd, reinterpret_cast<sockaddr*>(&addr), &addrlen);
436 608
437 if (new_socket == INVALID_SOCKET) { 609 if (new_socket == INVALID_SOCKET) {
438 return {AcceptResult{}, GetAndLogLastError()}; 610 return {AcceptResult{}, GetAndLogLastError()};
439 } 611 }
440 612
441 ASSERT(addrlen == sizeof(sockaddr_in));
442
443 AcceptResult result{ 613 AcceptResult result{
444 .socket = std::make_unique<Socket>(new_socket), 614 .socket = std::make_unique<Socket>(new_socket),
445 .sockaddr_in = TranslateToSockAddrIn(addr), 615 .sockaddr_in = TranslateToSockAddrIn(addr, addrlen),
446 }; 616 };
447 617
448 return {std::move(result), Errno::SUCCESS}; 618 return {std::move(result), Errno::SUCCESS};
@@ -458,25 +628,23 @@ Errno Socket::Connect(SockAddrIn addr_in) {
458} 628}
459 629
460std::pair<SockAddrIn, Errno> Socket::GetPeerName() { 630std::pair<SockAddrIn, Errno> Socket::GetPeerName() {
461 sockaddr addr; 631 sockaddr_in addr;
462 socklen_t addrlen = sizeof(addr); 632 socklen_t addrlen = sizeof(addr);
463 if (getpeername(fd, &addr, &addrlen) == SOCKET_ERROR) { 633 if (getpeername(fd, reinterpret_cast<sockaddr*>(&addr), &addrlen) == SOCKET_ERROR) {
464 return {SockAddrIn{}, GetAndLogLastError()}; 634 return {SockAddrIn{}, GetAndLogLastError()};
465 } 635 }
466 636
467 ASSERT(addrlen == sizeof(sockaddr_in)); 637 return {TranslateToSockAddrIn(addr, addrlen), Errno::SUCCESS};
468 return {TranslateToSockAddrIn(addr), Errno::SUCCESS};
469} 638}
470 639
471std::pair<SockAddrIn, Errno> Socket::GetSockName() { 640std::pair<SockAddrIn, Errno> Socket::GetSockName() {
472 sockaddr addr; 641 sockaddr_in addr;
473 socklen_t addrlen = sizeof(addr); 642 socklen_t addrlen = sizeof(addr);
474 if (getsockname(fd, &addr, &addrlen) == SOCKET_ERROR) { 643 if (getsockname(fd, reinterpret_cast<sockaddr*>(&addr), &addrlen) == SOCKET_ERROR) {
475 return {SockAddrIn{}, GetAndLogLastError()}; 644 return {SockAddrIn{}, GetAndLogLastError()};
476 } 645 }
477 646
478 ASSERT(addrlen == sizeof(sockaddr_in)); 647 return {TranslateToSockAddrIn(addr, addrlen), Errno::SUCCESS};
479 return {TranslateToSockAddrIn(addr), Errno::SUCCESS};
480} 648}
481 649
482Errno Socket::Bind(SockAddrIn addr) { 650Errno Socket::Bind(SockAddrIn addr) {
@@ -519,7 +687,7 @@ Errno Socket::Shutdown(ShutdownHow how) {
519 return GetAndLogLastError(); 687 return GetAndLogLastError();
520} 688}
521 689
522std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) { 690std::pair<s32, Errno> Socket::Recv(int flags, std::span<u8> message) {
523 ASSERT(flags == 0); 691 ASSERT(flags == 0);
524 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); 692 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
525 693
@@ -532,21 +700,20 @@ std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) {
532 return {-1, GetAndLogLastError()}; 700 return {-1, GetAndLogLastError()};
533} 701}
534 702
535std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) { 703std::pair<s32, Errno> Socket::RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) {
536 ASSERT(flags == 0); 704 ASSERT(flags == 0);
537 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); 705 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
538 706
539 sockaddr addr_in{}; 707 sockaddr_in addr_in{};
540 socklen_t addrlen = sizeof(addr_in); 708 socklen_t addrlen = sizeof(addr_in);
541 socklen_t* const p_addrlen = addr ? &addrlen : nullptr; 709 socklen_t* const p_addrlen = addr ? &addrlen : nullptr;
542 sockaddr* const p_addr_in = addr ? &addr_in : nullptr; 710 sockaddr* const p_addr_in = addr ? reinterpret_cast<sockaddr*>(&addr_in) : nullptr;
543 711
544 const auto result = recvfrom(fd, reinterpret_cast<char*>(message.data()), 712 const auto result = recvfrom(fd, reinterpret_cast<char*>(message.data()),
545 static_cast<int>(message.size()), 0, p_addr_in, p_addrlen); 713 static_cast<int>(message.size()), 0, p_addr_in, p_addrlen);
546 if (result != SOCKET_ERROR) { 714 if (result != SOCKET_ERROR) {
547 if (addr) { 715 if (addr) {
548 ASSERT(addrlen == sizeof(addr_in)); 716 *addr = TranslateToSockAddrIn(addr_in, addrlen);
549 *addr = TranslateToSockAddrIn(addr_in);
550 } 717 }
551 return {static_cast<s32>(result), Errno::SUCCESS}; 718 return {static_cast<s32>(result), Errno::SUCCESS};
552 } 719 }
@@ -597,6 +764,11 @@ Errno Socket::Close() {
597 return Errno::SUCCESS; 764 return Errno::SUCCESS;
598} 765}
599 766
767std::pair<Errno, Errno> Socket::GetPendingError() {
768 auto [pending_err, getsockopt_err] = GetSockOpt<int>(fd, SO_ERROR);
769 return {TranslateNativeError(pending_err), getsockopt_err};
770}
771
600Errno Socket::SetLinger(bool enable, u32 linger) { 772Errno Socket::SetLinger(bool enable, u32 linger) {
601 return SetSockOpt(fd, SO_LINGER, MakeLinger(enable, linger)); 773 return SetSockOpt(fd, SO_LINGER, MakeLinger(enable, linger));
602} 774}
diff --git a/src/core/internal_network/network.h b/src/core/internal_network/network.h
index 1e09a007a..badcb8369 100644
--- a/src/core/internal_network/network.h
+++ b/src/core/internal_network/network.h
@@ -5,6 +5,7 @@
5 5
6#include <array> 6#include <array>
7#include <optional> 7#include <optional>
8#include <vector>
8 9
9#include "common/common_funcs.h" 10#include "common/common_funcs.h"
10#include "common/common_types.h" 11#include "common/common_types.h"
@@ -16,6 +17,11 @@
16#include <netinet/in.h> 17#include <netinet/in.h>
17#endif 18#endif
18 19
20namespace Common {
21template <typename T, typename E>
22class Expected;
23}
24
19namespace Network { 25namespace Network {
20 26
21class SocketBase; 27class SocketBase;
@@ -36,6 +42,26 @@ enum class Errno {
36 NETUNREACH, 42 NETUNREACH,
37 TIMEDOUT, 43 TIMEDOUT,
38 MSGSIZE, 44 MSGSIZE,
45 INPROGRESS,
46 OTHER,
47};
48
49enum class GetAddrInfoError {
50 SUCCESS,
51 ADDRFAMILY,
52 AGAIN,
53 BADFLAGS,
54 FAIL,
55 FAMILY,
56 MEMORY,
57 NODATA,
58 NONAME,
59 SERVICE,
60 SOCKTYPE,
61 SYSTEM,
62 BADHINTS,
63 PROTOCOL,
64 OVERFLOW_,
39 OTHER, 65 OTHER,
40}; 66};
41 67
@@ -49,6 +75,9 @@ enum class PollEvents : u16 {
49 Err = 1 << 3, 75 Err = 1 << 3,
50 Hup = 1 << 4, 76 Hup = 1 << 4,
51 Nval = 1 << 5, 77 Nval = 1 << 5,
78 RdNorm = 1 << 6,
79 RdBand = 1 << 7,
80 WrBand = 1 << 8,
52}; 81};
53 82
54DECLARE_ENUM_FLAG_OPERATORS(PollEvents); 83DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
@@ -82,4 +111,11 @@ constexpr IPv4Address TranslateIPv4(in_addr addr) {
82/// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array 111/// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array
83std::optional<IPv4Address> GetHostIPv4Address(); 112std::optional<IPv4Address> GetHostIPv4Address();
84 113
114std::string IPv4AddressToString(IPv4Address ip_addr);
115u32 IPv4AddressToInteger(IPv4Address ip_addr);
116
117// named to avoid name collision with Windows macro
118Common::Expected<std::vector<AddrInfo>, GetAddrInfoError> GetAddressInfo(
119 const std::string& host, const std::optional<std::string>& service);
120
85} // namespace Network 121} // namespace Network
diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp
index 7a77171c2..ce0dee970 100644
--- a/src/core/internal_network/socket_proxy.cpp
+++ b/src/core/internal_network/socket_proxy.cpp
@@ -10,6 +10,7 @@
10#include "core/internal_network/network.h" 10#include "core/internal_network/network.h"
11#include "core/internal_network/network_interface.h" 11#include "core/internal_network/network_interface.h"
12#include "core/internal_network/socket_proxy.h" 12#include "core/internal_network/socket_proxy.h"
13#include "network/network.h"
13 14
14#if YUZU_UNIX 15#if YUZU_UNIX
15#include <sys/socket.h> 16#include <sys/socket.h>
@@ -98,7 +99,7 @@ Errno ProxySocket::Shutdown(ShutdownHow how) {
98 return Errno::SUCCESS; 99 return Errno::SUCCESS;
99} 100}
100 101
101std::pair<s32, Errno> ProxySocket::Recv(int flags, std::vector<u8>& message) { 102std::pair<s32, Errno> ProxySocket::Recv(int flags, std::span<u8> message) {
102 LOG_WARNING(Network, "(STUBBED) called"); 103 LOG_WARNING(Network, "(STUBBED) called");
103 ASSERT(flags == 0); 104 ASSERT(flags == 0);
104 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); 105 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
@@ -106,7 +107,7 @@ std::pair<s32, Errno> ProxySocket::Recv(int flags, std::vector<u8>& message) {
106 return {static_cast<s32>(0), Errno::SUCCESS}; 107 return {static_cast<s32>(0), Errno::SUCCESS};
107} 108}
108 109
109std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) { 110std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) {
110 ASSERT(flags == 0); 111 ASSERT(flags == 0);
111 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); 112 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
112 113
@@ -140,8 +141,8 @@ std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::vector<u8>& message,
140 } 141 }
141} 142}
142 143
143std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::vector<u8>& message, 144std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr,
144 SockAddrIn* addr, std::size_t max_length) { 145 std::size_t max_length) {
145 ProxyPacket& packet = received_packets.front(); 146 ProxyPacket& packet = received_packets.front();
146 if (addr) { 147 if (addr) {
147 addr->family = Domain::INET; 148 addr->family = Domain::INET;
@@ -153,10 +154,7 @@ std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::vector<u8>& mes
153 std::size_t read_bytes; 154 std::size_t read_bytes;
154 if (packet.data.size() > max_length) { 155 if (packet.data.size() > max_length) {
155 read_bytes = max_length; 156 read_bytes = max_length;
156 message.clear(); 157 memcpy(message.data(), packet.data.data(), max_length);
157 std::copy(packet.data.begin(), packet.data.begin() + read_bytes,
158 std::back_inserter(message));
159 message.resize(max_length);
160 158
161 if (protocol == Protocol::UDP) { 159 if (protocol == Protocol::UDP) {
162 if (!peek) { 160 if (!peek) {
@@ -171,9 +169,7 @@ std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::vector<u8>& mes
171 } 169 }
172 } else { 170 } else {
173 read_bytes = packet.data.size(); 171 read_bytes = packet.data.size();
174 message.clear(); 172 memcpy(message.data(), packet.data.data(), read_bytes);
175 std::copy(packet.data.begin(), packet.data.end(), std::back_inserter(message));
176 message.resize(max_length);
177 if (!peek) { 173 if (!peek) {
178 received_packets.pop(); 174 received_packets.pop();
179 } 175 }
@@ -293,6 +289,11 @@ Errno ProxySocket::SetNonBlock(bool enable) {
293 return Errno::SUCCESS; 289 return Errno::SUCCESS;
294} 290}
295 291
292std::pair<Errno, Errno> ProxySocket::GetPendingError() {
293 LOG_DEBUG(Network, "(STUBBED) called");
294 return {Errno::SUCCESS, Errno::SUCCESS};
295}
296
296bool ProxySocket::IsOpened() const { 297bool ProxySocket::IsOpened() const {
297 return fd != INVALID_SOCKET; 298 return fd != INVALID_SOCKET;
298} 299}
diff --git a/src/core/internal_network/socket_proxy.h b/src/core/internal_network/socket_proxy.h
index 6e991fa38..70500cf4a 100644
--- a/src/core/internal_network/socket_proxy.h
+++ b/src/core/internal_network/socket_proxy.h
@@ -10,10 +10,12 @@
10 10
11#include "common/common_funcs.h" 11#include "common/common_funcs.h"
12#include "core/internal_network/sockets.h" 12#include "core/internal_network/sockets.h"
13#include "network/network.h" 13#include "network/room_member.h"
14 14
15namespace Network { 15namespace Network {
16 16
17class RoomNetwork;
18
17class ProxySocket : public SocketBase { 19class ProxySocket : public SocketBase {
18public: 20public:
19 explicit ProxySocket(RoomNetwork& room_network_) noexcept; 21 explicit ProxySocket(RoomNetwork& room_network_) noexcept;
@@ -39,11 +41,11 @@ public:
39 41
40 Errno Shutdown(ShutdownHow how) override; 42 Errno Shutdown(ShutdownHow how) override;
41 43
42 std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override; 44 std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
43 45
44 std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override; 46 std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) override;
45 47
46 std::pair<s32, Errno> ReceivePacket(int flags, std::vector<u8>& message, SockAddrIn* addr, 48 std::pair<s32, Errno> ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr,
47 std::size_t max_length); 49 std::size_t max_length);
48 50
49 std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override; 51 std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
@@ -74,6 +76,8 @@ public:
74 template <typename T> 76 template <typename T>
75 Errno SetSockOpt(SOCKET fd, int option, T value); 77 Errno SetSockOpt(SOCKET fd, int option, T value);
76 78
79 std::pair<Errno, Errno> GetPendingError() override;
80
77 bool IsOpened() const override; 81 bool IsOpened() const override;
78 82
79private: 83private:
diff --git a/src/core/internal_network/sockets.h b/src/core/internal_network/sockets.h
index 11e479e50..4ba51f62c 100644
--- a/src/core/internal_network/sockets.h
+++ b/src/core/internal_network/sockets.h
@@ -15,12 +15,13 @@
15 15
16#include "common/common_types.h" 16#include "common/common_types.h"
17#include "core/internal_network/network.h" 17#include "core/internal_network/network.h"
18#include "network/network.h"
19 18
20// TODO: C++20 Replace std::vector usages with std::span 19// TODO: C++20 Replace std::vector usages with std::span
21 20
22namespace Network { 21namespace Network {
23 22
23struct ProxyPacket;
24
24class SocketBase { 25class SocketBase {
25public: 26public:
26#ifdef YUZU_UNIX 27#ifdef YUZU_UNIX
@@ -59,10 +60,9 @@ public:
59 60
60 virtual Errno Shutdown(ShutdownHow how) = 0; 61 virtual Errno Shutdown(ShutdownHow how) = 0;
61 62
62 virtual std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) = 0; 63 virtual std::pair<s32, Errno> Recv(int flags, std::span<u8> message) = 0;
63 64
64 virtual std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, 65 virtual std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) = 0;
65 SockAddrIn* addr) = 0;
66 66
67 virtual std::pair<s32, Errno> Send(std::span<const u8> message, int flags) = 0; 67 virtual std::pair<s32, Errno> Send(std::span<const u8> message, int flags) = 0;
68 68
@@ -87,6 +87,8 @@ public:
87 87
88 virtual Errno SetNonBlock(bool enable) = 0; 88 virtual Errno SetNonBlock(bool enable) = 0;
89 89
90 virtual std::pair<Errno, Errno> GetPendingError() = 0;
91
90 virtual bool IsOpened() const = 0; 92 virtual bool IsOpened() const = 0;
91 93
92 virtual void HandleProxyPacket(const ProxyPacket& packet) = 0; 94 virtual void HandleProxyPacket(const ProxyPacket& packet) = 0;
@@ -126,9 +128,9 @@ public:
126 128
127 Errno Shutdown(ShutdownHow how) override; 129 Errno Shutdown(ShutdownHow how) override;
128 130
129 std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override; 131 std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
130 132
131 std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override; 133 std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) override;
132 134
133 std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override; 135 std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
134 136
@@ -156,6 +158,11 @@ public:
156 template <typename T> 158 template <typename T>
157 Errno SetSockOpt(SOCKET fd, int option, T value); 159 Errno SetSockOpt(SOCKET fd, int option, T value);
158 160
161 std::pair<Errno, Errno> GetPendingError() override;
162
163 template <typename T>
164 std::pair<T, Errno> GetSockOpt(SOCKET fd, int option);
165
159 bool IsOpened() const override; 166 bool IsOpened() const override;
160 167
161 void HandleProxyPacket(const ProxyPacket& packet) override; 168 void HandleProxyPacket(const ProxyPacket& packet) override;
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 3be9b71cf..e04ad19db 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -153,7 +153,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
153 153
154 // Load NSO modules 154 // Load NSO modules
155 modules.clear(); 155 modules.clear();
156 const VAddr base_address{GetInteger(process.PageTable().GetCodeRegionStart())}; 156 const VAddr base_address{GetInteger(process.GetPageTable().GetCodeRegionStart())};
157 VAddr next_load_addr{base_address}; 157 VAddr next_load_addr{base_address};
158 const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(), 158 const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(),
159 system.GetContentProvider()}; 159 system.GetContentProvider()};
diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp
index 709e2564f..ffe976b94 100644
--- a/src/core/loader/kip.cpp
+++ b/src/core/loader/kip.cpp
@@ -96,7 +96,7 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
96 } 96 }
97 97
98 codeset.memory = std::move(program_image); 98 codeset.memory = std::move(program_image);
99 const VAddr base_address = GetInteger(process.PageTable().GetCodeRegionStart()); 99 const VAddr base_address = GetInteger(process.GetPageTable().GetCodeRegionStart());
100 process.LoadModule(std::move(codeset), base_address); 100 process.LoadModule(std::move(codeset), base_address);
101 101
102 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", kip->GetName(), base_address); 102 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", kip->GetName(), base_address);
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 7b43f70ed..7a2a52fd4 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -79,6 +79,8 @@ enum class ResultStatus : u16 {
79 ErrorBadPFSHeader, 79 ErrorBadPFSHeader,
80 ErrorIncorrectPFSFileSize, 80 ErrorIncorrectPFSFileSize,
81 ErrorBadNCAHeader, 81 ErrorBadNCAHeader,
82 ErrorCompressedNCA,
83 ErrorSparseNCA,
82 ErrorMissingProductionKeyFile, 84 ErrorMissingProductionKeyFile,
83 ErrorMissingHeaderKey, 85 ErrorMissingHeaderKey,
84 ErrorIncorrectHeaderKey, 86 ErrorIncorrectHeaderKey,
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 7be6cf5f3..506808b5d 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -203,7 +203,7 @@ static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data)
203 203
204 // Load codeset for current process 204 // Load codeset for current process
205 codeset.memory = std::move(program_image); 205 codeset.memory = std::move(program_image);
206 process.LoadModule(std::move(codeset), process.PageTable().GetCodeRegionStart()); 206 process.LoadModule(std::move(codeset), process.GetPageTable().GetCodeRegionStart());
207 207
208 return true; 208 return true;
209} 209}
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 79639f5e4..74cc9579f 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -167,7 +167,7 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::KProcess& process, Core::S
167 modules.clear(); 167 modules.clear();
168 168
169 // Load module 169 // Load module
170 const VAddr base_address = GetInteger(process.PageTable().GetCodeRegionStart()); 170 const VAddr base_address = GetInteger(process.GetPageTable().GetCodeRegionStart());
171 if (!LoadModule(process, system, *file, base_address, true, true)) { 171 if (!LoadModule(process, system, *file, base_address, true, true)) {
172 return {ResultStatus::ErrorLoadingNSO, {}}; 172 return {ResultStatus::ErrorLoadingNSO, {}};
173 } 173 }
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 257406f09..e1fbe8e00 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -31,10 +31,10 @@ struct Memory::Impl {
31 explicit Impl(Core::System& system_) : system{system_} {} 31 explicit Impl(Core::System& system_) : system{system_} {}
32 32
33 void SetCurrentPageTable(Kernel::KProcess& process, u32 core_id) { 33 void SetCurrentPageTable(Kernel::KProcess& process, u32 core_id) {
34 current_page_table = &process.PageTable().PageTableImpl(); 34 current_page_table = &process.GetPageTable().PageTableImpl();
35 current_page_table->fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer(); 35 current_page_table->fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer();
36 36
37 const std::size_t address_space_width = process.PageTable().GetAddressSpaceWidth(); 37 const std::size_t address_space_width = process.GetPageTable().GetAddressSpaceWidth();
38 38
39 system.ArmInterface(core_id).PageTableChanged(*current_page_table, address_space_width); 39 system.ArmInterface(core_id).PageTableChanged(*current_page_table, address_space_width);
40 } 40 }
@@ -186,7 +186,7 @@ struct Memory::Impl {
186 void WalkBlock(const Kernel::KProcess& process, const Common::ProcessAddress addr, 186 void WalkBlock(const Kernel::KProcess& process, const Common::ProcessAddress addr,
187 const std::size_t size, auto on_unmapped, auto on_memory, auto on_rasterizer, 187 const std::size_t size, auto on_unmapped, auto on_memory, auto on_rasterizer,
188 auto increment) { 188 auto increment) {
189 const auto& page_table = process.PageTable().PageTableImpl(); 189 const auto& page_table = process.GetPageTable().PageTableImpl();
190 std::size_t remaining_size = size; 190 std::size_t remaining_size = size;
191 std::size_t page_index = addr >> YUZU_PAGEBITS; 191 std::size_t page_index = addr >> YUZU_PAGEBITS;
192 std::size_t page_offset = addr & YUZU_PAGEMASK; 192 std::size_t page_offset = addr & YUZU_PAGEMASK;
@@ -266,6 +266,22 @@ struct Memory::Impl {
266 ReadBlockImpl<true>(*system.ApplicationProcess(), src_addr, dest_buffer, size); 266 ReadBlockImpl<true>(*system.ApplicationProcess(), src_addr, dest_buffer, size);
267 } 267 }
268 268
269 const u8* GetSpan(const VAddr src_addr, const std::size_t size) const {
270 if (current_page_table->blocks[src_addr >> YUZU_PAGEBITS] ==
271 current_page_table->blocks[(src_addr + size) >> YUZU_PAGEBITS]) {
272 return GetPointerSilent(src_addr);
273 }
274 return nullptr;
275 }
276
277 u8* GetSpan(const VAddr src_addr, const std::size_t size) {
278 if (current_page_table->blocks[src_addr >> YUZU_PAGEBITS] ==
279 current_page_table->blocks[(src_addr + size) >> YUZU_PAGEBITS]) {
280 return GetPointerSilent(src_addr);
281 }
282 return nullptr;
283 }
284
269 template <bool UNSAFE> 285 template <bool UNSAFE>
270 void WriteBlockImpl(const Kernel::KProcess& process, const Common::ProcessAddress dest_addr, 286 void WriteBlockImpl(const Kernel::KProcess& process, const Common::ProcessAddress dest_addr,
271 const void* src_buffer, const std::size_t size) { 287 const void* src_buffer, const std::size_t size) {
@@ -559,7 +575,7 @@ struct Memory::Impl {
559 } 575 }
560 } 576 }
561 577
562 const Common::ProcessAddress end = base + size; 578 const auto end = base + size;
563 ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}", 579 ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",
564 base + page_table.pointers.size()); 580 base + page_table.pointers.size());
565 581
@@ -570,14 +586,18 @@ struct Memory::Impl {
570 while (base != end) { 586 while (base != end) {
571 page_table.pointers[base].Store(nullptr, type); 587 page_table.pointers[base].Store(nullptr, type);
572 page_table.backing_addr[base] = 0; 588 page_table.backing_addr[base] = 0;
573 589 page_table.blocks[base] = 0;
574 base += 1; 590 base += 1;
575 } 591 }
576 } else { 592 } else {
593 auto orig_base = base;
577 while (base != end) { 594 while (base != end) {
578 page_table.pointers[base].Store( 595 auto host_ptr =
579 system.DeviceMemory().GetPointer<u8>(target) - (base << YUZU_PAGEBITS), type); 596 system.DeviceMemory().GetPointer<u8>(target) - (base << YUZU_PAGEBITS);
580 page_table.backing_addr[base] = GetInteger(target) - (base << YUZU_PAGEBITS); 597 auto backing = GetInteger(target) - (base << YUZU_PAGEBITS);
598 page_table.pointers[base].Store(host_ptr, type);
599 page_table.backing_addr[base] = backing;
600 page_table.blocks[base] = orig_base << YUZU_PAGEBITS;
581 601
582 ASSERT_MSG(page_table.pointers[base].Pointer(), 602 ASSERT_MSG(page_table.pointers[base].Pointer(),
583 "memory mapping base yield a nullptr within the table"); 603 "memory mapping base yield a nullptr within the table");
@@ -747,6 +767,14 @@ struct Memory::Impl {
747 VAddr last_address; 767 VAddr last_address;
748 }; 768 };
749 769
770 void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size) {
771 system.GPU().InvalidateRegion(GetInteger(dest_addr), size);
772 }
773
774 void FlushRegion(Common::ProcessAddress dest_addr, size_t size) {
775 system.GPU().FlushRegion(GetInteger(dest_addr), size);
776 }
777
750 Core::System& system; 778 Core::System& system;
751 Common::PageTable* current_page_table = nullptr; 779 Common::PageTable* current_page_table = nullptr;
752 std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES> 780 std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES>
@@ -780,7 +808,7 @@ void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress b
780 808
781bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const { 809bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const {
782 const Kernel::KProcess& process = *system.ApplicationProcess(); 810 const Kernel::KProcess& process = *system.ApplicationProcess();
783 const auto& page_table = process.PageTable().PageTableImpl(); 811 const auto& page_table = process.GetPageTable().PageTableImpl();
784 const size_t page = vaddr >> YUZU_PAGEBITS; 812 const size_t page = vaddr >> YUZU_PAGEBITS;
785 if (page >= page_table.pointers.size()) { 813 if (page >= page_table.pointers.size()) {
786 return false; 814 return false;
@@ -881,6 +909,14 @@ void Memory::ReadBlockUnsafe(const Common::ProcessAddress src_addr, void* dest_b
881 impl->ReadBlockUnsafe(src_addr, dest_buffer, size); 909 impl->ReadBlockUnsafe(src_addr, dest_buffer, size);
882} 910}
883 911
912const u8* Memory::GetSpan(const VAddr src_addr, const std::size_t size) const {
913 return impl->GetSpan(src_addr, size);
914}
915
916u8* Memory::GetSpan(const VAddr src_addr, const std::size_t size) {
917 return impl->GetSpan(src_addr, size);
918}
919
884void Memory::WriteBlock(const Common::ProcessAddress dest_addr, const void* src_buffer, 920void Memory::WriteBlock(const Common::ProcessAddress dest_addr, const void* src_buffer,
885 const std::size_t size) { 921 const std::size_t size) {
886 impl->WriteBlock(dest_addr, src_buffer, size); 922 impl->WriteBlock(dest_addr, src_buffer, size);
@@ -924,4 +960,12 @@ void Memory::MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug)
924 impl->MarkRegionDebug(GetInteger(vaddr), size, debug); 960 impl->MarkRegionDebug(GetInteger(vaddr), size, debug);
925} 961}
926 962
963void Memory::InvalidateRegion(Common::ProcessAddress dest_addr, size_t size) {
964 impl->InvalidateRegion(dest_addr, size);
965}
966
967void Memory::FlushRegion(Common::ProcessAddress dest_addr, size_t size) {
968 impl->FlushRegion(dest_addr, size);
969}
970
927} // namespace Core::Memory 971} // namespace Core::Memory
diff --git a/src/core/memory.h b/src/core/memory.h
index ea01824f8..ea33c769c 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -5,8 +5,12 @@
5 5
6#include <cstddef> 6#include <cstddef>
7#include <memory> 7#include <memory>
8#include <optional>
8#include <span> 9#include <span>
9#include <string> 10#include <string>
11#include <vector>
12
13#include "common/scratch_buffer.h"
10#include "common/typed_address.h" 14#include "common/typed_address.h"
11#include "core/hle/result.h" 15#include "core/hle/result.h"
12 16
@@ -24,6 +28,10 @@ class PhysicalMemory;
24class KProcess; 28class KProcess;
25} // namespace Kernel 29} // namespace Kernel
26 30
31namespace Tegra {
32class MemoryManager;
33}
34
27namespace Core::Memory { 35namespace Core::Memory {
28 36
29/** 37/**
@@ -343,6 +351,9 @@ public:
343 */ 351 */
344 void ReadBlockUnsafe(Common::ProcessAddress src_addr, void* dest_buffer, std::size_t size); 352 void ReadBlockUnsafe(Common::ProcessAddress src_addr, void* dest_buffer, std::size_t size);
345 353
354 const u8* GetSpan(const VAddr src_addr, const std::size_t size) const;
355 u8* GetSpan(const VAddr src_addr, const std::size_t size);
356
346 /** 357 /**
347 * Writes a range of bytes into the current process' address space at the specified 358 * Writes a range of bytes into the current process' address space at the specified
348 * virtual address. 359 * virtual address.
@@ -461,6 +472,8 @@ public:
461 void MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug); 472 void MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug);
462 473
463 void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers); 474 void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers);
475 void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size);
476 void FlushRegion(Common::ProcessAddress dest_addr, size_t size);
464 477
465private: 478private:
466 Core::System& system; 479 Core::System& system;
@@ -469,4 +482,203 @@ private:
469 std::unique_ptr<Impl> impl; 482 std::unique_ptr<Impl> impl;
470}; 483};
471 484
485enum GuestMemoryFlags : u32 {
486 Read = 1 << 0,
487 Write = 1 << 1,
488 Safe = 1 << 2,
489 Cached = 1 << 3,
490
491 SafeRead = Read | Safe,
492 SafeWrite = Write | Safe,
493 SafeReadWrite = SafeRead | SafeWrite,
494 SafeReadCachedWrite = SafeReadWrite | Cached,
495
496 UnsafeRead = Read,
497 UnsafeWrite = Write,
498 UnsafeReadWrite = UnsafeRead | UnsafeWrite,
499 UnsafeReadCachedWrite = UnsafeReadWrite | Cached,
500};
501
502namespace {
503template <typename M, typename T, GuestMemoryFlags FLAGS>
504class GuestMemory {
505 using iterator = T*;
506 using const_iterator = const T*;
507 using value_type = T;
508 using element_type = T;
509 using iterator_category = std::contiguous_iterator_tag;
510
511public:
512 GuestMemory() = delete;
513 explicit GuestMemory(M& memory_, u64 addr_, std::size_t size_,
514 Common::ScratchBuffer<T>* backup = nullptr)
515 : memory{memory_}, addr{addr_}, size{size_} {
516 static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write);
517 if constexpr (FLAGS & GuestMemoryFlags::Read) {
518 Read(addr, size, backup);
519 }
520 }
521
522 ~GuestMemory() = default;
523
524 T* data() noexcept {
525 return data_span.data();
526 }
527
528 const T* data() const noexcept {
529 return data_span.data();
530 }
531
532 [[nodiscard]] T* begin() noexcept {
533 return data();
534 }
535
536 [[nodiscard]] const T* begin() const noexcept {
537 return data();
538 }
539
540 [[nodiscard]] T* end() noexcept {
541 return data() + size;
542 }
543
544 [[nodiscard]] const T* end() const noexcept {
545 return data() + size;
546 }
547
548 T& operator[](size_t index) noexcept {
549 return data_span[index];
550 }
551
552 const T& operator[](size_t index) const noexcept {
553 return data_span[index];
554 }
555
556 void SetAddressAndSize(u64 addr_, std::size_t size_) noexcept {
557 addr = addr_;
558 size = size_;
559 addr_changed = true;
560 }
561
562 std::span<T> Read(u64 addr_, std::size_t size_,
563 Common::ScratchBuffer<T>* backup = nullptr) noexcept {
564 addr = addr_;
565 size = size_;
566 if (size == 0) {
567 is_data_copy = true;
568 return {};
569 }
570
571 if (TrySetSpan()) {
572 if constexpr (FLAGS & GuestMemoryFlags::Safe) {
573 memory.FlushRegion(addr, size * sizeof(T));
574 }
575 } else {
576 if (backup) {
577 backup->resize_destructive(size);
578 data_span = *backup;
579 } else {
580 data_copy.resize(size);
581 data_span = std::span(data_copy);
582 }
583 is_data_copy = true;
584 span_valid = true;
585 if constexpr (FLAGS & GuestMemoryFlags::Safe) {
586 memory.ReadBlock(addr, data_span.data(), size * sizeof(T));
587 } else {
588 memory.ReadBlockUnsafe(addr, data_span.data(), size * sizeof(T));
589 }
590 }
591 return data_span;
592 }
593
594 void Write(std::span<T> write_data) noexcept {
595 if constexpr (FLAGS & GuestMemoryFlags::Cached) {
596 memory.WriteBlockCached(addr, write_data.data(), size * sizeof(T));
597 } else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
598 memory.WriteBlock(addr, write_data.data(), size * sizeof(T));
599 } else {
600 memory.WriteBlockUnsafe(addr, write_data.data(), size * sizeof(T));
601 }
602 }
603
604 bool TrySetSpan() noexcept {
605 if (u8* ptr = memory.GetSpan(addr, size * sizeof(T)); ptr) {
606 data_span = {reinterpret_cast<T*>(ptr), size};
607 span_valid = true;
608 return true;
609 }
610 return false;
611 }
612
613protected:
614 bool IsDataCopy() const noexcept {
615 return is_data_copy;
616 }
617
618 bool AddressChanged() const noexcept {
619 return addr_changed;
620 }
621
622 M& memory;
623 u64 addr;
624 size_t size;
625 std::span<T> data_span{};
626 std::vector<T> data_copy;
627 bool span_valid{false};
628 bool is_data_copy{false};
629 bool addr_changed{false};
630};
631
632template <typename M, typename T, GuestMemoryFlags FLAGS>
633class GuestMemoryScoped : public GuestMemory<M, T, FLAGS> {
634public:
635 GuestMemoryScoped() = delete;
636 explicit GuestMemoryScoped(M& memory_, u64 addr_, std::size_t size_,
637 Common::ScratchBuffer<T>* backup = nullptr)
638 : GuestMemory<M, T, FLAGS>(memory_, addr_, size_, backup) {
639 if constexpr (!(FLAGS & GuestMemoryFlags::Read)) {
640 if (!this->TrySetSpan()) {
641 if (backup) {
642 this->data_span = *backup;
643 this->span_valid = true;
644 this->is_data_copy = true;
645 }
646 }
647 }
648 }
649
650 ~GuestMemoryScoped() {
651 if constexpr (FLAGS & GuestMemoryFlags::Write) {
652 if (this->size == 0) [[unlikely]] {
653 return;
654 }
655
656 if (this->AddressChanged() || this->IsDataCopy()) {
657 ASSERT(this->span_valid);
658 if constexpr (FLAGS & GuestMemoryFlags::Cached) {
659 this->memory.WriteBlockCached(this->addr, this->data_span.data(),
660 this->size * sizeof(T));
661 } else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
662 this->memory.WriteBlock(this->addr, this->data_span.data(),
663 this->size * sizeof(T));
664 } else {
665 this->memory.WriteBlockUnsafe(this->addr, this->data_span.data(),
666 this->size * sizeof(T));
667 }
668 } else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
669 this->memory.InvalidateRegion(this->addr, this->size * sizeof(T));
670 }
671 }
672 }
673};
674} // namespace
675
676template <typename T, GuestMemoryFlags FLAGS>
677using CpuGuestMemory = GuestMemory<Memory, T, FLAGS>;
678template <typename T, GuestMemoryFlags FLAGS>
679using CpuGuestMemoryScoped = GuestMemoryScoped<Memory, T, FLAGS>;
680template <typename T, GuestMemoryFlags FLAGS>
681using GpuGuestMemory = GuestMemory<Tegra::MemoryManager, T, FLAGS>;
682template <typename T, GuestMemoryFlags FLAGS>
683using GpuGuestMemoryScoped = GuestMemoryScoped<Tegra::MemoryManager, T, FLAGS>;
472} // namespace Core::Memory 684} // namespace Core::Memory
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index 8742dd164..7b52f61a7 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -199,7 +199,7 @@ void CheatEngine::Initialize() {
199 metadata.process_id = system.ApplicationProcess()->GetProcessId(); 199 metadata.process_id = system.ApplicationProcess()->GetProcessId();
200 metadata.title_id = system.GetApplicationProcessProgramID(); 200 metadata.title_id = system.GetApplicationProcessProgramID();
201 201
202 const auto& page_table = system.ApplicationProcess()->PageTable(); 202 const auto& page_table = system.ApplicationProcess()->GetPageTable();
203 metadata.heap_extents = { 203 metadata.heap_extents = {
204 .base = GetInteger(page_table.GetHeapRegionStart()), 204 .base = GetInteger(page_table.GetHeapRegionStart()),
205 .size = page_table.GetHeapRegionSize(), 205 .size = page_table.GetHeapRegionSize(),
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
index 6c3dc7369..b5b3e7eda 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -117,8 +117,8 @@ json GetProcessorStateDataAuto(Core::System& system) {
117 arm.SaveContext(context); 117 arm.SaveContext(context);
118 118
119 return GetProcessorStateData(process->Is64BitProcess() ? "AArch64" : "AArch32", 119 return GetProcessorStateData(process->Is64BitProcess() ? "AArch64" : "AArch32",
120 GetInteger(process->PageTable().GetCodeRegionStart()), context.sp, 120 GetInteger(process->GetPageTable().GetCodeRegionStart()),
121 context.pc, context.pstate, context.cpu_registers); 121 context.sp, context.pc, context.pstate, context.cpu_registers);
122} 122}
123 123
124json GetBacktraceData(Core::System& system) { 124json GetBacktraceData(Core::System& system) {
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index 9f26392b1..66e3ae9af 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -523,6 +523,8 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
523 } 523 }
524 524
525 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED, "1"); 525 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED, "1");
526 // Share the same button mapping with non-Nintendo controllers
527 SDL_SetHint(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, "0");
526 528
527 // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native 529 // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native
528 // driver on Linux. 530 // driver on Linux.
@@ -800,16 +802,9 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p
800 802
801 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController. 803 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
802 // We will add those afterwards 804 // We will add those afterwards
803 // This list also excludes Screenshot since there's not really a mapping for that
804 ButtonBindings switch_to_sdl_button; 805 ButtonBindings switch_to_sdl_button;
805 806
806 if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO || 807 switch_to_sdl_button = GetDefaultButtonBinding(joystick);
807 SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT ||
808 SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT) {
809 switch_to_sdl_button = GetNintendoButtonBinding(joystick);
810 } else {
811 switch_to_sdl_button = GetDefaultButtonBinding();
812 }
813 808
814 // Add the missing bindings for ZL/ZR 809 // Add the missing bindings for ZL/ZR
815 static constexpr ZButtonBindings switch_to_sdl_axis{{ 810 static constexpr ZButtonBindings switch_to_sdl_axis{{
@@ -830,32 +825,9 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p
830 return GetSingleControllerMapping(joystick, switch_to_sdl_button, switch_to_sdl_axis); 825 return GetSingleControllerMapping(joystick, switch_to_sdl_button, switch_to_sdl_axis);
831} 826}
832 827
833ButtonBindings SDLDriver::GetDefaultButtonBinding() const { 828ButtonBindings SDLDriver::GetDefaultButtonBinding(
834 return {
835 std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
836 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
837 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
838 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
839 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
840 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
841 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
842 {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
843 {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
844 {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
845 {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
846 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
847 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
848 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
849 {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
850 {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
851 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
852 {Settings::NativeButton::Screenshot, SDL_CONTROLLER_BUTTON_MISC1},
853 };
854}
855
856ButtonBindings SDLDriver::GetNintendoButtonBinding(
857 const std::shared_ptr<SDLJoystick>& joystick) const { 829 const std::shared_ptr<SDLJoystick>& joystick) const {
858 // Default SL/SR mapping for pro controllers 830 // Default SL/SR mapping for other controllers
859 auto sl_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; 831 auto sl_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
860 auto sr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; 832 auto sr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
861 833
@@ -869,10 +841,10 @@ ButtonBindings SDLDriver::GetNintendoButtonBinding(
869 } 841 }
870 842
871 return { 843 return {
872 std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_A}, 844 std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
873 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_B}, 845 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
874 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_X}, 846 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
875 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_Y}, 847 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
876 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK}, 848 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
877 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK}, 849 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
878 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, 850 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h
index ffde169b3..fcba4e3c6 100644
--- a/src/input_common/drivers/sdl_driver.h
+++ b/src/input_common/drivers/sdl_driver.h
@@ -100,11 +100,8 @@ private:
100 int axis_y, float offset_x, 100 int axis_y, float offset_x,
101 float offset_y) const; 101 float offset_y) const;
102 102
103 /// Returns the default button bindings list for generic controllers 103 /// Returns the default button bindings list
104 ButtonBindings GetDefaultButtonBinding() const; 104 ButtonBindings GetDefaultButtonBinding(const std::shared_ptr<SDLJoystick>& joystick) const;
105
106 /// Returns the default button bindings list for nintendo controllers
107 ButtonBindings GetNintendoButtonBinding(const std::shared_ptr<SDLJoystick>& joystick) const;
108 105
109 /// Returns the button mappings from a single controller 106 /// Returns the button mappings from a single controller
110 ButtonMapping GetSingleControllerMapping(const std::shared_ptr<SDLJoystick>& joystick, 107 ButtonMapping GetSingleControllerMapping(const std::shared_ptr<SDLJoystick>& joystick,
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 3b2fe01da..7f79111e0 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -274,6 +274,7 @@ add_library(video_core STATIC
274 vulkan_common/vulkan_wrapper.h 274 vulkan_common/vulkan_wrapper.h
275 vulkan_common/nsight_aftermath_tracker.cpp 275 vulkan_common/nsight_aftermath_tracker.cpp
276 vulkan_common/nsight_aftermath_tracker.h 276 vulkan_common/nsight_aftermath_tracker.h
277 vulkan_common/vma.cpp
277) 278)
278 279
279create_target_directory_groups(video_core) 280create_target_directory_groups(video_core)
@@ -291,7 +292,7 @@ target_link_options(video_core PRIVATE ${FFmpeg_LDFLAGS})
291 292
292add_dependencies(video_core host_shaders) 293add_dependencies(video_core host_shaders)
293target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE}) 294target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
294target_link_libraries(video_core PRIVATE sirit Vulkan::Headers vma) 295target_link_libraries(video_core PRIVATE sirit Vulkan::Headers GPUOpen::VulkanMemoryAllocator)
295 296
296if (ENABLE_NSIGHT_AFTERMATH) 297if (ENABLE_NSIGHT_AFTERMATH)
297 if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK}) 298 if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK})
@@ -324,6 +325,9 @@ else()
324 325
325 # xbyak 326 # xbyak
326 set_source_files_properties(macro/macro_jit_x64.cpp PROPERTIES COMPILE_OPTIONS "-Wno-conversion;-Wno-shadow") 327 set_source_files_properties(macro/macro_jit_x64.cpp PROPERTIES COMPILE_OPTIONS "-Wno-conversion;-Wno-shadow")
328
329 # VMA
330 set_source_files_properties(vulkan_common/vma.cpp PROPERTIES COMPILE_OPTIONS "-Wno-conversion;-Wno-unused-variable;-Wno-unused-parameter;-Wno-missing-field-initializers")
327endif() 331endif()
328 332
329if (ARCHITECTURE_x86_64) 333if (ARCHITECTURE_x86_64)
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index b5ed3380f..f0f450edb 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -234,9 +234,10 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
234 if (has_new_downloads) { 234 if (has_new_downloads) {
235 memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount); 235 memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
236 } 236 }
237 tmp_buffer.resize_destructive(amount); 237
238 cpu_memory.ReadBlockUnsafe(*cpu_src_address, tmp_buffer.data(), amount); 238 Core::Memory::CpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::UnsafeReadWrite> tmp(
239 cpu_memory.WriteBlockUnsafe(*cpu_dest_address, tmp_buffer.data(), amount); 239 cpu_memory, *cpu_src_address, amount, &tmp_buffer);
240 tmp.SetAddressAndSize(*cpu_dest_address, amount);
240 return true; 241 return true;
241} 242}
242 243
@@ -441,6 +442,11 @@ void BufferCache<P>::UnbindComputeStorageBuffers() {
441template <class P> 442template <class P>
442void BufferCache<P>::BindComputeStorageBuffer(size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset, 443void BufferCache<P>::BindComputeStorageBuffer(size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset,
443 bool is_written) { 444 bool is_written) {
445 if (ssbo_index >= channel_state->compute_storage_buffers.size()) [[unlikely]] {
446 LOG_ERROR(HW_GPU, "Storage buffer index {} exceeds maximum storage buffer count",
447 ssbo_index);
448 return;
449 }
444 channel_state->enabled_compute_storage_buffers |= 1U << ssbo_index; 450 channel_state->enabled_compute_storage_buffers |= 1U << ssbo_index;
445 channel_state->written_compute_storage_buffers |= (is_written ? 1U : 0U) << ssbo_index; 451 channel_state->written_compute_storage_buffers |= (is_written ? 1U : 0U) << ssbo_index;
446 452
@@ -463,6 +469,11 @@ void BufferCache<P>::UnbindComputeTextureBuffers() {
463template <class P> 469template <class P>
464void BufferCache<P>::BindComputeTextureBuffer(size_t tbo_index, GPUVAddr gpu_addr, u32 size, 470void BufferCache<P>::BindComputeTextureBuffer(size_t tbo_index, GPUVAddr gpu_addr, u32 size,
465 PixelFormat format, bool is_written, bool is_image) { 471 PixelFormat format, bool is_written, bool is_image) {
472 if (tbo_index >= channel_state->compute_texture_buffers.size()) [[unlikely]] {
473 LOG_ERROR(HW_GPU, "Texture buffer index {} exceeds maximum texture buffer count",
474 tbo_index);
475 return;
476 }
466 channel_state->enabled_compute_texture_buffers |= 1U << tbo_index; 477 channel_state->enabled_compute_texture_buffers |= 1U << tbo_index;
467 channel_state->written_compute_texture_buffers |= (is_written ? 1U : 0U) << tbo_index; 478 channel_state->written_compute_texture_buffers |= (is_written ? 1U : 0U) << tbo_index;
468 if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { 479 if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) {
diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h
index 460fc7551..0b7135d49 100644
--- a/src/video_core/buffer_cache/buffer_cache_base.h
+++ b/src/video_core/buffer_cache/buffer_cache_base.h
@@ -67,7 +67,7 @@ constexpr u32 NUM_TRANSFORM_FEEDBACK_BUFFERS = 4;
67constexpr u32 NUM_GRAPHICS_UNIFORM_BUFFERS = 18; 67constexpr u32 NUM_GRAPHICS_UNIFORM_BUFFERS = 18;
68constexpr u32 NUM_COMPUTE_UNIFORM_BUFFERS = 8; 68constexpr u32 NUM_COMPUTE_UNIFORM_BUFFERS = 8;
69constexpr u32 NUM_STORAGE_BUFFERS = 16; 69constexpr u32 NUM_STORAGE_BUFFERS = 16;
70constexpr u32 NUM_TEXTURE_BUFFERS = 16; 70constexpr u32 NUM_TEXTURE_BUFFERS = 32;
71constexpr u32 NUM_STAGES = 5; 71constexpr u32 NUM_STAGES = 5;
72 72
73using UniformBufferSizes = std::array<std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFERS>, NUM_STAGES>; 73using UniformBufferSizes = std::array<std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFERS>, NUM_STAGES>;
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index 551929824..9f1b340a9 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -5,6 +5,7 @@
5#include "common/microprofile.h" 5#include "common/microprofile.h"
6#include "common/settings.h" 6#include "common/settings.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/memory.h"
8#include "video_core/dma_pusher.h" 9#include "video_core/dma_pusher.h"
9#include "video_core/engines/maxwell_3d.h" 10#include "video_core/engines/maxwell_3d.h"
10#include "video_core/gpu.h" 11#include "video_core/gpu.h"
@@ -12,6 +13,8 @@
12 13
13namespace Tegra { 14namespace Tegra {
14 15
16constexpr u32 MacroRegistersStart = 0xE00;
17
15DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_, 18DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_,
16 Control::ChannelState& channel_state_) 19 Control::ChannelState& channel_state_)
17 : gpu{gpu_}, system{system_}, memory_manager{memory_manager_}, puller{gpu_, memory_manager_, 20 : gpu{gpu_}, system{system_}, memory_manager{memory_manager_}, puller{gpu_, memory_manager_,
@@ -74,25 +77,16 @@ bool DmaPusher::Step() {
74 } 77 }
75 78
76 // Push buffer non-empty, read a word 79 // Push buffer non-empty, read a word
77 command_headers.resize_destructive(command_list_header.size); 80 if (dma_state.method >= MacroRegistersStart) {
78 constexpr u32 MacroRegistersStart = 0xE00;
79 if (dma_state.method < MacroRegistersStart) {
80 if (Settings::IsGPULevelHigh()) {
81 memory_manager.ReadBlock(dma_state.dma_get, command_headers.data(),
82 command_list_header.size * sizeof(u32));
83 } else {
84 memory_manager.ReadBlockUnsafe(dma_state.dma_get, command_headers.data(),
85 command_list_header.size * sizeof(u32));
86 }
87 } else {
88 const size_t copy_size = command_list_header.size * sizeof(u32);
89 if (subchannels[dma_state.subchannel]) { 81 if (subchannels[dma_state.subchannel]) {
90 subchannels[dma_state.subchannel]->current_dirty = 82 subchannels[dma_state.subchannel]->current_dirty = memory_manager.IsMemoryDirty(
91 memory_manager.IsMemoryDirty(dma_state.dma_get, copy_size); 83 dma_state.dma_get, command_list_header.size * sizeof(u32));
92 } 84 }
93 memory_manager.ReadBlockUnsafe(dma_state.dma_get, command_headers.data(), copy_size);
94 } 85 }
95 ProcessCommands(command_headers); 86 Core::Memory::GpuGuestMemory<Tegra::CommandHeader,
87 Core::Memory::GuestMemoryFlags::UnsafeRead>
88 headers(memory_manager, dma_state.dma_get, command_list_header.size, &command_headers);
89 ProcessCommands(headers);
96 } 90 }
97 91
98 return true; 92 return true;
diff --git a/src/video_core/engines/engine_upload.cpp b/src/video_core/engines/engine_upload.cpp
index 7f5a0c29d..bc64d4486 100644
--- a/src/video_core/engines/engine_upload.cpp
+++ b/src/video_core/engines/engine_upload.cpp
@@ -5,6 +5,7 @@
5 5
6#include "common/algorithm.h" 6#include "common/algorithm.h"
7#include "common/assert.h" 7#include "common/assert.h"
8#include "core/memory.h"
8#include "video_core/engines/engine_upload.h" 9#include "video_core/engines/engine_upload.h"
9#include "video_core/memory_manager.h" 10#include "video_core/memory_manager.h"
10#include "video_core/rasterizer_interface.h" 11#include "video_core/rasterizer_interface.h"
@@ -46,15 +47,11 @@ void State::ProcessData(const u32* data, size_t num_data) {
46void State::ProcessData(std::span<const u8> read_buffer) { 47void State::ProcessData(std::span<const u8> read_buffer) {
47 const GPUVAddr address{regs.dest.Address()}; 48 const GPUVAddr address{regs.dest.Address()};
48 if (is_linear) { 49 if (is_linear) {
49 if (regs.line_count == 1) { 50 for (size_t line = 0; line < regs.line_count; ++line) {
50 rasterizer->AccelerateInlineToMemory(address, copy_size, read_buffer); 51 const GPUVAddr dest_line = address + line * regs.dest.pitch;
51 } else { 52 std::span<const u8> buffer(read_buffer.data() + line * regs.line_length_in,
52 for (size_t line = 0; line < regs.line_count; ++line) { 53 regs.line_length_in);
53 const GPUVAddr dest_line = address + line * regs.dest.pitch; 54 rasterizer->AccelerateInlineToMemory(dest_line, regs.line_length_in, buffer);
54 std::span<const u8> buffer(read_buffer.data() + line * regs.line_length_in,
55 regs.line_length_in);
56 rasterizer->AccelerateInlineToMemory(dest_line, regs.line_length_in, buffer);
57 }
58 } 55 }
59 } else { 56 } else {
60 u32 width = regs.dest.width; 57 u32 width = regs.dest.width;
@@ -70,13 +67,14 @@ void State::ProcessData(std::span<const u8> read_buffer) {
70 const std::size_t dst_size = Tegra::Texture::CalculateSize( 67 const std::size_t dst_size = Tegra::Texture::CalculateSize(
71 true, bytes_per_pixel, width, regs.dest.height, regs.dest.depth, 68 true, bytes_per_pixel, width, regs.dest.height, regs.dest.depth,
72 regs.dest.BlockHeight(), regs.dest.BlockDepth()); 69 regs.dest.BlockHeight(), regs.dest.BlockDepth());
73 tmp_buffer.resize_destructive(dst_size); 70
74 memory_manager.ReadBlock(address, tmp_buffer.data(), dst_size); 71 Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite>
75 Tegra::Texture::SwizzleSubrect(tmp_buffer, read_buffer, bytes_per_pixel, width, 72 tmp(memory_manager, address, dst_size, &tmp_buffer);
76 regs.dest.height, regs.dest.depth, x_offset, regs.dest.y, 73
77 x_elements, regs.line_count, regs.dest.BlockHeight(), 74 Tegra::Texture::SwizzleSubrect(tmp, read_buffer, bytes_per_pixel, width, regs.dest.height,
75 regs.dest.depth, x_offset, regs.dest.y, x_elements,
76 regs.line_count, regs.dest.BlockHeight(),
78 regs.dest.BlockDepth(), regs.line_length_in); 77 regs.dest.BlockDepth(), regs.line_length_in);
79 memory_manager.WriteBlockCached(address, tmp_buffer.data(), dst_size);
80 } 78 }
81} 79}
82 80
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index 601095f03..a38d9528a 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -84,7 +84,6 @@ Texture::TICEntry KeplerCompute::GetTICEntry(u32 tic_index) const {
84 84
85 Texture::TICEntry tic_entry; 85 Texture::TICEntry tic_entry;
86 memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry)); 86 memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry));
87
88 return tic_entry; 87 return tic_entry;
89} 88}
90 89
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 62d70e9f3..c3696096d 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -9,6 +9,7 @@
9#include "common/settings.h" 9#include "common/settings.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/core_timing.h" 11#include "core/core_timing.h"
12#include "core/memory.h"
12#include "video_core/dirty_flags.h" 13#include "video_core/dirty_flags.h"
13#include "video_core/engines/draw_manager.h" 14#include "video_core/engines/draw_manager.h"
14#include "video_core/engines/maxwell_3d.h" 15#include "video_core/engines/maxwell_3d.h"
@@ -679,17 +680,14 @@ void Maxwell3D::ProcessCBData(u32 value) {
679Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { 680Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
680 const GPUVAddr tic_address_gpu{regs.tex_header.Address() + 681 const GPUVAddr tic_address_gpu{regs.tex_header.Address() +
681 tic_index * sizeof(Texture::TICEntry)}; 682 tic_index * sizeof(Texture::TICEntry)};
682
683 Texture::TICEntry tic_entry; 683 Texture::TICEntry tic_entry;
684 memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry)); 684 memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry));
685
686 return tic_entry; 685 return tic_entry;
687} 686}
688 687
689Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const { 688Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const {
690 const GPUVAddr tsc_address_gpu{regs.tex_sampler.Address() + 689 const GPUVAddr tsc_address_gpu{regs.tex_sampler.Address() +
691 tsc_index * sizeof(Texture::TSCEntry)}; 690 tsc_index * sizeof(Texture::TSCEntry)};
692
693 Texture::TSCEntry tsc_entry; 691 Texture::TSCEntry tsc_entry;
694 memory_manager.ReadBlockUnsafe(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry)); 692 memory_manager.ReadBlockUnsafe(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry));
695 return tsc_entry; 693 return tsc_entry;
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index f8598fd98..cd8e24b0b 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -7,6 +7,7 @@
7#include "common/microprofile.h" 7#include "common/microprofile.h"
8#include "common/settings.h" 8#include "common/settings.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/memory.h"
10#include "video_core/engines/maxwell_3d.h" 11#include "video_core/engines/maxwell_3d.h"
11#include "video_core/engines/maxwell_dma.h" 12#include "video_core/engines/maxwell_dma.h"
12#include "video_core/memory_manager.h" 13#include "video_core/memory_manager.h"
@@ -130,11 +131,12 @@ void MaxwellDMA::Launch() {
130 UNIMPLEMENTED_IF(regs.offset_out % 16 != 0); 131 UNIMPLEMENTED_IF(regs.offset_out % 16 != 0);
131 read_buffer.resize_destructive(16); 132 read_buffer.resize_destructive(16);
132 for (u32 offset = 0; offset < regs.line_length_in; offset += 16) { 133 for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
133 memory_manager.ReadBlock( 134 Core::Memory::GpuGuestMemoryScoped<
134 convert_linear_2_blocklinear_addr(regs.offset_in + offset), 135 u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite>
135 read_buffer.data(), read_buffer.size()); 136 tmp_write_buffer(memory_manager,
136 memory_manager.WriteBlockCached(regs.offset_out + offset, read_buffer.data(), 137 convert_linear_2_blocklinear_addr(regs.offset_in + offset),
137 read_buffer.size()); 138 16, &read_buffer);
139 tmp_write_buffer.SetAddressAndSize(regs.offset_out + offset, 16);
138 } 140 }
139 } else if (is_src_pitch && !is_dst_pitch) { 141 } else if (is_src_pitch && !is_dst_pitch) {
140 UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0); 142 UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0);
@@ -142,20 +144,19 @@ void MaxwellDMA::Launch() {
142 UNIMPLEMENTED_IF(regs.offset_out % 16 != 0); 144 UNIMPLEMENTED_IF(regs.offset_out % 16 != 0);
143 read_buffer.resize_destructive(16); 145 read_buffer.resize_destructive(16);
144 for (u32 offset = 0; offset < regs.line_length_in; offset += 16) { 146 for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
145 memory_manager.ReadBlock(regs.offset_in + offset, read_buffer.data(), 147 Core::Memory::GpuGuestMemoryScoped<
146 read_buffer.size()); 148 u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite>
147 memory_manager.WriteBlockCached( 149 tmp_write_buffer(memory_manager, regs.offset_in + offset, 16, &read_buffer);
148 convert_linear_2_blocklinear_addr(regs.offset_out + offset), 150 tmp_write_buffer.SetAddressAndSize(
149 read_buffer.data(), read_buffer.size()); 151 convert_linear_2_blocklinear_addr(regs.offset_out + offset), 16);
150 } 152 }
151 } else { 153 } else {
152 if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) { 154 if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) {
153 read_buffer.resize_destructive(regs.line_length_in); 155 Core::Memory::GpuGuestMemoryScoped<
154 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), 156 u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite>
155 regs.line_length_in, 157 tmp_write_buffer(memory_manager, regs.offset_in, regs.line_length_in,
156 VideoCommon::CacheType::NoBufferCache); 158 &read_buffer);
157 memory_manager.WriteBlockCached(regs.offset_out, read_buffer.data(), 159 tmp_write_buffer.SetAddressAndSize(regs.offset_out, regs.line_length_in);
158 regs.line_length_in);
159 } 160 }
160 } 161 }
161 } 162 }
@@ -222,17 +223,15 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
222 CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth); 223 CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
223 224
224 const size_t dst_size = dst_operand.pitch * regs.line_count; 225 const size_t dst_size = dst_operand.pitch * regs.line_count;
225 read_buffer.resize_destructive(src_size);
226 write_buffer.resize_destructive(dst_size);
227 226
228 memory_manager.ReadBlock(src_operand.address, read_buffer.data(), src_size); 227 Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer(
229 memory_manager.ReadBlock(dst_operand.address, write_buffer.data(), dst_size); 228 memory_manager, src_operand.address, src_size, &read_buffer);
229 Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite>
230 tmp_write_buffer(memory_manager, dst_operand.address, dst_size, &write_buffer);
230 231
231 UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset, 232 UnswizzleSubrect(tmp_write_buffer, tmp_read_buffer, bytes_per_pixel, width, height, depth,
232 src_params.origin.y, x_elements, regs.line_count, block_height, block_depth, 233 x_offset, src_params.origin.y, x_elements, regs.line_count, block_height,
233 dst_operand.pitch); 234 block_depth, dst_operand.pitch);
234
235 memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size);
236} 235}
237 236
238void MaxwellDMA::CopyPitchToBlockLinear() { 237void MaxwellDMA::CopyPitchToBlockLinear() {
@@ -287,18 +286,17 @@ void MaxwellDMA::CopyPitchToBlockLinear() {
287 CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth); 286 CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
288 const size_t src_size = static_cast<size_t>(regs.pitch_in) * regs.line_count; 287 const size_t src_size = static_cast<size_t>(regs.pitch_in) * regs.line_count;
289 288
290 read_buffer.resize_destructive(src_size); 289 GPUVAddr src_addr = regs.offset_in;
291 write_buffer.resize_destructive(dst_size); 290 GPUVAddr dst_addr = regs.offset_out;
292 291 Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer(
293 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); 292 memory_manager, src_addr, src_size, &read_buffer);
294 memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size); 293 Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite>
295 294 tmp_write_buffer(memory_manager, dst_addr, dst_size, &write_buffer);
296 // If the input is linear and the output is tiled, swizzle the input and copy it over. 295
297 SwizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset, 296 // If the input is linear and the output is tiled, swizzle the input and copy it over.
298 dst_params.origin.y, x_elements, regs.line_count, block_height, block_depth, 297 SwizzleSubrect(tmp_write_buffer, tmp_read_buffer, bytes_per_pixel, width, height, depth,
299 regs.pitch_in); 298 x_offset, dst_params.origin.y, x_elements, regs.line_count, block_height,
300 299 block_depth, regs.pitch_in);
301 memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size);
302} 300}
303 301
304void MaxwellDMA::CopyBlockLinearToBlockLinear() { 302void MaxwellDMA::CopyBlockLinearToBlockLinear() {
@@ -342,23 +340,20 @@ void MaxwellDMA::CopyBlockLinearToBlockLinear() {
342 const u32 pitch = x_elements * bytes_per_pixel; 340 const u32 pitch = x_elements * bytes_per_pixel;
343 const size_t mid_buffer_size = pitch * regs.line_count; 341 const size_t mid_buffer_size = pitch * regs.line_count;
344 342
345 read_buffer.resize_destructive(src_size);
346 write_buffer.resize_destructive(dst_size);
347
348 intermediate_buffer.resize_destructive(mid_buffer_size); 343 intermediate_buffer.resize_destructive(mid_buffer_size);
349 344
350 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); 345 Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer(
351 memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size); 346 memory_manager, regs.offset_in, src_size, &read_buffer);
347 Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite>
348 tmp_write_buffer(memory_manager, regs.offset_out, dst_size, &write_buffer);
352 349
353 UnswizzleSubrect(intermediate_buffer, read_buffer, bytes_per_pixel, src_width, src.height, 350 UnswizzleSubrect(intermediate_buffer, tmp_read_buffer, bytes_per_pixel, src_width, src.height,
354 src.depth, src_x_offset, src.origin.y, x_elements, regs.line_count, 351 src.depth, src_x_offset, src.origin.y, x_elements, regs.line_count,
355 src.block_size.height, src.block_size.depth, pitch); 352 src.block_size.height, src.block_size.depth, pitch);
356 353
357 SwizzleSubrect(write_buffer, intermediate_buffer, bytes_per_pixel, dst_width, dst.height, 354 SwizzleSubrect(tmp_write_buffer, intermediate_buffer, bytes_per_pixel, dst_width, dst.height,
358 dst.depth, dst_x_offset, dst.origin.y, x_elements, regs.line_count, 355 dst.depth, dst_x_offset, dst.origin.y, x_elements, regs.line_count,
359 dst.block_size.height, dst.block_size.depth, pitch); 356 dst.block_size.height, dst.block_size.depth, pitch);
360
361 memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size);
362} 357}
363 358
364void MaxwellDMA::ReleaseSemaphore() { 359void MaxwellDMA::ReleaseSemaphore() {
diff --git a/src/video_core/engines/sw_blitter/blitter.cpp b/src/video_core/engines/sw_blitter/blitter.cpp
index ff88cd03d..3a599f466 100644
--- a/src/video_core/engines/sw_blitter/blitter.cpp
+++ b/src/video_core/engines/sw_blitter/blitter.cpp
@@ -159,11 +159,11 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
159 const auto src_bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format)); 159 const auto src_bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format));
160 const auto dst_bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(dst.format)); 160 const auto dst_bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(dst.format));
161 const size_t src_size = get_surface_size(src, src_bytes_per_pixel); 161 const size_t src_size = get_surface_size(src, src_bytes_per_pixel);
162 impl->tmp_buffer.resize_destructive(src_size);
163 memory_manager.ReadBlock(src.Address(), impl->tmp_buffer.data(), src_size);
164 162
165 const size_t src_copy_size = src_extent_x * src_extent_y * src_bytes_per_pixel; 163 Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_buffer(
164 memory_manager, src.Address(), src_size, &impl->tmp_buffer);
166 165
166 const size_t src_copy_size = src_extent_x * src_extent_y * src_bytes_per_pixel;
167 const size_t dst_copy_size = dst_extent_x * dst_extent_y * dst_bytes_per_pixel; 167 const size_t dst_copy_size = dst_extent_x * dst_extent_y * dst_bytes_per_pixel;
168 168
169 impl->src_buffer.resize_destructive(src_copy_size); 169 impl->src_buffer.resize_destructive(src_copy_size);
@@ -200,12 +200,11 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
200 200
201 impl->dst_buffer.resize_destructive(dst_copy_size); 201 impl->dst_buffer.resize_destructive(dst_copy_size);
202 if (src.linear == Fermi2D::MemoryLayout::BlockLinear) { 202 if (src.linear == Fermi2D::MemoryLayout::BlockLinear) {
203 UnswizzleSubrect(impl->src_buffer, impl->tmp_buffer, src_bytes_per_pixel, src.width, 203 UnswizzleSubrect(impl->src_buffer, tmp_buffer, src_bytes_per_pixel, src.width, src.height,
204 src.height, src.depth, config.src_x0, config.src_y0, src_extent_x, 204 src.depth, config.src_x0, config.src_y0, src_extent_x, src_extent_y,
205 src_extent_y, src.block_height, src.block_depth, 205 src.block_height, src.block_depth, src_extent_x * src_bytes_per_pixel);
206 src_extent_x * src_bytes_per_pixel);
207 } else { 206 } else {
208 process_pitch_linear(false, impl->tmp_buffer, impl->src_buffer, src_extent_x, src_extent_y, 207 process_pitch_linear(false, tmp_buffer, impl->src_buffer, src_extent_x, src_extent_y,
209 src.pitch, config.src_x0, config.src_y0, src_bytes_per_pixel); 208 src.pitch, config.src_x0, config.src_y0, src_bytes_per_pixel);
210 } 209 }
211 210
@@ -221,20 +220,18 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
221 } 220 }
222 221
223 const size_t dst_size = get_surface_size(dst, dst_bytes_per_pixel); 222 const size_t dst_size = get_surface_size(dst, dst_bytes_per_pixel);
224 impl->tmp_buffer.resize_destructive(dst_size); 223 Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadWrite>
225 memory_manager.ReadBlock(dst.Address(), impl->tmp_buffer.data(), dst_size); 224 tmp_buffer2(memory_manager, dst.Address(), dst_size, &impl->tmp_buffer);
226 225
227 if (dst.linear == Fermi2D::MemoryLayout::BlockLinear) { 226 if (dst.linear == Fermi2D::MemoryLayout::BlockLinear) {
228 SwizzleSubrect(impl->tmp_buffer, impl->dst_buffer, dst_bytes_per_pixel, dst.width, 227 SwizzleSubrect(tmp_buffer2, impl->dst_buffer, dst_bytes_per_pixel, dst.width, dst.height,
229 dst.height, dst.depth, config.dst_x0, config.dst_y0, dst_extent_x, 228 dst.depth, config.dst_x0, config.dst_y0, dst_extent_x, dst_extent_y,
230 dst_extent_y, dst.block_height, dst.block_depth, 229 dst.block_height, dst.block_depth, dst_extent_x * dst_bytes_per_pixel);
231 dst_extent_x * dst_bytes_per_pixel);
232 } else { 230 } else {
233 process_pitch_linear(true, impl->dst_buffer, impl->tmp_buffer, dst_extent_x, dst_extent_y, 231 process_pitch_linear(true, impl->dst_buffer, tmp_buffer2, dst_extent_x, dst_extent_y,
234 dst.pitch, config.dst_x0, config.dst_y0, 232 dst.pitch, config.dst_x0, config.dst_y0,
235 static_cast<size_t>(dst_bytes_per_pixel)); 233 static_cast<size_t>(dst_bytes_per_pixel));
236 } 234 }
237 memory_manager.WriteBlock(dst.Address(), impl->tmp_buffer.data(), dst_size);
238 return true; 235 return true;
239} 236}
240 237
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 45141e488..d16040613 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -10,13 +10,13 @@
10#include "core/device_memory.h" 10#include "core/device_memory.h"
11#include "core/hle/kernel/k_page_table.h" 11#include "core/hle/kernel/k_page_table.h"
12#include "core/hle/kernel/k_process.h" 12#include "core/hle/kernel/k_process.h"
13#include "core/memory.h"
14#include "video_core/invalidation_accumulator.h" 13#include "video_core/invalidation_accumulator.h"
15#include "video_core/memory_manager.h" 14#include "video_core/memory_manager.h"
16#include "video_core/rasterizer_interface.h" 15#include "video_core/rasterizer_interface.h"
17#include "video_core/renderer_base.h" 16#include "video_core/renderer_base.h"
18 17
19namespace Tegra { 18namespace Tegra {
19using Core::Memory::GuestMemoryFlags;
20 20
21std::atomic<size_t> MemoryManager::unique_identifier_generator{}; 21std::atomic<size_t> MemoryManager::unique_identifier_generator{};
22 22
@@ -587,13 +587,10 @@ void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size,
587 587
588void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size, 588void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size,
589 VideoCommon::CacheType which) { 589 VideoCommon::CacheType which) {
590 tmp_buffer.resize_destructive(size); 590 Core::Memory::GpuGuestMemoryScoped<u8, GuestMemoryFlags::SafeReadWrite> data(
591 ReadBlock(gpu_src_addr, tmp_buffer.data(), size, which); 591 *this, gpu_src_addr, size);
592 592 data.SetAddressAndSize(gpu_dest_addr, size);
593 // The output block must be flushed in case it has data modified from the GPU.
594 // Fixes NPC geometry in Zombie Panic in Wonderland DX
595 FlushRegion(gpu_dest_addr, size, which); 593 FlushRegion(gpu_dest_addr, size, which);
596 WriteBlock(gpu_dest_addr, tmp_buffer.data(), size, which);
597} 594}
598 595
599bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const { 596bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {
@@ -758,4 +755,23 @@ void MemoryManager::FlushCaching() {
758 accumulator->Clear(); 755 accumulator->Clear();
759} 756}
760 757
758const u8* MemoryManager::GetSpan(const GPUVAddr src_addr, const std::size_t size) const {
759 auto cpu_addr = GpuToCpuAddress(src_addr);
760 if (cpu_addr) {
761 return memory.GetSpan(*cpu_addr, size);
762 }
763 return nullptr;
764}
765
766u8* MemoryManager::GetSpan(const GPUVAddr src_addr, const std::size_t size) {
767 if (!IsContinuousRange(src_addr, size)) {
768 return nullptr;
769 }
770 auto cpu_addr = GpuToCpuAddress(src_addr);
771 if (cpu_addr) {
772 return memory.GetSpan(*cpu_addr, size);
773 }
774 return nullptr;
775}
776
761} // namespace Tegra 777} // namespace Tegra
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 4202c26ff..9b311b9e5 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -15,6 +15,7 @@
15#include "common/range_map.h" 15#include "common/range_map.h"
16#include "common/scratch_buffer.h" 16#include "common/scratch_buffer.h"
17#include "common/virtual_buffer.h" 17#include "common/virtual_buffer.h"
18#include "core/memory.h"
18#include "video_core/cache_types.h" 19#include "video_core/cache_types.h"
19#include "video_core/pte_kind.h" 20#include "video_core/pte_kind.h"
20 21
@@ -62,6 +63,20 @@ public:
62 [[nodiscard]] u8* GetPointer(GPUVAddr addr); 63 [[nodiscard]] u8* GetPointer(GPUVAddr addr);
63 [[nodiscard]] const u8* GetPointer(GPUVAddr addr) const; 64 [[nodiscard]] const u8* GetPointer(GPUVAddr addr) const;
64 65
66 template <typename T>
67 [[nodiscard]] T* GetPointer(GPUVAddr addr) {
68 const auto address{GpuToCpuAddress(addr)};
69 if (!address) {
70 return {};
71 }
72 return memory.GetPointer(*address);
73 }
74
75 template <typename T>
76 [[nodiscard]] const T* GetPointer(GPUVAddr addr) const {
77 return GetPointer<T*>(addr);
78 }
79
65 /** 80 /**
66 * ReadBlock and WriteBlock are full read and write operations over virtual 81 * ReadBlock and WriteBlock are full read and write operations over virtual
67 * GPU Memory. It's important to use these when GPU memory may not be continuous 82 * GPU Memory. It's important to use these when GPU memory may not be continuous
@@ -139,6 +154,9 @@ public:
139 154
140 void FlushCaching(); 155 void FlushCaching();
141 156
157 const u8* GetSpan(const GPUVAddr src_addr, const std::size_t size) const;
158 u8* GetSpan(const GPUVAddr src_addr, const std::size_t size);
159
142private: 160private:
143 template <bool is_big_pages, typename FuncMapped, typename FuncReserved, typename FuncUnmapped> 161 template <bool is_big_pages, typename FuncMapped, typename FuncReserved, typename FuncUnmapped>
144 inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped, 162 inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped,
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index 2d3f58201..4002fa72b 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -38,8 +38,8 @@ void RendererBase::RequestScreenshot(void* data, std::function<void(bool)> callb
38 LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request"); 38 LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request");
39 return; 39 return;
40 } 40 }
41 auto async_callback{[callback = std::move(callback)](bool invert_y) { 41 auto async_callback{[callback_ = std::move(callback)](bool invert_y) {
42 std::thread t{callback, invert_y}; 42 std::thread t{callback_, invert_y};
43 t.detach(); 43 t.detach();
44 }}; 44 }};
45 renderer_settings.screenshot_bits = data; 45 renderer_settings.screenshot_bits = data;
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index 23a48c6fe..71f720c63 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -231,24 +231,25 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
231 } 231 }
232 const bool in_parallel = thread_worker != nullptr; 232 const bool in_parallel = thread_worker != nullptr;
233 const auto backend = device.GetShaderBackend(); 233 const auto backend = device.GetShaderBackend();
234 auto func{[this, sources = std::move(sources), sources_spirv = std::move(sources_spirv), 234 auto func{[this, sources_ = std::move(sources), sources_spirv_ = std::move(sources_spirv),
235 shader_notify, backend, in_parallel, 235 shader_notify, backend, in_parallel,
236 force_context_flush](ShaderContext::Context*) mutable { 236 force_context_flush](ShaderContext::Context*) mutable {
237 for (size_t stage = 0; stage < 5; ++stage) { 237 for (size_t stage = 0; stage < 5; ++stage) {
238 switch (backend) { 238 switch (backend) {
239 case Settings::ShaderBackend::GLSL: 239 case Settings::ShaderBackend::GLSL:
240 if (!sources[stage].empty()) { 240 if (!sources_[stage].empty()) {
241 source_programs[stage] = CreateProgram(sources[stage], Stage(stage)); 241 source_programs[stage] = CreateProgram(sources_[stage], Stage(stage));
242 } 242 }
243 break; 243 break;
244 case Settings::ShaderBackend::GLASM: 244 case Settings::ShaderBackend::GLASM:
245 if (!sources[stage].empty()) { 245 if (!sources_[stage].empty()) {
246 assembly_programs[stage] = CompileProgram(sources[stage], AssemblyStage(stage)); 246 assembly_programs[stage] =
247 CompileProgram(sources_[stage], AssemblyStage(stage));
247 } 248 }
248 break; 249 break;
249 case Settings::ShaderBackend::SPIRV: 250 case Settings::ShaderBackend::SPIRV:
250 if (!sources_spirv[stage].empty()) { 251 if (!sources_spirv_[stage].empty()) {
251 source_programs[stage] = CreateProgram(sources_spirv[stage], Stage(stage)); 252 source_programs[stage] = CreateProgram(sources_spirv_[stage], Stage(stage));
252 } 253 }
253 break; 254 break;
254 } 255 }
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 0329ed820..7e1d7f92e 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -288,9 +288,9 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
288 const auto load_compute{[&](std::ifstream& file, FileEnvironment env) { 288 const auto load_compute{[&](std::ifstream& file, FileEnvironment env) {
289 ComputePipelineKey key; 289 ComputePipelineKey key;
290 file.read(reinterpret_cast<char*>(&key), sizeof(key)); 290 file.read(reinterpret_cast<char*>(&key), sizeof(key));
291 queue_work([this, key, env = std::move(env), &state, &callback](Context* ctx) mutable { 291 queue_work([this, key, env_ = std::move(env), &state, &callback](Context* ctx) mutable {
292 ctx->pools.ReleaseContents(); 292 ctx->pools.ReleaseContents();
293 auto pipeline{CreateComputePipeline(ctx->pools, key, env, true)}; 293 auto pipeline{CreateComputePipeline(ctx->pools, key, env_, true)};
294 std::scoped_lock lock{state.mutex}; 294 std::scoped_lock lock{state.mutex};
295 if (pipeline) { 295 if (pipeline) {
296 compute_cache.emplace(key, std::move(pipeline)); 296 compute_cache.emplace(key, std::move(pipeline));
@@ -305,9 +305,9 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
305 const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) { 305 const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) {
306 GraphicsPipelineKey key; 306 GraphicsPipelineKey key;
307 file.read(reinterpret_cast<char*>(&key), sizeof(key)); 307 file.read(reinterpret_cast<char*>(&key), sizeof(key));
308 queue_work([this, key, envs = std::move(envs), &state, &callback](Context* ctx) mutable { 308 queue_work([this, key, envs_ = std::move(envs), &state, &callback](Context* ctx) mutable {
309 boost::container::static_vector<Shader::Environment*, 5> env_ptrs; 309 boost::container::static_vector<Shader::Environment*, 5> env_ptrs;
310 for (auto& env : envs) { 310 for (auto& env : envs_) {
311 env_ptrs.push_back(&env); 311 env_ptrs.push_back(&env);
312 } 312 }
313 ctx->pools.ReleaseContents(); 313 ctx->pools.ReleaseContents();
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 51df18ec3..f8cd2a5d8 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -206,8 +206,8 @@ public:
206 const size_t sub_first_offset = static_cast<size_t>(first % 4) * GetQuadsNum(num_indices); 206 const size_t sub_first_offset = static_cast<size_t>(first % 4) * GetQuadsNum(num_indices);
207 const size_t offset = 207 const size_t offset =
208 (sub_first_offset + GetQuadsNum(first)) * 6ULL * BytesPerIndex(index_type); 208 (sub_first_offset + GetQuadsNum(first)) * 6ULL * BytesPerIndex(index_type);
209 scheduler.Record([buffer = *buffer, index_type_, offset](vk::CommandBuffer cmdbuf) { 209 scheduler.Record([buffer_ = *buffer, index_type_, offset](vk::CommandBuffer cmdbuf) {
210 cmdbuf.BindIndexBuffer(buffer, offset, index_type_); 210 cmdbuf.BindIndexBuffer(buffer_, offset, index_type_);
211 }); 211 });
212 } 212 }
213 213
@@ -528,17 +528,18 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bi
528 buffer_handles.push_back(handle); 528 buffer_handles.push_back(handle);
529 } 529 }
530 if (device.IsExtExtendedDynamicStateSupported()) { 530 if (device.IsExtExtendedDynamicStateSupported()) {
531 scheduler.Record([bindings = std::move(bindings), 531 scheduler.Record([bindings_ = std::move(bindings),
532 buffer_handles = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { 532 buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) {
533 cmdbuf.BindVertexBuffers2EXT( 533 cmdbuf.BindVertexBuffers2EXT(bindings_.min_index,
534 bindings.min_index, bindings.max_index - bindings.min_index, buffer_handles.data(), 534 bindings_.max_index - bindings_.min_index,
535 bindings.offsets.data(), bindings.sizes.data(), bindings.strides.data()); 535 buffer_handles_.data(), bindings_.offsets.data(),
536 bindings_.sizes.data(), bindings_.strides.data());
536 }); 537 });
537 } else { 538 } else {
538 scheduler.Record([bindings = std::move(bindings), 539 scheduler.Record([bindings_ = std::move(bindings),
539 buffer_handles = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { 540 buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) {
540 cmdbuf.BindVertexBuffers(bindings.min_index, bindings.max_index - bindings.min_index, 541 cmdbuf.BindVertexBuffers(bindings_.min_index, bindings_.max_index - bindings_.min_index,
541 buffer_handles.data(), bindings.offsets.data()); 542 buffer_handles_.data(), bindings_.offsets.data());
542 }); 543 });
543 } 544 }
544} 545}
@@ -573,11 +574,11 @@ void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings<
573 for (u32 index = 0; index < bindings.buffers.size(); ++index) { 574 for (u32 index = 0; index < bindings.buffers.size(); ++index) {
574 buffer_handles.push_back(bindings.buffers[index]->Handle()); 575 buffer_handles.push_back(bindings.buffers[index]->Handle());
575 } 576 }
576 scheduler.Record([bindings = std::move(bindings), 577 scheduler.Record([bindings_ = std::move(bindings),
577 buffer_handles = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { 578 buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) {
578 cmdbuf.BindTransformFeedbackBuffersEXT(0, static_cast<u32>(buffer_handles.size()), 579 cmdbuf.BindTransformFeedbackBuffersEXT(0, static_cast<u32>(buffer_handles_.size()),
579 buffer_handles.data(), bindings.offsets.data(), 580 buffer_handles_.data(), bindings_.offsets.data(),
580 bindings.sizes.data()); 581 bindings_.sizes.data());
581 }); 582 });
582} 583}
583 584
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index d600c4e61..4f84d8497 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -469,9 +469,9 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
469 ComputePipelineCacheKey key; 469 ComputePipelineCacheKey key;
470 file.read(reinterpret_cast<char*>(&key), sizeof(key)); 470 file.read(reinterpret_cast<char*>(&key), sizeof(key));
471 471
472 workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable { 472 workers.QueueWork([this, key, env_ = std::move(env), &state, &callback]() mutable {
473 ShaderPools pools; 473 ShaderPools pools;
474 auto pipeline{CreateComputePipeline(pools, key, env, state.statistics.get(), false)}; 474 auto pipeline{CreateComputePipeline(pools, key, env_, state.statistics.get(), false)};
475 std::scoped_lock lock{state.mutex}; 475 std::scoped_lock lock{state.mutex};
476 if (pipeline) { 476 if (pipeline) {
477 compute_cache.emplace(key, std::move(pipeline)); 477 compute_cache.emplace(key, std::move(pipeline));
@@ -500,10 +500,10 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
500 (key.state.dynamic_vertex_input != 0) != dynamic_features.has_dynamic_vertex_input) { 500 (key.state.dynamic_vertex_input != 0) != dynamic_features.has_dynamic_vertex_input) {
501 return; 501 return;
502 } 502 }
503 workers.QueueWork([this, key, envs = std::move(envs), &state, &callback]() mutable { 503 workers.QueueWork([this, key, envs_ = std::move(envs), &state, &callback]() mutable {
504 ShaderPools pools; 504 ShaderPools pools;
505 boost::container::static_vector<Shader::Environment*, 5> env_ptrs; 505 boost::container::static_vector<Shader::Environment*, 5> env_ptrs;
506 for (auto& env : envs) { 506 for (auto& env : envs_) {
507 env_ptrs.push_back(&env); 507 env_ptrs.push_back(&env);
508 } 508 }
509 auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs), 509 auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs),
@@ -702,8 +702,8 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
702 if (!pipeline || pipeline_cache_filename.empty()) { 702 if (!pipeline || pipeline_cache_filename.empty()) {
703 return pipeline; 703 return pipeline;
704 } 704 }
705 serialization_thread.QueueWork([this, key, env = std::move(env)] { 705 serialization_thread.QueueWork([this, key, env_ = std::move(env)] {
706 SerializePipeline(key, std::array<const GenericEnvironment*, 1>{&env}, 706 SerializePipeline(key, std::array<const GenericEnvironment*, 1>{&env_},
707 pipeline_cache_filename, CACHE_VERSION); 707 pipeline_cache_filename, CACHE_VERSION);
708 }); 708 });
709 return pipeline; 709 return pipeline;
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index d67490449..29e0b797b 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -98,10 +98,10 @@ HostCounter::HostCounter(QueryCache& cache_, std::shared_ptr<HostCounter> depend
98 : HostCounterBase{std::move(dependency_)}, cache{cache_}, type{type_}, 98 : HostCounterBase{std::move(dependency_)}, cache{cache_}, type{type_},
99 query{cache_.AllocateQuery(type_)}, tick{cache_.GetScheduler().CurrentTick()} { 99 query{cache_.AllocateQuery(type_)}, tick{cache_.GetScheduler().CurrentTick()} {
100 const vk::Device* logical = &cache.GetDevice().GetLogical(); 100 const vk::Device* logical = &cache.GetDevice().GetLogical();
101 cache.GetScheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) { 101 cache.GetScheduler().Record([logical, query_ = query](vk::CommandBuffer cmdbuf) {
102 const bool use_precise = Settings::IsGPULevelHigh(); 102 const bool use_precise = Settings::IsGPULevelHigh();
103 logical->ResetQueryPool(query.first, query.second, 1); 103 logical->ResetQueryPool(query_.first, query_.second, 1);
104 cmdbuf.BeginQuery(query.first, query.second, 104 cmdbuf.BeginQuery(query_.first, query_.second,
105 use_precise ? VK_QUERY_CONTROL_PRECISE_BIT : 0); 105 use_precise ? VK_QUERY_CONTROL_PRECISE_BIT : 0);
106 }); 106 });
107} 107}
@@ -111,8 +111,9 @@ HostCounter::~HostCounter() {
111} 111}
112 112
113void HostCounter::EndQuery() { 113void HostCounter::EndQuery() {
114 cache.GetScheduler().Record( 114 cache.GetScheduler().Record([query_ = query](vk::CommandBuffer cmdbuf) {
115 [query = query](vk::CommandBuffer cmdbuf) { cmdbuf.EndQuery(query.first, query.second); }); 115 cmdbuf.EndQuery(query_.first, query_.second);
116 });
116} 117}
117 118
118u64 HostCounter::BlockingQuery(bool async) const { 119u64 HostCounter::BlockingQuery(bool async) const {
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 3aac3cfab..bf6ad6c79 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -1412,7 +1412,7 @@ void Image::DownloadMemory(std::span<VkBuffer> buffers_span, std::span<VkDeviceS
1412 } 1412 }
1413 scheduler->RequestOutsideRenderPassOperationContext(); 1413 scheduler->RequestOutsideRenderPassOperationContext();
1414 scheduler->Record([buffers = std::move(buffers_vector), image = *original_image, 1414 scheduler->Record([buffers = std::move(buffers_vector), image = *original_image,
1415 aspect_mask = aspect_mask, vk_copies](vk::CommandBuffer cmdbuf) { 1415 aspect_mask_ = aspect_mask, vk_copies](vk::CommandBuffer cmdbuf) {
1416 const VkImageMemoryBarrier read_barrier{ 1416 const VkImageMemoryBarrier read_barrier{
1417 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 1417 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
1418 .pNext = nullptr, 1418 .pNext = nullptr,
@@ -1424,7 +1424,7 @@ void Image::DownloadMemory(std::span<VkBuffer> buffers_span, std::span<VkDeviceS
1424 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 1424 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
1425 .image = image, 1425 .image = image,
1426 .subresourceRange{ 1426 .subresourceRange{
1427 .aspectMask = aspect_mask, 1427 .aspectMask = aspect_mask_,
1428 .baseMipLevel = 0, 1428 .baseMipLevel = 0,
1429 .levelCount = VK_REMAINING_MIP_LEVELS, 1429 .levelCount = VK_REMAINING_MIP_LEVELS,
1430 .baseArrayLayer = 0, 1430 .baseArrayLayer = 0,
@@ -1456,7 +1456,7 @@ void Image::DownloadMemory(std::span<VkBuffer> buffers_span, std::span<VkDeviceS
1456 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 1456 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
1457 .image = image, 1457 .image = image,
1458 .subresourceRange{ 1458 .subresourceRange{
1459 .aspectMask = aspect_mask, 1459 .aspectMask = aspect_mask_,
1460 .baseMipLevel = 0, 1460 .baseMipLevel = 0,
1461 .levelCount = VK_REMAINING_MIP_LEVELS, 1461 .levelCount = VK_REMAINING_MIP_LEVELS,
1462 .baseArrayLayer = 0, 1462 .baseArrayLayer = 0,
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 3a859139c..4457b366f 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -8,6 +8,7 @@
8 8
9#include "common/alignment.h" 9#include "common/alignment.h"
10#include "common/settings.h" 10#include "common/settings.h"
11#include "core/memory.h"
11#include "video_core/control/channel_state.h" 12#include "video_core/control/channel_state.h"
12#include "video_core/dirty_flags.h" 13#include "video_core/dirty_flags.h"
13#include "video_core/engines/kepler_compute.h" 14#include "video_core/engines/kepler_compute.h"
@@ -1026,19 +1027,19 @@ void TextureCache<P>::UploadImageContents(Image& image, StagingBuffer& staging)
1026 runtime.AccelerateImageUpload(image, staging, uploads); 1027 runtime.AccelerateImageUpload(image, staging, uploads);
1027 return; 1028 return;
1028 } 1029 }
1029 const size_t guest_size_bytes = image.guest_size_bytes; 1030
1030 swizzle_data_buffer.resize_destructive(guest_size_bytes); 1031 Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> swizzle_data(
1031 gpu_memory->ReadBlockUnsafe(gpu_addr, swizzle_data_buffer.data(), guest_size_bytes); 1032 *gpu_memory, gpu_addr, image.guest_size_bytes, &swizzle_data_buffer);
1032 1033
1033 if (True(image.flags & ImageFlagBits::Converted)) { 1034 if (True(image.flags & ImageFlagBits::Converted)) {
1034 unswizzle_data_buffer.resize_destructive(image.unswizzled_size_bytes); 1035 unswizzle_data_buffer.resize_destructive(image.unswizzled_size_bytes);
1035 auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data_buffer, 1036 auto copies =
1036 unswizzle_data_buffer); 1037 UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data, unswizzle_data_buffer);
1037 ConvertImage(unswizzle_data_buffer, image.info, mapped_span, copies); 1038 ConvertImage(unswizzle_data_buffer, image.info, mapped_span, copies);
1038 image.UploadMemory(staging, copies); 1039 image.UploadMemory(staging, copies);
1039 } else { 1040 } else {
1040 const auto copies = 1041 const auto copies =
1041 UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data_buffer, mapped_span); 1042 UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data, mapped_span);
1042 image.UploadMemory(staging, copies); 1043 image.UploadMemory(staging, copies);
1043 } 1044 }
1044} 1045}
@@ -1231,11 +1232,12 @@ void TextureCache<P>::QueueAsyncDecode(Image& image, ImageId image_id) {
1231 decode->image_id = image_id; 1232 decode->image_id = image_id;
1232 async_decodes.push_back(std::move(decode)); 1233 async_decodes.push_back(std::move(decode));
1233 1234
1234 Common::ScratchBuffer<u8> local_unswizzle_data_buffer(image.unswizzled_size_bytes); 1235 static Common::ScratchBuffer<u8> local_unswizzle_data_buffer;
1235 const size_t guest_size_bytes = image.guest_size_bytes; 1236 local_unswizzle_data_buffer.resize_destructive(image.unswizzled_size_bytes);
1236 swizzle_data_buffer.resize_destructive(guest_size_bytes); 1237 Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> swizzle_data(
1237 gpu_memory->ReadBlockUnsafe(image.gpu_addr, swizzle_data_buffer.data(), guest_size_bytes); 1238 *gpu_memory, image.gpu_addr, image.guest_size_bytes, &swizzle_data_buffer);
1238 auto copies = UnswizzleImage(*gpu_memory, image.gpu_addr, image.info, swizzle_data_buffer, 1239
1240 auto copies = UnswizzleImage(*gpu_memory, image.gpu_addr, image.info, swizzle_data,
1239 local_unswizzle_data_buffer); 1241 local_unswizzle_data_buffer);
1240 const size_t out_size = MapSizeBytes(image); 1242 const size_t out_size = MapSizeBytes(image);
1241 1243
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index 0de6ed09d..a83f5d41c 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -20,6 +20,7 @@
20#include "common/div_ceil.h" 20#include "common/div_ceil.h"
21#include "common/scratch_buffer.h" 21#include "common/scratch_buffer.h"
22#include "common/settings.h" 22#include "common/settings.h"
23#include "core/memory.h"
23#include "video_core/compatible_formats.h" 24#include "video_core/compatible_formats.h"
24#include "video_core/engines/maxwell_3d.h" 25#include "video_core/engines/maxwell_3d.h"
25#include "video_core/memory_manager.h" 26#include "video_core/memory_manager.h"
@@ -544,17 +545,15 @@ void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr
544 tile_size.height, info.tile_width_spacing); 545 tile_size.height, info.tile_width_spacing);
545 const size_t subresource_size = sizes[level]; 546 const size_t subresource_size = sizes[level];
546 547
547 tmp_buffer.resize_destructive(subresource_size);
548 const std::span<u8> dst(tmp_buffer);
549
550 for (s32 layer = 0; layer < info.resources.layers; ++layer) { 548 for (s32 layer = 0; layer < info.resources.layers; ++layer) {
551 const std::span<const u8> src = input.subspan(host_offset); 549 const std::span<const u8> src = input.subspan(host_offset);
552 gpu_memory.ReadBlockUnsafe(gpu_addr + guest_offset, dst.data(), dst.size_bytes()); 550 {
553 551 Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::UnsafeReadWrite>
554 SwizzleTexture(dst, src, bytes_per_block, num_tiles.width, num_tiles.height, 552 dst(gpu_memory, gpu_addr + guest_offset, subresource_size, &tmp_buffer);
555 num_tiles.depth, block.height, block.depth);
556 553
557 gpu_memory.WriteBlockUnsafe(gpu_addr + guest_offset, dst.data(), dst.size_bytes()); 554 SwizzleTexture(dst, src, bytes_per_block, num_tiles.width, num_tiles.height,
555 num_tiles.depth, block.height, block.depth);
556 }
558 557
559 host_offset += host_bytes_per_layer; 558 host_offset += host_bytes_per_layer;
560 guest_offset += layer_stride; 559 guest_offset += layer_stride;
@@ -837,6 +836,7 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
837 const Extent3D size = info.size; 836 const Extent3D size = info.size;
838 837
839 if (info.type == ImageType::Linear) { 838 if (info.type == ImageType::Linear) {
839 ASSERT(output.size_bytes() >= guest_size_bytes);
840 gpu_memory.ReadBlockUnsafe(gpu_addr, output.data(), guest_size_bytes); 840 gpu_memory.ReadBlockUnsafe(gpu_addr, output.data(), guest_size_bytes);
841 841
842 ASSERT((info.pitch >> bpp_log2) << bpp_log2 == info.pitch); 842 ASSERT((info.pitch >> bpp_log2) << bpp_log2 == info.pitch);
@@ -904,16 +904,6 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
904 return copies; 904 return copies;
905} 905}
906 906
907BufferCopy UploadBufferCopy(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr,
908 const ImageBase& image, std::span<u8> output) {
909 gpu_memory.ReadBlockUnsafe(gpu_addr, output.data(), image.guest_size_bytes);
910 return BufferCopy{
911 .src_offset = 0,
912 .dst_offset = 0,
913 .size = image.guest_size_bytes,
914 };
915}
916
917void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8> output, 907void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8> output,
918 std::span<BufferImageCopy> copies) { 908 std::span<BufferImageCopy> copies) {
919 u32 output_offset = 0; 909 u32 output_offset = 0;
diff --git a/src/video_core/texture_cache/util.h b/src/video_core/texture_cache/util.h
index ab45a43c4..5a0649d24 100644
--- a/src/video_core/texture_cache/util.h
+++ b/src/video_core/texture_cache/util.h
@@ -66,9 +66,6 @@ struct OverlapResult {
66 Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info, 66 Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info,
67 std::span<const u8> input, std::span<u8> output); 67 std::span<const u8> input, std::span<u8> output);
68 68
69[[nodiscard]] BufferCopy UploadBufferCopy(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr,
70 const ImageBase& image, std::span<u8> output);
71
72void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8> output, 69void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8> output,
73 std::span<BufferImageCopy> copies); 70 std::span<BufferImageCopy> copies);
74 71
diff --git a/src/video_core/vulkan_common/vma.cpp b/src/video_core/vulkan_common/vma.cpp
new file mode 100644
index 000000000..1fe2cf52b
--- /dev/null
+++ b/src/video_core/vulkan_common/vma.cpp
@@ -0,0 +1,8 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#define VMA_IMPLEMENTATION
5#define VMA_STATIC_VULKAN_FUNCTIONS 0
6#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
7
8#include <vk_mem_alloc.h> \ No newline at end of file
diff --git a/src/web_service/announce_room_json.cpp b/src/web_service/announce_room_json.cpp
index 4c3195efd..f1020a5b8 100644
--- a/src/web_service/announce_room_json.cpp
+++ b/src/web_service/announce_room_json.cpp
@@ -135,11 +135,11 @@ void RoomJson::Delete() {
135 LOG_ERROR(WebService, "Room must be registered to be deleted"); 135 LOG_ERROR(WebService, "Room must be registered to be deleted");
136 return; 136 return;
137 } 137 }
138 Common::DetachedTasks::AddTask( 138 Common::DetachedTasks::AddTask([host_{this->host}, username_{this->username},
139 [host{this->host}, username{this->username}, token{this->token}, room_id{this->room_id}]() { 139 token_{this->token}, room_id_{this->room_id}]() {
140 // create a new client here because the this->client might be destroyed. 140 // create a new client here because the this->client might be destroyed.
141 Client{host, username, token}.DeleteJson(fmt::format("/lobby/{}", room_id), "", false); 141 Client{host_, username_, token_}.DeleteJson(fmt::format("/lobby/{}", room_id_), "", false);
142 }); 142 });
143} 143}
144 144
145} // namespace WebService 145} // namespace WebService
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 63326968b..5c910c9e0 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -235,7 +235,7 @@ GameListWorker::~GameListWorker() = default;
235void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) { 235void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
236 using namespace FileSys; 236 using namespace FileSys;
237 237
238 const auto& cache = dynamic_cast<ContentProviderUnion&>(system.GetContentProvider()); 238 const auto& cache = system.GetContentProviderUnion();
239 239
240 auto installed_games = cache.ListEntriesFilterOrigin(std::nullopt, TitleType::Application, 240 auto installed_games = cache.ListEntriesFilterOrigin(std::nullopt, TitleType::Application,
241 ContentRecordType::Program); 241 ContentRecordType::Program);