diff options
Diffstat (limited to 'src')
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 | |||
| 12 | import androidx.recyclerview.widget.RecyclerView | 12 | import androidx.recyclerview.widget.RecyclerView |
| 13 | import org.yuzu.yuzu_emu.R | 13 | import org.yuzu.yuzu_emu.R |
| 14 | import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding | 14 | import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding |
| 15 | import org.yuzu.yuzu_emu.fragments.MessageDialogFragment | ||
| 15 | import org.yuzu.yuzu_emu.model.HomeSetting | 16 | import org.yuzu.yuzu_emu.model.HomeSetting |
| 16 | 17 | ||
| 17 | class HomeSettingAdapter(private val activity: AppCompatActivity, var options: List<HomeSetting>) : | 18 | class 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}; | |||
| 28 | template <typename T> | 28 | template <typename T> |
| 29 | static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, | 29 | static 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 | */ |
| 102 | static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, | 101 | static 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() { | |||
| 30 | void DetachedTasks::AddTask(std::function<void()> task) { | 30 | void 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 | ||
| 8 | namespace Network { | 11 | namespace Network { |
| 9 | 12 | ||
| 10 | /// Address families | 13 | /// Address families |
| 11 | enum class Domain : u8 { | 14 | enum 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 |
| 16 | enum class Type { | 20 | enum 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 |
| 24 | enum class Protocol : u8 { | 29 | enum 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; | |||
| 48 | constexpr u32 FLAG_MSG_DONTWAIT = 0x80; | 54 | constexpr u32 FLAG_MSG_DONTWAIT = 0x80; |
| 49 | constexpr u32 FLAG_O_NONBLOCK = 0x800; | 55 | constexpr u32 FLAG_O_NONBLOCK = 0x800; |
| 50 | 56 | ||
| 57 | /// Cross-platform addrinfo structure | ||
| 58 | struct 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 | ||
| 16 | namespace Common::TimeZone { | 16 | namespace Common::TimeZone { |
| @@ -33,32 +33,29 @@ std::string GetDefaultTimeZone() { | |||
| 33 | return "GMT"; | 33 | return "GMT"; |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | static std::string GetOsTimeZoneOffset() { | 36 | // Results are not comparable to seconds since Epoch |
| 37 | const std::time_t t{std::time(nullptr)}; | 37 | static 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 | |
| 43 | static 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 | ||
| 55 | std::chrono::seconds GetCurrentOffsetSeconds() { | 48 | std::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 | ||
| 73 | std::string FindSystemTimeZone() { | 70 | std::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) |
| 864 | endif() | 866 | endif() |
| 865 | 867 | ||
| 868 | if(ENABLE_OPENSSL) | ||
| 869 | target_sources(core PRIVATE | ||
| 870 | hle/service/ssl/ssl_backend_openssl.cpp) | ||
| 871 | target_link_libraries(core PRIVATE OpenSSL::SSL) | ||
| 872 | elseif (APPLE) | ||
| 873 | target_sources(core PRIVATE | ||
| 874 | hle/service/ssl/ssl_backend_securetransport.cpp) | ||
| 875 | target_link_libraries(core PRIVATE "-framework Security") | ||
| 876 | elseif (WIN32) | ||
| 877 | target_sources(core PRIVATE | ||
| 878 | hle/service/ssl/ssl_backend_schannel.cpp) | ||
| 879 | target_link_libraries(core PRIVATE crypt32 secur32) | ||
| 880 | else() | ||
| 881 | target_sources(core PRIVATE | ||
| 882 | hle/service/ssl/ssl_backend_none.cpp) | ||
| 883 | endif() | ||
| 884 | |||
| 866 | if (YUZU_USE_PRECOMPILED_HEADERS) | 885 | if (YUZU_USE_PRECOMPILED_HEADERS) |
| 867 | target_precompile_headers(core PRIVATE precompiled_headers.h) | 886 | target_precompile_headers(core PRIVATE precompiled_headers.h) |
| 868 | endif() | 887 | endif() |
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 | ||
| 348 | ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, bool uses_wall_clock_, | 348 | ARM_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 | ||
| 355 | ARM_Dynarmic_32::~ARM_Dynarmic_32() = default; | 355 | ARM_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 | ||
| 17 | namespace Core::Memory { | 17 | namespace Core::Memory { |
| 18 | class Memory; | 18 | class Memory; |
| @@ -28,8 +28,8 @@ class System; | |||
| 28 | 28 | ||
| 29 | class ARM_Dynarmic_32 final : public ARM_Interface { | 29 | class ARM_Dynarmic_32 final : public ARM_Interface { |
| 30 | public: | 30 | public: |
| 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 | ||
| 407 | ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, bool uses_wall_clock_, | 407 | ARM_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 | ||
| 414 | ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; | 414 | ARM_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 | ||
| 16 | namespace Core::Memory { | 16 | namespace Core::Memory { |
| 17 | class Memory; | 17 | class Memory; |
| @@ -25,8 +25,8 @@ class System; | |||
| 25 | 25 | ||
| 26 | class ARM_Dynarmic_64 final : public ARM_Interface { | 26 | class ARM_Dynarmic_64 final : public ARM_Interface { |
| 27 | public: | 27 | public: |
| 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 | ||
| 13 | namespace Core::Memory { | 11 | namespace Core::Memory { |
| @@ -16,6 +14,9 @@ class Memory; | |||
| 16 | 14 | ||
| 17 | namespace Core { | 15 | namespace Core { |
| 18 | 16 | ||
| 17 | class ARM_Dynarmic_32; | ||
| 18 | class ARM_Dynarmic_64; | ||
| 19 | |||
| 19 | class DynarmicExclusiveMonitor final : public ExclusiveMonitor { | 20 | class DynarmicExclusiveMonitor final : public ExclusiveMonitor { |
| 20 | public: | 21 | public: |
| 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 | ||
| 883 | FileSys::ContentProviderUnion& System::GetContentProviderUnion() { | ||
| 884 | return *impl->content_provider; | ||
| 885 | } | ||
| 886 | |||
| 887 | const FileSys::ContentProviderUnion& System::GetContentProviderUnion() const { | ||
| 888 | return *impl->content_provider; | ||
| 889 | } | ||
| 890 | |||
| 883 | Service::FileSystem::FileSystemController& System::GetFileSystemController() { | 891 | Service::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 | }; |
| 58 | static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size."); | 58 | static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size."); |
| 59 | 59 | ||
| 60 | struct NCABucketInfo { | ||
| 61 | u64 table_offset; | ||
| 62 | u64 table_size; | ||
| 63 | std::array<u8, 0x10> table_header; | ||
| 64 | }; | ||
| 65 | static_assert(sizeof(NCABucketInfo) == 0x20, "NCABucketInfo has incorrect size."); | ||
| 66 | |||
| 67 | struct NCASparseInfo { | ||
| 68 | NCABucketInfo bucket; | ||
| 69 | u64 physical_offset; | ||
| 70 | u16 generation; | ||
| 71 | INSERT_PADDING_BYTES_NOINIT(0x6); | ||
| 72 | }; | ||
| 73 | static_assert(sizeof(NCASparseInfo) == 0x30, "NCASparseInfo has incorrect size."); | ||
| 74 | |||
| 75 | struct NCACompressionInfo { | ||
| 76 | NCABucketInfo bucket; | ||
| 77 | INSERT_PADDING_BYTES_NOINIT(0x8); | ||
| 78 | }; | ||
| 79 | static_assert(sizeof(NCACompressionInfo) == 0x28, "NCACompressionInfo has incorrect size."); | ||
| 80 | |||
| 60 | struct NCASectionRaw { | 81 | struct 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 | }; |
| 66 | static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size."); | 89 | static_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 | ||
| 440 | public: | 415 | public: |
| 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 | */ |
| 39 | void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority, | 39 | void 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 | ||
| 27 | namespace Kernel { | 28 | namespace Kernel { |
| 28 | 29 | ||
| 30 | namespace { | ||
| 31 | |||
| 32 | template <bool MoveHandleAllowed> | ||
| 33 | Result 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 | |||
| 113 | void 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 | |||
| 29 | using ThreadQueueImplForKServerSessionRequest = KThreadQueue; | 149 | using ThreadQueueImplForKServerSessionRequest = KThreadQueue; |
| 30 | 150 | ||
| 31 | KServerSession::KServerSession(KernelCore& kernel) | 151 | KServerSession::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 | ||
| 97 | Result KSharedMemory::Unmap(KProcess& target_process, KProcessAddress address, | 97 | Result 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 | ||
| 38 | Result KThreadLocalPage::Finalize() { | 38 | Result 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 | |||
| 10 | namespace Kernel { | ||
| 11 | |||
| 12 | constexpr inline size_t MessageBufferSize = 0x100; | ||
| 13 | |||
| 14 | class MessageBuffer { | ||
| 15 | public: | ||
| 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 | |||
| 418 | private: | ||
| 419 | u32* m_buffer; | ||
| 420 | size_t m_size; | ||
| 421 | |||
| 422 | public: | ||
| 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) { | |||
| 506 | void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) { | 506 | void 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 | ||
| 331 | std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { | 331 | std::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 | ||
| 182 | void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key, | 182 | void 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 |
| 77 | void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key, | 77 | void 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 |
| 81 | void CryptoStep(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, DrgbOutput& output); | 81 | void 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 | ||
| 40 | namespace Service::NFC { | 38 | namespace Service::NFC { |
| 41 | NfcDevice::NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, | 39 | NfcDevice::NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, |
| @@ -1486,6 +1484,7 @@ DeviceState NfcDevice::GetCurrentState() const { | |||
| 1486 | } | 1484 | } |
| 1487 | 1485 | ||
| 1488 | Result NfcDevice::GetNpadId(Core::HID::NpadIdType& out_npad_id) const { | 1486 | Result 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 | ||
| 14 | namespace Service::NFC { | 17 | namespace Service::NFC { |
| 15 | 18 | ||
| @@ -51,22 +54,53 @@ Result DeviceManager::Finalize() { | |||
| 51 | return ResultSuccess; | 54 | return ResultSuccess; |
| 52 | } | 55 | } |
| 53 | 56 | ||
| 54 | Result DeviceManager::ListDevices(std::vector<u64>& nfp_devices, | 57 | Result 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 | ||
| 72 | DeviceState DeviceManager::GetDeviceState(u64 device_handle) const { | 106 | DeviceState 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 | ||
| 85 | Result DeviceManager::GetNpadId(u64 device_handle, Core::HID::NpadIdType& npad_id) const { | 119 | Result 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 | ||
| 131 | Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info) const { | 165 | Result 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 | ||
| 145 | Kernel::KReadableEvent& DeviceManager::AttachActivateEvent(u64 device_handle) const { | 179 | Result 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 | ||
| 155 | Kernel::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 | ||
| 200 | Result 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 | ||
| 165 | Result DeviceManager::ReadMifare(u64 device_handle, | 221 | Result 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 | ||
| 256 | Result DeviceManager::GetApplicationArea(u64 device_handle, std::span<u8> data) const { | 312 | Result 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 | ||
| 327 | Result DeviceManager::GetRegisterInfo(u64 device_handle, NFP::RegisterInfo& register_info) const { | 383 | Result 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 | ||
| 341 | Result DeviceManager::GetCommonInfo(u64 device_handle, NFP::CommonInfo& common_info) const { | 397 | Result 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 | ||
| 355 | Result DeviceManager::GetModelInfo(u64 device_handle, NFP::ModelInfo& model_info) const { | 411 | Result 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 | ||
| 402 | Result DeviceManager::GetAdminInfo(u64 device_handle, NFP::AdminInfo& admin_info) const { | 458 | Result 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 | ||
| 416 | Result DeviceManager::GetRegisterInfoPrivate(u64 device_handle, | 472 | Result 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 | ||
| 474 | Result DeviceManager::ExistsApplicationArea(u64 device_handle, bool& has_application_area) const { | 530 | Result 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 | ||
| 488 | Result DeviceManager::GetAll(u64 device_handle, NFP::NfpData& nfp_data) const { | 544 | Result 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 | ||
| 544 | Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) const { | 600 | Result 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 | ||
| 652 | Result 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 | |||
| 596 | Result DeviceManager::GetDeviceFromHandle(u64 handle, std::shared_ptr<NfcDevice>& nfc_device, | 665 | Result 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 | ||
| 649 | Result DeviceManager::VerifyDeviceResult(std::shared_ptr<NfcDevice> device, | 718 | Result 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 | ||
| 198 | void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) { | 202 | void 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 | ||
| 208 | void NfcInterface::ReadMifare(HLERequestContext& ctx) { | 216 | void 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); | |||
| 17 | constexpr Result ResultNfcDisabled(ErrorModule::NFC, 80); | 17 | constexpr Result ResultNfcDisabled(ErrorModule::NFC, 80); |
| 18 | constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFC, 88); | 18 | constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFC, 88); |
| 19 | constexpr Result ResultTagRemoved(ErrorModule::NFC, 97); | 19 | constexpr Result ResultTagRemoved(ErrorModule::NFC, 97); |
| 20 | constexpr Result ResultUnknown112(ErrorModule::NFC, 112); | ||
| 20 | constexpr Result ResultUnableToAccessBackupFile(ErrorModule::NFC, 113); | 21 | constexpr Result ResultUnableToAccessBackupFile(ErrorModule::NFC, 113); |
| 22 | constexpr Result ResultUnknown114(ErrorModule::NFC, 114); | ||
| 23 | constexpr Result ResultUnknown115(ErrorModule::NFC, 115); | ||
| 21 | constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFC, 120); | 24 | constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFC, 120); |
| 22 | constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFC, 128); | 25 | constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFC, 128); |
| 23 | constexpr Result ResultCorruptedDataWithBackup(ErrorModule::NFC, 136); | 26 | constexpr 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 | ||
| 11 | namespace { | 12 | namespace { |
| 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 | ||
| 11 | namespace Core { | 8 | namespace Core { |
| 12 | class System; | 9 | class System; |
| 13 | } | 10 | } |
| 14 | 11 | ||
| 12 | namespace Network { | ||
| 13 | class RoomNetwork; | ||
| 14 | } | ||
| 15 | |||
| 15 | namespace Service::NIFM { | 16 | namespace Service::NIFM { |
| 16 | 17 | ||
| 17 | void LoopProcess(Core::System& system); | 18 | void 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 | ||
| 23 | using Common::Expected; | ||
| 24 | using Common::Unexpected; | ||
| 25 | |||
| 23 | namespace Service::Sockets { | 26 | namespace Service::Sockets { |
| 24 | 27 | ||
| 25 | namespace { | 28 | namespace { |
| @@ -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 | ||
| 445 | void 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 | |||
| 439 | void BSD::EventFd(HLERequestContext& ctx) { | 470 | void 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 | ||
| 674 | Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval) { | 707 | Errno 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 | ||
| 738 | Errno 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 | ||
| 914 | Expected<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 | |||
| 929 | std::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 | |||
| 844 | s32 BSD::FindFreeFileDescriptorHandle() noexcept { | 936 | s32 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 | |||
| 32 | private: | 40 | private: |
| 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 | |||
| 6 | namespace Service::Sockets { | 9 | namespace Service::Sockets { |
| 7 | 10 | ||
| 11 | constexpr Result ResultOverflow{ErrorModule::NSD, 6}; | ||
| 12 | |||
| 13 | // This is nn::oe::ServerEnvironmentType | ||
| 14 | enum class ServerEnvironmentType : u8 { | ||
| 15 | Dd, | ||
| 16 | Lp, | ||
| 17 | Sd, | ||
| 18 | Sp, | ||
| 19 | Dp, | ||
| 20 | }; | ||
| 21 | |||
| 8 | NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} { | 22 | NSD::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 | ||
| 57 | static 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 | |||
| 65 | static 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 | |||
| 77 | void 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 | |||
| 88 | void 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 | |||
| 106 | void NSD::GetApplicationServerEnvironmentType(HLERequestContext& ctx) { | ||
| 107 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 108 | rb.Push(ResultSuccess); | ||
| 109 | rb.Push(static_cast<u32>(ServerEnvironmentType::Lp)); | ||
| 110 | } | ||
| 111 | |||
| 43 | NSD::~NSD() = default; | 112 | NSD::~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> { | |||
| 15 | public: | 15 | public: |
| 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 | |||
| 19 | private: | ||
| 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 | |||
| 27 | namespace Service::Sockets { | 18 | namespace Service::Sockets { |
| 28 | 19 | ||
| 29 | SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"} { | 20 | SFDNSRES::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 | ||
| 62 | static NetDbError AddrInfoErrorToNetDbError(s32 result) { | 53 | static 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 | ||
| 76 | static std::vector<u8> SerializeAddrInfo(const addrinfo* addrinfo, s32 result_code, | 70 | static 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 | |||
| 89 | template <typename T> | ||
| 90 | static 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 | |||
| 96 | static 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. | ||
| 106 | static 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 | |||
| 133 | static 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 | |||
| 165 | void 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 | |||
| 184 | void 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 | |||
| 203 | static 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 | ||
| 188 | static std::pair<u32, s32> GetAddrInfoRequestImpl(HLERequestContext& ctx) { | 242 | static 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 | ||
| 224 | void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) { | 284 | void 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 | ||
| 234 | void SFDNSRES::GetAddrInfoRequestWithOptions(HLERequestContext& ctx) { | 303 | void 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 | |||
| 325 | void 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 | ||
| 19 | private: | 19 | private: |
| 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 | |||
| 28 | enum 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 | ||
| 27 | enum class Domain : u32 { | 47 | enum class Domain : u32 { |
| 48 | Unspecified = 0, | ||
| 28 | INET = 2, | 49 | INET = 2, |
| 29 | }; | 50 | }; |
| 30 | 51 | ||
| 31 | enum class Type : u32 { | 52 | enum 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 | ||
| 38 | enum class Protocol : u32 { | 60 | enum 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 | ||
| 67 | enum class SocketLevel : u32 { | ||
| 68 | SOCKET = 0xffff, // i.e. SOL_SOCKET | ||
| 69 | }; | ||
| 70 | |||
| 45 | enum class OptName : u32 { | 71 | enum 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 | ||
| 56 | enum class ShutdownHow : s32 { | 84 | enum 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 | ||
| 85 | DECLARE_ENUM_FLAG_OPERATORS(PollEvents); | 116 | DECLARE_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 | ||
| 44 | GetAddrInfoError 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 | |||
| 42 | Network::Domain Translate(Domain domain) { | 84 | Network::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 | ||
| 52 | Domain Translate(Network::Domain domain) { | 96 | Domain 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 | ||
| 62 | Network::Type Translate(Type type) { | 108 | Network::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 | ||
| 74 | Network::Protocol Translate(Type type, Protocol protocol) { | 126 | Type 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 | |||
| 144 | Network::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 | |||
| 158 | Protocol 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 | ||
| 96 | Network::PollEvents TranslatePollEventsToHost(PollEvents flags) { | 172 | Network::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 | ||
| 115 | PollEvents TranslatePollEventsToGuest(Network::PollEvents flags) { | 194 | PollEvents 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 | ||
| 135 | Network::SockAddrIn Translate(SockAddrIn value) { | 217 | Network::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 |
| 18 | std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value); | 18 | std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value); |
| 19 | 19 | ||
| 20 | /// Translate abstract getaddrinfo error to guest getaddrinfo error | ||
| 21 | GetAddrInfoError Translate(Network::GetAddrInfoError value); | ||
| 22 | |||
| 20 | /// Translate guest domain to abstract domain | 23 | /// Translate guest domain to abstract domain |
| 21 | Network::Domain Translate(Domain domain); | 24 | Network::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 |
| 27 | Network::Type Translate(Type type); | 30 | Network::Type Translate(Type type); |
| 28 | 31 | ||
| 32 | /// Translate abstract type to guest type | ||
| 33 | Type Translate(Network::Type type); | ||
| 34 | |||
| 29 | /// Translate guest protocol to abstract protocol | 35 | /// Translate guest protocol to abstract protocol |
| 30 | Network::Protocol Translate(Type type, Protocol protocol); | 36 | Network::Protocol Translate(Protocol protocol); |
| 31 | 37 | ||
| 32 | /// Translate abstract poll event flags to guest poll event flags | 38 | /// Translate abstract protocol to guest protocol |
| 33 | Network::PollEvents TranslatePollEventsToHost(PollEvents flags); | 39 | Protocol 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 |
| 36 | PollEvents TranslatePollEventsToGuest(Network::PollEvents flags); | 42 | Network::PollEvents Translate(PollEvents flags); |
| 43 | |||
| 44 | /// Translate abstract poll event flags to guest poll event flags | ||
| 45 | PollEvents 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 |
| 39 | Network::SockAddrIn Translate(SockAddrIn value); | 48 | Network::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 | ||
| 9 | namespace Service::SSL { | 17 | namespace 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 | ||
| 32 | enum class IoMode : u32 { | ||
| 33 | Blocking = 1, | ||
| 34 | NonBlocking = 2, | ||
| 35 | }; | ||
| 36 | |||
| 37 | // This is nn::ssl::sf::OptionType | ||
| 38 | enum 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 |
| 24 | struct SslVersion { | 44 | struct SslVersion { |
| 25 | union { | 45 | union { |
| @@ -34,35 +54,42 @@ struct SslVersion { | |||
| 34 | }; | 54 | }; |
| 35 | }; | 55 | }; |
| 36 | 56 | ||
| 57 | struct SslContextSharedData { | ||
| 58 | u32 connection_count = 0; | ||
| 59 | }; | ||
| 60 | |||
| 37 | class ISslConnection final : public ServiceFramework<ISslConnection> { | 61 | class ISslConnection final : public ServiceFramework<ISslConnection> { |
| 38 | public: | 62 | public: |
| 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 | ||
| 85 | private: | 133 | private: |
| 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 | ||
| 89 | class ISslContext final : public ServiceFramework<ISslContext> { | 393 | class ISslContext final : public ServiceFramework<ISslContext> { |
| 90 | public: | 394 | public: |
| 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 | ||
| 112 | private: | 417 | private: |
| 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 | |||
| 15 | namespace Network { | ||
| 16 | class SocketBase; | ||
| 17 | } | ||
| 18 | |||
| 19 | namespace Service::SSL { | ||
| 20 | |||
| 21 | constexpr Result ResultNoSocket{ErrorModule::SSLSrv, 103}; | ||
| 22 | constexpr Result ResultInvalidSocket{ErrorModule::SSLSrv, 106}; | ||
| 23 | constexpr Result ResultTimeout{ErrorModule::SSLSrv, 205}; | ||
| 24 | constexpr 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). | ||
| 30 | constexpr Result ResultWouldBlock{ErrorModule::SSLSrv, 204}; | ||
| 31 | |||
| 32 | class SSLConnectionBackend { | ||
| 33 | public: | ||
| 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 | |||
| 43 | ResultVal<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 | |||
| 8 | namespace Service::SSL { | ||
| 9 | |||
| 10 | ResultVal<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 | |||
| 19 | using namespace Common::FS; | ||
| 20 | |||
| 21 | namespace Service::SSL { | ||
| 22 | |||
| 23 | // Import OpenSSL's `SSL` type into the namespace. This is needed because the | ||
| 24 | // namespace is also named `SSL`. | ||
| 25 | using ::SSL; | ||
| 26 | |||
| 27 | namespace { | ||
| 28 | |||
| 29 | std::once_flag one_time_init_flag; | ||
| 30 | bool one_time_init_success = false; | ||
| 31 | |||
| 32 | SSL_CTX* ssl_ctx; | ||
| 33 | IOFile key_log_file; // only open if SSLKEYLOGFILE set in environment | ||
| 34 | BIO_METHOD* bio_meth; | ||
| 35 | |||
| 36 | Result CheckOpenSSLErrors(); | ||
| 37 | void OneTimeInit(); | ||
| 38 | void OneTimeInitLogFile(); | ||
| 39 | bool OneTimeInitBIO(); | ||
| 40 | |||
| 41 | } // namespace | ||
| 42 | |||
| 43 | class SSLConnectionBackendOpenSSL final : public SSLConnectionBackend { | ||
| 44 | public: | ||
| 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 | |||
| 256 | ResultVal<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 | |||
| 265 | namespace { | ||
| 266 | |||
| 267 | Result 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 | |||
| 297 | void 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 | |||
| 322 | void 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 | |||
| 336 | bool 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 | |||
| 15 | namespace { | ||
| 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 | |||
| 25 | std::once_flag one_time_init_flag; | ||
| 26 | bool one_time_init_success = false; | ||
| 27 | |||
| 28 | SCHANNEL_CRED schannel_cred{}; | ||
| 29 | CredHandle cred_handle; | ||
| 30 | |||
| 31 | static 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 | |||
| 63 | namespace Service::SSL { | ||
| 64 | |||
| 65 | class SSLConnectionBackendSchannel final : public SSLConnectionBackend { | ||
| 66 | public: | ||
| 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 | |||
| 535 | ResultVal<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 | |||
| 20 | namespace { | ||
| 21 | |||
| 22 | template <typename T> | ||
| 23 | struct 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 | |||
| 39 | std::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 | |||
| 47 | std::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 | |||
| 57 | namespace Service::SSL { | ||
| 58 | |||
| 59 | class SSLConnectionBackendSecureTransport final : public SSLConnectionBackend { | ||
| 60 | public: | ||
| 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 | |||
| 206 | private: | ||
| 207 | CFReleaser<SSLContextRef> context = nullptr; | ||
| 208 | bool got_read_eof = false; | ||
| 209 | |||
| 210 | std::shared_ptr<Network::SocketBase> socket; | ||
| 211 | }; | ||
| 212 | |||
| 213 | ResultVal<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 | ||
| 98 | Errno TranslateNativeError(int e) { | 99 | Errno 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 | ||
| 196 | Errno TranslateNativeError(int e) { | 201 | Errno 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 | ||
| 244 | int TranslateDomain(Domain domain) { | 255 | GetAddrInfoError 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 | |||
| 308 | Domain 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 | |||
| 320 | int 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 | ||
| 254 | int TranslateType(Type type) { | 332 | Type 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 | |||
| 350 | int 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 | ||
| 266 | int TranslateProtocol(Protocol protocol) { | 366 | Protocol 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 | |||
| 380 | int 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 | ||
| 278 | SockAddrIn TranslateToSockAddrIn(sockaddr input_) { | 394 | SockAddrIn 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_) { | |||
| 301 | short TranslatePollEvents(PollEvents events) { | 406 | short 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 | ||
| 486 | std::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 | |||
| 492 | u32 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 | |||
| 497 | Common::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 | |||
| 369 | std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { | 528 | std::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 | ||
| 413 | template <typename T> | 572 | template <typename T> |
| 414 | Errno Socket::SetSockOpt(SOCKET fd_, int option, T value) { | 573 | std::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 | |||
| 584 | template <typename T> | ||
| 585 | Errno 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 | ||
| 423 | Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { | 594 | Errno 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 | ||
| 432 | std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() { | 604 | std::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 | ||
| 460 | std::pair<SockAddrIn, Errno> Socket::GetPeerName() { | 630 | std::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 | ||
| 471 | std::pair<SockAddrIn, Errno> Socket::GetSockName() { | 640 | std::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 | ||
| 482 | Errno Socket::Bind(SockAddrIn addr) { | 650 | Errno Socket::Bind(SockAddrIn addr) { |
| @@ -519,7 +687,7 @@ Errno Socket::Shutdown(ShutdownHow how) { | |||
| 519 | return GetAndLogLastError(); | 687 | return GetAndLogLastError(); |
| 520 | } | 688 | } |
| 521 | 689 | ||
| 522 | std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) { | 690 | std::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 | ||
| 535 | std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) { | 703 | std::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 | ||
| 767 | std::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 | |||
| 600 | Errno Socket::SetLinger(bool enable, u32 linger) { | 772 | Errno 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 | ||
| 20 | namespace Common { | ||
| 21 | template <typename T, typename E> | ||
| 22 | class Expected; | ||
| 23 | } | ||
| 24 | |||
| 19 | namespace Network { | 25 | namespace Network { |
| 20 | 26 | ||
| 21 | class SocketBase; | 27 | class 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 | |||
| 49 | enum 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 | ||
| 54 | DECLARE_ENUM_FLAG_OPERATORS(PollEvents); | 83 | DECLARE_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 |
| 83 | std::optional<IPv4Address> GetHostIPv4Address(); | 112 | std::optional<IPv4Address> GetHostIPv4Address(); |
| 84 | 113 | ||
| 114 | std::string IPv4AddressToString(IPv4Address ip_addr); | ||
| 115 | u32 IPv4AddressToInteger(IPv4Address ip_addr); | ||
| 116 | |||
| 117 | // named to avoid name collision with Windows macro | ||
| 118 | Common::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 | ||
| 101 | std::pair<s32, Errno> ProxySocket::Recv(int flags, std::vector<u8>& message) { | 102 | std::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 | ||
| 109 | std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) { | 110 | std::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 | ||
| 143 | std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::vector<u8>& message, | 144 | std::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 | ||
| 292 | std::pair<Errno, Errno> ProxySocket::GetPendingError() { | ||
| 293 | LOG_DEBUG(Network, "(STUBBED) called"); | ||
| 294 | return {Errno::SUCCESS, Errno::SUCCESS}; | ||
| 295 | } | ||
| 296 | |||
| 296 | bool ProxySocket::IsOpened() const { | 297 | bool 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 | ||
| 15 | namespace Network { | 15 | namespace Network { |
| 16 | 16 | ||
| 17 | class RoomNetwork; | ||
| 18 | |||
| 17 | class ProxySocket : public SocketBase { | 19 | class ProxySocket : public SocketBase { |
| 18 | public: | 20 | public: |
| 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 | ||
| 79 | private: | 83 | private: |
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 | ||
| 22 | namespace Network { | 21 | namespace Network { |
| 23 | 22 | ||
| 23 | struct ProxyPacket; | ||
| 24 | |||
| 24 | class SocketBase { | 25 | class SocketBase { |
| 25 | public: | 26 | public: |
| 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 | ||
| 781 | bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const { | 809 | bool 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 | ||
| 912 | const u8* Memory::GetSpan(const VAddr src_addr, const std::size_t size) const { | ||
| 913 | return impl->GetSpan(src_addr, size); | ||
| 914 | } | ||
| 915 | |||
| 916 | u8* Memory::GetSpan(const VAddr src_addr, const std::size_t size) { | ||
| 917 | return impl->GetSpan(src_addr, size); | ||
| 918 | } | ||
| 919 | |||
| 884 | void Memory::WriteBlock(const Common::ProcessAddress dest_addr, const void* src_buffer, | 920 | void 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 | ||
| 963 | void Memory::InvalidateRegion(Common::ProcessAddress dest_addr, size_t size) { | ||
| 964 | impl->InvalidateRegion(dest_addr, size); | ||
| 965 | } | ||
| 966 | |||
| 967 | void 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; | |||
| 24 | class KProcess; | 28 | class KProcess; |
| 25 | } // namespace Kernel | 29 | } // namespace Kernel |
| 26 | 30 | ||
| 31 | namespace Tegra { | ||
| 32 | class MemoryManager; | ||
| 33 | } | ||
| 34 | |||
| 27 | namespace Core::Memory { | 35 | namespace 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 | ||
| 465 | private: | 478 | private: |
| 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 | ||
| 485 | enum 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 | |||
| 502 | namespace { | ||
| 503 | template <typename M, typename T, GuestMemoryFlags FLAGS> | ||
| 504 | class 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 | |||
| 511 | public: | ||
| 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 | |||
| 613 | protected: | ||
| 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 | |||
| 632 | template <typename M, typename T, GuestMemoryFlags FLAGS> | ||
| 633 | class GuestMemoryScoped : public GuestMemory<M, T, FLAGS> { | ||
| 634 | public: | ||
| 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 | |||
| 676 | template <typename T, GuestMemoryFlags FLAGS> | ||
| 677 | using CpuGuestMemory = GuestMemory<Memory, T, FLAGS>; | ||
| 678 | template <typename T, GuestMemoryFlags FLAGS> | ||
| 679 | using CpuGuestMemoryScoped = GuestMemoryScoped<Memory, T, FLAGS>; | ||
| 680 | template <typename T, GuestMemoryFlags FLAGS> | ||
| 681 | using GpuGuestMemory = GuestMemory<Tegra::MemoryManager, T, FLAGS>; | ||
| 682 | template <typename T, GuestMemoryFlags FLAGS> | ||
| 683 | using 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 | ||
| 124 | json GetBacktraceData(Core::System& system) { | 124 | json 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 | ||
| 833 | ButtonBindings SDLDriver::GetDefaultButtonBinding() const { | 828 | ButtonBindings 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 | |||
| 856 | ButtonBindings 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 | ||
| 279 | create_target_directory_groups(video_core) | 280 | create_target_directory_groups(video_core) |
| @@ -291,7 +292,7 @@ target_link_options(video_core PRIVATE ${FFmpeg_LDFLAGS}) | |||
| 291 | 292 | ||
| 292 | add_dependencies(video_core host_shaders) | 293 | add_dependencies(video_core host_shaders) |
| 293 | target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE}) | 294 | target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE}) |
| 294 | target_link_libraries(video_core PRIVATE sirit Vulkan::Headers vma) | 295 | target_link_libraries(video_core PRIVATE sirit Vulkan::Headers GPUOpen::VulkanMemoryAllocator) |
| 295 | 296 | ||
| 296 | if (ENABLE_NSIGHT_AFTERMATH) | 297 | if (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") | ||
| 327 | endif() | 331 | endif() |
| 328 | 332 | ||
| 329 | if (ARCHITECTURE_x86_64) | 333 | if (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() { | |||
| 441 | template <class P> | 442 | template <class P> |
| 442 | void BufferCache<P>::BindComputeStorageBuffer(size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset, | 443 | void 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() { | |||
| 463 | template <class P> | 469 | template <class P> |
| 464 | void BufferCache<P>::BindComputeTextureBuffer(size_t tbo_index, GPUVAddr gpu_addr, u32 size, | 470 | void 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; | |||
| 67 | constexpr u32 NUM_GRAPHICS_UNIFORM_BUFFERS = 18; | 67 | constexpr u32 NUM_GRAPHICS_UNIFORM_BUFFERS = 18; |
| 68 | constexpr u32 NUM_COMPUTE_UNIFORM_BUFFERS = 8; | 68 | constexpr u32 NUM_COMPUTE_UNIFORM_BUFFERS = 8; |
| 69 | constexpr u32 NUM_STORAGE_BUFFERS = 16; | 69 | constexpr u32 NUM_STORAGE_BUFFERS = 16; |
| 70 | constexpr u32 NUM_TEXTURE_BUFFERS = 16; | 70 | constexpr u32 NUM_TEXTURE_BUFFERS = 32; |
| 71 | constexpr u32 NUM_STAGES = 5; | 71 | constexpr u32 NUM_STAGES = 5; |
| 72 | 72 | ||
| 73 | using UniformBufferSizes = std::array<std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFERS>, NUM_STAGES>; | 73 | using 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 | ||
| 13 | namespace Tegra { | 14 | namespace Tegra { |
| 14 | 15 | ||
| 16 | constexpr u32 MacroRegistersStart = 0xE00; | ||
| 17 | |||
| 15 | DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_, | 18 | DmaPusher::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) { | |||
| 46 | void State::ProcessData(std::span<const u8> read_buffer) { | 47 | void 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) { | |||
| 679 | Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { | 680 | Texture::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 | ||
| 689 | Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const { | 688 | Texture::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 | ||
| 238 | void MaxwellDMA::CopyPitchToBlockLinear() { | 237 | void 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 | ||
| 304 | void MaxwellDMA::CopyBlockLinearToBlockLinear() { | 302 | void 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 | ||
| 364 | void MaxwellDMA::ReleaseSemaphore() { | 359 | void 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 | ||
| 19 | namespace Tegra { | 18 | namespace Tegra { |
| 19 | using Core::Memory::GuestMemoryFlags; | ||
| 20 | 20 | ||
| 21 | std::atomic<size_t> MemoryManager::unique_identifier_generator{}; | 21 | std::atomic<size_t> MemoryManager::unique_identifier_generator{}; |
| 22 | 22 | ||
| @@ -587,13 +587,10 @@ void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size, | |||
| 587 | 587 | ||
| 588 | void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size, | 588 | void 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 | ||
| 599 | bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const { | 596 | bool 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 | ||
| 758 | const 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 | |||
| 766 | u8* 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 | |||
| 142 | private: | 160 | private: |
| 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 | ||
| 113 | void HostCounter::EndQuery() { | 113 | void 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 | ||
| 118 | u64 HostCounter::BlockingQuery(bool async) const { | 119 | u64 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 | ||
| 907 | BufferCopy 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 | |||
| 917 | void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8> output, | 907 | void 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 | |||
| 72 | void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8> output, | 69 | void 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; | |||
| 235 | void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) { | 235 | void 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); |