Compare commits

...

115 Commits

Author SHA1 Message Date
b5d0c6a58e Android #136 2023-11-20 00:57:18 +00:00
67bb87db29 Merge PR 12074 2023-11-20 00:57:17 +00:00
0543726586 Merge PR 11535 2023-11-20 00:57:17 +00:00
12fba361bd Merge pull request #12083 from liamwhite/viewport
renderer_vulkan: ignore viewport stores on non-supporting drivers
2023-11-19 14:09:25 -05:00
6bcde572dd Merge pull request #12085 from liamwhite/build-fix
qt: fix linux build
2023-11-19 14:09:18 -05:00
20a17607ae qt: fix linux build 2023-11-19 11:49:51 -05:00
473caaff5b renderer_vulkan: ignore viewport stores on non-supporting drivers 2023-11-19 11:27:12 -05:00
787552f832 Merge pull request #12081 from FernandoS27/check-out-on-your-broke-crypto-friends
Vulkan: Be more generous with pipeline workers for Android
2023-11-19 11:16:19 -05:00
5f945e2fcd Merge pull request #12036 from FernandoS27/you-should-have-more-than-one-towel
Query Cache: Disable write syncing on Android
2023-11-19 09:52:22 -05:00
c08da2d6ad Merge pull request #11792 from boludoz/new-shortcut
Improved shortcut: add games in applist for Windows, question for sta…
2023-11-19 09:50:49 -05:00
4458920799 Merge pull request #12066 from ameerj/nvidia-nsanity
shader_recompiler: add byteswap pattern workaround for Nvidia
2023-11-19 09:49:45 -05:00
61fed8a3a6 Merge pull request #12068 from ameerj/glasm-xfb-fixes
gl_graphics_pipeline: GLASM: Fix transform feedback attribs buffer mode
2023-11-19 09:49:38 -05:00
efb3165e3d Merge pull request #12072 from FernandoS27/winter-is-2-seconds-too-soon
Buffer Cache: Eliminate clears on Indirect buffers
2023-11-19 09:49:30 -05:00
a493ba76b4 Vulkan: Be more generous with pipeline workers for Android 2023-11-19 14:54:15 +01:00
ae60a5657e Buffer Cache: Eliminate clears on Indirect buffers 2023-11-18 19:26:14 +01:00
feb60de5c3 shader_recompiler: Fix spelling of "derivate" (#12067) 2023-11-18 13:39:47 +01:00
c67644f1da gl_graphics_pipeline: GLASM: Fix transform feedback attribs buffer mode
GL_SEPARATE_ATTRIBS only applies when multiple buffers are being used, else GL_INTERLEAVED_ATTRIBS handles the cases for a single buffer with potentially more than one attribute
2023-11-18 00:44:05 -05:00
9343b81afd shader_recompiler: add byteswap pattern workaround for Nvidia 2023-11-17 22:29:12 -05:00
71f53b4218 Merge pull request #12058 from liamwhite/opus-again
opus: fix stereo stream count check in multistream decoder object
2023-11-17 20:41:50 -05:00
f131b0faeb Merge pull request #11912 from liamwhite/nv-graphic-buffer
nvnflinger: use graphic buffer lifetime for map handle
2023-11-17 20:41:34 -05:00
6c64d5aff2 Merge pull request #11917 from liamwhite/abandonment
nvnflinger: implement consumer abandonment
2023-11-17 20:41:23 -05:00
de594995da opus: fix stereo stream count check in multistream decoder object 2023-11-17 08:26:22 -05:00
4c16a1a26f Merge pull request #12055 from german77/activate
service: hid: Introduce firmware settings and update activate controller calls
2023-11-16 21:28:06 -05:00
862e66202c service: hid: Introduce firmware settings and update activate controller calls 2023-11-16 18:51:14 -06:00
2136a46ab7 Merge pull request #12053 from german77/no_functional
service: hid: Implement xpad calls
2023-11-16 16:54:01 -05:00
b9c7e5c2c8 service: hid: Implement xpad calls 2023-11-16 11:18:11 -06:00
d86e88a622 Merge pull request #11995 from FernandoS27/you-dont-need-the-new-iphone
Revert PR #11806 and do a proper fix to the memory handling.
2023-11-16 09:17:13 -05:00
7eac28e410 Merge pull request #12038 from german77/no_implement
service: hid: Split hid.cpp into individual interfaces
2023-11-16 09:13:56 -05:00
ea4c92f734 Merge pull request #12007 from german77/moar_buttons
core: hid: Split SL and SR buttons
2023-11-16 09:13:39 -05:00
c9cd938dfd service: hid: Split hid.cpp into individual interfaces 2023-11-15 09:59:54 -06:00
4c5e3d5f7a Merge pull request #12043 from t895/disable-pip-default
android: Disable PiP by default
2023-11-15 10:33:12 -05:00
e2be180136 android: Disable PiP by default 2023-11-15 10:30:43 -05:00
c9437e5244 Query Cache: Disable write syncing on Android 2023-11-15 02:23:39 +01:00
24548b1f5c Merge pull request #12034 from t895/fps-outline
android: Add drop shadow to FPS counter
2023-11-14 18:20:24 -05:00
be0ecae108 Merge pull request #12035 from t895/pip-startup-shutdown-fix
android: Don't enter PiP during startup or shutdown
2023-11-14 18:20:18 -05:00
fcd54c6479 android: Don't enter PiP during startup or shutdown 2023-11-14 18:11:38 -05:00
08296f151e android: Add drop shadow to FPS counter
Also let the style control the text size
2023-11-14 17:53:40 -05:00
a134e924ff Merge pull request #12032 from liamwhite/fruit-compiler
fix apple clang build again
2023-11-14 12:23:28 -05:00
31ed6bae11 Merge pull request #12030 from t895/blocking-fix
android: Use suspend function for creating dynamic shortcuts
2023-11-14 12:23:17 -05:00
9a5ef835cc Merge pull request #12028 from liamwhite/coretiming-shutdown
core_timing: lock event queue access
2023-11-14 12:23:11 -05:00
df0d3698ae Merge pull request #12025 from liamwhite/kernel-shutdown-deadlock
core: check for thread dpc before eret
2023-11-14 12:23:04 -05:00
51fc608f68 Merge pull request #12019 from liamwhite/more-shutdown-deadlocks
audio_core: ignore renderer wait when stream is paused
2023-11-14 12:22:56 -05:00
b30e19ba24 fix apple clang build again 2023-11-14 11:53:02 -05:00
ec6b67d862 core_timing: lock event queue access 2023-11-14 11:51:04 -05:00
4d0b7f8496 android: Use suspend function for creating dynamic shortcuts
If the coil loader ever got stuck when creating a dynamic shortcut icon, the app would freeze. This would happen most notably when booting nca format games. This pushes that process to a separate coroutine that can be cancelled by the main activity's lifecycle.
2023-11-14 10:57:00 -05:00
e3b510a4b4 core: check for thread dpc before eret 2023-11-13 11:28:23 -05:00
247d66a680 Merge pull request #11990 from german77/audio
yuzu: Save mute when in background setting
2023-11-13 09:13:12 -05:00
0047d8a01e Merge pull request #12014 from t895/shrink-logo
android: Shrink branding images
2023-11-13 09:12:16 -05:00
efc0187537 Merge pull request #12015 from t895/remove-auto
android: Remove "auto" region option
2023-11-13 09:12:07 -05:00
b6fe8a0b3f Merge pull request #12018 from t895/settings-cleanup
android: Settings visual cleanup
2023-11-13 09:11:55 -05:00
ecaa038b4d audio_core: ignore renderer wait when stream is paused 2023-11-12 23:10:53 -05:00
4aac971864 android: Use the same transition animation between every fragment in settings
The animation that I used for entering search was prone to weird visual bugs and could appear visually jarring. This just makes things appear more consistent.
2023-11-12 21:36:51 -05:00
6c93cdffb1 android: Use more padding on top of each settings header 2023-11-12 21:36:51 -05:00
470714e2d1 android: Remove dividers between each setting 2023-11-12 21:36:51 -05:00
6b888b0fa8 android: Add icons and descriptions to the first-level advanced settings page
Additionally adjusts padding to place the icons in-line with the back button in the top app bar and makes the text for normal settings appear in-line with the expanded top app bar title.
2023-11-12 21:36:50 -05:00
1a1393dad7 android: Remove general section from settings
Limit speed options were moved to system, cpu accuracy was moved to debug, and PiP was moved to graphics.
2023-11-12 19:08:57 -05:00
55412962c0 android: Remove "auto" region option
This doesn't exist and if you clicked it, your region would be set to Taiwan.
2023-11-12 15:45:42 -05:00
d920da2631 android: Add a landscape-specific layout to the about page
Moves the logo to the side to fit more information on screen
2023-11-12 13:56:42 -05:00
ff72bf2cb2 android: Shrink logo in about page 2023-11-12 13:56:13 -05:00
4efb9763d9 android: Shrink logo in settings tab
Adjusts padding between the cards and logo to fit appropriately
2023-11-12 13:55:20 -05:00
c600bc8652 android: Fix top app bar tint being cut off in the about fragment
Adjust margin on the toolbar, not the app bar
2023-11-12 13:54:31 -05:00
f1806d237f Memory: Fix invalidation handling from the CPU/Services 2023-11-12 14:10:40 +01:00
ae57a99d7d core: hid: Split SL and SR buttons 2023-11-11 21:03:15 -06:00
767c4b5a99 Merge pull request #11980 from german77/moment
service: irs: Implement moment image processor
2023-11-11 20:02:23 -05:00
904d03b01f Merge pull request #12005 from german77/homebrew_id
yuzu: Keep homebrew on the recently played list
2023-11-11 20:02:09 -05:00
9f1c9599a2 Merge pull request #12004 from liamwhite/fix-hbl
k_capabilities: ignore map region when KTrace is disabled
2023-11-11 20:02:01 -05:00
5f6666a7cd Merge pull request #12003 from liamwhite/read-modules
gdbstub: read module information from memory layout
2023-11-11 20:01:53 -05:00
1906e2724f Merge pull request #11992 from t895/frame-check
android: Hide loading animation on first frame
2023-11-11 20:01:32 -05:00
0c032d3f2f yuzu: Keep homebrew on the recently played list 2023-11-11 10:14:11 -06:00
a6735cba5f k_capabilities: ignore map region when KTrace is disabled 2023-11-11 10:45:43 -05:00
3b872b89d1 gdbstub: read module information from memory layout 2023-11-11 10:41:06 -05:00
40d4e9543b Merge pull request #11914 from liamwhite/newer-kpagetable
kernel: add KPageTableBase
2023-11-11 09:45:29 -05:00
e588f341ed service: irs: Implement moment image processor 2023-11-11 00:28:12 -06:00
875246f5b2 k_page_table: fix shutdown 2023-11-10 12:01:35 -05:00
b16fefa106 k_page_table: use more precise icache invalidates 2023-11-10 12:01:35 -05:00
2a255b2d61 kernel: add KPageTableBase
Co-authored-by: Kelebek1 <eeeedddccc@hotmail.co.uk>
2023-11-10 12:01:35 -05:00
9e331f9957 yuzu: Make mute audio persistent 2023-11-10 10:22:04 -06:00
9169cbf728 yuzu: Save mute when in background setting 2023-11-10 10:22:02 -06:00
2f9487cd38 Merge pull request #11981 from lucasreis1/patch
Allocate resources for test window before getting system info
2023-11-10 10:38:49 -05:00
1d03a0fa75 Revert "renderer_vulkan: add locks to avoid scheduler flushes from CPU"
This reverts commit d9dde7e6f3.
2023-11-10 15:40:48 +01:00
09f993899e android: Hide loading animation on first frame 2023-11-09 22:27:40 -05:00
c9038af29e Fix out_icon_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::IconsDir); 2023-11-09 04:53:10 +00:00
f3053920bf Minor changes 2023-11-09 03:37:06 +00:00
c7b31d24b9 Final change, i think 2023-11-08 21:04:30 +00:00
edce713fc9 Allocate resources for test window before getting system info 2023-11-07 22:47:02 -04:00
8d0d0e1c7a Fixed clang 2023-11-07 02:32:19 +00:00
4b8b223db2 modified: src/yuzu/main.cpp 2023-11-05 00:39:43 +00:00
728aca7703 Merge branch 'master' into new-shortcut 2023-11-04 21:28:16 -03:00
a872030a35 nvnflinger: implement consumer abandonment 2023-10-29 23:38:24 -04:00
79e7d7f4ba nvnflinger: use graphic buffer lifetime for map handle 2023-10-29 22:12:16 -04:00
7f62a48ab5 We dont need that 2023-10-27 00:30:35 -03:00
b5415b6872 Merge branch 'yuzu-emu:master' into new-shortcut 2023-10-26 19:11:15 -03:00
b76a1d987f Merge branch 'yuzu-emu:master' into new-shortcut 2023-10-21 02:25:27 -03:00
ae2130470e Reverted dirty code in main. 2023-10-18 19:30:21 -03:00
ac6290bea7 TODO: Implement shortcut creation for Apple. 2023-10-18 02:35:23 -03:00
4051bbbed7 Useless code removed related to admin privileges, if it is not an error we can add it later, that is what git is for. 2023-10-18 01:26:50 -03:00
2a7edda70a Deleted admin requisite (maybe it was another mistake). 2023-10-18 01:20:46 -03:00
59b6ada7b7 Merge branch 'yuzu-emu:master' into new-shortcut 2023-10-18 01:06:58 -03:00
9908434c14 Final refactorization 2023-10-17 02:57:35 -03:00
668a10f9b9 Merge branch 'yuzu-emu:master' into new-shortcut 2023-10-17 01:01:41 -03:00
fc4b45ebd3 Moved check. 2023-10-16 23:50:09 -03:00
1afe6d51ee More @liamwhite suggestions applied. 2023-10-16 23:42:45 -03:00
1ae0f0f3f6 shortcut_stream.close(); fixed 2023-10-16 18:59:21 -03:00
de0b35b974 Comment using fmt instead qt. 2023-10-16 16:54:51 -03:00
ae88d01d8d .clear() instead = ""; and switch improved. 2023-10-16 16:47:21 -03:00
d759de9f96 More missed suggestions 2023-10-16 16:11:24 -03:00
89d3e81be8 Sugestions and fixes. 2023-10-16 16:01:46 -03:00
71f264c498 Merge branch 'new-shortcut' of https://github.com/boludoz/yuzu into new-shortcut 2023-10-16 03:27:10 -03:00
26417da5d3 Some improvements (suggestions) 2023-10-16 03:26:40 -03:00
b3b458edf9 Merge branch 'yuzu-emu:master' into new-shortcut 2023-10-16 03:25:11 -03:00
74961d4dfb Less code, simpler, better. 2023-10-15 21:40:10 -03:00
9ffa1801c7 Typing and formatting errors fixed. 2023-10-15 20:57:06 -03:00
4d4fe69223 Unnecessary feature removed 2023-10-15 14:44:23 -03:00
0a75519ab5 Fixes and improvements 2023-10-15 03:16:29 -03:00
3062a35eb1 Improved shortcut: add games in applist for Windows, question for start game at fullscreen & better unicode support for some Windows path funcs. 2023-10-15 02:02:22 -03:00
227 changed files with 15234 additions and 9022 deletions

View File

@ -3,4 +3,4 @@
[codespell] [codespell]
skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES,./src/android/app/src/main/res skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES,./src/android/app/src/main/res
ignore-words-list = aci,allright,ba,canonicalizations,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,vas,zink ignore-words-list = aci,allright,ba,canonicalizations,deques,froms,hda,inout,lod,masia,nam,nax,nce,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,vas,zink

3
.gitmodules vendored
View File

@ -61,3 +61,6 @@
[submodule "breakpad"] [submodule "breakpad"]
path = externals/breakpad path = externals/breakpad
url = https://github.com/yuzu-emu/breakpad.git url = https://github.com/yuzu-emu/breakpad.git
[submodule "oaknut"]
path = externals/oaknut
url = https://github.com/merryhime/oaknut

View File

@ -1,3 +1,13 @@
| Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----|
| [11535](https://github.com/yuzu-emu/yuzu//pull/11535) | [`50bcfa5fb`](https://github.com/yuzu-emu/yuzu//pull/11535/files) | renderer_vulkan: Introduce separate cmd buffer for uploads | [GPUCode](https://github.com/GPUCode/) | Yes |
| [12074](https://github.com/yuzu-emu/yuzu//pull/12074) | [`d0285e882`](https://github.com/yuzu-emu/yuzu//pull/12074/files) | Implement Native Code Execution (NCE) | [GPUCode](https://github.com/GPUCode/) | Yes |
End of merge log. You can find the original README.md below the break.
-----
<!-- <!--
SPDX-FileCopyrightText: 2018 yuzu Emulator Project SPDX-FileCopyrightText: 2018 yuzu Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later SPDX-License-Identifier: GPL-2.0-or-later

View File

@ -20,6 +20,10 @@ if ((ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) AND NOT TARGET xbyak::xbyak)
endif() endif()
# Dynarmic # Dynarmic
if (ARCHITECTURE_arm64 AND NOT TARGET merry::oaknut)
add_subdirectory(oaknut)
endif()
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) AND NOT TARGET dynarmic::dynarmic) if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) AND NOT TARGET dynarmic::dynarmic)
set(DYNARMIC_IGNORE_ASSERTS ON) set(DYNARMIC_IGNORE_ASSERTS ON)
add_subdirectory(dynarmic) add_subdirectory(dynarmic)

1
externals/oaknut vendored Submodule

Submodule externals/oaknut added at 316d8869e8

View File

@ -373,8 +373,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder() val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder() .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val isEmulationActive = emulationViewModel.emulationStarted.value &&
!emulationViewModel.isEmulationStopping.value
pictureInPictureParamsBuilder.setAutoEnterEnabled( pictureInPictureParamsBuilder.setAutoEnterEnabled(
BooleanSetting.PICTURE_IN_PICTURE.boolean BooleanSetting.PICTURE_IN_PICTURE.boolean && isEmulationActive
) )
} }
setPictureInPictureParams(pictureInPictureParamsBuilder.build()) setPictureInPictureParams(pictureInPictureParamsBuilder.build())

View File

@ -22,12 +22,16 @@ import androidx.core.graphics.drawable.toBitmap
import androidx.core.graphics.drawable.toDrawable import androidx.core.graphics.drawable.toDrawable
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.AsyncDifferConfig import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.YuzuApplication
@ -92,6 +96,8 @@ class GameAdapter(private val activity: AppCompatActivity) :
data = Uri.parse(holder.game.path) data = Uri.parse(holder.game.path)
} }
activity.lifecycleScope.launch {
withContext(Dispatchers.IO) {
val layerDrawable = ResourcesCompat.getDrawable( val layerDrawable = ResourcesCompat.getDrawable(
YuzuApplication.appContext.resources, YuzuApplication.appContext.resources,
R.drawable.shortcut, R.drawable.shortcut,
@ -99,12 +105,14 @@ class GameAdapter(private val activity: AppCompatActivity) :
) as LayerDrawable ) as LayerDrawable
layerDrawable.setDrawableByLayerId( layerDrawable.setDrawableByLayerId(
R.id.shortcut_foreground, R.id.shortcut_foreground,
GameIconUtils.getGameIcon(holder.game).toDrawable(YuzuApplication.appContext.resources) GameIconUtils.getGameIcon(activity, holder.game)
.toDrawable(YuzuApplication.appContext.resources)
) )
val inset = YuzuApplication.appContext.resources val inset = YuzuApplication.appContext.resources
.getDimensionPixelSize(R.dimen.icon_inset) .getDimensionPixelSize(R.dimen.icon_inset)
layerDrawable.setLayerInset(1, inset, inset, inset, inset) layerDrawable.setLayerInset(1, inset, inset, inset, inset)
val shortcut = ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path) val shortcut =
ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path)
.setShortLabel(holder.game.title) .setShortLabel(holder.game.title)
.setIcon( .setIcon(
IconCompat.createWithAdaptiveBitmap( IconCompat.createWithAdaptiveBitmap(
@ -114,6 +122,8 @@ class GameAdapter(private val activity: AppCompatActivity) :
.setIntent(openIntent) .setIntent(openIntent)
.build() .build()
ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut) ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
}
}
val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game) val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game)
view.findNavController().navigate(action) view.findNavController().navigate(action)

View File

@ -10,6 +10,7 @@ enum class IntSetting(
override val category: Settings.Category, override val category: Settings.Category,
override val androidDefault: Int? = null override val androidDefault: Int? = null
) : AbstractIntSetting { ) : AbstractIntSetting {
CPU_BACKEND("cpu_backend", Settings.Category.Cpu),
CPU_ACCURACY("cpu_accuracy", Settings.Category.Cpu), CPU_ACCURACY("cpu_accuracy", Settings.Category.Cpu),
REGION_INDEX("region_index", Settings.Category.System), REGION_INDEX("region_index", Settings.Category.System),
LANGUAGE_INDEX("language_index", Settings.Category.System), LANGUAGE_INDEX("language_index", Settings.Category.System),

View File

@ -82,7 +82,6 @@ object Settings {
enum class MenuTag(val titleId: Int) { enum class MenuTag(val titleId: Int) {
SECTION_ROOT(R.string.advanced_settings), SECTION_ROOT(R.string.advanced_settings),
SECTION_GENERAL(R.string.preferences_general),
SECTION_SYSTEM(R.string.preferences_system), SECTION_SYSTEM(R.string.preferences_system),
SECTION_RENDERER(R.string.preferences_graphics), SECTION_RENDERER(R.string.preferences_graphics),
SECTION_AUDIO(R.string.preferences_audio), SECTION_AUDIO(R.string.preferences_audio),

View File

@ -3,10 +3,13 @@
package org.yuzu.yuzu_emu.features.settings.model.view package org.yuzu.yuzu_emu.features.settings.model.view
import androidx.annotation.DrawableRes
class RunnableSetting( class RunnableSetting(
titleId: Int, titleId: Int,
descriptionId: Int, descriptionId: Int,
val isRuntimeRunnable: Boolean, val isRuntimeRunnable: Boolean,
@DrawableRes val iconId: Int = 0,
val runnable: () -> Unit val runnable: () -> Unit
) : SettingsItem(emptySetting, titleId, descriptionId) { ) : SettingsItem(emptySetting, titleId, descriptionId) {
override val type = TYPE_RUNNABLE override val type = TYPE_RUNNABLE

View File

@ -77,6 +77,15 @@ abstract class SettingsItem(
"%" "%"
) )
) )
put(
SingleChoiceSetting(
IntSetting.CPU_BACKEND,
R.string.cpu_backend,
0,
R.array.cpuBackendNames,
R.array.cpuBackendValues
)
)
put( put(
SingleChoiceSetting( SingleChoiceSetting(
IntSetting.CPU_ACCURACY, IntSetting.CPU_ACCURACY,

View File

@ -3,11 +3,14 @@
package org.yuzu.yuzu_emu.features.settings.model.view package org.yuzu.yuzu_emu.features.settings.model.view
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
class SubmenuSetting( class SubmenuSetting(
titleId: Int, @StringRes titleId: Int,
descriptionId: Int, @StringRes descriptionId: Int,
@DrawableRes val iconId: Int,
val menuKey: Settings.MenuTag val menuKey: Settings.MenuTag
) : SettingsItem(emptySetting, titleId, descriptionId) { ) : SettingsItem(emptySetting, titleId, descriptionId) {
override val type = TYPE_SUBMENU override val type = TYPE_SUBMENU

View File

@ -20,7 +20,6 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.divider.MaterialDividerItemDecoration
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -68,15 +67,9 @@ class SettingsFragment : Fragment() {
) )
binding.toolbarSettingsLayout.title = getString(args.menuTag.titleId) binding.toolbarSettingsLayout.title = getString(args.menuTag.titleId)
val dividerDecoration = MaterialDividerItemDecoration(
requireContext(),
LinearLayoutManager.VERTICAL
)
dividerDecoration.isLastItemDecorated = false
binding.listSettings.apply { binding.listSettings.apply {
adapter = settingsAdapter adapter = settingsAdapter
layoutManager = LinearLayoutManager(requireContext()) layoutManager = LinearLayoutManager(requireContext())
addItemDecoration(dividerDecoration)
} }
binding.toolbarSettings.setNavigationOnClickListener { binding.toolbarSettings.setNavigationOnClickListener {
@ -94,17 +87,6 @@ class SettingsFragment : Fragment() {
} }
} }
} }
launch {
settingsViewModel.isUsingSearch.collectLatest {
if (it) {
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
} else {
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
}
}
}
} }
if (args.menuTag == Settings.MenuTag.SECTION_ROOT) { if (args.menuTag == Settings.MenuTag.SECTION_ROOT) {
@ -112,8 +94,6 @@ class SettingsFragment : Fragment() {
binding.toolbarSettings.setOnMenuItemClickListener { binding.toolbarSettings.setOnMenuItemClickListener {
when (it.itemId) { when (it.itemId) {
R.id.action_search -> { R.id.action_search -> {
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
view.findNavController() view.findNavController()
.navigate(R.id.action_settingsFragment_to_settingsSearchFragment) .navigate(R.id.action_settingsFragment_to_settingsSearchFragment)
true true
@ -129,11 +109,6 @@ class SettingsFragment : Fragment() {
setInsets() setInsets()
} }
override fun onResume() {
super.onResume()
settingsViewModel.setIsUsingSearch(false)
}
private fun setInsets() { private fun setInsets() {
ViewCompat.setOnApplyWindowInsetsListener( ViewCompat.setOnApplyWindowInsetsListener(
binding.root binding.root
@ -144,10 +119,9 @@ class SettingsFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right val rightInsets = barInsets.right + cutoutInsets.right
val sideMargin = resources.getDimensionPixelSize(R.dimen.spacing_medlarge)
val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams
mlpSettingsList.leftMargin = sideMargin + leftInsets mlpSettingsList.leftMargin = leftInsets
mlpSettingsList.rightMargin = sideMargin + rightInsets mlpSettingsList.rightMargin = rightInsets
binding.listSettings.layoutParams = mlpSettingsList binding.listSettings.layoutParams = mlpSettingsList
binding.listSettings.updatePadding( binding.listSettings.updatePadding(
bottom = barInsets.bottom bottom = barInsets.bottom

View File

@ -3,7 +3,6 @@
package org.yuzu.yuzu_emu.features.settings.ui package org.yuzu.yuzu_emu.features.settings.ui
import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Build import android.os.Build
import android.widget.Toast import android.widget.Toast
@ -32,8 +31,6 @@ class SettingsFragmentPresenter(
private val preferences: SharedPreferences private val preferences: SharedPreferences
get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
private val context: Context get() = YuzuApplication.appContext
// Extension for populating settings list based on paired settings // Extension for populating settings list based on paired settings
fun ArrayList<SettingsItem>.add(key: String) { fun ArrayList<SettingsItem>.add(key: String) {
val item = SettingsItem.settingsItems[key]!! val item = SettingsItem.settingsItems[key]!!
@ -53,7 +50,6 @@ class SettingsFragmentPresenter(
val sl = ArrayList<SettingsItem>() val sl = ArrayList<SettingsItem>()
when (menuTag) { when (menuTag) {
Settings.MenuTag.SECTION_ROOT -> addConfigSettings(sl) Settings.MenuTag.SECTION_ROOT -> addConfigSettings(sl)
Settings.MenuTag.SECTION_GENERAL -> addGeneralSettings(sl)
Settings.MenuTag.SECTION_SYSTEM -> addSystemSettings(sl) Settings.MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
Settings.MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl) Settings.MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
Settings.MenuTag.SECTION_AUDIO -> addAudioSettings(sl) Settings.MenuTag.SECTION_AUDIO -> addAudioSettings(sl)
@ -75,30 +71,53 @@ class SettingsFragmentPresenter(
private fun addConfigSettings(sl: ArrayList<SettingsItem>) { private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
sl.apply { sl.apply {
add(SubmenuSetting(R.string.preferences_general, 0, Settings.MenuTag.SECTION_GENERAL))
add(SubmenuSetting(R.string.preferences_system, 0, Settings.MenuTag.SECTION_SYSTEM))
add(SubmenuSetting(R.string.preferences_graphics, 0, Settings.MenuTag.SECTION_RENDERER))
add(SubmenuSetting(R.string.preferences_audio, 0, Settings.MenuTag.SECTION_AUDIO))
add(SubmenuSetting(R.string.preferences_debug, 0, Settings.MenuTag.SECTION_DEBUG))
add( add(
RunnableSetting(R.string.reset_to_default, 0, false) { SubmenuSetting(
settingsViewModel.setShouldShowResetSettingsDialog(true) R.string.preferences_system,
} R.string.preferences_system_description,
R.drawable.ic_system_settings,
Settings.MenuTag.SECTION_SYSTEM
)
)
add(
SubmenuSetting(
R.string.preferences_graphics,
R.string.preferences_graphics_description,
R.drawable.ic_graphics,
Settings.MenuTag.SECTION_RENDERER
)
)
add(
SubmenuSetting(
R.string.preferences_audio,
R.string.preferences_audio_description,
R.drawable.ic_audio,
Settings.MenuTag.SECTION_AUDIO
)
)
add(
SubmenuSetting(
R.string.preferences_debug,
R.string.preferences_debug_description,
R.drawable.ic_code,
Settings.MenuTag.SECTION_DEBUG
)
)
add(
RunnableSetting(
R.string.reset_to_default,
R.string.reset_to_default_description,
false,
R.drawable.ic_restore
) { settingsViewModel.setShouldShowResetSettingsDialog(true) }
) )
}
}
private fun addGeneralSettings(sl: ArrayList<SettingsItem>) {
sl.apply {
add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
add(ShortSetting.RENDERER_SPEED_LIMIT.key)
add(IntSetting.CPU_ACCURACY.key)
add(BooleanSetting.PICTURE_IN_PICTURE.key)
} }
} }
private fun addSystemSettings(sl: ArrayList<SettingsItem>) { private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
sl.apply { sl.apply {
add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
add(ShortSetting.RENDERER_SPEED_LIMIT.key)
add(BooleanSetting.USE_DOCKED_MODE.key) add(BooleanSetting.USE_DOCKED_MODE.key)
add(IntSetting.REGION_INDEX.key) add(IntSetting.REGION_INDEX.key)
add(IntSetting.LANGUAGE_INDEX.key) add(IntSetting.LANGUAGE_INDEX.key)
@ -116,6 +135,7 @@ class SettingsFragmentPresenter(
add(IntSetting.RENDERER_ANTI_ALIASING.key) add(IntSetting.RENDERER_ANTI_ALIASING.key)
add(IntSetting.RENDERER_SCREEN_LAYOUT.key) add(IntSetting.RENDERER_SCREEN_LAYOUT.key)
add(IntSetting.RENDERER_ASPECT_RATIO.key) add(IntSetting.RENDERER_ASPECT_RATIO.key)
add(BooleanSetting.PICTURE_IN_PICTURE.key)
add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key) add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key) add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key) add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
@ -249,6 +269,8 @@ class SettingsFragmentPresenter(
add(BooleanSetting.RENDERER_DEBUG.key) add(BooleanSetting.RENDERER_DEBUG.key)
add(HeaderSetting(R.string.cpu)) add(HeaderSetting(R.string.cpu))
add(IntSetting.CPU_BACKEND.key)
add(IntSetting.CPU_ACCURACY.key)
add(BooleanSetting.CPU_DEBUG_MODE.key) add(BooleanSetting.CPU_DEBUG_MODE.key)
add(SettingsItem.FASTMEM_COMBINED) add(SettingsItem.FASTMEM_COMBINED)
} }

View File

@ -4,6 +4,7 @@
package org.yuzu.yuzu_emu.features.settings.ui.viewholder package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View import android.view.View
import androidx.core.content.res.ResourcesCompat
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting
@ -16,6 +17,19 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
override fun bind(item: SettingsItem) { override fun bind(item: SettingsItem) {
setting = item as RunnableSetting setting = item as RunnableSetting
if (item.iconId != 0) {
binding.icon.visibility = View.VISIBLE
binding.icon.setImageDrawable(
ResourcesCompat.getDrawable(
binding.icon.resources,
item.iconId,
binding.icon.context.theme
)
)
} else {
binding.icon.visibility = View.GONE
}
binding.textSettingName.setText(item.nameId) binding.textSettingName.setText(item.nameId)
if (item.descriptionId != 0) { if (item.descriptionId != 0) {
binding.textSettingDescription.setText(item.descriptionId) binding.textSettingDescription.setText(item.descriptionId)

View File

@ -4,6 +4,7 @@
package org.yuzu.yuzu_emu.features.settings.ui.viewholder package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View import android.view.View
import androidx.core.content.res.ResourcesCompat
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting
@ -15,6 +16,19 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd
override fun bind(item: SettingsItem) { override fun bind(item: SettingsItem) {
this.item = item as SubmenuSetting this.item = item as SubmenuSetting
if (item.iconId != 0) {
binding.icon.visibility = View.VISIBLE
binding.icon.setImageDrawable(
ResourcesCompat.getDrawable(
binding.icon.resources,
item.iconId,
binding.icon.context.theme
)
)
} else {
binding.icon.visibility = View.GONE
}
binding.textSettingName.setText(item.nameId) binding.textSettingName.setText(item.nameId)
if (item.descriptionId != 0) { if (item.descriptionId != 0) {
binding.textSettingDescription.setText(item.descriptionId) binding.textSettingDescription.setText(item.descriptionId)

View File

@ -114,10 +114,10 @@ class AboutFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.appbarAbout.layoutParams as MarginLayoutParams val mlpToolbar = binding.toolbarAbout.layoutParams as MarginLayoutParams
mlpAppBar.leftMargin = leftInsets mlpToolbar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets mlpToolbar.rightMargin = rightInsets
binding.appbarAbout.layoutParams = mlpAppBar binding.toolbarAbout.layoutParams = mlpToolbar
val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams
mlpScrollAbout.leftMargin = leftInsets mlpScrollAbout.leftMargin = leftInsets

View File

@ -40,8 +40,10 @@ class SettingsSearchFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
} }
override fun onCreateView( override fun onCreateView(
@ -55,7 +57,6 @@ class SettingsSearchFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
settingsViewModel.setIsUsingSearch(true)
if (savedInstanceState != null) { if (savedInstanceState != null) {
binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT)) binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT))

View File

@ -29,9 +29,6 @@ class SettingsViewModel : ViewModel() {
val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList
private val _shouldReloadSettingsList = MutableStateFlow(false) private val _shouldReloadSettingsList = MutableStateFlow(false)
val isUsingSearch: StateFlow<Boolean> get() = _isUsingSearch
private val _isUsingSearch = MutableStateFlow(false)
val sliderProgress: StateFlow<Int> get() = _sliderProgress val sliderProgress: StateFlow<Int> get() = _sliderProgress
private val _sliderProgress = MutableStateFlow(-1) private val _sliderProgress = MutableStateFlow(-1)
@ -57,10 +54,6 @@ class SettingsViewModel : ViewModel() {
_shouldReloadSettingsList.value = value _shouldReloadSettingsList.value = value
} }
fun setIsUsingSearch(value: Boolean) {
_isUsingSearch.value = value
}
fun setSliderTextValue(value: Float, units: String) { fun setSliderTextValue(value: Float, units: String) {
_sliderProgress.value = value.toInt() _sliderProgress.value = value.toInt()
_sliderTextValue.value = String.format( _sliderTextValue.value = String.format(

View File

@ -8,9 +8,9 @@ import android.graphics.BitmapFactory
import android.widget.ImageView import android.widget.ImageView
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
import androidx.core.graphics.drawable.toDrawable import androidx.core.graphics.drawable.toDrawable
import androidx.lifecycle.LifecycleOwner
import coil.ImageLoader import coil.ImageLoader
import coil.decode.DataSource import coil.decode.DataSource
import coil.executeBlocking
import coil.fetch.DrawableResult import coil.fetch.DrawableResult
import coil.fetch.FetchResult import coil.fetch.FetchResult
import coil.fetch.Fetcher import coil.fetch.Fetcher
@ -76,12 +76,13 @@ object GameIconUtils {
imageLoader.enqueue(request) imageLoader.enqueue(request)
} }
fun getGameIcon(game: Game): Bitmap { suspend fun getGameIcon(lifecycleOwner: LifecycleOwner, game: Game): Bitmap {
val request = ImageRequest.Builder(YuzuApplication.appContext) val request = ImageRequest.Builder(YuzuApplication.appContext)
.data(game) .data(game)
.lifecycle(lifecycleOwner)
.error(R.drawable.default_icon) .error(R.drawable.default_icon)
.build() .build()
return imageLoader.executeBlocking(request) return imageLoader.execute(request)
.drawable!!.toBitmap(config = Bitmap.Config.ARGB_8888) .drawable!!.toBitmap(config = Bitmap.Config.ARGB_8888)
} }
} }

View File

@ -177,6 +177,7 @@ void Config::ReadValues() {
ReadSetting("Core", Settings::values.memory_layout_mode); ReadSetting("Core", Settings::values.memory_layout_mode);
// Cpu // Cpu
ReadSetting("Cpu", Settings::values.cpu_backend);
ReadSetting("Cpu", Settings::values.cpu_accuracy); ReadSetting("Cpu", Settings::values.cpu_accuracy);
ReadSetting("Cpu", Settings::values.cpu_debug_mode); ReadSetting("Cpu", Settings::values.cpu_debug_mode);
ReadSetting("Cpu", Settings::values.cpuopt_page_tables); ReadSetting("Cpu", Settings::values.cpuopt_page_tables);

View File

@ -153,6 +153,10 @@ use_multi_core =
use_unsafe_extended_memory_layout = use_unsafe_extended_memory_layout =
[Cpu] [Cpu]
Selects the preferred CPU backend for executing ARM instructions
# 0 (default): Dynarmic, 1: NCE
cpu_backend =
# Adjusts various optimizations. # Adjusts various optimizations.
# Auto-select mode enables choice unsafe optimizations. # Auto-select mode enables choice unsafe optimizations.
# Accurate enables only safe optimizations. # Accurate enables only safe optimizations.

View File

@ -9,6 +9,7 @@
#include "input_common/drivers/virtual_gamepad.h" #include "input_common/drivers/virtual_gamepad.h"
#include "input_common/main.h" #include "input_common/main.h"
#include "jni/emu_window/emu_window.h" #include "jni/emu_window/emu_window.h"
#include "jni/native.h"
void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) { void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
m_window_width = ANativeWindow_getWidth(surface); m_window_width = ANativeWindow_getWidth(surface);
@ -57,6 +58,13 @@ void EmuWindow_Android::OnRemoveNfcTag() {
m_input_subsystem->GetVirtualAmiibo()->CloseAmiibo(); m_input_subsystem->GetVirtualAmiibo()->CloseAmiibo();
} }
void EmuWindow_Android::OnFrameDisplayed() {
if (!m_first_frame) {
EmulationSession::GetInstance().OnEmulationStarted();
m_first_frame = true;
}
}
EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem, EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem,
ANativeWindow* surface, ANativeWindow* surface,
std::shared_ptr<Common::DynamicLibrary> driver_library) std::shared_ptr<Common::DynamicLibrary> driver_library)

View File

@ -45,7 +45,7 @@ public:
float gyro_z, float accel_x, float accel_y, float accel_z); float gyro_z, float accel_x, float accel_y, float accel_z);
void OnReadNfcTag(std::span<u8> data); void OnReadNfcTag(std::span<u8> data);
void OnRemoveNfcTag(); void OnRemoveNfcTag();
void OnFrameDisplayed() override {} void OnFrameDisplayed() override;
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override { std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override {
return {std::make_unique<GraphicsContext_Android>(m_driver_library)}; return {std::make_unique<GraphicsContext_Android>(m_driver_library)};
@ -61,4 +61,6 @@ private:
float m_window_height{}; float m_window_height{};
std::shared_ptr<Common::DynamicLibrary> m_driver_library; std::shared_ptr<Common::DynamicLibrary> m_driver_library;
bool m_first_frame = false;
}; };

View File

@ -372,8 +372,6 @@ void EmulationSession::RunEmulation() {
m_system.InitializeDebugger(); m_system.InitializeDebugger();
} }
OnEmulationStarted();
while (true) { while (true) {
{ {
[[maybe_unused]] std::unique_lock lock(m_mutex); [[maybe_unused]] std::unique_lock lock(m_mutex);

View File

@ -52,9 +52,10 @@ public:
void OnGamepadDisconnectEvent([[maybe_unused]] int index); void OnGamepadDisconnectEvent([[maybe_unused]] int index);
SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard(); SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard();
static void OnEmulationStarted();
private: private:
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max); static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
static void OnEmulationStarted();
static void OnEmulationStopped(Core::SystemResultStatus result); static void OnEmulationStopped(Core::SystemResultStatus result);
private: private:

View File

@ -13,7 +13,7 @@ struct Values {
Settings::Linkage linkage; Settings::Linkage linkage;
// Android // Android
Settings::Setting<bool> picture_in_picture{linkage, true, "picture_in_picture", Settings::Setting<bool> picture_in_picture{linkage, false, "picture_in_picture",
Settings::Category::Android}; Settings::Category::Android};
Settings::Setting<s32> screen_layout{linkage, Settings::Setting<s32> screen_layout{linkage,
5, 5,

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M320,720 L80,480l240,-240 57,57 -184,184 183,183 -56,56ZM640,720 L583,663 767,479 584,296 640,240 880,480 640,720Z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M160,840q-33,0 -56.5,-23.5T80,760v-560q0,-33 23.5,-56.5T160,120h560q33,0 56.5,23.5T800,200v80h80v80h-80v80h80v80h-80v80h80v80h-80v80q0,33 -23.5,56.5T720,840L160,840ZM160,760h560v-560L160,200v560ZM240,680h200v-160L240,520v160ZM480,400h160v-120L480,280v120ZM240,480h200v-200L240,280v200ZM480,680h160v-240L480,440v240ZM160,200v560,-560Z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M320,960q-17,0 -28.5,-11.5T280,920q0,-17 11.5,-28.5T320,880q17,0 28.5,11.5T360,920q0,17 -11.5,28.5T320,960ZM480,960q-17,0 -28.5,-11.5T440,920q0,-17 11.5,-28.5T480,880q17,0 28.5,11.5T520,920q0,17 -11.5,28.5T480,960ZM640,960q-17,0 -28.5,-11.5T600,920q0,-17 11.5,-28.5T640,880q17,0 28.5,11.5T680,920q0,17 -11.5,28.5T640,960ZM320,800q-33,0 -56.5,-23.5T240,720v-640q0,-33 23.5,-56.5T320,0h320q33,0 56.5,23.5T720,80v640q0,33 -23.5,56.5T640,800L320,800ZM320,720h320v-40L320,680v40ZM320,600h320v-400L320,200v400ZM320,120h320v-40L320,80v40ZM320,120v-40,40ZM320,720v-40,40Z"/>
</vector>

View File

@ -0,0 +1,233 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinator_about"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_about"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_about"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:navigationIcon="@drawable/ic_back"
app:title="@string/about" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:id="@+id/scroll_about"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fadeScrollbars="false"
android:scrollbars="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:id="@+id/content_about"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:id="@+id/image_logo"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center_horizontal"
android:padding="20dp"
android:src="@drawable/ic_yuzu_title" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="16dp"
android:paddingVertical="16dp">
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:text="@string/about"
android:textAlignment="viewStart" />
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="6dp"
android:text="@string/about_app_description"
android:textAlignment="viewStart" />
</LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp" />
<LinearLayout
android:id="@+id/button_contributors"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:orientation="vertical"
android:paddingHorizontal="16dp"
android:paddingVertical="16dp">
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:text="@string/contributors"
android:textAlignment="viewStart" />
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="6dp"
android:text="@string/contributors_description"
android:textAlignment="viewStart" />
</LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp" />
<LinearLayout
android:id="@+id/button_licenses"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:orientation="vertical"
android:paddingHorizontal="16dp"
android:paddingVertical="16dp">
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:text="@string/licenses"
android:textAlignment="viewStart" />
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="6dp"
android:text="@string/licenses_description"
android:textAlignment="viewStart" />
</LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp" />
<LinearLayout
android:id="@+id/button_build_hash"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:orientation="vertical"
android:paddingHorizontal="16dp"
android:paddingVertical="16dp">
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:text="@string/build"
android:textAlignment="viewStart" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_build_hash"
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="6dp"
android:textAlignment="viewStart"
tools:text="abc123" />
</LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="40dp"
android:layout_marginTop="12dp"
android:layout_marginBottom="16dp"
android:gravity="center_horizontal"
android:orientation="horizontal">
<Button
android:id="@+id/button_discord"
style="?attr/materialIconButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:icon="@drawable/ic_discord"
app:iconGravity="textEnd"
app:iconSize="24dp"
app:iconTint="?attr/colorOnSurface" />
<Button
android:id="@+id/button_website"
style="?attr/materialIconButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:icon="@drawable/ic_website"
app:iconGravity="textEnd"
app:iconSize="24dp"
app:iconTint="?attr/colorOnSurface" />
<Button
android:id="@+id/button_github"
style="?attr/materialIconButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:icon="@drawable/ic_github"
app:iconGravity="textEnd"
app:iconSize="24dp"
app:iconTint="?attr/colorOnSurface" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -6,8 +6,8 @@
android:id="@+id/option_card" android:id="@+id/option_card"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginVertical="12dp" android:layout_marginBottom="24dp"
android:layout_marginHorizontal="16dp" android:layout_marginHorizontal="12dp"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:backgroundTint="?attr/colorSurfaceVariant" android:backgroundTint="?attr/colorSurfaceVariant"
android:clickable="true" android:clickable="true"

View File

@ -38,17 +38,17 @@
<ImageView <ImageView
android:id="@+id/image_logo" android:id="@+id/image_logo"
android:layout_width="250dp" android:layout_width="150dp"
android:layout_height="250dp" android:layout_height="150dp"
android:layout_marginTop="20dp" android:layout_marginTop="24dp"
android:layout_marginBottom="28dp"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:src="@drawable/ic_yuzu_title" /> android:src="@drawable/ic_yuzu_title" />
<com.google.android.material.divider.MaterialDivider <com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp" android:layout_marginHorizontal="20dp" />
android:layout_marginTop="28dp" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -139,7 +139,7 @@
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/show_fps_text" android:id="@+id/show_fps_text"
style="@style/TextAppearance.Material3.BodyMedium" style="@style/TextAppearance.Material3.BodySmall"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="left" android:layout_gravity="left"
@ -147,7 +147,8 @@
android:focusable="false" android:focusable="false"
android:paddingHorizontal="20dp" android:paddingHorizontal="20dp"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:textSize="12sp" android:shadowColor="@android:color/black"
android:shadowRadius="3"
tools:ignore="RtlHardcoded" /> tools:ignore="RtlHardcoded" />
</FrameLayout> </FrameLayout>

View File

@ -14,13 +14,14 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface"
android:paddingHorizontal="8dp">
<ImageView <ImageView
android:id="@+id/logo_image" android:id="@+id/logo_image"
android:layout_width="128dp" android:layout_width="96dp"
android:layout_height="128dp" android:layout_height="96dp"
android:layout_margin="64dp" android:layout_marginVertical="32dp"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:src="@drawable/ic_yuzu_full" /> android:src="@drawable/ic_yuzu_full" />

View File

@ -10,7 +10,22 @@
android:focusable="true" android:focusable="true"
android:gravity="center_vertical" android:gravity="center_vertical"
android:minHeight="72dp" android:minHeight="72dp"
android:padding="@dimen/spacing_large"> android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="24dp"
android:layout_gravity="center_vertical"
android:visibility="gone"
app:tint="?attr/colorOnSurface" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -23,7 +38,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textSize="16sp" android:textSize="17sp"
app:lineHeight="22dp" app:lineHeight="22dp"
tools:text="Setting Name" /> tools:text="Setting Name" />
@ -44,8 +59,11 @@
android:layout_marginTop="@dimen/spacing_small" android:layout_marginTop="@dimen/spacing_small"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textStyle="bold" android:textStyle="bold"
android:textSize="13sp"
tools:text="1x" /> tools:text="1x" />
</LinearLayout> </LinearLayout>
</LinearLayout>
</RelativeLayout> </RelativeLayout>

View File

@ -8,9 +8,7 @@
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:minHeight="72dp" android:minHeight="72dp"
android:paddingVertical="@dimen/spacing_large" android:padding="16dp">
android:paddingStart="@dimen/spacing_large"
android:paddingEnd="24dp">
<com.google.android.material.materialswitch.MaterialSwitch <com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/switch_widget" android:id="@+id/switch_widget"
@ -24,7 +22,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/spacing_large" android:layout_marginEnd="24dp"
android:layout_toStartOf="@+id/switch_widget" android:layout_toStartOf="@+id/switch_widget"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="vertical"> android:orientation="vertical">
@ -35,7 +33,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textSize="16sp" android:textSize="17sp"
app:lineHeight="28dp" app:lineHeight="28dp"
tools:text="@string/frame_limit_enable" /> tools:text="@string/frame_limit_enable" />

View File

@ -7,7 +7,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical" android:layout_gravity="start|center_vertical"
android:paddingHorizontal="@dimen/spacing_large" android:paddingHorizontal="@dimen/spacing_large"
android:paddingVertical="16dp" android:paddingTop="16dp"
android:paddingBottom="8dp"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textColor="?attr/colorPrimary" android:textColor="?attr/colorPrimary"
android:textStyle="bold" android:textStyle="bold"

View File

@ -2,7 +2,6 @@
<resources> <resources>
<string-array name="regionNames"> <string-array name="regionNames">
<item>@string/auto</item>
<item>@string/region_australia</item> <item>@string/region_australia</item>
<item>@string/region_china</item> <item>@string/region_china</item>
<item>@string/region_europe</item> <item>@string/region_europe</item>
@ -13,7 +12,6 @@
</string-array> </string-array>
<integer-array name="regionValues"> <integer-array name="regionValues">
<item>-1</item>
<item>3</item> <item>3</item>
<item>4</item> <item>4</item>
<item>2</item> <item>2</item>
@ -177,6 +175,16 @@
<item>2</item> <item>2</item>
</integer-array> </integer-array>
<string-array name="cpuBackendNames">
<item>@string/cpu_backend_dynarmic</item>
<item>@string/cpu_backend_nce</item>
</string-array>
<integer-array name="cpuBackendValues">
<item>0</item>
<item>1</item>
</integer-array>
<string-array name="cpuAccuracyNames"> <string-array name="cpuAccuracyNames">
<item>@string/auto</item> <item>@string/auto</item>
<item>@string/cpu_accuracy_accurate</item> <item>@string/cpu_accuracy_accurate</item>

View File

@ -184,6 +184,7 @@
<string name="frame_limit_enable_description">Limits emulation speed to a specified percentage of normal speed.</string> <string name="frame_limit_enable_description">Limits emulation speed to a specified percentage of normal speed.</string>
<string name="frame_limit_slider">Limit speed percent</string> <string name="frame_limit_slider">Limit speed percent</string>
<string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. 100% is the normal speed. Values higher or lower will increase or decrease the speed limit.</string> <string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. 100% is the normal speed. Values higher or lower will increase or decrease the speed limit.</string>
<string name="cpu_backend">CPU Backend</string>
<string name="cpu_accuracy">CPU accuracy</string> <string name="cpu_accuracy">CPU accuracy</string>
<string name="value_with_units">%1$s%2$s</string> <string name="value_with_units">%1$s%2$s</string>
@ -240,6 +241,7 @@
<string name="shutting_down">Shutting down…</string> <string name="shutting_down">Shutting down…</string>
<string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string> <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string>
<string name="reset_to_default">Reset to default</string> <string name="reset_to_default">Reset to default</string>
<string name="reset_to_default_description">Resets all advanced settings</string>
<string name="reset_all_settings">Reset all settings?</string> <string name="reset_all_settings">Reset all settings?</string>
<string name="reset_all_settings_description">All advanced settings will be reset to their default configuration. This can not be undone.</string> <string name="reset_all_settings_description">All advanced settings will be reset to their default configuration. This can not be undone.</string>
<string name="settings_reset">Settings reset</string> <string name="settings_reset">Settings reset</string>
@ -271,10 +273,14 @@
<string name="preferences_settings">Settings</string> <string name="preferences_settings">Settings</string>
<string name="preferences_general">General</string> <string name="preferences_general">General</string>
<string name="preferences_system">System</string> <string name="preferences_system">System</string>
<string name="preferences_system_description">Docked mode, region, language</string>
<string name="preferences_graphics">Graphics</string> <string name="preferences_graphics">Graphics</string>
<string name="preferences_graphics_description">Accuracy level, resolution, shader cache</string>
<string name="preferences_audio">Audio</string> <string name="preferences_audio">Audio</string>
<string name="preferences_audio_description">Output engine, volume</string>
<string name="preferences_theme">Theme and color</string> <string name="preferences_theme">Theme and color</string>
<string name="preferences_debug">Debug</string> <string name="preferences_debug">Debug</string>
<string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string>
<!-- ROM loading errors --> <!-- ROM loading errors -->
<string name="loader_error_encrypted">Your ROM is encrypted</string> <string name="loader_error_encrypted">Your ROM is encrypted</string>
@ -409,6 +415,10 @@
<string name="ratio_force_sixteen_ten">Force 16:10</string> <string name="ratio_force_sixteen_ten">Force 16:10</string>
<string name="ratio_stretch">Stretch to window</string> <string name="ratio_stretch">Stretch to window</string>
<!-- CPU Backend -->
<string name="cpu_backend_dynarmic">Dynarmic (Slow)</string>
<string name="cpu_backend_nce">Native code execution (NCE)</string>
<!-- CPU Accuracy --> <!-- CPU Accuracy -->
<string name="cpu_accuracy_accurate">Accurate</string> <string name="cpu_accuracy_accurate">Accurate</string>
<string name="cpu_accuracy_unsafe">Unsafe</string> <string name="cpu_accuracy_unsafe">Unsafe</string>

View File

@ -12,7 +12,7 @@ bool IsValidChannelCount(u32 channel_count) {
} }
bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) { bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
return total_stream_count > 0 && stereo_stream_count > 0 && return total_stream_count > 0 && static_cast<s32>(stereo_stream_count) >= 0 &&
stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count); stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count);
} }
} // namespace } // namespace

View File

@ -148,7 +148,7 @@ Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out
auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())}; auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
OpusPacketHeader header{ReverseHeader(*header_p)}; OpusPacketHeader header{ReverseHeader(*header_p)};
LOG_ERROR(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}", LOG_TRACE(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}",
header.size, input_data.size_bytes(), in_data.size_bytes()); header.size, input_data.size_bytes(), in_data.size_bytes());
R_UNLESS(in_data.size_bytes() >= header.size && R_UNLESS(in_data.size_bytes() >= header.size &&

View File

@ -146,7 +146,7 @@ public:
return; return;
} }
paused = true; SignalPause();
if (cubeb_stream_stop(stream_backend) != CUBEB_OK) { if (cubeb_stream_stop(stream_backend) != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream"); LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream");
} }

View File

@ -111,7 +111,7 @@ public:
if (device == 0 || paused) { if (device == 0 || paused) {
return; return;
} }
paused = true; SignalPause();
SDL_PauseAudioDevice(device, 1); SDL_PauseAudioDevice(device, 1);
} }

View File

@ -282,11 +282,19 @@ u64 SinkStream::GetExpectedPlayedSampleCount() {
void SinkStream::WaitFreeSpace(std::stop_token stop_token) { void SinkStream::WaitFreeSpace(std::stop_token stop_token) {
std::unique_lock lk{release_mutex}; std::unique_lock lk{release_mutex};
release_cv.wait_for(lk, std::chrono::milliseconds(5), release_cv.wait_for(lk, std::chrono::milliseconds(5),
[this]() { return queued_buffers < max_queue_size; }); [this]() { return paused || queued_buffers < max_queue_size; });
if (queued_buffers > max_queue_size + 3) { if (queued_buffers > max_queue_size + 3) {
Common::CondvarWait(release_cv, lk, stop_token, Common::CondvarWait(release_cv, lk, stop_token,
[this] { return queued_buffers < max_queue_size; }); [this] { return paused || queued_buffers < max_queue_size; });
} }
} }
void SinkStream::SignalPause() {
{
std::scoped_lock lk{release_mutex};
paused = true;
}
release_cv.notify_one();
}
} // namespace AudioCore::Sink } // namespace AudioCore::Sink

View File

@ -213,6 +213,12 @@ public:
*/ */
void WaitFreeSpace(std::stop_token stop_token); void WaitFreeSpace(std::stop_token stop_token);
protected:
/**
* Unblocks the ADSP if the stream is paused.
*/
void SignalPause();
protected: protected:
/// Core system /// Core system
Core::System& system; Core::System& system;

View File

@ -52,6 +52,7 @@ add_library(common STATIC
fiber.cpp fiber.cpp
fiber.h fiber.h
fixed_point.h fixed_point.h
free_region_manager.h
fs/file.cpp fs/file.cpp
fs/file.h fs/file.h
fs/fs.cpp fs/fs.cpp
@ -166,6 +167,13 @@ if (WIN32)
target_link_libraries(common PRIVATE ntdll) target_link_libraries(common PRIVATE ntdll)
endif() endif()
if (NOT WIN32)
target_sources(common PRIVATE
signal_chain.cpp
signal_chain.h
)
endif()
if(ANDROID) if(ANDROID)
target_sources(common target_sources(common
PRIVATE PRIVATE

View File

@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#include <boost/icl/interval_set.hpp>
namespace Common {
class FreeRegionManager {
public:
explicit FreeRegionManager() = default;
~FreeRegionManager() = default;
void SetAddressSpace(void* start, size_t size) {
this->FreeBlock(start, size);
}
std::pair<void*, size_t> FreeBlock(void* block_ptr, size_t size) {
std::scoped_lock lk(m_mutex);
// Check to see if we are adjacent to any regions.
auto start_address = reinterpret_cast<uintptr_t>(block_ptr);
auto end_address = start_address + size;
auto it = m_free_regions.find({start_address - 1, end_address + 1});
// If we are, join with them, ensuring we stay in bounds.
if (it != m_free_regions.end()) {
start_address = std::min(start_address, it->lower());
end_address = std::max(end_address, it->upper());
}
// Free the relevant region.
m_free_regions.insert({start_address, end_address});
// Return the adjusted pointers.
block_ptr = reinterpret_cast<void*>(start_address);
size = end_address - start_address;
return {block_ptr, size};
}
void AllocateBlock(void* block_ptr, size_t size) {
std::scoped_lock lk(m_mutex);
auto address = reinterpret_cast<uintptr_t>(block_ptr);
m_free_regions.subtract({address, address + size});
}
private:
std::mutex m_mutex;
boost::icl::interval_set<uintptr_t> m_free_regions;
};
} // namespace Common

View File

@ -21,15 +21,18 @@
#include <boost/icl/interval_set.hpp> #include <boost/icl/interval_set.hpp>
#include <fcntl.h> #include <fcntl.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/random.h>
#include <unistd.h> #include <unistd.h>
#include "common/scope_exit.h" #include "common/scope_exit.h"
#endif // ^^^ Linux ^^^ #endif // ^^^ Linux ^^^
#include <mutex> #include <mutex>
#include <random>
#include "common/alignment.h" #include "common/alignment.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/free_region_manager.h"
#include "common/host_memory.h" #include "common/host_memory.h"
#include "common/logging/log.h" #include "common/logging/log.h"
@ -141,7 +144,7 @@ public:
Release(); Release();
} }
void Map(size_t virtual_offset, size_t host_offset, size_t length) { void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
std::unique_lock lock{placeholder_mutex}; std::unique_lock lock{placeholder_mutex};
if (!IsNiechePlaceholder(virtual_offset, length)) { if (!IsNiechePlaceholder(virtual_offset, length)) {
Split(virtual_offset, length); Split(virtual_offset, length);
@ -160,7 +163,7 @@ public:
} }
} }
void Protect(size_t virtual_offset, size_t length, bool read, bool write) { void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {
DWORD new_flags{}; DWORD new_flags{};
if (read && write) { if (read && write) {
new_flags = PAGE_READWRITE; new_flags = PAGE_READWRITE;
@ -186,6 +189,11 @@ public:
} }
} }
void EnableDirectMappedAddress() {
// TODO
UNREACHABLE();
}
const size_t backing_size; ///< Size of the backing memory in bytes const size_t backing_size; ///< Size of the backing memory in bytes
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
@ -353,6 +361,64 @@ private:
#elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv #elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
#ifdef ARCHITECTURE_arm64
static uint64_t GetRandomU64() {
uint64_t ret;
ASSERT(getrandom(&ret, sizeof(ret), 0) == 0);
return ret;
}
static void* ChooseVirtualBase(size_t virtual_size) {
constexpr uintptr_t Map39BitSize = (1ULL << 39);
constexpr uintptr_t Map36BitSize = (1ULL << 36);
// Seed the MT with some initial strong randomness.
//
// This is not a cryptographic application, we just want something more
// random than the current time.
std::mt19937_64 rng(GetRandomU64());
// We want to ensure we are allocating at an address aligned to the L2 block size.
// For Qualcomm devices, we must also allocate memory above 36 bits.
const size_t lower = Map36BitSize / HugePageSize;
const size_t upper = (Map39BitSize - virtual_size) / HugePageSize;
const size_t range = upper - lower;
// Try up to 64 times to allocate memory at random addresses in the range.
for (int i = 0; i < 64; i++) {
// Calculate a possible location.
uintptr_t hint_address = ((rng() % range) + lower) * HugePageSize;
// Try to map.
// Note: we may be able to take advantage of MAP_FIXED_NOREPLACE here.
void* map_pointer =
mmap(reinterpret_cast<void*>(hint_address), virtual_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
// If we successfully mapped, we're done.
if (reinterpret_cast<uintptr_t>(map_pointer) == hint_address) {
return map_pointer;
}
// Unmap if necessary, and try again.
if (map_pointer != MAP_FAILED) {
munmap(map_pointer, virtual_size);
}
}
return MAP_FAILED;
}
#else
static void* ChooseVirtualBase(size_t virtual_size) {
return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
}
#endif
class HostMemory::Impl { class HostMemory::Impl {
public: public:
explicit Impl(size_t backing_size_, size_t virtual_size_) explicit Impl(size_t backing_size_, size_t virtual_size_)
@ -415,8 +481,7 @@ public:
} }
} }
#else #else
virtual_base = static_cast<u8*>(mmap(nullptr, virtual_size, PROT_NONE, virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size));
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0));
if (virtual_base == MAP_FAILED) { if (virtual_base == MAP_FAILED) {
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno)); LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
throw std::bad_alloc{}; throw std::bad_alloc{};
@ -424,7 +489,7 @@ public:
madvise(virtual_base, virtual_size, MADV_HUGEPAGE); madvise(virtual_base, virtual_size, MADV_HUGEPAGE);
#endif #endif
placeholders.add({0, virtual_size}); free_manager.SetAddressSpace(virtual_base, virtual_size);
good = true; good = true;
} }
@ -432,14 +497,29 @@ public:
Release(); Release();
} }
void Map(size_t virtual_offset, size_t host_offset, size_t length) { void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
{ // Intersect the range with our address space.
std::scoped_lock lock{placeholder_mutex}; AdjustMap(&virtual_offset, &length);
placeholders.subtract({virtual_offset, virtual_offset + length});
}
void* ret = mmap(virtual_base + virtual_offset, length, PROT_READ | PROT_WRITE, // We are removing a placeholder.
MAP_SHARED | MAP_FIXED, fd, host_offset); free_manager.AllocateBlock(virtual_base + virtual_offset, length);
// Deduce mapping protection flags.
int flags = PROT_NONE;
if (True(perms & MemoryPermission::Read)) {
flags |= PROT_READ;
}
if (True(perms & MemoryPermission::Write)) {
flags |= PROT_WRITE;
}
#ifdef ARCHITECTURE_arm64
if (True(perms & MemoryPermission::Execute)) {
flags |= PROT_EXEC;
}
#endif
void* ret = mmap(virtual_base + virtual_offset, length, flags, MAP_SHARED | MAP_FIXED, fd,
host_offset);
ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno)); ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
} }
@ -447,47 +527,54 @@ public:
// The method name is wrong. We're still talking about the virtual range. // The method name is wrong. We're still talking about the virtual range.
// We don't want to unmap, we want to reserve this memory. // We don't want to unmap, we want to reserve this memory.
{ // Intersect the range with our address space.
std::scoped_lock lock{placeholder_mutex}; AdjustMap(&virtual_offset, &length);
auto it = placeholders.find({virtual_offset - 1, virtual_offset + length + 1});
if (it != placeholders.end()) { // Merge with any adjacent placeholder mappings.
size_t prev_upper = virtual_offset + length; auto [merged_pointer, merged_size] =
virtual_offset = std::min(virtual_offset, it->lower()); free_manager.FreeBlock(virtual_base + virtual_offset, length);
length = std::max(it->upper(), prev_upper) - virtual_offset;
}
placeholders.add({virtual_offset, virtual_offset + length}); void* ret = mmap(merged_pointer, merged_size, PROT_NONE,
}
void* ret = mmap(virtual_base + virtual_offset, length, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno)); ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
} }
void Protect(size_t virtual_offset, size_t length, bool read, bool write) { void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {
int flags = 0; // Intersect the range with our address space.
AdjustMap(&virtual_offset, &length);
int flags = PROT_NONE;
if (read) { if (read) {
flags |= PROT_READ; flags |= PROT_READ;
} }
if (write) { if (write) {
flags |= PROT_WRITE; flags |= PROT_WRITE;
} }
#ifdef ARCHITECTURE_arm64
if (execute) {
flags |= PROT_EXEC;
}
#endif
int ret = mprotect(virtual_base + virtual_offset, length, flags); int ret = mprotect(virtual_base + virtual_offset, length, flags);
ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno)); ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno));
} }
void EnableDirectMappedAddress() {
virtual_base = nullptr;
}
const size_t backing_size; ///< Size of the backing memory in bytes const size_t backing_size; ///< Size of the backing memory in bytes
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)}; u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)};
u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)}; u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)};
u8* virtual_map_base{reinterpret_cast<u8*>(MAP_FAILED)};
private: private:
/// Release all resources in the object /// Release all resources in the object
void Release() { void Release() {
if (virtual_base != MAP_FAILED) { if (virtual_map_base != MAP_FAILED) {
int ret = munmap(virtual_base, virtual_size); int ret = munmap(virtual_map_base, virtual_size);
ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno)); ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno));
} }
@ -502,10 +589,29 @@ private:
} }
} }
int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create void AdjustMap(size_t* virtual_offset, size_t* length) {
if (virtual_base != nullptr) {
return;
}
boost::icl::interval_set<size_t> placeholders; ///< Mapped placeholders // If we are direct mapped, we want to make sure we are operating on a region
std::mutex placeholder_mutex; ///< Mutex for placeholders // that is in range of our virtual mapping.
size_t intended_start = *virtual_offset;
size_t intended_end = intended_start + *length;
size_t address_space_start = reinterpret_cast<size_t>(virtual_map_base);
size_t address_space_end = address_space_start + virtual_size;
if (address_space_start > intended_end || intended_start > address_space_end) {
*virtual_offset = 0;
*length = 0;
} else {
*virtual_offset = std::max(intended_start, address_space_start);
*length = std::min(intended_end, address_space_end) - *virtual_offset;
}
}
int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create
FreeRegionManager free_manager{};
}; };
#else // ^^^ Linux ^^^ vvv Generic vvv #else // ^^^ Linux ^^^ vvv Generic vvv
@ -518,11 +624,11 @@ public:
throw std::bad_alloc{}; throw std::bad_alloc{};
} }
void Map(size_t virtual_offset, size_t host_offset, size_t length) {} void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm) {}
void Unmap(size_t virtual_offset, size_t length) {} void Unmap(size_t virtual_offset, size_t length) {}
void Protect(size_t virtual_offset, size_t length, bool read, bool write) {} void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {}
u8* backing_base{nullptr}; u8* backing_base{nullptr};
u8* virtual_base{nullptr}; u8* virtual_base{nullptr};
@ -535,15 +641,16 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
try { try {
// Try to allocate a fastmem arena. // Try to allocate a fastmem arena.
// The implementation will fail with std::bad_alloc on errors. // The implementation will fail with std::bad_alloc on errors.
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), impl =
AlignUp(virtual_size, PageAlignment) + std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment),
3 * HugePageSize); AlignUp(virtual_size, PageAlignment) + HugePageSize);
backing_base = impl->backing_base; backing_base = impl->backing_base;
virtual_base = impl->virtual_base; virtual_base = impl->virtual_base;
if (virtual_base) { if (virtual_base) {
virtual_base += 2 * HugePageSize - 1; // Ensure the virtual base is aligned to the L2 block size.
virtual_base -= reinterpret_cast<size_t>(virtual_base) & (HugePageSize - 1); virtual_base = reinterpret_cast<u8*>(
Common::AlignUp(reinterpret_cast<uintptr_t>(virtual_base), HugePageSize));
virtual_base_offset = virtual_base - impl->virtual_base; virtual_base_offset = virtual_base - impl->virtual_base;
} }
@ -562,7 +669,8 @@ HostMemory::HostMemory(HostMemory&&) noexcept = default;
HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default; HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default;
void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length) { void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
MemoryPermission perms) {
ASSERT(virtual_offset % PageAlignment == 0); ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(host_offset % PageAlignment == 0); ASSERT(host_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0); ASSERT(length % PageAlignment == 0);
@ -571,7 +679,7 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length) {
if (length == 0 || !virtual_base || !impl) { if (length == 0 || !virtual_base || !impl) {
return; return;
} }
impl->Map(virtual_offset + virtual_base_offset, host_offset, length); impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms);
} }
void HostMemory::Unmap(size_t virtual_offset, size_t length) { void HostMemory::Unmap(size_t virtual_offset, size_t length) {
@ -584,14 +692,22 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length) {
impl->Unmap(virtual_offset + virtual_base_offset, length); impl->Unmap(virtual_offset + virtual_base_offset, length);
} }
void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write) { void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write,
bool execute) {
ASSERT(virtual_offset % PageAlignment == 0); ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0); ASSERT(length % PageAlignment == 0);
ASSERT(virtual_offset + length <= virtual_size); ASSERT(virtual_offset + length <= virtual_size);
if (length == 0 || !virtual_base || !impl) { if (length == 0 || !virtual_base || !impl) {
return; return;
} }
impl->Protect(virtual_offset + virtual_base_offset, length, read, write); impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute);
}
void HostMemory::EnableDirectMappedAddress() {
if (impl) {
impl->EnableDirectMappedAddress();
virtual_size += reinterpret_cast<uintptr_t>(virtual_base);
}
} }
} // namespace Common } // namespace Common

View File

@ -4,11 +4,20 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/virtual_buffer.h" #include "common/virtual_buffer.h"
namespace Common { namespace Common {
enum class MemoryPermission : u32 {
Read = 1 << 0,
Write = 1 << 1,
ReadWrite = Read | Write,
Execute = 1 << 2,
};
DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission)
/** /**
* A low level linear memory buffer, which supports multiple mappings * A low level linear memory buffer, which supports multiple mappings
* Its purpose is to rebuild a given sparse memory layout, including mirrors. * Its purpose is to rebuild a given sparse memory layout, including mirrors.
@ -31,11 +40,13 @@ public:
HostMemory(HostMemory&& other) noexcept; HostMemory(HostMemory&& other) noexcept;
HostMemory& operator=(HostMemory&& other) noexcept; HostMemory& operator=(HostMemory&& other) noexcept;
void Map(size_t virtual_offset, size_t host_offset, size_t length); void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms);
void Unmap(size_t virtual_offset, size_t length); void Unmap(size_t virtual_offset, size_t length);
void Protect(size_t virtual_offset, size_t length, bool read, bool write); void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute = false);
void EnableDirectMappedAddress();
[[nodiscard]] u8* BackingBasePointer() noexcept { [[nodiscard]] u8* BackingBasePointer() noexcept {
return backing_base; return backing_base;

View File

@ -9,12 +9,12 @@ PageTable::PageTable() = default;
PageTable::~PageTable() noexcept = default; PageTable::~PageTable() noexcept = default;
bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context, bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
u64 address) const { Common::ProcessAddress address) const {
// Setup invalid defaults. // Setup invalid defaults.
out_entry.phys_addr = 0; out_entry->phys_addr = 0;
out_entry.block_size = page_size; out_entry->block_size = page_size;
out_context.next_page = 0; out_context->next_page = 0;
// Validate that we can read the actual entry. // Validate that we can read the actual entry.
const auto page = address / page_size; const auto page = address / page_size;
@ -29,20 +29,20 @@ bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_
} }
// Populate the results. // Populate the results.
out_entry.phys_addr = phys_addr + address; out_entry->phys_addr = phys_addr + GetInteger(address);
out_context.next_page = page + 1; out_context->next_page = page + 1;
out_context.next_offset = address + page_size; out_context->next_offset = GetInteger(address) + page_size;
return true; return true;
} }
bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const { bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const {
// Setup invalid defaults. // Setup invalid defaults.
out_entry.phys_addr = 0; out_entry->phys_addr = 0;
out_entry.block_size = page_size; out_entry->block_size = page_size;
// Validate that we can read the actual entry. // Validate that we can read the actual entry.
const auto page = context.next_page; const auto page = context->next_page;
if (page >= backing_addr.size()) { if (page >= backing_addr.size()) {
return false; return false;
} }
@ -54,9 +54,9 @@ bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& c
} }
// Populate the results. // Populate the results.
out_entry.phys_addr = phys_addr + context.next_offset; out_entry->phys_addr = phys_addr + context->next_offset;
context.next_page = page + 1; context->next_page = page + 1;
context.next_offset += page_size; context->next_offset += page_size;
return true; return true;
} }

View File

@ -6,6 +6,7 @@
#include <atomic> #include <atomic>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/typed_address.h"
#include "common/virtual_buffer.h" #include "common/virtual_buffer.h"
namespace Common { namespace Common {
@ -100,9 +101,9 @@ struct PageTable {
PageTable(PageTable&&) noexcept = default; PageTable(PageTable&&) noexcept = default;
PageTable& operator=(PageTable&&) noexcept = default; PageTable& operator=(PageTable&&) noexcept = default;
bool BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context, bool BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
u64 address) const; Common::ProcessAddress address) const;
bool ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const; bool ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const;
/** /**
* Resizes the page table to be able to accommodate enough pages within * Resizes the page table to be able to accommodate enough pages within
@ -117,6 +118,16 @@ struct PageTable {
return current_address_space_width_in_bits; return current_address_space_width_in_bits;
} }
bool GetPhysicalAddress(Common::PhysicalAddress* out_phys_addr,
Common::ProcessAddress virt_addr) const {
if (virt_addr > (1ULL << this->GetAddressSpaceBits())) {
return false;
}
*out_phys_addr = backing_addr[virt_addr / page_size] + GetInteger(virt_addr);
return true;
}
/** /**
* Vector of memory pointers backing each page. An entry can only be non-null if the * Vector of memory pointers backing each page. An entry can only be non-null if the
* corresponding attribute element is of type `Memory`. * corresponding attribute element is of type `Memory`.

View File

@ -41,6 +41,7 @@ SWITCHABLE(AspectRatio, true);
SWITCHABLE(AstcDecodeMode, true); SWITCHABLE(AstcDecodeMode, true);
SWITCHABLE(AstcRecompression, true); SWITCHABLE(AstcRecompression, true);
SWITCHABLE(AudioMode, true); SWITCHABLE(AudioMode, true);
SWITCHABLE(CpuBackend, true);
SWITCHABLE(CpuAccuracy, true); SWITCHABLE(CpuAccuracy, true);
SWITCHABLE(FullscreenMode, true); SWITCHABLE(FullscreenMode, true);
SWITCHABLE(GpuAccuracy, true); SWITCHABLE(GpuAccuracy, true);
@ -155,6 +156,22 @@ bool IsFastmemEnabled() {
return true; return true;
} }
static bool is_nce_enabled = false;
void SetNceEnabled(bool is_39bit) {
const bool is_nce_selected = values.cpu_backend.GetValue() == CpuBackend::Nce;
is_nce_enabled = is_nce_selected && is_39bit;
if (is_nce_selected && !is_nce_enabled) {
LOG_WARNING(
Common,
"Program does not utilize 39-bit address space, unable to natively execute code");
}
}
bool IsNceEnabled() {
return is_nce_enabled;
}
bool IsDockedMode() { bool IsDockedMode() {
return values.use_docked_mode.GetValue() == Settings::ConsoleMode::Docked; return values.use_docked_mode.GetValue() == Settings::ConsoleMode::Docked;
} }
@ -203,6 +220,8 @@ const char* TranslateCategory(Category category) {
case Category::Ui: case Category::Ui:
case Category::UiGeneral: case Category::UiGeneral:
return "UI"; return "UI";
case Category::UiAudio:
return "UiAudio";
case Category::UiLayout: case Category::UiLayout:
return "UiLayout"; return "UiLayout";
case Category::UiGameList: case Category::UiGameList:

View File

@ -63,6 +63,7 @@ SWITCHABLE(AspectRatio, true);
SWITCHABLE(AstcDecodeMode, true); SWITCHABLE(AstcDecodeMode, true);
SWITCHABLE(AstcRecompression, true); SWITCHABLE(AstcRecompression, true);
SWITCHABLE(AudioMode, true); SWITCHABLE(AudioMode, true);
SWITCHABLE(CpuBackend, true);
SWITCHABLE(CpuAccuracy, true); SWITCHABLE(CpuAccuracy, true);
SWITCHABLE(FullscreenMode, true); SWITCHABLE(FullscreenMode, true);
SWITCHABLE(GpuAccuracy, true); SWITCHABLE(GpuAccuracy, true);
@ -153,7 +154,7 @@ struct Values {
true, true,
true}; true};
Setting<bool, false> audio_muted{ Setting<bool, false> audio_muted{
linkage, false, "audio_muted", Category::Audio, Specialization::Default, false, true}; linkage, false, "audio_muted", Category::Audio, Specialization::Default, true, true};
Setting<bool, false> dump_audio_commands{ Setting<bool, false> dump_audio_commands{
linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false}; linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false};
@ -179,6 +180,14 @@ struct Values {
&use_speed_limit}; &use_speed_limit};
// Cpu // Cpu
SwitchableSetting<CpuBackend, true> cpu_backend{
linkage, CpuBackend::Dynarmic, CpuBackend::Dynarmic,
#ifdef ARCHITECTURE_arm64
CpuBackend::Nce,
#else
CpuBackend::Dynarmic,
#endif
"cpu_backend", Category::Cpu};
SwitchableSetting<CpuAccuracy, true> cpu_accuracy{linkage, CpuAccuracy::Auto, SwitchableSetting<CpuAccuracy, true> cpu_accuracy{linkage, CpuAccuracy::Auto,
CpuAccuracy::Auto, CpuAccuracy::Paranoid, CpuAccuracy::Auto, CpuAccuracy::Paranoid,
"cpu_accuracy", Category::Cpu}; "cpu_accuracy", Category::Cpu};
@ -358,6 +367,8 @@ struct Values {
Category::RendererDebug}; Category::RendererDebug};
// TODO: remove this once AMDVLK supports VK_EXT_depth_bias_control // TODO: remove this once AMDVLK supports VK_EXT_depth_bias_control
bool renderer_amdvlk_depth_bias_workaround{}; bool renderer_amdvlk_depth_bias_workaround{};
Setting<bool> disable_buffer_reorder{linkage, false, "disable_buffer_reorder",
Category::RendererDebug};
// System // System
SwitchableSetting<Language, true> language_index{linkage, SwitchableSetting<Language, true> language_index{linkage,
@ -534,6 +545,8 @@ bool IsGPULevelExtreme();
bool IsGPULevelHigh(); bool IsGPULevelHigh();
bool IsFastmemEnabled(); bool IsFastmemEnabled();
void SetNceEnabled(bool is_64bit);
bool IsNceEnabled();
bool IsDockedMode(); bool IsDockedMode();

View File

@ -32,6 +32,7 @@ enum class Category : u32 {
AddOns, AddOns,
Controls, Controls,
Ui, Ui,
UiAudio,
UiGeneral, UiGeneral,
UiLayout, UiLayout,
UiGameList, UiGameList,

View File

@ -129,6 +129,8 @@ ENUM(ShaderBackend, Glsl, Glasm, SpirV);
ENUM(GpuAccuracy, Normal, High, Extreme); ENUM(GpuAccuracy, Normal, High, Extreme);
ENUM(CpuBackend, Dynarmic, Nce);
ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid); ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid);
ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb); ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb);

View File

@ -9,7 +9,8 @@ const std::array<const char*, NumButtons> mapping = {{
"button_a", "button_b", "button_x", "button_y", "button_lstick", "button_a", "button_b", "button_x", "button_y", "button_lstick",
"button_rstick", "button_l", "button_r", "button_zl", "button_zr", "button_rstick", "button_l", "button_r", "button_zl", "button_zr",
"button_plus", "button_minus", "button_dleft", "button_dup", "button_dright", "button_plus", "button_minus", "button_dleft", "button_dup", "button_dright",
"button_ddown", "button_sl", "button_sr", "button_home", "button_screenshot", "button_ddown", "button_slleft", "button_srleft", "button_home", "button_screenshot",
"button_slright", "button_srright",
}}; }};
} }

View File

@ -29,12 +29,15 @@ enum Values : int {
DRight, DRight,
DDown, DDown,
SL, SLLeft,
SR, SRLeft,
Home, Home,
Screenshot, Screenshot,
SLRight,
SRRight,
NumButtons, NumButtons,
}; };

View File

@ -0,0 +1,42 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <dlfcn.h>
#include "common/assert.h"
#include "common/dynamic_library.h"
#include "common/scope_exit.h"
#include "common/signal_chain.h"
namespace Common {
template <typename T>
T* LookupLibcSymbol(const char* name) {
#if defined(__BIONIC__)
Common::DynamicLibrary provider("libc.so");
if (!provider.IsOpen()) {
UNREACHABLE_MSG("Failed to open libc!");
}
#else
// For other operating environments, we assume the symbol is not overridden.
const char* base = nullptr;
Common::DynamicLibrary provider(base);
#endif
void* sym = provider.GetSymbolAddress(name);
if (sym == nullptr) {
sym = dlsym(RTLD_DEFAULT, name);
}
if (sym == nullptr) {
UNREACHABLE_MSG("Unable to find symbol {}!", name);
}
return reinterpret_cast<T*>(sym);
}
int SigAction(int signum, const struct sigaction* act, struct sigaction* oldact) {
static auto libc_sigaction = LookupLibcSymbol<decltype(sigaction)>("sigaction");
return libc_sigaction(signum, act, oldact);
}
} // namespace Common

19
src/common/signal_chain.h Normal file
View File

@ -0,0 +1,19 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#ifndef _WIN32
#include <signal.h>
namespace Common {
// Android's ART overrides sigaction with its own wrapper. This is problematic for SIGSEGV
// in particular, because ART's handler accesses tpidr_el0, which conflicts with NCE.
// This extracts the libc symbol and calls it directly.
int SigAction(int signum, const struct sigaction* act, struct sigaction* oldact);
} // namespace Common
#endif

View File

@ -271,8 +271,9 @@ add_library(core STATIC
hle/kernel/k_page_heap.h hle/kernel/k_page_heap.h
hle/kernel/k_page_group.cpp hle/kernel/k_page_group.cpp
hle/kernel/k_page_group.h hle/kernel/k_page_group.h
hle/kernel/k_page_table.cpp
hle/kernel/k_page_table.h hle/kernel/k_page_table.h
hle/kernel/k_page_table_base.cpp
hle/kernel/k_page_table_base.h
hle/kernel/k_page_table_manager.h hle/kernel/k_page_table_manager.h
hle/kernel/k_page_table_slab_heap.h hle/kernel/k_page_table_slab_heap.h
hle/kernel/k_port.cpp hle/kernel/k_port.cpp
@ -280,6 +281,7 @@ add_library(core STATIC
hle/kernel/k_priority_queue.h hle/kernel/k_priority_queue.h
hle/kernel/k_process.cpp hle/kernel/k_process.cpp
hle/kernel/k_process.h hle/kernel/k_process.h
hle/kernel/k_process_page_table.h
hle/kernel/k_readable_event.cpp hle/kernel/k_readable_event.cpp
hle/kernel/k_readable_event.h hle/kernel/k_readable_event.h
hle/kernel/k_resource_limit.cpp hle/kernel/k_resource_limit.cpp
@ -330,8 +332,6 @@ add_library(core STATIC
hle/kernel/physical_core.cpp hle/kernel/physical_core.cpp
hle/kernel/physical_core.h hle/kernel/physical_core.h
hle/kernel/physical_memory.h hle/kernel/physical_memory.h
hle/kernel/process_capability.cpp
hle/kernel/process_capability.h
hle/kernel/slab_helpers.h hle/kernel/slab_helpers.h
hle/kernel/svc.cpp hle/kernel/svc.cpp
hle/kernel/svc.h hle/kernel/svc.h
@ -521,11 +521,21 @@ add_library(core STATIC
hle/service/grc/grc.h hle/service/grc/grc.h
hle/service/hid/hid.cpp hle/service/hid/hid.cpp
hle/service/hid/hid.h hle/service/hid/hid.h
hle/service/hid/hid_debug_server.cpp
hle/service/hid/hid_debug_server.h
hle/service/hid/hid_firmware_settings.cpp
hle/service/hid/hid_firmware_settings.h
hle/service/hid/hid_server.cpp
hle/service/hid/hid_server.h
hle/service/hid/hid_system_server.cpp
hle/service/hid/hid_system_server.h
hle/service/hid/hidbus.cpp hle/service/hid/hidbus.cpp
hle/service/hid/hidbus.h hle/service/hid/hidbus.h
hle/service/hid/irs.cpp hle/service/hid/irs.cpp
hle/service/hid/irs.h hle/service/hid/irs.h
hle/service/hid/irs_ring_lifo.h hle/service/hid/irs_ring_lifo.h
hle/service/hid/resource_manager.cpp
hle/service/hid/resource_manager.h
hle/service/hid/ring_lifo.h hle/service/hid/ring_lifo.h
hle/service/hid/xcd.cpp hle/service/hid/xcd.cpp
hle/service/hid/xcd.h hle/service/hid/xcd.h
@ -715,6 +725,7 @@ add_library(core STATIC
hle/service/nvnflinger/producer_listener.h hle/service/nvnflinger/producer_listener.h
hle/service/nvnflinger/status.h hle/service/nvnflinger/status.h
hle/service/nvnflinger/ui/fence.h hle/service/nvnflinger/ui/fence.h
hle/service/nvnflinger/ui/graphic_buffer.cpp
hle/service/nvnflinger/ui/graphic_buffer.h hle/service/nvnflinger/ui/graphic_buffer.h
hle/service/nvnflinger/window.h hle/service/nvnflinger/window.h
hle/service/olsc/olsc.cpp hle/service/olsc/olsc.cpp
@ -910,6 +921,22 @@ if (ENABLE_WEB_SERVICE)
target_link_libraries(core PRIVATE web_service) target_link_libraries(core PRIVATE web_service)
endif() endif()
if (ARCHITECTURE_arm64)
enable_language(C ASM)
set(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp")
target_sources(core PRIVATE
arm/nce/arm_nce.cpp
arm/nce/arm_nce.h
arm/nce/arm_nce.s
arm/nce/guest_context.h
arm/nce/patch.cpp
arm/nce/patch.h
arm/nce/instructions.h
)
target_link_libraries(core PRIVATE merry::oaknut)
endif()
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
target_sources(core PRIVATE target_sources(core PRIVATE
arm/dynarmic/arm_dynarmic.h arm/dynarmic/arm_dynarmic.h

View File

@ -153,6 +153,14 @@ void ARM_Interface::Run() {
Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())}; Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())};
HaltReason hr{}; HaltReason hr{};
// If the thread is scheduled for termination, exit the thread.
if (current_thread->HasDpc()) {
if (current_thread->IsTerminationRequested()) {
current_thread->Exit();
UNREACHABLE();
}
}
// Notify the debugger and go to sleep if a step was performed // Notify the debugger and go to sleep if a step was performed
// and this thread has been scheduled again. // and this thread has been scheduled again.
if (current_thread->GetStepState() == StepState::StepPerformed) { if (current_thread->GetStepState() == StepState::StepPerformed) {
@ -174,14 +182,6 @@ void ARM_Interface::Run() {
} }
system.ExitCPUProfile(); system.ExitCPUProfile();
// If the thread is scheduled for termination, exit the thread.
if (current_thread->HasDpc()) {
if (current_thread->IsTerminationRequested()) {
current_thread->Exit();
UNREACHABLE();
}
}
// Notify the debugger and go to sleep if a breakpoint was hit, // Notify the debugger and go to sleep if a breakpoint was hit,
// or if the thread is unable to continue for any reason. // or if the thread is unable to continue for any reason.
if (True(hr & HaltReason::InstructionBreakpoint) || True(hr & HaltReason::PrefetchAbort)) { if (True(hr & HaltReason::InstructionBreakpoint) || True(hr & HaltReason::PrefetchAbort)) {

View File

@ -81,6 +81,9 @@ public:
// thread context to be 800 bytes in size. // thread context to be 800 bytes in size.
static_assert(sizeof(ThreadContext64) == 0x320); static_assert(sizeof(ThreadContext64) == 0x320);
/// Perform any backend-specific initialization.
virtual void Initialize() {}
/// Runs the CPU until an event happens /// Runs the CPU until an event happens
void Run(); void Run();

View File

@ -0,0 +1,392 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cinttypes>
#include <memory>
#include "common/signal_chain.h"
#include "core/arm/nce/arm_nce.h"
#include "core/arm/nce/patch.h"
#include "core/core.h"
#include "core/memory.h"
#include "core/hle/kernel/k_process.h"
#include <signal.h>
#include <sys/syscall.h>
#include <unistd.h>
namespace Core {
namespace {
struct sigaction g_orig_action;
// Verify assembly offsets.
using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters;
static_assert(offsetof(NativeExecutionParameters, native_context) == TpidrEl0NativeContext);
static_assert(offsetof(NativeExecutionParameters, lock) == TpidrEl0Lock);
static_assert(offsetof(NativeExecutionParameters, magic) == TpidrEl0TlsMagic);
fpsimd_context* GetFloatingPointState(mcontext_t& host_ctx) {
_aarch64_ctx* header = reinterpret_cast<_aarch64_ctx*>(&host_ctx.__reserved);
while (header->magic != FPSIMD_MAGIC) {
header = reinterpret_cast<_aarch64_ctx*>(reinterpret_cast<char*>(header) + header->size);
}
return reinterpret_cast<fpsimd_context*>(header);
}
} // namespace
void* ARM_NCE::RestoreGuestContext(void* raw_context) {
// Retrieve the host context.
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
// Thread-local parameters will be located in x9.
auto* tpidr = reinterpret_cast<NativeExecutionParameters*>(host_ctx.regs[9]);
auto* guest_ctx = static_cast<GuestContext*>(tpidr->native_context);
// Retrieve the host floating point state.
auto* fpctx = GetFloatingPointState(host_ctx);
// Save host callee-saved registers.
std::memcpy(guest_ctx->host_ctx.host_saved_vregs.data(), &fpctx->vregs[8],
sizeof(guest_ctx->host_ctx.host_saved_vregs));
std::memcpy(guest_ctx->host_ctx.host_saved_regs.data(), &host_ctx.regs[19],
sizeof(guest_ctx->host_ctx.host_saved_regs));
// Save stack pointer.
guest_ctx->host_ctx.host_sp = host_ctx.sp;
// Restore all guest state except tpidr_el0.
host_ctx.sp = guest_ctx->sp;
host_ctx.pc = guest_ctx->pc;
host_ctx.pstate = guest_ctx->pstate;
fpctx->fpcr = guest_ctx->fpcr;
fpctx->fpsr = guest_ctx->fpsr;
std::memcpy(host_ctx.regs, guest_ctx->cpu_registers.data(), sizeof(host_ctx.regs));
std::memcpy(fpctx->vregs, guest_ctx->vector_registers.data(), sizeof(fpctx->vregs));
// Return the new thread-local storage pointer.
return tpidr;
}
void ARM_NCE::SaveGuestContext(GuestContext* guest_ctx, void* raw_context) {
// Retrieve the host context.
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
// Retrieve the host floating point state.
auto* fpctx = GetFloatingPointState(host_ctx);
// Save all guest registers except tpidr_el0.
std::memcpy(guest_ctx->cpu_registers.data(), host_ctx.regs, sizeof(host_ctx.regs));
std::memcpy(guest_ctx->vector_registers.data(), fpctx->vregs, sizeof(fpctx->vregs));
guest_ctx->fpsr = fpctx->fpsr;
guest_ctx->fpcr = fpctx->fpcr;
guest_ctx->pstate = static_cast<u32>(host_ctx.pstate);
guest_ctx->pc = host_ctx.pc;
guest_ctx->sp = host_ctx.sp;
// Restore stack pointer.
host_ctx.sp = guest_ctx->host_ctx.host_sp;
// Restore host callee-saved registers.
std::memcpy(&host_ctx.regs[19], guest_ctx->host_ctx.host_saved_regs.data(),
sizeof(guest_ctx->host_ctx.host_saved_regs));
std::memcpy(&fpctx->vregs[8], guest_ctx->host_ctx.host_saved_vregs.data(),
sizeof(guest_ctx->host_ctx.host_saved_vregs));
// Return from the call on exit by setting pc to x30.
host_ctx.pc = guest_ctx->host_ctx.host_saved_regs[11];
// Clear esr_el1 and return it.
host_ctx.regs[0] = guest_ctx->esr_el1.exchange(0);
}
bool ARM_NCE::HandleGuestFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
auto* info = static_cast<siginfo_t*>(raw_info);
// Try to handle an invalid access.
// TODO: handle accesses which split a page?
const Common::ProcessAddress addr =
(reinterpret_cast<u64>(info->si_addr) & ~Memory::YUZU_PAGEMASK);
if (guest_ctx->system->ApplicationMemory().InvalidateNCE(addr, Memory::YUZU_PAGESIZE)) {
// We handled the access successfully and are returning to guest code.
return true;
}
// We can't handle the access, so trigger an exception.
const bool is_prefetch_abort = host_ctx.pc == reinterpret_cast<u64>(info->si_addr);
guest_ctx->esr_el1.fetch_or(
static_cast<u64>(is_prefetch_abort ? HaltReason::PrefetchAbort : HaltReason::DataAbort));
// Forcibly mark the context as locked. We are still running.
// We may race with SignalInterrupt here:
// - If we lose the race, then SignalInterrupt will send us a signal we are masking,
// and it will do nothing when it is unmasked, as we have already left guest code.
// - If we win the race, then SignalInterrupt will wait for us to unlock first.
auto& thread_params = guest_ctx->parent->running_thread->GetNativeExecutionParameters();
thread_params.lock.store(SpinLockLocked);
// Return to host.
SaveGuestContext(guest_ctx, raw_context);
return false;
}
void ARM_NCE::HandleHostFault(int sig, void* raw_info, void* raw_context) {
return g_orig_action.sa_sigaction(sig, static_cast<siginfo_t*>(raw_info), raw_context);
}
HaltReason ARM_NCE::RunJit() {
// Get the thread parameters.
// TODO: pass the current thread down from ::Run
auto* thread = Kernel::GetCurrentThreadPointer(system.Kernel());
auto* thread_params = &thread->GetNativeExecutionParameters();
{
// Lock our core context.
std::scoped_lock lk{lock};
// We should not be running.
ASSERT(running_thread == nullptr);
// Check if we need to run. If we have already been halted, we are done.
u64 halt = guest_ctx.esr_el1.exchange(0);
if (halt != 0) {
return static_cast<HaltReason>(halt);
}
// Mark that we are running.
running_thread = thread;
// Acquire the lock on the thread parameters.
// This allows us to force synchronization with SignalInterrupt.
LockThreadParameters(thread_params);
}
// Assign current members.
guest_ctx.parent = this;
thread_params->native_context = &guest_ctx;
thread_params->tpidr_el0 = guest_ctx.tpidr_el0;
thread_params->tpidrro_el0 = guest_ctx.tpidrro_el0;
thread_params->is_running = true;
HaltReason halt{};
// TODO: finding and creating the post handler needs to be locked
// to deal with dynamic loading of NROs.
const auto& post_handlers = system.ApplicationProcess()->GetPostHandlers();
if (auto it = post_handlers.find(guest_ctx.pc); it != post_handlers.end()) {
halt = ReturnToRunCodeByTrampoline(thread_params, &guest_ctx, it->second);
} else {
halt = ReturnToRunCodeByExceptionLevelChange(thread_id, thread_params);
}
// Unload members.
// The thread does not change, so we can persist the old reference.
guest_ctx.tpidr_el0 = thread_params->tpidr_el0;
thread_params->native_context = nullptr;
thread_params->is_running = false;
// Unlock the thread parameters.
UnlockThreadParameters(thread_params);
{
// Lock the core context.
std::scoped_lock lk{lock};
// On exit, we no longer have an active thread.
running_thread = nullptr;
}
// Return the halt reason.
return halt;
}
HaltReason ARM_NCE::StepJit() {
return HaltReason::StepThread;
}
u32 ARM_NCE::GetSvcNumber() const {
return guest_ctx.svc_swi;
}
ARM_NCE::ARM_NCE(System& system_, bool uses_wall_clock_, std::size_t core_index_)
: ARM_Interface{system_, uses_wall_clock_}, core_index{core_index_} {
guest_ctx.system = &system_;
}
ARM_NCE::~ARM_NCE() = default;
void ARM_NCE::Initialize() {
thread_id = gettid();
// Setup our signals
static std::once_flag flag;
std::call_once(flag, [] {
using HandlerType = decltype(sigaction::sa_sigaction);
sigset_t signal_mask;
sigemptyset(&signal_mask);
sigaddset(&signal_mask, ReturnToRunCodeByExceptionLevelChangeSignal);
sigaddset(&signal_mask, BreakFromRunCodeSignal);
sigaddset(&signal_mask, GuestFaultSignal);
struct sigaction return_to_run_code_action {};
return_to_run_code_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
return_to_run_code_action.sa_sigaction = reinterpret_cast<HandlerType>(
&ARM_NCE::ReturnToRunCodeByExceptionLevelChangeSignalHandler);
return_to_run_code_action.sa_mask = signal_mask;
Common::SigAction(ReturnToRunCodeByExceptionLevelChangeSignal, &return_to_run_code_action,
nullptr);
struct sigaction break_from_run_code_action {};
break_from_run_code_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
break_from_run_code_action.sa_sigaction =
reinterpret_cast<HandlerType>(&ARM_NCE::BreakFromRunCodeSignalHandler);
break_from_run_code_action.sa_mask = signal_mask;
Common::SigAction(BreakFromRunCodeSignal, &break_from_run_code_action, nullptr);
struct sigaction fault_action {};
fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART;
fault_action.sa_sigaction =
reinterpret_cast<HandlerType>(&ARM_NCE::GuestFaultSignalHandler);
fault_action.sa_mask = signal_mask;
Common::SigAction(GuestFaultSignal, &fault_action, &g_orig_action);
// Simplify call for g_orig_action.
// These fields occupy the same space in memory, so this should be a no-op in practice.
if (!(g_orig_action.sa_flags & SA_SIGINFO)) {
g_orig_action.sa_sigaction =
reinterpret_cast<decltype(g_orig_action.sa_sigaction)>(g_orig_action.sa_handler);
}
});
}
void ARM_NCE::SetPC(u64 pc) {
guest_ctx.pc = pc;
}
u64 ARM_NCE::GetPC() const {
return guest_ctx.pc;
}
u64 ARM_NCE::GetSP() const {
return guest_ctx.sp;
}
u64 ARM_NCE::GetReg(int index) const {
return guest_ctx.cpu_registers[index];
}
void ARM_NCE::SetReg(int index, u64 value) {
guest_ctx.cpu_registers[index] = value;
}
u128 ARM_NCE::GetVectorReg(int index) const {
return guest_ctx.vector_registers[index];
}
void ARM_NCE::SetVectorReg(int index, u128 value) {
guest_ctx.vector_registers[index] = value;
}
u32 ARM_NCE::GetPSTATE() const {
return guest_ctx.pstate;
}
void ARM_NCE::SetPSTATE(u32 pstate) {
guest_ctx.pstate = pstate;
}
u64 ARM_NCE::GetTlsAddress() const {
return guest_ctx.tpidrro_el0;
}
void ARM_NCE::SetTlsAddress(u64 address) {
guest_ctx.tpidrro_el0 = address;
}
u64 ARM_NCE::GetTPIDR_EL0() const {
return guest_ctx.tpidr_el0;
}
void ARM_NCE::SetTPIDR_EL0(u64 value) {
guest_ctx.tpidr_el0 = value;
}
void ARM_NCE::SaveContext(ThreadContext64& ctx) const {
ctx.cpu_registers = guest_ctx.cpu_registers;
ctx.sp = guest_ctx.sp;
ctx.pc = guest_ctx.pc;
ctx.pstate = guest_ctx.pstate;
ctx.vector_registers = guest_ctx.vector_registers;
ctx.fpcr = guest_ctx.fpcr;
ctx.fpsr = guest_ctx.fpsr;
ctx.tpidr = guest_ctx.tpidr_el0;
}
void ARM_NCE::LoadContext(const ThreadContext64& ctx) {
guest_ctx.cpu_registers = ctx.cpu_registers;
guest_ctx.sp = ctx.sp;
guest_ctx.pc = ctx.pc;
guest_ctx.pstate = ctx.pstate;
guest_ctx.vector_registers = ctx.vector_registers;
guest_ctx.fpcr = ctx.fpcr;
guest_ctx.fpsr = ctx.fpsr;
guest_ctx.tpidr_el0 = ctx.tpidr;
}
void ARM_NCE::SignalInterrupt() {
// Lock core context.
std::scoped_lock lk{lock};
// Add break loop condition.
guest_ctx.esr_el1.fetch_or(static_cast<u64>(HaltReason::BreakLoop));
// If there is no thread running, we are done.
if (running_thread == nullptr) {
return;
}
// Lock the thread context.
auto* params = &running_thread->GetNativeExecutionParameters();
LockThreadParameters(params);
if (params->is_running) {
// We should signal to the running thread.
// The running thread will unlock the thread context.
syscall(SYS_tkill, thread_id, BreakFromRunCodeSignal);
} else {
// If the thread is no longer running, we have nothing to do.
UnlockThreadParameters(params);
}
}
void ARM_NCE::ClearInterrupt() {
guest_ctx.esr_el1 = {};
}
void ARM_NCE::ClearInstructionCache() {
// TODO: This is not possible to implement correctly on Linux because
// we do not have any access to ic iallu.
// Require accesses to complete.
std::atomic_thread_fence(std::memory_order_seq_cst);
}
void ARM_NCE::InvalidateCacheRange(u64 addr, std::size_t size) {
this->ClearInstructionCache();
}
void ARM_NCE::ClearExclusiveState() {
// No-op.
}
void ARM_NCE::PageTableChanged(Common::PageTable& page_table,
std::size_t new_address_space_size_in_bits) {
// No-op. Page table is never used.
}
} // namespace Core

108
src/core/arm/nce/arm_nce.h Normal file
View File

@ -0,0 +1,108 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <memory>
#include <span>
#include <unordered_map>
#include <vector>
#include "core/arm/arm_interface.h"
#include "core/arm/nce/guest_context.h"
namespace Core::Memory {
class Memory;
}
namespace Core {
class System;
class ARM_NCE final : public ARM_Interface {
public:
ARM_NCE(System& system_, bool uses_wall_clock_, std::size_t core_index_);
~ARM_NCE() override;
void Initialize() override;
void SetPC(u64 pc) override;
u64 GetPC() const override;
u64 GetSP() const override;
u64 GetReg(int index) const override;
void SetReg(int index, u64 value) override;
u128 GetVectorReg(int index) const override;
void SetVectorReg(int index, u128 value) override;
u32 GetPSTATE() const override;
void SetPSTATE(u32 pstate) override;
u64 GetTlsAddress() const override;
void SetTlsAddress(u64 address) override;
void SetTPIDR_EL0(u64 value) override;
u64 GetTPIDR_EL0() const override;
Architecture GetArchitecture() const override {
return Architecture::Aarch64;
}
void SaveContext(ThreadContext32& ctx) const override {}
void SaveContext(ThreadContext64& ctx) const override;
void LoadContext(const ThreadContext32& ctx) override {}
void LoadContext(const ThreadContext64& ctx) override;
void SignalInterrupt() override;
void ClearInterrupt() override;
void ClearExclusiveState() override;
void ClearInstructionCache() override;
void InvalidateCacheRange(u64 addr, std::size_t size) override;
void PageTableChanged(Common::PageTable& new_page_table,
std::size_t new_address_space_size_in_bits) override;
protected:
HaltReason RunJit() override;
HaltReason StepJit() override;
u32 GetSvcNumber() const override;
const Kernel::DebugWatchpoint* HaltedWatchpoint() const override {
return nullptr;
}
void RewindBreakpointInstruction() override {}
private:
// Assembly definitions.
static HaltReason ReturnToRunCodeByTrampoline(void* tpidr, GuestContext* ctx,
u64 trampoline_addr);
static HaltReason ReturnToRunCodeByExceptionLevelChange(int tid, void* tpidr);
static void ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void* info,
void* raw_context);
static void BreakFromRunCodeSignalHandler(int sig, void* info, void* raw_context);
static void GuestFaultSignalHandler(int sig, void* info, void* raw_context);
static void LockThreadParameters(void* tpidr);
static void UnlockThreadParameters(void* tpidr);
private:
// C++ implementation functions for assembly definitions.
static void* RestoreGuestContext(void* raw_context);
static void SaveGuestContext(GuestContext* ctx, void* raw_context);
static bool HandleGuestFault(GuestContext* ctx, void* info, void* raw_context);
static void HandleHostFault(int sig, void* info, void* raw_context);
public:
// Members set on initialization.
std::size_t core_index{};
pid_t thread_id{-1};
// Core context.
GuestContext guest_ctx;
// Thread and invalidation info.
std::mutex lock;
Kernel::KThread* running_thread{};
};
} // namespace Core

222
src/core/arm/nce/arm_nce.s Normal file
View File

@ -0,0 +1,222 @@
/* SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project */
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "core/arm/nce/arm_nce_asm_definitions.h"
#define LOAD_IMMEDIATE_32(reg, val) \
mov reg, #(((val) >> 0x00) & 0xFFFF); \
movk reg, #(((val) >> 0x10) & 0xFFFF), lsl #16
/* static HaltReason Core::ARM_NCE::ReturnToRunCodeByTrampoline(void* tpidr, Core::GuestContext* ctx, u64 trampoline_addr) */
.section .text._ZN4Core7ARM_NCE27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm, "ax", %progbits
.global _ZN4Core7ARM_NCE27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm
.type _ZN4Core7ARM_NCE27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm, %function
_ZN4Core7ARM_NCE27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm:
/* Back up host sp to x3. */
/* Back up host tpidr_el0 to x4. */
mov x3, sp
mrs x4, tpidr_el0
/* Load guest sp. x5 is used as a scratch register. */
ldr x5, [x1, #(GuestContextSp)]
mov sp, x5
/* Offset GuestContext pointer to the host member. */
add x5, x1, #(GuestContextHostContext)
/* Save original host sp and tpidr_el0 (x3, x4) to host context. */
stp x3, x4, [x5, #(HostContextSpTpidrEl0)]
/* Save all callee-saved host GPRs. */
stp x19, x20, [x5, #(HostContextRegs+0x0)]
stp x21, x22, [x5, #(HostContextRegs+0x10)]
stp x23, x24, [x5, #(HostContextRegs+0x20)]
stp x25, x26, [x5, #(HostContextRegs+0x30)]
stp x27, x28, [x5, #(HostContextRegs+0x40)]
stp x29, x30, [x5, #(HostContextRegs+0x50)]
/* Save all callee-saved host FPRs. */
stp q8, q9, [x5, #(HostContextVregs+0x0)]
stp q10, q11, [x5, #(HostContextVregs+0x20)]
stp q12, q13, [x5, #(HostContextVregs+0x40)]
stp q14, q15, [x5, #(HostContextVregs+0x60)]
/* Load guest tpidr_el0 from argument. */
msr tpidr_el0, x0
/* Tail call the trampoline to restore guest state. */
br x2
/* static HaltReason Core::ARM_NCE::ReturnToRunCodeByExceptionLevelChange(int tid, void* tpidr) */
.section .text._ZN4Core7ARM_NCE37ReturnToRunCodeByExceptionLevelChangeEiPv, "ax", %progbits
.global _ZN4Core7ARM_NCE37ReturnToRunCodeByExceptionLevelChangeEiPv
.type _ZN4Core7ARM_NCE37ReturnToRunCodeByExceptionLevelChangeEiPv, %function
_ZN4Core7ARM_NCE37ReturnToRunCodeByExceptionLevelChangeEiPv:
/* This jumps to the signal handler, which will restore the entire context. */
/* On entry, x0 = thread id, which is already in the right place. */
/* Move tpidr to x9 so it is not trampled. */
mov x9, x1
/* Set up arguments. */
mov x8, #(__NR_tkill)
mov x1, #(ReturnToRunCodeByExceptionLevelChangeSignal)
/* Tail call the signal handler. */
svc #0
/* Block execution from flowing here. */
brk #1000
/* static void Core::ARM_NCE::ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void* info, void* raw_context) */
.section .text._ZN4Core7ARM_NCE50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_, "ax", %progbits
.global _ZN4Core7ARM_NCE50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_
.type _ZN4Core7ARM_NCE50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_, %function
_ZN4Core7ARM_NCE50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_:
stp x29, x30, [sp, #-0x10]!
mov x29, sp
/* Call the context restorer with the raw context. */
mov x0, x2
bl _ZN4Core7ARM_NCE19RestoreGuestContextEPv
/* Save the old value of tpidr_el0. */
mrs x8, tpidr_el0
ldr x9, [x0, #(TpidrEl0NativeContext)]
str x8, [x9, #(GuestContextHostContext + HostContextTpidrEl0)]
/* Set our new tpidr_el0. */
msr tpidr_el0, x0
/* Unlock the context. */
bl _ZN4Core7ARM_NCE22UnlockThreadParametersEPv
/* Returning from here will enter the guest. */
ldp x29, x30, [sp], #0x10
ret
/* static void Core::ARM_NCE::BreakFromRunCodeSignalHandler(int sig, void* info, void* raw_context) */
.section .text._ZN4Core7ARM_NCE29BreakFromRunCodeSignalHandlerEiPvS1_, "ax", %progbits
.global _ZN4Core7ARM_NCE29BreakFromRunCodeSignalHandlerEiPvS1_
.type _ZN4Core7ARM_NCE29BreakFromRunCodeSignalHandlerEiPvS1_, %function
_ZN4Core7ARM_NCE29BreakFromRunCodeSignalHandlerEiPvS1_:
/* Check to see if we have the correct TLS magic. */
mrs x8, tpidr_el0
ldr w9, [x8, #(TpidrEl0TlsMagic)]
LOAD_IMMEDIATE_32(w10, TlsMagic)
cmp w9, w10
b.ne 1f
/* Correct TLS magic, so this is a guest interrupt. */
/* Restore host tpidr_el0. */
ldr x0, [x8, #(TpidrEl0NativeContext)]
ldr x3, [x0, #(GuestContextHostContext + HostContextTpidrEl0)]
msr tpidr_el0, x3
/* Tail call the restorer. */
mov x1, x2
b _ZN4Core7ARM_NCE16SaveGuestContextEPNS_12GuestContextEPv
/* Returning from here will enter host code. */
1:
/* Incorrect TLS magic, so this is a spurious signal. */
ret
/* static void Core::ARM_NCE::GuestFaultSignalHandler(int sig, void* info, void* raw_context) */
.section .text._ZN4Core7ARM_NCE23GuestFaultSignalHandlerEiPvS1_, "ax", %progbits
.global _ZN4Core7ARM_NCE23GuestFaultSignalHandlerEiPvS1_
.type _ZN4Core7ARM_NCE23GuestFaultSignalHandlerEiPvS1_, %function
_ZN4Core7ARM_NCE23GuestFaultSignalHandlerEiPvS1_:
/* Check to see if we have the correct TLS magic. */
mrs x8, tpidr_el0
ldr w9, [x8, #(TpidrEl0TlsMagic)]
LOAD_IMMEDIATE_32(w10, TlsMagic)
cmp w9, w10
b.eq 1f
/* Incorrect TLS magic, so this is a host fault. */
/* Tail call the handler. */
b _ZN4Core7ARM_NCE15HandleHostFaultEiPvS1_
1:
/* Correct TLS magic, so this is a guest fault. */
stp x29, x30, [sp, #-0x20]!
str x19, [sp, #0x10]
mov x29, sp
/* Save the old tpidr_el0. */
mov x19, x8
/* Restore host tpidr_el0. */
ldr x0, [x8, #(TpidrEl0NativeContext)]
ldr x3, [x0, #(GuestContextHostContext + HostContextTpidrEl0)]
msr tpidr_el0, x3
/* Call the handler. */
bl _ZN4Core7ARM_NCE16HandleGuestFaultEPNS_12GuestContextEPvS3_
/* If the handler returned false, we want to preserve the host tpidr_el0. */
cbz x0, 2f
/* Otherwise, restore guest tpidr_el0. */
msr tpidr_el0, x19
2:
ldr x19, [sp, #0x10]
ldp x29, x30, [sp], #0x20
ret
/* static void Core::ARM_NCE::LockThreadParameters(void* tpidr) */
.section .text._ZN4Core7ARM_NCE20LockThreadParametersEPv, "ax", %progbits
.global _ZN4Core7ARM_NCE20LockThreadParametersEPv
.type _ZN4Core7ARM_NCE20LockThreadParametersEPv, %function
_ZN4Core7ARM_NCE20LockThreadParametersEPv:
/* Offset to lock member. */
add x0, x0, #(TpidrEl0Lock)
1:
/* Clear the monitor. */
clrex
2:
/* Load-linked with acquire ordering. */
ldaxr w1, [x0]
/* If the value was SpinLockLocked, clear monitor and retry. */
cbz w1, 1b
/* Store-conditional SpinLockLocked with relaxed ordering. */
stxr w1, wzr, [x0]
/* If we failed to store, retry. */
cbnz w1, 2b
ret
/* static void Core::ARM_NCE::UnlockThreadParameters(void* tpidr) */
.section .text._ZN4Core7ARM_NCE22UnlockThreadParametersEPv, "ax", %progbits
.global _ZN4Core7ARM_NCE22UnlockThreadParametersEPv
.type _ZN4Core7ARM_NCE22UnlockThreadParametersEPv, %function
_ZN4Core7ARM_NCE22UnlockThreadParametersEPv:
/* Offset to lock member. */
add x0, x0, #(TpidrEl0Lock)
/* Load SpinLockUnlocked. */
mov w1, #(SpinLockUnlocked)
/* Store value with release ordering. */
stlr w1, [x0]
ret

View File

@ -0,0 +1,29 @@
/* SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project */
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#define __ASSEMBLY__
#include <asm-generic/signal.h>
#include <asm-generic/unistd.h>
#define ReturnToRunCodeByExceptionLevelChangeSignal SIGUSR2
#define BreakFromRunCodeSignal SIGURG
#define GuestFaultSignal SIGSEGV
#define GuestContextSp 0xF8
#define GuestContextHostContext 0x320
#define HostContextSpTpidrEl0 0xE0
#define HostContextTpidrEl0 0xE8
#define HostContextRegs 0x0
#define HostContextVregs 0x60
#define TpidrEl0NativeContext 0x10
#define TpidrEl0Lock 0x18
#define TpidrEl0TlsMagic 0x20
#define TlsMagic 0x555a5559
#define SpinLockLocked 0
#define SpinLockUnlocked 1

View File

@ -0,0 +1,50 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/arm/arm_interface.h"
#include "core/arm/nce/arm_nce_asm_definitions.h"
namespace Core {
class ARM_NCE;
class System;
struct HostContext {
alignas(16) std::array<u64, 12> host_saved_regs{};
alignas(16) std::array<u128, 8> host_saved_vregs{};
u64 host_sp{};
void* host_tpidr_el0{};
};
struct GuestContext {
std::array<u64, 31> cpu_registers{};
u64 sp{};
u64 pc{};
u32 fpcr{};
u32 fpsr{};
std::array<u128, 32> vector_registers{};
u32 pstate{};
alignas(16) HostContext host_ctx{};
u64 tpidrro_el0{};
u64 tpidr_el0{};
std::atomic<u64> esr_el1{};
u32 nzcv{};
u32 svc_swi{};
System* system{};
ARM_NCE* parent{};
};
// Verify assembly offsets.
static_assert(offsetof(GuestContext, sp) == GuestContextSp);
static_assert(offsetof(GuestContext, host_ctx) == GuestContextHostContext);
static_assert(offsetof(HostContext, host_sp) == HostContextSpTpidrEl0);
static_assert(offsetof(HostContext, host_tpidr_el0) - 8 == HostContextSpTpidrEl0);
static_assert(offsetof(HostContext, host_tpidr_el0) == HostContextTpidrEl0);
static_assert(offsetof(HostContext, host_saved_regs) == HostContextRegs);
static_assert(offsetof(HostContext, host_saved_vregs) == HostContextVregs);
} // namespace Core

View File

@ -0,0 +1,147 @@
// SPDX-FileCopyrightText: Copyright © 2020 Skyline Team and Contributors
// SPDX-License-Identifier: MPL-2.0
#include "common/bit_field.h"
#include "common/common_types.h"
namespace Core::NCE {
enum SystemRegister : u32 {
TpidrEl0 = 0x5E82,
TpidrroEl0 = 0x5E83,
CntfrqEl0 = 0x5F00,
CntpctEl0 = 0x5F01,
};
// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/SVC--Supervisor-Call-
union SVC {
constexpr explicit SVC(u32 raw_) : raw{raw_} {}
constexpr bool Verify() {
return (this->GetSig0() == 0x1 && this->GetSig1() == 0x6A0);
}
constexpr u32 GetSig0() {
return decltype(sig0)::ExtractValue(raw);
}
constexpr u32 GetValue() {
return decltype(value)::ExtractValue(raw);
}
constexpr u32 GetSig1() {
return decltype(sig1)::ExtractValue(raw);
}
u32 raw;
private:
BitField<0, 5, u32> sig0; // 0x1
BitField<5, 16, u32> value; // 16-bit immediate
BitField<21, 11, u32> sig1; // 0x6A0
};
static_assert(sizeof(SVC) == sizeof(u32));
static_assert(SVC(0xD40000C1).Verify());
static_assert(SVC(0xD40000C1).GetValue() == 0x6);
// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/MRS--Move-System-Register-
union MRS {
constexpr explicit MRS(u32 raw_) : raw{raw_} {}
constexpr bool Verify() {
return (this->GetSig() == 0xD53);
}
constexpr u32 GetRt() {
return decltype(rt)::ExtractValue(raw);
}
constexpr u32 GetSystemReg() {
return decltype(system_reg)::ExtractValue(raw);
}
constexpr u32 GetSig() {
return decltype(sig)::ExtractValue(raw);
}
u32 raw;
private:
BitField<0, 5, u32> rt; // destination register
BitField<5, 15, u32> system_reg; // source system register
BitField<20, 12, u32> sig; // 0xD53
};
static_assert(sizeof(MRS) == sizeof(u32));
static_assert(MRS(0xD53BE020).Verify());
static_assert(MRS(0xD53BE020).GetSystemReg() == CntpctEl0);
static_assert(MRS(0xD53BE020).GetRt() == 0x0);
// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/MSR--register---Move-general-purpose-register-to-System-Register-
union MSR {
constexpr explicit MSR(u32 raw_) : raw{raw_} {}
constexpr bool Verify() {
return this->GetSig() == 0xD51;
}
constexpr u32 GetRt() {
return decltype(rt)::ExtractValue(raw);
}
constexpr u32 GetSystemReg() {
return decltype(system_reg)::ExtractValue(raw);
}
constexpr u32 GetSig() {
return decltype(sig)::ExtractValue(raw);
}
u32 raw;
private:
BitField<0, 5, u32> rt; // source register
BitField<5, 15, u32> system_reg; // destination system register
BitField<20, 12, u32> sig; // 0xD51
};
static_assert(sizeof(MSR) == sizeof(u32));
static_assert(MSR(0xD51BD040).Verify());
static_assert(MSR(0xD51BD040).GetSystemReg() == TpidrEl0);
static_assert(MSR(0xD51BD040).GetRt() == 0x0);
// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDXR--Load-Exclusive-Register-
// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDXP--Load-Exclusive-Pair-of-Registers-
// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/STXR--Store-Exclusive-Register-
// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/STXP--Store-Exclusive-Pair-of-registers-
union Exclusive {
constexpr explicit Exclusive(u32 raw_) : raw{raw_} {}
constexpr bool Verify() {
return this->GetSig() == 0x10;
}
constexpr u32 GetSig() {
return decltype(sig)::ExtractValue(raw);
}
constexpr u32 AsOrdered() {
return raw | decltype(o0)::FormatValue(1);
}
u32 raw;
private:
BitField<0, 5, u32> rt; // memory operand
BitField<5, 5, u32> rn; // register operand 1
BitField<10, 5, u32> rt2; // register operand 2
BitField<15, 1, u32> o0; // ordered
BitField<16, 5, u32> rs; // status register
BitField<21, 2, u32> l; // operation type
BitField<23, 7, u32> sig; // 0x10
BitField<30, 2, u32> size; // size
};
static_assert(Exclusive(0xC85FFC00).Verify());
static_assert(Exclusive(0xC85FFC00).AsOrdered() == 0xC85FFC00);
static_assert(Exclusive(0xC85F7C00).AsOrdered() == 0xC85FFC00);
static_assert(Exclusive(0xC8200440).AsOrdered() == 0xC8208440);
} // namespace Core::NCE

471
src/core/arm/nce/patch.cpp Normal file
View File

@ -0,0 +1,471 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/arm64/native_clock.h"
#include "common/bit_cast.h"
#include "common/literals.h"
#include "core/arm/nce/arm_nce.h"
#include "core/arm/nce/guest_context.h"
#include "core/arm/nce/instructions.h"
#include "core/arm/nce/patch.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/svc.h"
namespace Core::NCE {
using namespace Common::Literals;
using namespace oaknut::util;
using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters;
constexpr size_t MaxRelativeBranch = 128_MiB;
Patcher::Patcher() : c(m_patch_instructions) {}
Patcher::~Patcher() = default;
void Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
const Kernel::CodeSet::Segment& code) {
// Write save context helper function.
c.l(m_save_context);
WriteSaveContext();
// Write load context helper function.
c.l(m_load_context);
WriteLoadContext();
// Retrieve text segment data.
const auto text = std::span{program_image}.subspan(code.offset, code.size);
const auto text_words =
std::span<const u32>{reinterpret_cast<const u32*>(text.data()), text.size() / sizeof(u32)};
// Loop through instructions, patching as needed.
for (u32 i = 0; i < static_cast<u32>(text_words.size()); i++) {
const u32 inst = text_words[i];
const auto AddRelocations = [&] {
const uintptr_t this_offset = i * sizeof(u32);
const uintptr_t next_offset = this_offset + sizeof(u32);
// Relocate from here to patch.
this->BranchToPatch(this_offset);
// Relocate from patch to next instruction.
return next_offset;
};
// SVC
if (auto svc = SVC{inst}; svc.Verify()) {
WriteSvcTrampoline(AddRelocations(), svc.GetValue());
continue;
}
// MRS Xn, TPIDR_EL0
// MRS Xn, TPIDRRO_EL0
if (auto mrs = MRS{inst};
mrs.Verify() && (mrs.GetSystemReg() == TpidrroEl0 || mrs.GetSystemReg() == TpidrEl0)) {
const auto src_reg = mrs.GetSystemReg() == TpidrroEl0 ? oaknut::SystemReg::TPIDRRO_EL0
: oaknut::SystemReg::TPIDR_EL0;
const auto dest_reg = oaknut::XReg{static_cast<int>(mrs.GetRt())};
WriteMrsHandler(AddRelocations(), dest_reg, src_reg);
continue;
}
// MRS Xn, CNTPCT_EL0
if (auto mrs = MRS{inst}; mrs.Verify() && mrs.GetSystemReg() == CntpctEl0) {
WriteCntpctHandler(AddRelocations(), oaknut::XReg{static_cast<int>(mrs.GetRt())});
continue;
}
// MRS Xn, CNTFRQ_EL0
if (auto mrs = MRS{inst}; mrs.Verify() && mrs.GetSystemReg() == CntfrqEl0) {
UNREACHABLE();
}
// MSR TPIDR_EL0, Xn
if (auto msr = MSR{inst}; msr.Verify() && msr.GetSystemReg() == TpidrEl0) {
WriteMsrHandler(AddRelocations(), oaknut::XReg{static_cast<int>(msr.GetRt())});
continue;
}
}
// Determine patching mode for the final relocation step
const size_t image_size = program_image.size();
this->mode = image_size > MaxRelativeBranch ? PatchMode::PreText : PatchMode::PostData;
}
void Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
const Kernel::CodeSet::Segment& code,
Kernel::PhysicalMemory& program_image,
EntryTrampolines* out_trampolines) {
const size_t patch_size = GetSectionSize();
const size_t image_size = program_image.size();
// Retrieve text segment data.
const auto text = std::span{program_image}.subspan(code.offset, code.size);
const auto text_words =
std::span<u32>{reinterpret_cast<u32*>(text.data()), text.size() / sizeof(u32)};
const auto ApplyBranchToPatchRelocation = [&](u32* target, const Relocation& rel) {
oaknut::CodeGenerator rc{target};
if (mode == PatchMode::PreText) {
rc.B(rel.patch_offset - patch_size - rel.module_offset);
} else {
rc.B(image_size - rel.module_offset + rel.patch_offset);
}
};
const auto ApplyBranchToModuleRelocation = [&](u32* target, const Relocation& rel) {
oaknut::CodeGenerator rc{target};
if (mode == PatchMode::PreText) {
rc.B(patch_size - rel.patch_offset + rel.module_offset);
} else {
rc.B(rel.module_offset - image_size - rel.patch_offset);
}
};
const auto RebasePatch = [&](ptrdiff_t patch_offset) {
if (mode == PatchMode::PreText) {
return GetInteger(load_base) + patch_offset;
} else {
return GetInteger(load_base) + image_size + patch_offset;
}
};
const auto RebasePc = [&](uintptr_t module_offset) {
if (mode == PatchMode::PreText) {
return GetInteger(load_base) + patch_size + module_offset;
} else {
return GetInteger(load_base) + module_offset;
}
};
// We are now ready to relocate!
for (const Relocation& rel : m_branch_to_patch_relocations) {
ApplyBranchToPatchRelocation(text_words.data() + rel.module_offset / sizeof(u32), rel);
}
for (const Relocation& rel : m_branch_to_module_relocations) {
ApplyBranchToModuleRelocation(m_patch_instructions.data() + rel.patch_offset / sizeof(u32),
rel);
}
// Rewrite PC constants and record post trampolines
for (const Relocation& rel : m_write_module_pc_relocations) {
oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)};
rc.dx(RebasePc(rel.module_offset));
}
for (const Trampoline& rel : m_trampolines) {
out_trampolines->insert({RebasePc(rel.module_offset), RebasePatch(rel.patch_offset)});
}
// Cortex-A57 seems to treat all exclusives as ordered, but newer processors do not.
// Convert to ordered to preserve this assumption
for (u32 i = 0; i < static_cast<u32>(text_words.size()); i++) {
const u32 inst = text_words[i];
if (auto exclusive = Exclusive{inst}; exclusive.Verify()) {
text_words[i] = exclusive.AsOrdered();
}
}
// Copy to program image
if (this->mode == PatchMode::PreText) {
std::memcpy(program_image.data(), m_patch_instructions.data(),
m_patch_instructions.size() * sizeof(u32));
} else {
program_image.resize(image_size + patch_size);
std::memcpy(program_image.data() + image_size, m_patch_instructions.data(),
m_patch_instructions.size() * sizeof(u32));
}
}
size_t Patcher::GetSectionSize() const noexcept {
return Common::AlignUp(m_patch_instructions.size() * sizeof(u32), Core::Memory::YUZU_PAGESIZE);
}
void Patcher::WriteLoadContext() {
// This function was called, which modifies X30, so use that as a scratch register.
// SP contains the guest X30, so save our return X30 to SP + 8, since we have allocated 16 bytes
// of stack.
c.STR(X30, SP, 8);
c.MRS(X30, oaknut::SystemReg::TPIDR_EL0);
c.LDR(X30, X30, offsetof(NativeExecutionParameters, native_context));
// Load system registers.
c.LDR(W0, X30, offsetof(GuestContext, fpsr));
c.MSR(oaknut::SystemReg::FPSR, X0);
c.LDR(W0, X30, offsetof(GuestContext, fpcr));
c.MSR(oaknut::SystemReg::FPCR, X0);
c.LDR(W0, X30, offsetof(GuestContext, nzcv));
c.MSR(oaknut::SystemReg::NZCV, X0);
// Load all vector registers.
static constexpr size_t VEC_OFF = offsetof(GuestContext, vector_registers);
for (int i = 0; i <= 30; i += 2) {
c.LDP(oaknut::QReg{i}, oaknut::QReg{i + 1}, X30, VEC_OFF + 16 * i);
}
// Load all general-purpose registers except X30.
for (int i = 0; i <= 28; i += 2) {
c.LDP(oaknut::XReg{i}, oaknut::XReg{i + 1}, X30, 8 * i);
}
// Reload our return X30 from the stack and return.
// The patch code will reload the guest X30 for us.
c.LDR(X30, SP, 8);
c.RET();
}
void Patcher::WriteSaveContext() {
// This function was called, which modifies X30, so use that as a scratch register.
// SP contains the guest X30, so save our X30 to SP + 8, since we have allocated 16 bytes of
// stack.
c.STR(X30, SP, 8);
c.MRS(X30, oaknut::SystemReg::TPIDR_EL0);
c.LDR(X30, X30, offsetof(NativeExecutionParameters, native_context));
// Store all general-purpose registers except X30.
for (int i = 0; i <= 28; i += 2) {
c.STP(oaknut::XReg{i}, oaknut::XReg{i + 1}, X30, 8 * i);
}
// Store all vector registers.
static constexpr size_t VEC_OFF = offsetof(GuestContext, vector_registers);
for (int i = 0; i <= 30; i += 2) {
c.STP(oaknut::QReg{i}, oaknut::QReg{i + 1}, X30, VEC_OFF + 16 * i);
}
// Store guest system registers, X30 and SP, using X0 as a scratch register.
c.STR(X0, SP, PRE_INDEXED, -16);
c.LDR(X0, SP, 16);
c.STR(X0, X30, 8 * 30);
c.ADD(X0, SP, 32);
c.STR(X0, X30, offsetof(GuestContext, sp));
c.MRS(X0, oaknut::SystemReg::FPSR);
c.STR(W0, X30, offsetof(GuestContext, fpsr));
c.MRS(X0, oaknut::SystemReg::FPCR);
c.STR(W0, X30, offsetof(GuestContext, fpcr));
c.MRS(X0, oaknut::SystemReg::NZCV);
c.STR(W0, X30, offsetof(GuestContext, nzcv));
c.LDR(X0, SP, POST_INDEXED, 16);
// Reload our return X30 from the stack, and return.
c.LDR(X30, SP, 8);
c.RET();
}
void Patcher::WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id) {
// We are about to start saving state, so we need to lock the context.
this->LockContext();
// Store guest X30 to the stack. Then, save the context and restore the stack.
// This will save all registers except PC, but we know PC at patch time.
c.STR(X30, SP, PRE_INDEXED, -16);
c.BL(m_save_context);
c.LDR(X30, SP, POST_INDEXED, 16);
// Now that we've saved all registers, we can use any registers as scratch.
// Store PC + 4 to arm interface, since we know the instruction offset from the entry point.
oaknut::Label pc_after_svc;
c.MRS(X1, oaknut::SystemReg::TPIDR_EL0);
c.LDR(X1, X1, offsetof(NativeExecutionParameters, native_context));
c.LDR(X2, pc_after_svc);
c.STR(X2, X1, offsetof(GuestContext, pc));
// Store SVC number to execute when we return
c.MOV(X2, svc_id);
c.STR(W2, X1, offsetof(GuestContext, svc_swi));
// We are calling a SVC. Clear esr_el1 and return it.
static_assert(std::is_same_v<std::underlying_type_t<HaltReason>, u64>);
oaknut::Label retry;
c.ADD(X2, X1, offsetof(GuestContext, esr_el1));
c.l(retry);
c.LDAXR(X0, X2);
c.STLXR(W3, XZR, X2);
c.CBNZ(W3, retry);
// Add "calling SVC" flag. Since this is X0, this is now our return value.
c.ORR(X0, X0, static_cast<u64>(HaltReason::SupervisorCall));
// Offset the GuestContext pointer to the HostContext member.
// STP has limited range of [-512, 504] which we can't reach otherwise
// NB: Due to this all offsets below are from the start of HostContext.
c.ADD(X1, X1, offsetof(GuestContext, host_ctx));
// Reload host TPIDR_EL0 and SP.
static_assert(offsetof(HostContext, host_sp) + 8 == offsetof(HostContext, host_tpidr_el0));
c.LDP(X2, X3, X1, offsetof(HostContext, host_sp));
c.MOV(SP, X2);
c.MSR(oaknut::SystemReg::TPIDR_EL0, X3);
// Load callee-saved host registers and return to host.
static constexpr size_t HOST_REGS_OFF = offsetof(HostContext, host_saved_regs);
static constexpr size_t HOST_VREGS_OFF = offsetof(HostContext, host_saved_vregs);
c.LDP(X19, X20, X1, HOST_REGS_OFF);
c.LDP(X21, X22, X1, HOST_REGS_OFF + 2 * sizeof(u64));
c.LDP(X23, X24, X1, HOST_REGS_OFF + 4 * sizeof(u64));
c.LDP(X25, X26, X1, HOST_REGS_OFF + 6 * sizeof(u64));
c.LDP(X27, X28, X1, HOST_REGS_OFF + 8 * sizeof(u64));
c.LDP(X29, X30, X1, HOST_REGS_OFF + 10 * sizeof(u64));
c.LDP(Q8, Q9, X1, HOST_VREGS_OFF);
c.LDP(Q10, Q11, X1, HOST_VREGS_OFF + 2 * sizeof(u128));
c.LDP(Q12, Q13, X1, HOST_VREGS_OFF + 4 * sizeof(u128));
c.LDP(Q14, Q15, X1, HOST_VREGS_OFF + 6 * sizeof(u128));
c.RET();
// Write the post-SVC trampoline address, which will jump back to the guest after restoring its
// state.
m_trampolines.push_back({c.offset(), module_dest});
// Host called this location. Save the return address so we can
// unwind the stack properly when jumping back.
c.MRS(X2, oaknut::SystemReg::TPIDR_EL0);
c.LDR(X2, X2, offsetof(NativeExecutionParameters, native_context));
c.ADD(X0, X2, offsetof(GuestContext, host_ctx));
c.STR(X30, X0, offsetof(HostContext, host_saved_regs) + 11 * sizeof(u64));
// Reload all guest registers except X30 and PC.
// The function also expects 16 bytes of stack already allocated.
c.STR(X30, SP, PRE_INDEXED, -16);
c.BL(m_load_context);
c.LDR(X30, SP, POST_INDEXED, 16);
// Use X1 as a scratch register to restore X30.
c.STR(X1, SP, PRE_INDEXED, -16);
c.MRS(X1, oaknut::SystemReg::TPIDR_EL0);
c.LDR(X1, X1, offsetof(NativeExecutionParameters, native_context));
c.LDR(X30, X1, offsetof(GuestContext, cpu_registers) + sizeof(u64) * 30);
c.LDR(X1, SP, POST_INDEXED, 16);
// Unlock the context.
this->UnlockContext();
// Jump back to the instruction after the emulated SVC.
this->BranchToModule(module_dest);
// Store PC after call.
c.l(pc_after_svc);
this->WriteModulePc(module_dest);
}
void Patcher::WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg,
oaknut::SystemReg src_reg) {
// Retrieve emulated TLS register from GuestContext.
c.MRS(dest_reg, oaknut::SystemReg::TPIDR_EL0);
if (src_reg == oaknut::SystemReg::TPIDRRO_EL0) {
c.LDR(dest_reg, dest_reg, offsetof(NativeExecutionParameters, tpidrro_el0));
} else {
c.LDR(dest_reg, dest_reg, offsetof(NativeExecutionParameters, tpidr_el0));
}
// Jump back to the instruction after the emulated MRS.
this->BranchToModule(module_dest);
}
void Patcher::WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg) {
const auto scratch_reg = src_reg.index() == 0 ? X1 : X0;
c.STR(scratch_reg, SP, PRE_INDEXED, -16);
// Save guest value to NativeExecutionParameters::tpidr_el0.
c.MRS(scratch_reg, oaknut::SystemReg::TPIDR_EL0);
c.STR(src_reg, scratch_reg, offsetof(NativeExecutionParameters, tpidr_el0));
// Restore scratch register.
c.LDR(scratch_reg, SP, POST_INDEXED, 16);
// Jump back to the instruction after the emulated MSR.
this->BranchToModule(module_dest);
}
void Patcher::WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg) {
static Common::Arm64::NativeClock clock{};
const auto factor = clock.GetGuestCNTFRQFactor();
const auto raw_factor = Common::BitCast<std::array<u64, 2>>(factor);
const auto use_x2_x3 = dest_reg.index() == 0 || dest_reg.index() == 1;
oaknut::XReg scratch0 = use_x2_x3 ? X2 : X0;
oaknut::XReg scratch1 = use_x2_x3 ? X3 : X1;
oaknut::Label factorlo;
oaknut::Label factorhi;
// Save scratches.
c.STP(scratch0, scratch1, SP, PRE_INDEXED, -16);
// Load counter value.
c.MRS(dest_reg, oaknut::SystemReg::CNTVCT_EL0);
// Load scaling factor.
c.LDR(scratch0, factorlo);
c.LDR(scratch1, factorhi);
// Multiply low bits and get result.
c.UMULH(scratch0, dest_reg, scratch0);
// Multiply high bits and add low bit result.
c.MADD(dest_reg, dest_reg, scratch1, scratch0);
// Reload scratches.
c.LDP(scratch0, scratch1, SP, POST_INDEXED, 16);
// Jump back to the instruction after the emulated MRS.
this->BranchToModule(module_dest);
// Scaling factor constant values.
c.l(factorlo);
c.dx(raw_factor[0]);
c.l(factorhi);
c.dx(raw_factor[1]);
}
void Patcher::LockContext() {
oaknut::Label retry;
// Save scratches.
c.STP(X0, X1, SP, PRE_INDEXED, -16);
// Reload lock pointer.
c.l(retry);
c.CLREX();
c.MRS(X0, oaknut::SystemReg::TPIDR_EL0);
c.ADD(X0, X0, offsetof(NativeExecutionParameters, lock));
static_assert(SpinLockLocked == 0);
// Load-linked with acquire ordering.
c.LDAXR(W1, X0);
// If the value was SpinLockLocked, clear monitor and retry.
c.CBZ(W1, retry);
// Store-conditional SpinLockLocked with relaxed ordering.
c.STXR(W1, WZR, X0);
// If we failed to store, retry.
c.CBNZ(W1, retry);
// We succeeded! Reload scratches.
c.LDP(X0, X1, SP, POST_INDEXED, 16);
}
void Patcher::UnlockContext() {
// Save scratches.
c.STP(X0, X1, SP, PRE_INDEXED, -16);
// Load lock pointer.
c.MRS(X0, oaknut::SystemReg::TPIDR_EL0);
c.ADD(X0, X0, offsetof(NativeExecutionParameters, lock));
// Load SpinLockUnlocked.
c.MOV(W1, SpinLockUnlocked);
// Store value with release ordering.
c.STLR(W1, X0);
// Load scratches.
c.LDP(X0, X1, SP, POST_INDEXED, 16);
}
} // namespace Core::NCE

101
src/core/arm/nce/patch.h Normal file
View File

@ -0,0 +1,101 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <span>
#include <unordered_map>
#include <vector>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshorten-64-to-32"
#include <oaknut/code_block.hpp>
#include <oaknut/oaknut.hpp>
#pragma GCC diagnostic pop
#include "common/common_types.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/k_typed_address.h"
#include "core/hle/kernel/physical_memory.h"
namespace Core::NCE {
enum class PatchMode : u32 {
None,
PreText, ///< Patch section is inserted before .text
PostData, ///< Patch section is inserted after .data
};
using ModuleTextAddress = u64;
using PatchTextAddress = u64;
using EntryTrampolines = std::unordered_map<ModuleTextAddress, PatchTextAddress>;
class Patcher {
public:
explicit Patcher();
~Patcher();
void PatchText(const Kernel::PhysicalMemory& program_image,
const Kernel::CodeSet::Segment& code);
void RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code,
Kernel::PhysicalMemory& program_image, EntryTrampolines* out_trampolines);
size_t GetSectionSize() const noexcept;
[[nodiscard]] PatchMode GetPatchMode() const noexcept {
return mode;
}
private:
using ModuleDestLabel = uintptr_t;
struct Trampoline {
ptrdiff_t patch_offset;
uintptr_t module_offset;
};
void WriteLoadContext();
void WriteSaveContext();
void LockContext();
void UnlockContext();
void WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id);
void WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg,
oaknut::SystemReg src_reg);
void WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg);
void WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg);
private:
void BranchToPatch(uintptr_t module_dest) {
m_branch_to_patch_relocations.push_back({c.offset(), module_dest});
}
void BranchToModule(uintptr_t module_dest) {
m_branch_to_module_relocations.push_back({c.offset(), module_dest});
c.dw(0);
}
void WriteModulePc(uintptr_t module_dest) {
m_write_module_pc_relocations.push_back({c.offset(), module_dest});
c.dx(0);
}
private:
// List of patch instructions we have generated.
std::vector<u32> m_patch_instructions{};
// Relocation type for relative branch from module to patch.
struct Relocation {
ptrdiff_t patch_offset; ///< Offset in bytes from the start of the patch section.
uintptr_t module_offset; ///< Offset in bytes from the start of the text section.
};
oaknut::VectorCodeGenerator c;
std::vector<Trampoline> m_trampolines;
std::vector<Relocation> m_branch_to_patch_relocations{};
std::vector<Relocation> m_branch_to_module_relocations{};
std::vector<Relocation> m_write_module_pc_relocations{};
oaknut::Label m_save_context{};
oaknut::Label m_load_context{};
PatchMode mode{PatchMode::None};
};
} // namespace Core::NCE

View File

@ -76,6 +76,7 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
} }
void CoreTiming::ClearPendingEvents() { void CoreTiming::ClearPendingEvents() {
std::scoped_lock lock{basic_lock};
event_queue.clear(); event_queue.clear();
} }
@ -113,6 +114,7 @@ bool CoreTiming::IsRunning() const {
} }
bool CoreTiming::HasPendingEvents() const { bool CoreTiming::HasPendingEvents() const {
std::scoped_lock lock{basic_lock};
return !(wait_set && event_queue.empty()); return !(wait_set && event_queue.empty());
} }

View File

@ -161,7 +161,7 @@ private:
std::shared_ptr<EventType> ev_lost; std::shared_ptr<EventType> ev_lost;
Common::Event event{}; Common::Event event{};
Common::Event pause_event{}; Common::Event pause_event{};
std::mutex basic_lock; mutable std::mutex basic_lock;
std::mutex advance_lock; std::mutex advance_lock;
std::unique_ptr<std::jthread> timer_thread; std::unique_ptr<std::jthread> timer_thread;
std::atomic<bool> paused{}; std::atomic<bool> paused{};

View File

@ -211,6 +211,8 @@ void CpuManager::RunThread(std::stop_token token, std::size_t core) {
system.GPU().ObtainContext(); system.GPU().ObtainContext();
} }
system.ArmInterface(core).Initialize();
auto& kernel = system.Kernel(); auto& kernel = system.Kernel();
auto& scheduler = *kernel.CurrentScheduler(); auto& scheduler = *kernel.CurrentScheduler();
auto* thread = scheduler.GetSchedulerCurrentThread(); auto* thread = scheduler.GetSchedulerCurrentThread();

View File

@ -562,6 +562,120 @@ static std::string PaginateBuffer(std::string_view buffer, std::string_view requ
} }
} }
static VAddr GetModuleEnd(Kernel::KProcessPageTable& page_table, VAddr base) {
Kernel::KMemoryInfo mem_info;
Kernel::Svc::MemoryInfo svc_mem_info;
Kernel::Svc::PageInfo page_info;
VAddr cur_addr{base};
// Expect: r-x Code (.text)
R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
svc_mem_info = mem_info.GetSvcMemoryInfo();
cur_addr = svc_mem_info.base_address + svc_mem_info.size;
if (svc_mem_info.state != Kernel::Svc::MemoryState::Code ||
svc_mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) {
return cur_addr - 1;
}
// Expect: r-- Code (.rodata)
R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
svc_mem_info = mem_info.GetSvcMemoryInfo();
cur_addr = svc_mem_info.base_address + svc_mem_info.size;
if (svc_mem_info.state != Kernel::Svc::MemoryState::Code ||
svc_mem_info.permission != Kernel::Svc::MemoryPermission::Read) {
return cur_addr - 1;
}
// Expect: rw- CodeData (.data)
R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
svc_mem_info = mem_info.GetSvcMemoryInfo();
cur_addr = svc_mem_info.base_address + svc_mem_info.size;
return cur_addr - 1;
}
static Loader::AppLoader::Modules FindModules(Core::System& system) {
Loader::AppLoader::Modules modules;
auto& page_table = system.ApplicationProcess()->GetPageTable();
auto& memory = system.ApplicationMemory();
VAddr cur_addr = 0;
// Look for executable sections in Code or AliasCode regions.
while (true) {
Kernel::KMemoryInfo mem_info{};
Kernel::Svc::PageInfo page_info{};
R_ASSERT(
page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
auto svc_mem_info = mem_info.GetSvcMemoryInfo();
if (svc_mem_info.permission == Kernel::Svc::MemoryPermission::ReadExecute &&
(svc_mem_info.state == Kernel::Svc::MemoryState::Code ||
svc_mem_info.state == Kernel::Svc::MemoryState::AliasCode)) {
// Try to read the module name from its path.
constexpr s32 PathLengthMax = 0x200;
struct {
u32 zero;
s32 path_length;
std::array<char, PathLengthMax> path;
} module_path;
if (memory.ReadBlock(svc_mem_info.base_address + svc_mem_info.size, &module_path,
sizeof(module_path))) {
if (module_path.zero == 0 && module_path.path_length > 0) {
// Truncate module name.
module_path.path[PathLengthMax - 1] = '\0';
// Ignore leading directories.
char* path_pointer = module_path.path.data();
for (s32 i = 0; i < std::min(PathLengthMax, module_path.path_length) &&
module_path.path[i] != '\0';
i++) {
if (module_path.path[i] == '/' || module_path.path[i] == '\\') {
path_pointer = module_path.path.data() + i + 1;
}
}
// Insert output.
modules.emplace(svc_mem_info.base_address, path_pointer);
}
}
}
// Check if we're done.
const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size;
if (next_address <= cur_addr) {
break;
}
cur_addr = next_address;
}
return modules;
}
static VAddr FindMainModuleEntrypoint(Core::System& system) {
Loader::AppLoader::Modules modules;
system.GetAppLoader().ReadNSOModules(modules);
// Do we have a module named main?
const auto main = std::find_if(modules.begin(), modules.end(),
[](const auto& key) { return key.second == "main"; });
if (main != modules.end()) {
return main->first;
}
// Do we have any loaded executable sections?
modules = FindModules(system);
if (!modules.empty()) {
return modules.begin()->first;
}
// As a last resort, use the start of the code region.
return GetInteger(system.ApplicationProcess()->GetPageTable().GetCodeRegionStart());
}
void GDBStub::HandleQuery(std::string_view command) { void GDBStub::HandleQuery(std::string_view command) {
if (command.starts_with("TStatus")) { if (command.starts_with("TStatus")) {
// no tracepoint support // no tracepoint support
@ -573,21 +687,10 @@ void GDBStub::HandleQuery(std::string_view command) {
const auto target_xml{arch->GetTargetXML()}; const auto target_xml{arch->GetTargetXML()};
SendReply(PaginateBuffer(target_xml, command.substr(30))); SendReply(PaginateBuffer(target_xml, command.substr(30)));
} else if (command.starts_with("Offsets")) { } else if (command.starts_with("Offsets")) {
Loader::AppLoader::Modules modules; const auto main_offset = FindMainModuleEntrypoint(system);
system.GetAppLoader().ReadNSOModules(modules); SendReply(fmt::format("TextSeg={:x}", main_offset));
const auto main = std::find_if(modules.begin(), modules.end(),
[](const auto& key) { return key.second == "main"; });
if (main != modules.end()) {
SendReply(fmt::format("TextSeg={:x}", main->first));
} else {
SendReply(fmt::format(
"TextSeg={:x}",
GetInteger(system.ApplicationProcess()->GetPageTable().GetCodeRegionStart())));
}
} else if (command.starts_with("Xfer:libraries:read::")) { } else if (command.starts_with("Xfer:libraries:read::")) {
Loader::AppLoader::Modules modules; auto modules = FindModules(system);
system.GetAppLoader().ReadNSOModules(modules);
std::string buffer; std::string buffer;
buffer += R"(<?xml version="1.0"?>)"; buffer += R"(<?xml version="1.0"?>)";
@ -727,32 +830,6 @@ static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::Memory
} }
} }
static VAddr GetModuleEnd(Kernel::KPageTable& page_table, VAddr base) {
Kernel::Svc::MemoryInfo mem_info;
VAddr cur_addr{base};
// Expect: r-x Code (.text)
mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
cur_addr = mem_info.base_address + mem_info.size;
if (mem_info.state != Kernel::Svc::MemoryState::Code ||
mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) {
return cur_addr - 1;
}
// Expect: r-- Code (.rodata)
mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
cur_addr = mem_info.base_address + mem_info.size;
if (mem_info.state != Kernel::Svc::MemoryState::Code ||
mem_info.permission != Kernel::Svc::MemoryPermission::Read) {
return cur_addr - 1;
}
// Expect: rw- CodeData (.data)
mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
cur_addr = mem_info.base_address + mem_info.size;
return cur_addr - 1;
}
void GDBStub::HandleRcmd(const std::vector<u8>& command) { void GDBStub::HandleRcmd(const std::vector<u8>& command) {
std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()}; std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
std::string reply; std::string reply;
@ -767,7 +844,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
if (command_str == "get fastmem") { if (command_str == "get fastmem") {
if (Settings::IsFastmemEnabled()) { if (Settings::IsFastmemEnabled()) {
const auto& impl = page_table.PageTableImpl(); const auto& impl = page_table.GetImpl();
const auto region = reinterpret_cast<uintptr_t>(impl.fastmem_arena); const auto region = reinterpret_cast<uintptr_t>(impl.fastmem_arena);
const auto region_bits = impl.current_address_space_width_in_bits; const auto region_bits = impl.current_address_space_width_in_bits;
const auto region_size = 1ULL << region_bits; const auto region_size = 1ULL << region_bits;
@ -779,26 +856,27 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
reply = "Fastmem is not enabled.\n"; reply = "Fastmem is not enabled.\n";
} }
} else if (command_str == "get info") { } else if (command_str == "get info") {
Loader::AppLoader::Modules modules; auto modules = FindModules(system);
system.GetAppLoader().ReadNSOModules(modules);
reply = fmt::format("Process: {:#x} ({})\n" reply = fmt::format("Process: {:#x} ({})\n"
"Program Id: {:#018x}\n", "Program Id: {:#018x}\n",
process->GetProcessId(), process->GetName(), process->GetProgramId()); process->GetProcessId(), process->GetName(), process->GetProgramId());
reply += fmt::format("Layout:\n" reply += fmt::format(
"Layout:\n"
" Alias: {:#012x} - {:#012x}\n" " Alias: {:#012x} - {:#012x}\n"
" Heap: {:#012x} - {:#012x}\n" " Heap: {:#012x} - {:#012x}\n"
" Aslr: {:#012x} - {:#012x}\n" " Aslr: {:#012x} - {:#012x}\n"
" Stack: {:#012x} - {:#012x}\n" " Stack: {:#012x} - {:#012x}\n"
"Modules:\n", "Modules:\n",
GetInteger(page_table.GetAliasRegionStart()), GetInteger(page_table.GetAliasRegionStart()),
GetInteger(page_table.GetAliasRegionEnd()), GetInteger(page_table.GetAliasRegionStart()) + page_table.GetAliasRegionSize() - 1,
GetInteger(page_table.GetHeapRegionStart()), GetInteger(page_table.GetHeapRegionStart()),
GetInteger(page_table.GetHeapRegionEnd()), GetInteger(page_table.GetHeapRegionStart()) + page_table.GetHeapRegionSize() - 1,
GetInteger(page_table.GetAliasCodeRegionStart()), GetInteger(page_table.GetAliasCodeRegionStart()),
GetInteger(page_table.GetAliasCodeRegionEnd()), GetInteger(page_table.GetAliasCodeRegionStart()) + page_table.GetAliasCodeRegionSize() -
1,
GetInteger(page_table.GetStackRegionStart()), GetInteger(page_table.GetStackRegionStart()),
GetInteger(page_table.GetStackRegionEnd())); GetInteger(page_table.GetStackRegionStart()) + page_table.GetStackRegionSize() - 1);
for (const auto& [vaddr, name] : modules) { for (const auto& [vaddr, name] : modules) {
reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr, reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr,
@ -811,27 +889,34 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
while (true) { while (true) {
using MemoryAttribute = Kernel::Svc::MemoryAttribute; using MemoryAttribute = Kernel::Svc::MemoryAttribute;
auto mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); Kernel::KMemoryInfo mem_info{};
Kernel::Svc::PageInfo page_info{};
R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info),
cur_addr));
auto svc_mem_info = mem_info.GetSvcMemoryInfo();
if (mem_info.state != Kernel::Svc::MemoryState::Inaccessible || if (svc_mem_info.state != Kernel::Svc::MemoryState::Inaccessible ||
mem_info.base_address + mem_info.size - 1 != std::numeric_limits<u64>::max()) { svc_mem_info.base_address + svc_mem_info.size - 1 !=
const char* state = GetMemoryStateName(mem_info.state); std::numeric_limits<u64>::max()) {
const char* perm = GetMemoryPermissionString(mem_info); const char* state = GetMemoryStateName(svc_mem_info.state);
const char* perm = GetMemoryPermissionString(svc_mem_info);
const char l = True(mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-'; const char l = True(svc_mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-';
const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-'; const char i =
const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-'; True(svc_mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-'; const char d =
True(svc_mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
const char u = True(svc_mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
const char p = const char p =
True(mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-'; True(svc_mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-';
reply += fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n", reply += fmt::format(
mem_info.base_address, " {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n", svc_mem_info.base_address,
mem_info.base_address + mem_info.size - 1, perm, state, l, i, svc_mem_info.base_address + svc_mem_info.size - 1, perm, state, l, i, d, u, p,
d, u, p, mem_info.ipc_count, mem_info.device_count); svc_mem_info.ipc_count, svc_mem_info.device_count);
} }
const uintptr_t next_address = mem_info.base_address + mem_info.size; const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size;
if (next_address <= cur_addr) { if (next_address <= cur_addr) {
break; break;
} }

View File

@ -6,7 +6,7 @@
namespace Core { namespace Core {
#ifdef ANDROID #ifdef ARCHITECTURE_arm64
constexpr size_t VirtualReserveSize = 1ULL << 38; constexpr size_t VirtualReserveSize = 1ULL << 38;
#else #else
constexpr size_t VirtualReserveSize = 1ULL << 39; constexpr size_t VirtualReserveSize = 1ULL << 39;
@ -15,6 +15,7 @@ constexpr size_t VirtualReserveSize = 1ULL << 39;
DeviceMemory::DeviceMemory() DeviceMemory::DeviceMemory()
: buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(), : buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(),
VirtualReserveSize} {} VirtualReserveSize} {}
DeviceMemory::~DeviceMemory() = default; DeviceMemory::~DeviceMemory() = default;
} // namespace Core } // namespace Core

View File

@ -38,14 +38,6 @@ using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>;
using ConsoleMotionValues = ConsoleMotionInfo; using ConsoleMotionValues = ConsoleMotionInfo;
using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>; using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>;
struct TouchFinger {
u64 last_touch{};
Common::Point<float> position{};
u32 id{};
TouchAttribute attribute{};
bool pressed{};
};
// Contains all motion related data that is used on the services // Contains all motion related data that is used on the services
struct ConsoleMotion { struct ConsoleMotion {
Common::Vec3f accel{}; Common::Vec3f accel{};

View File

@ -243,10 +243,12 @@ void EmulatedController::LoadTASParams() {
tas_button_params[Settings::NativeButton::DUp].Set("button", 13); tas_button_params[Settings::NativeButton::DUp].Set("button", 13);
tas_button_params[Settings::NativeButton::DRight].Set("button", 14); tas_button_params[Settings::NativeButton::DRight].Set("button", 14);
tas_button_params[Settings::NativeButton::DDown].Set("button", 15); tas_button_params[Settings::NativeButton::DDown].Set("button", 15);
tas_button_params[Settings::NativeButton::SL].Set("button", 16); tas_button_params[Settings::NativeButton::SLLeft].Set("button", 16);
tas_button_params[Settings::NativeButton::SR].Set("button", 17); tas_button_params[Settings::NativeButton::SRLeft].Set("button", 17);
tas_button_params[Settings::NativeButton::Home].Set("button", 18); tas_button_params[Settings::NativeButton::Home].Set("button", 18);
tas_button_params[Settings::NativeButton::Screenshot].Set("button", 19); tas_button_params[Settings::NativeButton::Screenshot].Set("button", 19);
tas_button_params[Settings::NativeButton::SLRight].Set("button", 20);
tas_button_params[Settings::NativeButton::SRRight].Set("button", 21);
tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0);
tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1);
@ -296,10 +298,12 @@ void EmulatedController::LoadVirtualGamepadParams() {
virtual_button_params[Settings::NativeButton::DUp].Set("button", 13); virtual_button_params[Settings::NativeButton::DUp].Set("button", 13);
virtual_button_params[Settings::NativeButton::DRight].Set("button", 14); virtual_button_params[Settings::NativeButton::DRight].Set("button", 14);
virtual_button_params[Settings::NativeButton::DDown].Set("button", 15); virtual_button_params[Settings::NativeButton::DDown].Set("button", 15);
virtual_button_params[Settings::NativeButton::SL].Set("button", 16); virtual_button_params[Settings::NativeButton::SLLeft].Set("button", 16);
virtual_button_params[Settings::NativeButton::SR].Set("button", 17); virtual_button_params[Settings::NativeButton::SRLeft].Set("button", 17);
virtual_button_params[Settings::NativeButton::Home].Set("button", 18); virtual_button_params[Settings::NativeButton::Home].Set("button", 18);
virtual_button_params[Settings::NativeButton::Screenshot].Set("button", 19); virtual_button_params[Settings::NativeButton::Screenshot].Set("button", 19);
virtual_button_params[Settings::NativeButton::SLRight].Set("button", 20);
virtual_button_params[Settings::NativeButton::SRRight].Set("button", 21);
virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0);
virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1);
@ -867,12 +871,16 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
controller.npad_button_state.down.Assign(current_status.value); controller.npad_button_state.down.Assign(current_status.value);
controller.debug_pad_button_state.d_down.Assign(current_status.value); controller.debug_pad_button_state.d_down.Assign(current_status.value);
break; break;
case Settings::NativeButton::SL: case Settings::NativeButton::SLLeft:
controller.npad_button_state.left_sl.Assign(current_status.value); controller.npad_button_state.left_sl.Assign(current_status.value);
break;
case Settings::NativeButton::SLRight:
controller.npad_button_state.right_sl.Assign(current_status.value); controller.npad_button_state.right_sl.Assign(current_status.value);
break; break;
case Settings::NativeButton::SR: case Settings::NativeButton::SRLeft:
controller.npad_button_state.left_sr.Assign(current_status.value); controller.npad_button_state.left_sr.Assign(current_status.value);
break;
case Settings::NativeButton::SRRight:
controller.npad_button_state.right_sr.Assign(current_status.value); controller.npad_button_state.right_sr.Assign(current_status.value);
break; break;
case Settings::NativeButton::Home: case Settings::NativeButton::Home:
@ -1890,12 +1898,16 @@ NpadButton EmulatedController::GetTurboButtonMask() const {
case Settings::NativeButton::DDown: case Settings::NativeButton::DDown:
button_mask.down.Assign(1); button_mask.down.Assign(1);
break; break;
case Settings::NativeButton::SL: case Settings::NativeButton::SLLeft:
button_mask.left_sl.Assign(1); button_mask.left_sl.Assign(1);
break;
case Settings::NativeButton::SLRight:
button_mask.right_sl.Assign(1); button_mask.right_sl.Assign(1);
break; break;
case Settings::NativeButton::SR: case Settings::NativeButton::SRLeft:
button_mask.left_sr.Assign(1); button_mask.left_sr.Assign(1);
break;
case Settings::NativeButton::SRRight:
button_mask.right_sr.Assign(1); button_mask.right_sr.Assign(1);
break; break;
default: default:

View File

@ -356,6 +356,14 @@ struct TouchState {
}; };
static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
struct TouchFinger {
u64 last_touch{};
Common::Point<float> position{};
u32 id{};
TouchAttribute attribute{};
bool pressed{};
};
// This is nn::hid::TouchScreenConfigurationForNx // This is nn::hid::TouchScreenConfigurationForNx
struct TouchScreenConfigurationForNx { struct TouchScreenConfigurationForNx {
TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting}; TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting};

View File

@ -5,13 +5,14 @@
#include "core/hid/hid_types.h" #include "core/hid/hid_types.h"
#include "core/hid/input_interpreter.h" #include "core/hid/input_interpreter.h"
#include "core/hle/service/hid/controllers/npad.h" #include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/hid.h" #include "core/hle/service/hid/hid_server.h"
#include "core/hle/service/hid/resource_manager.h"
#include "core/hle/service/sm/sm.h" #include "core/hle/service/sm/sm.h"
InputInterpreter::InputInterpreter(Core::System& system) InputInterpreter::InputInterpreter(Core::System& system)
: npad{system.ServiceManager() : npad{system.ServiceManager()
.GetService<Service::HID::Hid>("hid") .GetService<Service::HID::IHidServer>("hid")
->GetAppletResource() ->GetResourceManager()
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} { ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} {
ResetButtonStates(); ResetButtonStates();
} }

View File

@ -222,7 +222,7 @@ Result KSystemControl::AllocateSecureMemory(KernelCore& kernel, KVirtualAddress*
}; };
// We succeeded. // We succeeded.
*out = KPageTable::GetHeapVirtualAddress(kernel.MemoryLayout(), paddr); *out = KPageTable::GetHeapVirtualAddress(kernel, paddr);
R_SUCCEED(); R_SUCCEED();
} }
@ -238,8 +238,17 @@ void KSystemControl::FreeSecureMemory(KernelCore& kernel, KVirtualAddress addres
ASSERT(Common::IsAligned(size, alignment)); ASSERT(Common::IsAligned(size, alignment));
// Close the secure region's pages. // Close the secure region's pages.
kernel.MemoryManager().Close(KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), address), kernel.MemoryManager().Close(KPageTable::GetHeapPhysicalAddress(kernel, address),
size / PageSize); size / PageSize);
} }
// Insecure Memory.
KResourceLimit* KSystemControl::GetInsecureMemoryResourceLimit(KernelCore& kernel) {
return kernel.GetSystemResourceLimit();
}
u32 KSystemControl::GetInsecureMemoryPool() {
return static_cast<u32>(KMemoryManager::Pool::SystemNonSecure);
}
} // namespace Kernel::Board::Nintendo::Nx } // namespace Kernel::Board::Nintendo::Nx

View File

@ -8,7 +8,8 @@
namespace Kernel { namespace Kernel {
class KernelCore; class KernelCore;
} class KResourceLimit;
} // namespace Kernel
namespace Kernel::Board::Nintendo::Nx { namespace Kernel::Board::Nintendo::Nx {
@ -40,6 +41,10 @@ public:
u32 pool); u32 pool);
static void FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size, static void FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size,
u32 pool); u32 pool);
// Insecure Memory.
static KResourceLimit* GetInsecureMemoryResourceLimit(KernelCore& kernel);
static u32 GetInsecureMemoryPool();
}; };
} // namespace Kernel::Board::Nintendo::Nx } // namespace Kernel::Board::Nintendo::Nx

View File

@ -75,11 +75,20 @@ struct CodeSet final {
return segments[2]; return segments[2];
} }
Segment& PatchSegment() {
return patch_segment;
}
const Segment& PatchSegment() const {
return patch_segment;
}
/// The overall data that backs this code set. /// The overall data that backs this code set.
Kernel::PhysicalMemory memory; Kernel::PhysicalMemory memory;
/// The segments that comprise this code set. /// The segments that comprise this code set.
std::array<Segment, 3> segments; std::array<Segment, 3> segments;
Segment patch_segment;
/// The entry point address for this code set. /// The entry point address for this code set.
KProcessAddress entrypoint = 0; KProcessAddress entrypoint = 0;

View File

@ -25,8 +25,8 @@ constexpr std::array<KAddressSpaceInfo, 13> AddressSpaceInfos{{
{ .bit_width = 36, .address = 2_GiB , .size = 64_GiB - 2_GiB , .type = KAddressSpaceInfo::Type::MapLarge, }, { .bit_width = 36, .address = 2_GiB , .size = 64_GiB - 2_GiB , .type = KAddressSpaceInfo::Type::MapLarge, },
{ .bit_width = 36, .address = Size_Invalid, .size = 8_GiB , .type = KAddressSpaceInfo::Type::Heap, }, { .bit_width = 36, .address = Size_Invalid, .size = 8_GiB , .type = KAddressSpaceInfo::Type::Heap, },
{ .bit_width = 36, .address = Size_Invalid, .size = 6_GiB , .type = KAddressSpaceInfo::Type::Alias, }, { .bit_width = 36, .address = Size_Invalid, .size = 6_GiB , .type = KAddressSpaceInfo::Type::Alias, },
#ifdef ANDROID #ifdef ARCHITECTURE_arm64
// With Android, we use a 38-bit address space due to memory limitations. This should (safely) truncate ASLR region. // With NCE, we use a 38-bit address space due to memory limitations. This should (safely) truncate ASLR region.
{ .bit_width = 39, .address = 128_MiB , .size = 256_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, }, { .bit_width = 39, .address = 128_MiB , .size = 256_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, },
#else #else
{ .bit_width = 39, .address = 128_MiB , .size = 512_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, }, { .bit_width = 39, .address = 128_MiB , .size = 512_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, },

View File

@ -4,14 +4,16 @@
#include "core/hardware_properties.h" #include "core/hardware_properties.h"
#include "core/hle/kernel/k_capabilities.h" #include "core/hle/kernel/k_capabilities.h"
#include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process_page_table.h"
#include "core/hle/kernel/k_trace.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h" #include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/svc_version.h" #include "core/hle/kernel/svc_version.h"
namespace Kernel { namespace Kernel {
Result KCapabilities::InitializeForKip(std::span<const u32> kern_caps, KPageTable* page_table) { Result KCapabilities::InitializeForKip(std::span<const u32> kern_caps,
KProcessPageTable* page_table) {
// We're initializing an initial process. // We're initializing an initial process.
m_svc_access_flags.reset(); m_svc_access_flags.reset();
m_irq_access_flags.reset(); m_irq_access_flags.reset();
@ -41,7 +43,8 @@ Result KCapabilities::InitializeForKip(std::span<const u32> kern_caps, KPageTabl
R_RETURN(this->SetCapabilities(kern_caps, page_table)); R_RETURN(this->SetCapabilities(kern_caps, page_table));
} }
Result KCapabilities::InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table) { Result KCapabilities::InitializeForUser(std::span<const u32> user_caps,
KProcessPageTable* page_table) {
// We're initializing a user process. // We're initializing a user process.
m_svc_access_flags.reset(); m_svc_access_flags.reset();
m_irq_access_flags.reset(); m_irq_access_flags.reset();
@ -121,7 +124,7 @@ Result KCapabilities::SetSyscallMaskCapability(const u32 cap, u32& set_svc) {
R_SUCCEED(); R_SUCCEED();
} }
Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table) { Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KProcessPageTable* page_table) {
const auto range_pack = MapRange{cap}; const auto range_pack = MapRange{cap};
const auto size_pack = MapRangeSize{size_cap}; const auto size_pack = MapRangeSize{size_cap};
@ -142,16 +145,13 @@ Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KPageTable* p
? KMemoryPermission::UserRead ? KMemoryPermission::UserRead
: KMemoryPermission::UserReadWrite; : KMemoryPermission::UserReadWrite;
if (MapRangeSize{size_cap}.normal) { if (MapRangeSize{size_cap}.normal) {
// R_RETURN(page_table->MapStatic(phys_addr, size, perm)); R_RETURN(page_table->MapStatic(phys_addr, size, perm));
} else { } else {
// R_RETURN(page_table->MapIo(phys_addr, size, perm)); R_RETURN(page_table->MapIo(phys_addr, size, perm));
}
} }
UNIMPLEMENTED(); Result KCapabilities::MapIoPage_(const u32 cap, KProcessPageTable* page_table) {
R_SUCCEED();
}
Result KCapabilities::MapIoPage_(const u32 cap, KPageTable* page_table) {
// Get/validate address/size // Get/validate address/size
const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize; const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize;
const size_t num_pages = 1; const size_t num_pages = 1;
@ -160,10 +160,7 @@ Result KCapabilities::MapIoPage_(const u32 cap, KPageTable* page_table) {
R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress); R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress);
// Do the mapping. // Do the mapping.
// R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission_UserReadWrite)); R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission::UserReadWrite));
UNIMPLEMENTED();
R_SUCCEED();
} }
template <typename F> template <typename F>
@ -200,13 +197,11 @@ Result KCapabilities::ProcessMapRegionCapability(const u32 cap, F f) {
R_SUCCEED(); R_SUCCEED();
} }
Result KCapabilities::MapRegion_(const u32 cap, KPageTable* page_table) { Result KCapabilities::MapRegion_(const u32 cap, KProcessPageTable* page_table) {
// Map each region into the process's page table. // Map each region into the process's page table.
return ProcessMapRegionCapability( return ProcessMapRegionCapability(
cap, [](KMemoryRegionType region_type, KMemoryPermission perm) -> Result { cap, [page_table](KMemoryRegionType region_type, KMemoryPermission perm) -> Result {
// R_RETURN(page_table->MapRegion(region_type, perm)); R_RETURN(page_table->MapRegion(region_type, perm));
UNIMPLEMENTED();
R_SUCCEED();
}); });
} }
@ -280,7 +275,7 @@ Result KCapabilities::SetDebugFlagsCapability(const u32 cap) {
} }
Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc, Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
KPageTable* page_table) { KProcessPageTable* page_table) {
// Validate this is a capability we can act on. // Validate this is a capability we can act on.
const auto type = GetCapabilityType(cap); const auto type = GetCapabilityType(cap);
R_UNLESS(type != CapabilityType::Invalid, ResultInvalidArgument); R_UNLESS(type != CapabilityType::Invalid, ResultInvalidArgument);
@ -318,7 +313,7 @@ Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
} }
} }
Result KCapabilities::SetCapabilities(std::span<const u32> caps, KPageTable* page_table) { Result KCapabilities::SetCapabilities(std::span<const u32> caps, KProcessPageTable* page_table) {
u32 set_flags = 0, set_svc = 0; u32 set_flags = 0, set_svc = 0;
for (size_t i = 0; i < caps.size(); i++) { for (size_t i = 0; i < caps.size(); i++) {
@ -335,6 +330,8 @@ Result KCapabilities::SetCapabilities(std::span<const u32> caps, KPageTable* pag
// Map the range. // Map the range.
R_TRY(this->MapRange_(cap, size_cap, page_table)); R_TRY(this->MapRange_(cap, size_cap, page_table));
} else if (GetCapabilityType(cap) == CapabilityType::MapRegion && !IsKTraceEnabled) {
continue;
} else { } else {
R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table)); R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table));
} }

View File

@ -15,15 +15,15 @@
namespace Kernel { namespace Kernel {
class KPageTable; class KProcessPageTable;
class KernelCore; class KernelCore;
class KCapabilities { class KCapabilities {
public: public:
constexpr explicit KCapabilities() = default; constexpr explicit KCapabilities() = default;
Result InitializeForKip(std::span<const u32> kern_caps, KPageTable* page_table); Result InitializeForKip(std::span<const u32> kern_caps, KProcessPageTable* page_table);
Result InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table); Result InitializeForUser(std::span<const u32> user_caps, KProcessPageTable* page_table);
static Result CheckCapabilities(KernelCore& kernel, std::span<const u32> user_caps); static Result CheckCapabilities(KernelCore& kernel, std::span<const u32> user_caps);
@ -264,9 +264,9 @@ private:
Result SetCorePriorityCapability(const u32 cap); Result SetCorePriorityCapability(const u32 cap);
Result SetSyscallMaskCapability(const u32 cap, u32& set_svc); Result SetSyscallMaskCapability(const u32 cap, u32& set_svc);
Result MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table); Result MapRange_(const u32 cap, const u32 size_cap, KProcessPageTable* page_table);
Result MapIoPage_(const u32 cap, KPageTable* page_table); Result MapIoPage_(const u32 cap, KProcessPageTable* page_table);
Result MapRegion_(const u32 cap, KPageTable* page_table); Result MapRegion_(const u32 cap, KProcessPageTable* page_table);
Result SetInterruptPairCapability(const u32 cap); Result SetInterruptPairCapability(const u32 cap);
Result SetProgramTypeCapability(const u32 cap); Result SetProgramTypeCapability(const u32 cap);
Result SetKernelVersionCapability(const u32 cap); Result SetKernelVersionCapability(const u32 cap);
@ -277,8 +277,9 @@ private:
static Result ProcessMapRegionCapability(const u32 cap, F f); static Result ProcessMapRegionCapability(const u32 cap, F f);
static Result CheckMapRegion(KernelCore& kernel, const u32 cap); static Result CheckMapRegion(KernelCore& kernel, const u32 cap);
Result SetCapability(const u32 cap, u32& set_flags, u32& set_svc, KPageTable* page_table); Result SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
Result SetCapabilities(std::span<const u32> caps, KPageTable* page_table); KProcessPageTable* page_table);
Result SetCapabilities(std::span<const u32> caps, KProcessPageTable* page_table);
private: private:
Svc::SvcAccessFlagSet m_svc_access_flags{}; Svc::SvcAccessFlagSet m_svc_access_flags{};

View File

@ -54,7 +54,7 @@ Result KDeviceAddressSpace::Detach(Svc::DeviceName device_name) {
R_SUCCEED(); R_SUCCEED();
} }
Result KDeviceAddressSpace::Map(KPageTable* page_table, KProcessAddress process_address, Result KDeviceAddressSpace::Map(KProcessPageTable* page_table, KProcessAddress process_address,
size_t size, u64 device_address, u32 option, bool is_aligned) { size_t size, u64 device_address, u32 option, bool is_aligned) {
// Check that the address falls within the space. // Check that the address falls within the space.
R_UNLESS((m_space_address <= device_address && R_UNLESS((m_space_address <= device_address &&
@ -113,7 +113,7 @@ Result KDeviceAddressSpace::Map(KPageTable* page_table, KProcessAddress process_
R_SUCCEED(); R_SUCCEED();
} }
Result KDeviceAddressSpace::Unmap(KPageTable* page_table, KProcessAddress process_address, Result KDeviceAddressSpace::Unmap(KProcessPageTable* page_table, KProcessAddress process_address,
size_t size, u64 device_address) { size_t size, u64 device_address) {
// Check that the address falls within the space. // Check that the address falls within the space.
R_UNLESS((m_space_address <= device_address && R_UNLESS((m_space_address <= device_address &&

View File

@ -5,7 +5,7 @@
#include <string> #include <string>
#include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process_page_table.h"
#include "core/hle/kernel/k_typed_address.h" #include "core/hle/kernel/k_typed_address.h"
#include "core/hle/kernel/slab_helpers.h" #include "core/hle/kernel/slab_helpers.h"
#include "core/hle/result.h" #include "core/hle/result.h"
@ -31,23 +31,23 @@ public:
Result Attach(Svc::DeviceName device_name); Result Attach(Svc::DeviceName device_name);
Result Detach(Svc::DeviceName device_name); Result Detach(Svc::DeviceName device_name);
Result MapByForce(KPageTable* page_table, KProcessAddress process_address, size_t size, Result MapByForce(KProcessPageTable* page_table, KProcessAddress process_address, size_t size,
u64 device_address, u32 option) { u64 device_address, u32 option) {
R_RETURN(this->Map(page_table, process_address, size, device_address, option, false)); R_RETURN(this->Map(page_table, process_address, size, device_address, option, false));
} }
Result MapAligned(KPageTable* page_table, KProcessAddress process_address, size_t size, Result MapAligned(KProcessPageTable* page_table, KProcessAddress process_address, size_t size,
u64 device_address, u32 option) { u64 device_address, u32 option) {
R_RETURN(this->Map(page_table, process_address, size, device_address, option, true)); R_RETURN(this->Map(page_table, process_address, size, device_address, option, true));
} }
Result Unmap(KPageTable* page_table, KProcessAddress process_address, size_t size, Result Unmap(KProcessPageTable* page_table, KProcessAddress process_address, size_t size,
u64 device_address); u64 device_address);
static void Initialize(); static void Initialize();
private: private:
Result Map(KPageTable* page_table, KProcessAddress process_address, size_t size, Result Map(KProcessPageTable* page_table, KProcessAddress process_address, size_t size,
u64 device_address, u32 option, bool is_aligned); u64 device_address, u32 option, bool is_aligned);
private: private:

View File

@ -394,6 +394,14 @@ private:
return region.GetEndAddress(); return region.GetEndAddress();
} }
public:
static const KMemoryRegion* Find(const KMemoryLayout& layout, KVirtualAddress address) {
return Find(address, layout.GetVirtualMemoryRegionTree());
}
static const KMemoryRegion* Find(const KMemoryLayout& layout, KPhysicalAddress address) {
return Find(address, layout.GetPhysicalMemoryRegionTree());
}
private: private:
u64 m_linear_phys_to_virt_diff{}; u64 m_linear_phys_to_virt_diff{};
u64 m_linear_virt_to_phys_diff{}; u64 m_linear_virt_to_phys_diff{};

View File

@ -456,8 +456,7 @@ size_t KMemoryManager::Impl::Initialize(KPhysicalAddress address, size_t size,
} }
void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) { void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) {
auto optimize_pa = auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa); auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
std::memset(optimize_map, 0, CalculateOptimizedProcessOverheadSize(m_heap.GetSize())); std::memset(optimize_map, 0, CalculateOptimizedProcessOverheadSize(m_heap.GetSize()));
@ -465,8 +464,7 @@ void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) {
void KMemoryManager::Impl::TrackUnoptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, void KMemoryManager::Impl::TrackUnoptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
size_t num_pages) { size_t num_pages) {
auto optimize_pa = auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa); auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
// Get the range we're tracking. // Get the range we're tracking.
@ -485,8 +483,7 @@ void KMemoryManager::Impl::TrackUnoptimizedAllocation(KernelCore& kernel, KPhysi
void KMemoryManager::Impl::TrackOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, void KMemoryManager::Impl::TrackOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
size_t num_pages) { size_t num_pages) {
auto optimize_pa = auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa); auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
// Get the range we're tracking. // Get the range we're tracking.
@ -506,8 +503,7 @@ void KMemoryManager::Impl::TrackOptimizedAllocation(KernelCore& kernel, KPhysica
bool KMemoryManager::Impl::ProcessOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, bool KMemoryManager::Impl::ProcessOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
size_t num_pages, u8 fill_pattern) { size_t num_pages, u8 fill_pattern) {
auto& device_memory = kernel.System().DeviceMemory(); auto& device_memory = kernel.System().DeviceMemory();
auto optimize_pa = auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
auto* optimize_map = device_memory.GetPointer<u64>(optimize_pa); auto* optimize_map = device_memory.GetPointer<u64>(optimize_pa);
// We want to return whether any pages were newly allocated. // We want to return whether any pages were newly allocated.

File diff suppressed because it is too large Load Diff

View File

@ -3,548 +3,14 @@
#pragma once #pragma once
#include <memory> #include "core/hle/kernel/k_page_table_base.h"
#include "common/common_funcs.h"
#include "common/page_table.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/k_dynamic_resource_manager.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_memory_block_manager.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_typed_address.h"
#include "core/hle/result.h"
#include "core/memory.h"
namespace Core {
class System;
}
namespace Kernel { namespace Kernel {
enum class DisableMergeAttribute : u8 { class KPageTable final : public KPageTableBase {
None = (0U << 0),
DisableHead = (1U << 0),
DisableHeadAndBody = (1U << 1),
EnableHeadAndBody = (1U << 2),
DisableTail = (1U << 3),
EnableTail = (1U << 4),
EnableAndMergeHeadBodyTail = (1U << 5),
EnableHeadBodyTail = EnableHeadAndBody | EnableTail,
DisableHeadBodyTail = DisableHeadAndBody | DisableTail,
};
struct KPageProperties {
KMemoryPermission perm;
bool io;
bool uncached;
DisableMergeAttribute disable_merge_attributes;
};
static_assert(std::is_trivial_v<KPageProperties>);
static_assert(sizeof(KPageProperties) == sizeof(u32));
class KBlockInfoManager;
class KMemoryBlockManager;
class KResourceLimit;
class KSystemResource;
class KPageTable final {
protected:
struct PageLinkedList;
public: public:
enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll }; explicit KPageTable(KernelCore& kernel) : KPageTableBase(kernel) {}
~KPageTable() = default;
YUZU_NON_COPYABLE(KPageTable);
YUZU_NON_MOVEABLE(KPageTable);
explicit KPageTable(Core::System& system_);
~KPageTable();
Result InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr,
bool enable_das_merge, bool from_back, KMemoryManager::Pool pool,
KProcessAddress code_addr, size_t code_size,
KSystemResource* system_resource, KResourceLimit* resource_limit,
Core::Memory::Memory& memory);
void Finalize();
Result MapProcessCode(KProcessAddress addr, size_t pages_count, KMemoryState state,
KMemoryPermission perm);
Result MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
Result UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size,
ICacheInvalidationStrategy icache_invalidation_strategy);
Result UnmapProcessMemory(KProcessAddress dst_addr, size_t size, KPageTable& src_page_table,
KProcessAddress src_addr);
Result MapPhysicalMemory(KProcessAddress addr, size_t size);
Result UnmapPhysicalMemory(KProcessAddress addr, size_t size);
Result MapMemory(KProcessAddress dst_addr, KProcessAddress src_addr, size_t size);
Result UnmapMemory(KProcessAddress dst_addr, KProcessAddress src_addr, size_t size);
Result SetProcessMemoryPermission(KProcessAddress addr, size_t size,
Svc::MemoryPermission svc_perm);
KMemoryInfo QueryInfo(KProcessAddress addr);
Result SetMemoryPermission(KProcessAddress addr, size_t size, Svc::MemoryPermission perm);
Result SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mask, u32 attr);
Result SetMaxHeapSize(size_t size);
Result SetHeapSize(u64* out, size_t size);
Result LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address, size_t size,
KMemoryPermission perm, bool is_aligned, bool check_heap);
Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size, bool check_heap);
Result UnlockForDeviceAddressSpace(KProcessAddress addr, size_t size);
Result LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address, size_t size);
Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size);
Result SetupForIpc(KProcessAddress* out_dst_addr, size_t size, KProcessAddress src_addr,
KPageTable& src_page_table, KMemoryPermission test_perm,
KMemoryState dst_state, bool send);
Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state);
Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state);
Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size,
KMemoryPermission perm);
Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg);
Result LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size);
Result UnlockForCodeMemory(KProcessAddress addr, size_t size, const KPageGroup& pg);
Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr);
Common::PageTable& PageTableImpl() {
return *m_page_table_impl;
}
const Common::PageTable& PageTableImpl() const {
return *m_page_table_impl;
}
KBlockInfoManager* GetBlockInfoManager() {
return m_block_info_manager;
}
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
KPhysicalAddress phys_addr, KProcessAddress region_start,
size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, region_start,
region_num_pages, state, perm));
}
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) {
R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
this->GetRegionAddress(state),
this->GetRegionSize(state) / PageSize, state, perm));
}
Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state,
KMemoryPermission perm) {
R_RETURN(this->MapPages(out_addr, num_pages, PageSize, 0, false,
this->GetRegionAddress(state),
this->GetRegionSize(state) / PageSize, state, perm));
}
Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
KMemoryPermission perm);
Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state);
Result MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
KProcessAddress region_start, size_t region_num_pages, KMemoryState state,
KMemoryPermission perm);
Result MapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state,
KMemoryPermission perm);
Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state);
void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
const KPageGroup& pg);
KProcessAddress GetRegionAddress(Svc::MemoryState state) const;
size_t GetRegionSize(Svc::MemoryState state) const;
bool CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const;
KProcessAddress GetRegionAddress(KMemoryState state) const {
return this->GetRegionAddress(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
}
size_t GetRegionSize(KMemoryState state) const {
return this->GetRegionSize(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
}
bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const {
return this->CanContain(addr, size,
static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
}
protected:
struct PageLinkedList {
private:
struct Node {
Node* m_next;
std::array<u8, PageSize - sizeof(Node*)> m_buffer;
};
public:
constexpr PageLinkedList() = default;
void Push(Node* n) {
ASSERT(Common::IsAligned(reinterpret_cast<uintptr_t>(n), PageSize));
n->m_next = m_root;
m_root = n;
}
void Push(Core::Memory::Memory& memory, KVirtualAddress addr) {
this->Push(memory.GetPointer<Node>(GetInteger(addr)));
}
Node* Peek() const {
return m_root;
}
Node* Pop() {
Node* const r = m_root;
m_root = r->m_next;
r->m_next = nullptr;
return r;
}
private:
Node* m_root{};
};
static_assert(std::is_trivially_destructible<PageLinkedList>::value);
private:
enum class OperationType : u32 {
Map = 0,
MapGroup = 1,
MapFirstGroup = 2,
Unmap = 3,
ChangePermissions = 4,
ChangePermissionsAndRefresh = 5,
ChangePermissionsAndRefreshAndFlush = 6,
Separate = 7,
};
static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared;
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start,
size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
bool IsRegionContiguous(KProcessAddress addr, u64 size) const;
void AddRegionToPages(KProcessAddress start, size_t num_pages, KPageGroup& page_linked_list);
KMemoryInfo QueryInfoImpl(KProcessAddress addr);
KProcessAddress AllocateVirtualMemory(KProcessAddress start, size_t region_num_pages,
u64 needed_num_pages, size_t align);
Result Operate(KProcessAddress addr, size_t num_pages, const KPageGroup& page_group,
OperationType operation);
Result Operate(KProcessAddress addr, size_t num_pages, KMemoryPermission perm,
OperationType operation, KPhysicalAddress map_addr = 0);
void FinalizeUpdate(PageLinkedList* page_list);
KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages,
size_t num_pages, size_t alignment, size_t offset,
size_t guard_pages);
Result CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
Result CheckMemoryStateContiguous(KProcessAddress addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr) const {
R_RETURN(this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask,
perm, attr_mask, attr));
}
Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
KMemoryBlockManager::const_iterator it, KProcessAddress last_addr,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
KProcessAddress addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
Result CheckMemoryState(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
R_RETURN(CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
state_mask, state, perm_mask, perm, attr_mask, attr,
ignore_attr));
}
Result CheckMemoryState(KProcessAddress addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
R_RETURN(this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm,
attr_mask, attr, ignore_attr));
}
Result LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_KPhysicalAddress,
KProcessAddress addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr, KMemoryPermission new_perm,
KMemoryAttribute lock_attr);
Result UnlockMemory(KProcessAddress addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryPermission new_perm, KMemoryAttribute lock_attr,
const KPageGroup* pg);
Result MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_t num_pages);
bool IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr, size_t num_pages);
bool IsLockedByCurrentThread() const {
return m_general_lock.IsLockedByCurrentThread();
}
bool IsHeapPhysicalAddress(const KMemoryLayout& layout, KPhysicalAddress phys_addr) {
ASSERT(this->IsLockedByCurrentThread());
return layout.IsHeapPhysicalAddress(m_cached_physical_heap_region, phys_addr);
}
bool GetPhysicalAddressLocked(KPhysicalAddress* out, KProcessAddress virt_addr) const {
ASSERT(this->IsLockedByCurrentThread());
*out = GetPhysicalAddr(virt_addr);
return *out != 0;
}
Result SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed,
KProcessAddress address, size_t size, KMemoryPermission test_perm,
KMemoryState dst_state);
Result SetupForIpcServer(KProcessAddress* out_addr, size_t size, KProcessAddress src_addr,
KMemoryPermission test_perm, KMemoryState dst_state,
KPageTable& src_page_table, bool send);
void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, KProcessAddress address,
size_t size, KMemoryPermission prot_perm);
Result AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
size_t num_pages, KMemoryPermission perm);
Result MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
const KPageGroup& pg, const KPageProperties properties, bool reuse_ll);
mutable KLightLock m_general_lock;
mutable KLightLock m_map_physical_memory_lock;
public:
constexpr KProcessAddress GetAddressSpaceStart() const {
return m_address_space_start;
}
constexpr KProcessAddress GetAddressSpaceEnd() const {
return m_address_space_end;
}
constexpr size_t GetAddressSpaceSize() const {
return m_address_space_end - m_address_space_start;
}
constexpr KProcessAddress GetHeapRegionStart() const {
return m_heap_region_start;
}
constexpr KProcessAddress GetHeapRegionEnd() const {
return m_heap_region_end;
}
constexpr size_t GetHeapRegionSize() const {
return m_heap_region_end - m_heap_region_start;
}
constexpr KProcessAddress GetAliasRegionStart() const {
return m_alias_region_start;
}
constexpr KProcessAddress GetAliasRegionEnd() const {
return m_alias_region_end;
}
constexpr size_t GetAliasRegionSize() const {
return m_alias_region_end - m_alias_region_start;
}
constexpr KProcessAddress GetStackRegionStart() const {
return m_stack_region_start;
}
constexpr KProcessAddress GetStackRegionEnd() const {
return m_stack_region_end;
}
constexpr size_t GetStackRegionSize() const {
return m_stack_region_end - m_stack_region_start;
}
constexpr KProcessAddress GetKernelMapRegionStart() const {
return m_kernel_map_region_start;
}
constexpr KProcessAddress GetKernelMapRegionEnd() const {
return m_kernel_map_region_end;
}
constexpr KProcessAddress GetCodeRegionStart() const {
return m_code_region_start;
}
constexpr KProcessAddress GetCodeRegionEnd() const {
return m_code_region_end;
}
constexpr KProcessAddress GetAliasCodeRegionStart() const {
return m_alias_code_region_start;
}
constexpr KProcessAddress GetAliasCodeRegionEnd() const {
return m_alias_code_region_end;
}
constexpr size_t GetAliasCodeRegionSize() const {
return m_alias_code_region_end - m_alias_code_region_start;
}
size_t GetNormalMemorySize() const {
KScopedLightLock lk(m_general_lock);
return GetHeapSize() + m_mapped_physical_memory_size;
}
constexpr size_t GetAddressSpaceWidth() const {
return m_address_space_width;
}
constexpr size_t GetHeapSize() const {
return m_current_heap_end - m_heap_region_start;
}
constexpr size_t GetNumGuardPages() const {
return IsKernel() ? 1 : 4;
}
KPhysicalAddress GetPhysicalAddr(KProcessAddress addr) const {
const auto backing_addr = m_page_table_impl->backing_addr[addr >> PageBits];
ASSERT(backing_addr);
return backing_addr + GetInteger(addr);
}
constexpr bool Contains(KProcessAddress addr) const {
return m_address_space_start <= addr && addr <= m_address_space_end - 1;
}
constexpr bool Contains(KProcessAddress addr, size_t size) const {
return m_address_space_start <= addr && addr < addr + size &&
addr + size - 1 <= m_address_space_end - 1;
}
constexpr bool IsInAliasRegion(KProcessAddress addr, size_t size) const {
return this->Contains(addr, size) && m_alias_region_start <= addr &&
addr + size - 1 <= m_alias_region_end - 1;
}
constexpr bool IsInHeapRegion(KProcessAddress addr, size_t size) const {
return this->Contains(addr, size) && m_heap_region_start <= addr &&
addr + size - 1 <= m_heap_region_end - 1;
}
public:
static KVirtualAddress GetLinearMappedVirtualAddress(const KMemoryLayout& layout,
KPhysicalAddress addr) {
return layout.GetLinearVirtualAddress(addr);
}
static KPhysicalAddress GetLinearMappedPhysicalAddress(const KMemoryLayout& layout,
KVirtualAddress addr) {
return layout.GetLinearPhysicalAddress(addr);
}
static KVirtualAddress GetHeapVirtualAddress(const KMemoryLayout& layout,
KPhysicalAddress addr) {
return GetLinearMappedVirtualAddress(layout, addr);
}
static KPhysicalAddress GetHeapPhysicalAddress(const KMemoryLayout& layout,
KVirtualAddress addr) {
return GetLinearMappedPhysicalAddress(layout, addr);
}
static KVirtualAddress GetPageTableVirtualAddress(const KMemoryLayout& layout,
KPhysicalAddress addr) {
return GetLinearMappedVirtualAddress(layout, addr);
}
static KPhysicalAddress GetPageTablePhysicalAddress(const KMemoryLayout& layout,
KVirtualAddress addr) {
return GetLinearMappedPhysicalAddress(layout, addr);
}
private:
constexpr bool IsKernel() const {
return m_is_kernel;
}
constexpr bool IsAslrEnabled() const {
return m_enable_aslr;
}
constexpr bool ContainsPages(KProcessAddress addr, size_t num_pages) const {
return (m_address_space_start <= addr) &&
(num_pages <= (m_address_space_end - m_address_space_start) / PageSize) &&
(addr + num_pages * PageSize - 1 <= m_address_space_end - 1);
}
private:
class KScopedPageTableUpdater {
private:
KPageTable* m_pt{};
PageLinkedList m_ll;
public:
explicit KScopedPageTableUpdater(KPageTable* pt) : m_pt(pt) {}
explicit KScopedPageTableUpdater(KPageTable& pt) : KScopedPageTableUpdater(&pt) {}
~KScopedPageTableUpdater() {
m_pt->FinalizeUpdate(this->GetPageList());
}
PageLinkedList* GetPageList() {
return std::addressof(m_ll);
}
};
private:
KProcessAddress m_address_space_start{};
KProcessAddress m_address_space_end{};
KProcessAddress m_heap_region_start{};
KProcessAddress m_heap_region_end{};
KProcessAddress m_current_heap_end{};
KProcessAddress m_alias_region_start{};
KProcessAddress m_alias_region_end{};
KProcessAddress m_stack_region_start{};
KProcessAddress m_stack_region_end{};
KProcessAddress m_kernel_map_region_start{};
KProcessAddress m_kernel_map_region_end{};
KProcessAddress m_code_region_start{};
KProcessAddress m_code_region_end{};
KProcessAddress m_alias_code_region_start{};
KProcessAddress m_alias_code_region_end{};
size_t m_max_heap_size{};
size_t m_mapped_physical_memory_size{};
size_t m_mapped_unsafe_physical_memory{};
size_t m_mapped_insecure_memory{};
size_t m_mapped_ipc_server_memory{};
size_t m_address_space_width{};
KMemoryBlockManager m_memory_block_manager;
u32 m_allocate_option{};
bool m_is_kernel{};
bool m_enable_aslr{};
bool m_enable_device_address_space_merge{};
KMemoryBlockSlabManager* m_memory_block_slab_manager{};
KBlockInfoManager* m_block_info_manager{};
KResourceLimit* m_resource_limit{};
u32 m_heap_fill_value{};
u32 m_ipc_fill_value{};
u32 m_stack_fill_value{};
const KMemoryRegion* m_cached_physical_heap_region{};
KMemoryManager::Pool m_memory_pool{KMemoryManager::Pool::Application};
KMemoryManager::Direction m_allocation_option{KMemoryManager::Direction::FromFront};
std::unique_ptr<Common::PageTable> m_page_table_impl;
Core::System& m_system;
KernelCore& m_kernel;
Core::Memory::Memory* m_memory{};
}; };
} // namespace Kernel } // namespace Kernel

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,760 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include "common/common_funcs.h"
#include "common/page_table.h"
#include "core/core.h"
#include "core/hle/kernel/k_dynamic_resource_manager.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_memory_block_manager.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_typed_address.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/result.h"
#include "core/memory.h"
namespace Kernel {
enum class DisableMergeAttribute : u8 {
None = (0U << 0),
DisableHead = (1U << 0),
DisableHeadAndBody = (1U << 1),
EnableHeadAndBody = (1U << 2),
DisableTail = (1U << 3),
EnableTail = (1U << 4),
EnableAndMergeHeadBodyTail = (1U << 5),
EnableHeadBodyTail = EnableHeadAndBody | EnableTail,
DisableHeadBodyTail = DisableHeadAndBody | DisableTail,
};
DECLARE_ENUM_FLAG_OPERATORS(DisableMergeAttribute);
struct KPageProperties {
KMemoryPermission perm;
bool io;
bool uncached;
DisableMergeAttribute disable_merge_attributes;
};
static_assert(std::is_trivial_v<KPageProperties>);
static_assert(sizeof(KPageProperties) == sizeof(u32));
class KResourceLimit;
class KSystemResource;
class KPageTableBase {
YUZU_NON_COPYABLE(KPageTableBase);
YUZU_NON_MOVEABLE(KPageTableBase);
public:
using TraversalEntry = Common::PageTable::TraversalEntry;
using TraversalContext = Common::PageTable::TraversalContext;
class MemoryRange {
private:
KernelCore& m_kernel;
KPhysicalAddress m_address;
size_t m_size;
bool m_heap;
public:
explicit MemoryRange(KernelCore& kernel)
: m_kernel(kernel), m_address(0), m_size(0), m_heap(false) {}
void Set(KPhysicalAddress address, size_t size, bool heap) {
m_address = address;
m_size = size;
m_heap = heap;
}
KPhysicalAddress GetAddress() const {
return m_address;
}
size_t GetSize() const {
return m_size;
}
bool IsHeap() const {
return m_heap;
}
void Open();
void Close();
};
protected:
enum MemoryFillValue : u8 {
MemoryFillValue_Zero = 0,
MemoryFillValue_Stack = 'X',
MemoryFillValue_Ipc = 'Y',
MemoryFillValue_Heap = 'Z',
};
enum class OperationType {
Map = 0,
MapGroup = 1,
MapFirstGroup = 2,
Unmap = 3,
ChangePermissions = 4,
ChangePermissionsAndRefresh = 5,
ChangePermissionsAndRefreshAndFlush = 6,
Separate = 7,
};
static constexpr size_t MaxPhysicalMapAlignment = 1_GiB;
static constexpr size_t RegionAlignment = 2_MiB;
static_assert(RegionAlignment == KernelAslrAlignment);
struct PageLinkedList {
private:
struct Node {
Node* m_next;
std::array<u8, PageSize - sizeof(Node*)> m_buffer;
};
static_assert(std::is_trivial_v<Node>);
private:
Node* m_root{};
public:
constexpr PageLinkedList() : m_root(nullptr) {}
void Push(Node* n) {
ASSERT(Common::IsAligned(reinterpret_cast<uintptr_t>(n), PageSize));
n->m_next = m_root;
m_root = n;
}
Node* Peek() const {
return m_root;
}
Node* Pop() {
Node* const r = m_root;
m_root = r->m_next;
r->m_next = nullptr;
return r;
}
};
static_assert(std::is_trivially_destructible_v<PageLinkedList>);
static constexpr auto DefaultMemoryIgnoreAttr =
KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared;
static constexpr size_t GetAddressSpaceWidth(Svc::CreateProcessFlag as_type) {
switch (static_cast<Svc::CreateProcessFlag>(as_type &
Svc::CreateProcessFlag::AddressSpaceMask)) {
case Svc::CreateProcessFlag::AddressSpace64Bit:
return 39;
case Svc::CreateProcessFlag::AddressSpace64BitDeprecated:
return 36;
case Svc::CreateProcessFlag::AddressSpace32Bit:
case Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias:
return 32;
default:
UNREACHABLE();
}
}
private:
class KScopedPageTableUpdater {
private:
KPageTableBase* m_pt;
PageLinkedList m_ll;
public:
explicit KScopedPageTableUpdater(KPageTableBase* pt) : m_pt(pt), m_ll() {}
explicit KScopedPageTableUpdater(KPageTableBase& pt)
: KScopedPageTableUpdater(std::addressof(pt)) {}
~KScopedPageTableUpdater() {
m_pt->FinalizeUpdate(this->GetPageList());
}
PageLinkedList* GetPageList() {
return std::addressof(m_ll);
}
};
private:
KernelCore& m_kernel;
Core::System& m_system;
KProcessAddress m_address_space_start{};
KProcessAddress m_address_space_end{};
KProcessAddress m_heap_region_start{};
KProcessAddress m_heap_region_end{};
KProcessAddress m_current_heap_end{};
KProcessAddress m_alias_region_start{};
KProcessAddress m_alias_region_end{};
KProcessAddress m_stack_region_start{};
KProcessAddress m_stack_region_end{};
KProcessAddress m_kernel_map_region_start{};
KProcessAddress m_kernel_map_region_end{};
KProcessAddress m_alias_code_region_start{};
KProcessAddress m_alias_code_region_end{};
KProcessAddress m_code_region_start{};
KProcessAddress m_code_region_end{};
size_t m_max_heap_size{};
size_t m_mapped_physical_memory_size{};
size_t m_mapped_unsafe_physical_memory{};
size_t m_mapped_insecure_memory{};
size_t m_mapped_ipc_server_memory{};
mutable KLightLock m_general_lock;
mutable KLightLock m_map_physical_memory_lock;
KLightLock m_device_map_lock;
std::unique_ptr<Common::PageTable> m_impl{};
Core::Memory::Memory* m_memory{};
KMemoryBlockManager m_memory_block_manager{};
u32 m_allocate_option{};
u32 m_address_space_width{};
bool m_is_kernel{};
bool m_enable_aslr{};
bool m_enable_device_address_space_merge{};
KMemoryBlockSlabManager* m_memory_block_slab_manager{};
KBlockInfoManager* m_block_info_manager{};
KResourceLimit* m_resource_limit{};
const KMemoryRegion* m_cached_physical_linear_region{};
const KMemoryRegion* m_cached_physical_heap_region{};
MemoryFillValue m_heap_fill_value{};
MemoryFillValue m_ipc_fill_value{};
MemoryFillValue m_stack_fill_value{};
public:
explicit KPageTableBase(KernelCore& kernel);
~KPageTableBase();
Result InitializeForKernel(bool is_64_bit, KVirtualAddress start, KVirtualAddress end,
Core::Memory::Memory& memory);
Result InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr,
bool enable_device_address_space_merge, bool from_back,
KMemoryManager::Pool pool, KProcessAddress code_address,
size_t code_size, KSystemResource* system_resource,
KResourceLimit* resource_limit, Core::Memory::Memory& memory,
KProcessAddress aslr_space_start);
void Finalize();
bool IsKernel() const {
return m_is_kernel;
}
bool IsAslrEnabled() const {
return m_enable_aslr;
}
bool Contains(KProcessAddress addr) const {
return m_address_space_start <= addr && addr <= m_address_space_end - 1;
}
bool Contains(KProcessAddress addr, size_t size) const {
return m_address_space_start <= addr && addr < addr + size &&
addr + size - 1 <= m_address_space_end - 1;
}
bool IsInAliasRegion(KProcessAddress addr, size_t size) const {
return this->Contains(addr, size) && m_alias_region_start <= addr &&
addr + size - 1 <= m_alias_region_end - 1;
}
bool IsInHeapRegion(KProcessAddress addr, size_t size) const {
return this->Contains(addr, size) && m_heap_region_start <= addr &&
addr + size - 1 <= m_heap_region_end - 1;
}
bool IsInUnsafeAliasRegion(KProcessAddress addr, size_t size) const {
// Even though Unsafe physical memory is KMemoryState_Normal, it must be mapped inside the
// alias code region.
return this->CanContain(addr, size, Svc::MemoryState::AliasCode);
}
KScopedLightLock AcquireDeviceMapLock() {
return KScopedLightLock(m_device_map_lock);
}
KProcessAddress GetRegionAddress(Svc::MemoryState state) const;
size_t GetRegionSize(Svc::MemoryState state) const;
bool CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const;
KProcessAddress GetRegionAddress(KMemoryState state) const {
return this->GetRegionAddress(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
}
size_t GetRegionSize(KMemoryState state) const {
return this->GetRegionSize(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
}
bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const {
return this->CanContain(addr, size,
static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
}
public:
Core::Memory::Memory& GetMemory() {
return *m_memory;
}
Core::Memory::Memory& GetMemory() const {
return *m_memory;
}
Common::PageTable& GetImpl() {
return *m_impl;
}
Common::PageTable& GetImpl() const {
return *m_impl;
}
size_t GetNumGuardPages() const {
return this->IsKernel() ? 1 : 4;
}
protected:
// NOTE: These three functions (Operate, Operate, FinalizeUpdate) are virtual functions
// in Nintendo's kernel. We devirtualize them, since KPageTable is the only derived
// class, and this avoids unnecessary virtual function calls.
Result Operate(PageLinkedList* page_list, KProcessAddress virt_addr, size_t num_pages,
KPhysicalAddress phys_addr, bool is_pa_valid, const KPageProperties properties,
OperationType operation, bool reuse_ll);
Result Operate(PageLinkedList* page_list, KProcessAddress virt_addr, size_t num_pages,
const KPageGroup& page_group, const KPageProperties properties,
OperationType operation, bool reuse_ll);
void FinalizeUpdate(PageLinkedList* page_list);
bool IsLockedByCurrentThread() const {
return m_general_lock.IsLockedByCurrentThread();
}
bool IsLinearMappedPhysicalAddress(KPhysicalAddress phys_addr) {
ASSERT(this->IsLockedByCurrentThread());
return m_kernel.MemoryLayout().IsLinearMappedPhysicalAddress(
m_cached_physical_linear_region, phys_addr);
}
bool IsLinearMappedPhysicalAddress(KPhysicalAddress phys_addr, size_t size) {
ASSERT(this->IsLockedByCurrentThread());
return m_kernel.MemoryLayout().IsLinearMappedPhysicalAddress(
m_cached_physical_linear_region, phys_addr, size);
}
bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr) {
ASSERT(this->IsLockedByCurrentThread());
return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region,
phys_addr);
}
bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr, size_t size) {
ASSERT(this->IsLockedByCurrentThread());
return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region,
phys_addr, size);
}
bool IsHeapPhysicalAddressForFinalize(KPhysicalAddress phys_addr) {
ASSERT(!this->IsLockedByCurrentThread());
return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region,
phys_addr);
}
bool ContainsPages(KProcessAddress addr, size_t num_pages) const {
return (m_address_space_start <= addr) &&
(num_pages <= (m_address_space_end - m_address_space_start) / PageSize) &&
(addr + num_pages * PageSize - 1 <= m_address_space_end - 1);
}
private:
KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages,
size_t num_pages, size_t alignment, size_t offset,
size_t guard_pages) const;
Result CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
Result CheckMemoryStateContiguous(KProcessAddress addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr) const {
R_RETURN(this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask,
perm, attr_mask, attr));
}
Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
KMemoryBlockManager::const_iterator it, KProcessAddress last_addr,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
KProcessAddress addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
Result CheckMemoryState(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
R_RETURN(this->CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
state_mask, state, perm_mask, perm, attr_mask, attr,
ignore_attr));
}
Result CheckMemoryState(KProcessAddress addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
R_RETURN(this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm,
attr_mask, attr, ignore_attr));
}
Result LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_paddr, KProcessAddress addr,
size_t size, KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryPermission new_perm, KMemoryAttribute lock_attr);
Result UnlockMemory(KProcessAddress addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryPermission new_perm, KMemoryAttribute lock_attr,
const KPageGroup* pg);
Result QueryInfoImpl(KMemoryInfo* out_info, Svc::PageInfo* out_page,
KProcessAddress address) const;
Result QueryMappingImpl(KProcessAddress* out, KPhysicalAddress address, size_t size,
Svc::MemoryState state) const;
Result AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
size_t num_pages, KMemoryPermission perm);
Result MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
const KPageGroup& pg, const KPageProperties properties, bool reuse_ll);
void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
const KPageGroup& pg);
Result MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_t num_pages);
bool IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr, size_t num_pages);
Result GetContiguousMemoryRangeWithState(MemoryRange* out, KProcessAddress address, size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr);
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start,
size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
Result MapIoImpl(KProcessAddress* out, PageLinkedList* page_list, KPhysicalAddress phys_addr,
size_t size, KMemoryState state, KMemoryPermission perm);
Result ReadIoMemoryImpl(KProcessAddress dst_addr, KPhysicalAddress phys_addr, size_t size,
KMemoryState state);
Result WriteIoMemoryImpl(KPhysicalAddress phys_addr, KProcessAddress src_addr, size_t size,
KMemoryState state);
Result SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed,
KProcessAddress address, size_t size, KMemoryPermission test_perm,
KMemoryState dst_state);
Result SetupForIpcServer(KProcessAddress* out_addr, size_t size, KProcessAddress src_addr,
KMemoryPermission test_perm, KMemoryState dst_state,
KPageTableBase& src_page_table, bool send);
void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, KProcessAddress address,
size_t size, KMemoryPermission prot_perm);
size_t GetSize(KMemoryState state) const;
bool GetPhysicalAddressLocked(KPhysicalAddress* out, KProcessAddress virt_addr) const {
// Validate pre-conditions.
ASSERT(this->IsLockedByCurrentThread());
return this->GetImpl().GetPhysicalAddress(out, virt_addr);
}
public:
bool GetPhysicalAddress(KPhysicalAddress* out, KProcessAddress virt_addr) const {
// Validate pre-conditions.
ASSERT(!this->IsLockedByCurrentThread());
// Acquire exclusive access to the table while doing address translation.
KScopedLightLock lk(m_general_lock);
return this->GetPhysicalAddressLocked(out, virt_addr);
}
KBlockInfoManager* GetBlockInfoManager() const {
return m_block_info_manager;
}
Result SetMemoryPermission(KProcessAddress addr, size_t size, Svc::MemoryPermission perm);
Result SetProcessMemoryPermission(KProcessAddress addr, size_t size,
Svc::MemoryPermission perm);
Result SetMemoryAttribute(KProcessAddress addr, size_t size, KMemoryAttribute mask,
KMemoryAttribute attr);
Result SetHeapSize(KProcessAddress* out, size_t size);
Result SetMaxHeapSize(size_t size);
Result QueryInfo(KMemoryInfo* out_info, Svc::PageInfo* out_page_info,
KProcessAddress addr) const;
Result QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out, KProcessAddress address) const;
Result QueryStaticMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) const {
R_RETURN(this->QueryMappingImpl(out, address, size, Svc::MemoryState::Static));
}
Result QueryIoMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) const {
R_RETURN(this->QueryMappingImpl(out, address, size, Svc::MemoryState::Io));
}
Result MapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
Result UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
Result MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
Result UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
Result MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm);
Result MapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size,
Svc::MemoryMapping mapping, Svc::MemoryPermission perm);
Result UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size,
Svc::MemoryMapping mapping);
Result MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm);
Result MapRegion(KMemoryRegionType region_type, KMemoryPermission perm);
Result MapInsecureMemory(KProcessAddress address, size_t size);
Result UnmapInsecureMemory(KProcessAddress address, size_t size);
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
KPhysicalAddress phys_addr, KProcessAddress region_start,
size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, region_start,
region_num_pages, state, perm));
}
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) {
R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
this->GetRegionAddress(state),
this->GetRegionSize(state) / PageSize, state, perm));
}
Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state,
KMemoryPermission perm) {
R_RETURN(this->MapPages(out_addr, num_pages, PageSize, 0, false,
this->GetRegionAddress(state),
this->GetRegionSize(state) / PageSize, state, perm));
}
Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
KMemoryPermission perm);
Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state);
Result MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
KProcessAddress region_start, size_t region_num_pages, KMemoryState state,
KMemoryPermission perm);
Result MapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state,
KMemoryPermission perm);
Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state);
Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr);
Result InvalidateProcessDataCache(KProcessAddress address, size_t size);
Result InvalidateCurrentProcessDataCache(KProcessAddress address, size_t size);
Result ReadDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
Result ReadDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size,
KMemoryState state);
Result WriteDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
Result WriteDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size,
KMemoryState state);
Result LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address, size_t size,
KMemoryPermission perm, bool is_aligned, bool check_heap);
Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size, bool check_heap);
Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size);
Result UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size);
Result OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange* out,
KProcessAddress address, size_t size,
KMemoryPermission perm, bool is_aligned);
Result OpenMemoryRangeForUnmapDeviceAddressSpace(MemoryRange* out, KProcessAddress address,
size_t size);
Result LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address, size_t size);
Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size);
Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size,
KMemoryPermission perm);
Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg);
Result LockForCodeMemory(KPageGroup* out, KProcessAddress address, size_t size);
Result UnlockForCodeMemory(KProcessAddress address, size_t size, const KPageGroup& pg);
Result OpenMemoryRangeForProcessCacheOperation(MemoryRange* out, KProcessAddress address,
size_t size);
Result CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size,
KProcessAddress src_addr, KMemoryState src_state_mask,
KMemoryState src_state, KMemoryPermission src_test_perm,
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr);
Result CopyMemoryFromLinearToKernel(void* buffer, size_t size, KProcessAddress src_addr,
KMemoryState src_state_mask, KMemoryState src_state,
KMemoryPermission src_test_perm,
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr);
Result CopyMemoryFromUserToLinear(KProcessAddress dst_addr, size_t size,
KMemoryState dst_state_mask, KMemoryState dst_state,
KMemoryPermission dst_test_perm,
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
KProcessAddress src_addr);
Result CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, size_t size,
KMemoryState dst_state_mask, KMemoryState dst_state,
KMemoryPermission dst_test_perm,
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
void* buffer);
Result CopyMemoryFromHeapToHeap(KPageTableBase& dst_page_table, KProcessAddress dst_addr,
size_t size, KMemoryState dst_state_mask,
KMemoryState dst_state, KMemoryPermission dst_test_perm,
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
KProcessAddress src_addr, KMemoryState src_state_mask,
KMemoryState src_state, KMemoryPermission src_test_perm,
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr);
Result CopyMemoryFromHeapToHeapWithoutCheckDestination(
KPageTableBase& dst_page_table, KProcessAddress dst_addr, size_t size,
KMemoryState dst_state_mask, KMemoryState dst_state, KMemoryPermission dst_test_perm,
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, KProcessAddress src_addr,
KMemoryState src_state_mask, KMemoryState src_state, KMemoryPermission src_test_perm,
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr);
Result SetupForIpc(KProcessAddress* out_dst_addr, size_t size, KProcessAddress src_addr,
KPageTableBase& src_page_table, KMemoryPermission test_perm,
KMemoryState dst_state, bool send);
Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state);
Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state);
Result MapPhysicalMemory(KProcessAddress address, size_t size);
Result UnmapPhysicalMemory(KProcessAddress address, size_t size);
Result MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size);
Result UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size);
Result UnmapProcessMemory(KProcessAddress dst_address, size_t size, KPageTableBase& src_pt,
KProcessAddress src_address);
public:
KProcessAddress GetAddressSpaceStart() const {
return m_address_space_start;
}
KProcessAddress GetHeapRegionStart() const {
return m_heap_region_start;
}
KProcessAddress GetAliasRegionStart() const {
return m_alias_region_start;
}
KProcessAddress GetStackRegionStart() const {
return m_stack_region_start;
}
KProcessAddress GetKernelMapRegionStart() const {
return m_kernel_map_region_start;
}
KProcessAddress GetCodeRegionStart() const {
return m_code_region_start;
}
KProcessAddress GetAliasCodeRegionStart() const {
return m_alias_code_region_start;
}
size_t GetAddressSpaceSize() const {
return m_address_space_end - m_address_space_start;
}
size_t GetHeapRegionSize() const {
return m_heap_region_end - m_heap_region_start;
}
size_t GetAliasRegionSize() const {
return m_alias_region_end - m_alias_region_start;
}
size_t GetStackRegionSize() const {
return m_stack_region_end - m_stack_region_start;
}
size_t GetKernelMapRegionSize() const {
return m_kernel_map_region_end - m_kernel_map_region_start;
}
size_t GetCodeRegionSize() const {
return m_code_region_end - m_code_region_start;
}
size_t GetAliasCodeRegionSize() const {
return m_alias_code_region_end - m_alias_code_region_start;
}
size_t GetNormalMemorySize() const {
// Lock the table.
KScopedLightLock lk(m_general_lock);
return (m_current_heap_end - m_heap_region_start) + m_mapped_physical_memory_size;
}
size_t GetCodeSize() const;
size_t GetCodeDataSize() const;
size_t GetAliasCodeSize() const;
size_t GetAliasCodeDataSize() const;
u32 GetAllocateOption() const {
return m_allocate_option;
}
u32 GetAddressSpaceWidth() const {
return m_address_space_width;
}
public:
// Linear mapped
static u8* GetLinearMappedVirtualPointer(KernelCore& kernel, KPhysicalAddress addr) {
return kernel.System().DeviceMemory().GetPointer<u8>(addr);
}
static KPhysicalAddress GetLinearMappedPhysicalAddress(KernelCore& kernel,
KVirtualAddress addr) {
return kernel.MemoryLayout().GetLinearPhysicalAddress(addr);
}
static KVirtualAddress GetLinearMappedVirtualAddress(KernelCore& kernel,
KPhysicalAddress addr) {
return kernel.MemoryLayout().GetLinearVirtualAddress(addr);
}
// Heap
static u8* GetHeapVirtualPointer(KernelCore& kernel, KPhysicalAddress addr) {
return kernel.System().DeviceMemory().GetPointer<u8>(addr);
}
static KPhysicalAddress GetHeapPhysicalAddress(KernelCore& kernel, KVirtualAddress addr) {
return GetLinearMappedPhysicalAddress(kernel, addr);
}
static KVirtualAddress GetHeapVirtualAddress(KernelCore& kernel, KPhysicalAddress addr) {
return GetLinearMappedVirtualAddress(kernel, addr);
}
// Member heap
u8* GetHeapVirtualPointer(KPhysicalAddress addr) {
return GetHeapVirtualPointer(m_kernel, addr);
}
KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress addr) {
return GetHeapPhysicalAddress(m_kernel, addr);
}
KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress addr) {
return GetHeapVirtualAddress(m_kernel, addr);
}
// TODO: GetPageTableVirtualAddress
// TODO: GetPageTablePhysicalAddress
};
} // namespace Kernel

View File

@ -298,9 +298,9 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa
const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr); const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr);
const bool enable_das_merge = const bool enable_das_merge =
False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
R_TRY(m_page_table.InitializeForProcess( R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, params.code_address, params.code_num_pages * PageSize,
params.code_num_pages * PageSize, m_system_resource, res_limit, this->GetMemory())); m_system_resource, res_limit, this->GetMemory(), 0));
} }
ON_RESULT_FAILURE_2 { ON_RESULT_FAILURE_2 {
m_page_table.Finalize(); m_page_table.Finalize();
@ -332,7 +332,7 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa
Result KProcess::Initialize(const Svc::CreateProcessParameter& params, Result KProcess::Initialize(const Svc::CreateProcessParameter& params,
std::span<const u32> user_caps, KResourceLimit* res_limit, std::span<const u32> user_caps, KResourceLimit* res_limit,
KMemoryManager::Pool pool) { KMemoryManager::Pool pool, KProcessAddress aslr_space_start) {
ASSERT(res_limit != nullptr); ASSERT(res_limit != nullptr);
// Set members. // Set members.
@ -391,9 +391,9 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params,
const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr); const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr);
const bool enable_das_merge = const bool enable_das_merge =
False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
R_TRY(m_page_table.InitializeForProcess(as_type, enable_aslr, enable_das_merge, R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
!enable_aslr, pool, params.code_address, code_size, params.code_address, code_size, m_system_resource, res_limit,
m_system_resource, res_limit, this->GetMemory())); this->GetMemory(), aslr_space_start));
} }
ON_RESULT_FAILURE_2 { ON_RESULT_FAILURE_2 {
m_page_table.Finalize(); m_page_table.Finalize();
@ -1122,13 +1122,13 @@ Result KProcess::GetThreadList(s32* out_num_threads, KProcessAddress out_thread_
void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {} void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {}
KProcess::KProcess(KernelCore& kernel) KProcess::KProcess(KernelCore& kernel)
: KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel.System()}, : KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel}, m_state_lock{kernel},
m_state_lock{kernel}, m_list_lock{kernel}, m_cond_var{kernel.System()}, m_list_lock{kernel}, m_cond_var{kernel.System()}, m_address_arbiter{kernel.System()},
m_address_arbiter{kernel.System()}, m_handle_table{kernel} {} m_handle_table{kernel} {}
KProcess::~KProcess() = default; KProcess::~KProcess() = default;
Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
bool is_hbl) { KProcessAddress aslr_space_start, bool is_hbl) {
// Create a resource limit for the process. // Create a resource limit for the process.
const auto physical_memory_size = const auto physical_memory_size =
m_kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application); m_kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application);
@ -1179,7 +1179,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
.name = {}, .name = {},
.version = {}, .version = {},
.program_id = metadata.GetTitleID(), .program_id = metadata.GetTitleID(),
.code_address = code_address, .code_address = code_address + GetInteger(aslr_space_start),
.code_num_pages = static_cast<s32>(code_size / PageSize), .code_num_pages = static_cast<s32>(code_size / PageSize),
.flags = flag, .flags = flag,
.reslimit = Svc::InvalidHandle, .reslimit = Svc::InvalidHandle,
@ -1193,7 +1193,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
// Initialize for application process. // Initialize for application process.
R_TRY(this->Initialize(params, metadata.GetKernelCapabilities(), res_limit, R_TRY(this->Initialize(params, metadata.GetKernelCapabilities(), res_limit,
KMemoryManager::Pool::Application)); KMemoryManager::Pool::Application, aslr_space_start));
// Assign remaining properties. // Assign remaining properties.
m_is_hbl = is_hbl; m_is_hbl = is_hbl;
@ -1214,6 +1214,17 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) {
ReprotectSegment(code_set.CodeSegment(), Svc::MemoryPermission::ReadExecute); ReprotectSegment(code_set.CodeSegment(), Svc::MemoryPermission::ReadExecute);
ReprotectSegment(code_set.RODataSegment(), Svc::MemoryPermission::Read); ReprotectSegment(code_set.RODataSegment(), Svc::MemoryPermission::Read);
ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite); ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite);
#ifdef ARCHITECTURE_arm64
if (Settings::IsNceEnabled()) {
auto& buffer = m_kernel.System().DeviceMemory().buffer;
const auto& code = code_set.CodeSegment();
const auto& patch = code_set.PatchSegment();
buffer.Protect(GetInteger(base_addr + code.addr), code.size, true, true, true);
buffer.Protect(GetInteger(base_addr + patch.addr), patch.size, true, true, true);
ReprotectSegment(code_set.PatchSegment(), Svc::MemoryPermission::None);
}
#endif
} }
bool KProcess::InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type) { bool KProcess::InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type) {

View File

@ -5,13 +5,14 @@
#include <map> #include <map>
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/code_set.h" #include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/k_address_arbiter.h" #include "core/hle/kernel/k_address_arbiter.h"
#include "core/hle/kernel/k_capabilities.h" #include "core/hle/kernel/k_capabilities.h"
#include "core/hle/kernel/k_condition_variable.h" #include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_page_table_manager.h" #include "core/hle/kernel/k_page_table_manager.h"
#include "core/hle/kernel/k_process_page_table.h"
#include "core/hle/kernel/k_system_resource.h" #include "core/hle/kernel/k_system_resource.h"
#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_local_page.h" #include "core/hle/kernel/k_thread_local_page.h"
@ -65,7 +66,7 @@ private:
using TLPIterator = TLPTree::iterator; using TLPIterator = TLPTree::iterator;
private: private:
KPageTable m_page_table; KProcessPageTable m_page_table;
std::atomic<size_t> m_used_kernel_memory_size{}; std::atomic<size_t> m_used_kernel_memory_size{};
TLPTree m_fully_used_tlp_tree{}; TLPTree m_fully_used_tlp_tree{};
TLPTree m_partially_used_tlp_tree{}; TLPTree m_partially_used_tlp_tree{};
@ -111,6 +112,7 @@ private:
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> m_pinned_threads{}; std::array<KThread*, Core::Hardware::NUM_CPU_CORES> m_pinned_threads{};
std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS> m_watchpoints{}; std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS> m_watchpoints{};
std::map<KProcessAddress, u64> m_debug_page_refcounts{}; std::map<KProcessAddress, u64> m_debug_page_refcounts{};
std::unordered_map<u64, u64> m_post_handlers{};
std::atomic<s64> m_cpu_time{}; std::atomic<s64> m_cpu_time{};
std::atomic<s64> m_num_process_switches{}; std::atomic<s64> m_num_process_switches{};
std::atomic<s64> m_num_thread_switches{}; std::atomic<s64> m_num_thread_switches{};
@ -149,7 +151,8 @@ public:
std::span<const u32> caps, KResourceLimit* res_limit, std::span<const u32> caps, KResourceLimit* res_limit,
KMemoryManager::Pool pool, bool immortal); KMemoryManager::Pool pool, bool immortal);
Result Initialize(const Svc::CreateProcessParameter& params, std::span<const u32> user_caps, Result Initialize(const Svc::CreateProcessParameter& params, std::span<const u32> user_caps,
KResourceLimit* res_limit, KMemoryManager::Pool pool); KResourceLimit* res_limit, KMemoryManager::Pool pool,
KProcessAddress aslr_space_start);
void Exit(); void Exit();
const char* GetName() const { const char* GetName() const {
@ -254,9 +257,8 @@ public:
return m_is_hbl; return m_is_hbl;
} }
Kernel::KMemoryManager::Direction GetAllocateOption() const { u32 GetAllocateOption() const {
// TODO: property of the KPageTableBase return m_page_table.GetAllocateOption();
return KMemoryManager::Direction::FromFront;
} }
ThreadList& GetThreadList() { ThreadList& GetThreadList() {
@ -295,10 +297,10 @@ public:
return m_list_lock; return m_list_lock;
} }
KPageTable& GetPageTable() { KProcessPageTable& GetPageTable() {
return m_page_table; return m_page_table;
} }
const KPageTable& GetPageTable() const { const KProcessPageTable& GetPageTable() const {
return m_page_table; return m_page_table;
} }
@ -466,6 +468,10 @@ public:
static void Switch(KProcess* cur_process, KProcess* next_process); static void Switch(KProcess* cur_process, KProcess* next_process);
std::unordered_map<u64, u64>& GetPostHandlers() noexcept {
return m_post_handlers;
}
public: public:
// Attempts to insert a watchpoint into a free slot. Returns false if none are available. // Attempts to insert a watchpoint into a free slot. Returns false if none are available.
bool InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type); bool InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type);
@ -479,7 +485,7 @@ public:
public: public:
Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
bool is_hbl); KProcessAddress aslr_space_start, bool is_hbl);
void LoadModule(CodeSet code_set, KProcessAddress base_addr); void LoadModule(CodeSet code_set, KProcessAddress base_addr);

View File

@ -0,0 +1,481 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_scoped_lock.h"
#include "core/hle/kernel/svc_types.h"
namespace Core {
class ARM_Interface;
}
namespace Kernel {
class KProcessPageTable {
private:
KPageTable m_page_table;
public:
KProcessPageTable(KernelCore& kernel) : m_page_table(kernel) {}
Result Initialize(Svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge,
bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address,
size_t code_size, KSystemResource* system_resource,
KResourceLimit* resource_limit, Core::Memory::Memory& memory,
KProcessAddress aslr_space_start) {
R_RETURN(m_page_table.InitializeForProcess(
as_type, enable_aslr, enable_das_merge, from_back, pool, code_address, code_size,
system_resource, resource_limit, memory, aslr_space_start));
}
void Finalize() {
m_page_table.Finalize();
}
Core::Memory::Memory& GetMemory() {
return m_page_table.GetMemory();
}
Core::Memory::Memory& GetMemory() const {
return m_page_table.GetMemory();
}
Common::PageTable& GetImpl() {
return m_page_table.GetImpl();
}
Common::PageTable& GetImpl() const {
return m_page_table.GetImpl();
}
size_t GetNumGuardPages() const {
return m_page_table.GetNumGuardPages();
}
KScopedLightLock AcquireDeviceMapLock() {
return m_page_table.AcquireDeviceMapLock();
}
Result SetMemoryPermission(KProcessAddress addr, size_t size, Svc::MemoryPermission perm) {
R_RETURN(m_page_table.SetMemoryPermission(addr, size, perm));
}
Result SetProcessMemoryPermission(KProcessAddress addr, size_t size,
Svc::MemoryPermission perm) {
R_RETURN(m_page_table.SetProcessMemoryPermission(addr, size, perm));
}
Result SetMemoryAttribute(KProcessAddress addr, size_t size, KMemoryAttribute mask,
KMemoryAttribute attr) {
R_RETURN(m_page_table.SetMemoryAttribute(addr, size, mask, attr));
}
Result SetHeapSize(KProcessAddress* out, size_t size) {
R_RETURN(m_page_table.SetHeapSize(out, size));
}
Result SetMaxHeapSize(size_t size) {
R_RETURN(m_page_table.SetMaxHeapSize(size));
}
Result QueryInfo(KMemoryInfo* out_info, Svc::PageInfo* out_page_info,
KProcessAddress addr) const {
R_RETURN(m_page_table.QueryInfo(out_info, out_page_info, addr));
}
Result QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out, KProcessAddress address) {
R_RETURN(m_page_table.QueryPhysicalAddress(out, address));
}
Result QueryStaticMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) {
R_RETURN(m_page_table.QueryStaticMapping(out, address, size));
}
Result QueryIoMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) {
R_RETURN(m_page_table.QueryIoMapping(out, address, size));
}
Result MapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
R_RETURN(m_page_table.MapMemory(dst_address, src_address, size));
}
Result UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
R_RETURN(m_page_table.UnmapMemory(dst_address, src_address, size));
}
Result MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
R_RETURN(m_page_table.MapCodeMemory(dst_address, src_address, size));
}
Result UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
R_RETURN(m_page_table.UnmapCodeMemory(dst_address, src_address, size));
}
Result MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) {
R_RETURN(m_page_table.MapIo(phys_addr, size, perm));
}
Result MapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size,
Svc::MemoryMapping mapping, Svc::MemoryPermission perm) {
R_RETURN(m_page_table.MapIoRegion(dst_address, phys_addr, size, mapping, perm));
}
Result UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size,
Svc::MemoryMapping mapping) {
R_RETURN(m_page_table.UnmapIoRegion(dst_address, phys_addr, size, mapping));
}
Result MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) {
R_RETURN(m_page_table.MapStatic(phys_addr, size, perm));
}
Result MapRegion(KMemoryRegionType region_type, KMemoryPermission perm) {
R_RETURN(m_page_table.MapRegion(region_type, perm));
}
Result MapInsecureMemory(KProcessAddress address, size_t size) {
R_RETURN(m_page_table.MapInsecureMemory(address, size));
}
Result UnmapInsecureMemory(KProcessAddress address, size_t size) {
R_RETURN(m_page_table.UnmapInsecureMemory(address, size));
}
Result MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMemoryState state,
KMemoryPermission perm) {
R_RETURN(m_page_table.MapPageGroup(addr, pg, state, perm));
}
Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state) {
R_RETURN(m_page_table.UnmapPageGroup(address, pg, state));
}
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) {
R_RETURN(m_page_table.MapPages(out_addr, num_pages, alignment, phys_addr, state, perm));
}
Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state,
KMemoryPermission perm) {
R_RETURN(m_page_table.MapPages(out_addr, num_pages, state, perm));
}
Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
KMemoryPermission perm) {
R_RETURN(m_page_table.MapPages(address, num_pages, state, perm));
}
Result UnmapPages(KProcessAddress addr, size_t num_pages, KMemoryState state) {
R_RETURN(m_page_table.UnmapPages(addr, num_pages, state));
}
Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr) {
R_RETURN(m_page_table.MakeAndOpenPageGroup(out, address, num_pages, state_mask, state,
perm_mask, perm, attr_mask, attr));
}
Result InvalidateProcessDataCache(KProcessAddress address, size_t size) {
R_RETURN(m_page_table.InvalidateProcessDataCache(address, size));
}
Result ReadDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
R_RETURN(m_page_table.ReadDebugMemory(dst_address, src_address, size));
}
Result ReadDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size,
KMemoryState state) {
R_RETURN(m_page_table.ReadDebugIoMemory(dst_address, src_address, size, state));
}
Result WriteDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
R_RETURN(m_page_table.WriteDebugMemory(dst_address, src_address, size));
}
Result WriteDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size,
KMemoryState state) {
R_RETURN(m_page_table.WriteDebugIoMemory(dst_address, src_address, size, state));
}
Result LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address, size_t size,
KMemoryPermission perm, bool is_aligned, bool check_heap) {
R_RETURN(m_page_table.LockForMapDeviceAddressSpace(out_is_io, address, size, perm,
is_aligned, check_heap));
}
Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size, bool check_heap) {
R_RETURN(m_page_table.LockForUnmapDeviceAddressSpace(address, size, check_heap));
}
Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size) {
R_RETURN(m_page_table.UnlockForDeviceAddressSpace(address, size));
}
Result UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size) {
R_RETURN(m_page_table.UnlockForDeviceAddressSpacePartialMap(address, size));
}
Result OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange* out,
KProcessAddress address, size_t size,
KMemoryPermission perm, bool is_aligned) {
R_RETURN(m_page_table.OpenMemoryRangeForMapDeviceAddressSpace(out, address, size, perm,
is_aligned));
}
Result OpenMemoryRangeForUnmapDeviceAddressSpace(KPageTableBase::MemoryRange* out,
KProcessAddress address, size_t size) {
R_RETURN(m_page_table.OpenMemoryRangeForUnmapDeviceAddressSpace(out, address, size));
}
Result LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address, size_t size) {
R_RETURN(m_page_table.LockForIpcUserBuffer(out, address, size));
}
Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size) {
R_RETURN(m_page_table.UnlockForIpcUserBuffer(address, size));
}
Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size,
KMemoryPermission perm) {
R_RETURN(m_page_table.LockForTransferMemory(out, address, size, perm));
}
Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg) {
R_RETURN(m_page_table.UnlockForTransferMemory(address, size, pg));
}
Result LockForCodeMemory(KPageGroup* out, KProcessAddress address, size_t size) {
R_RETURN(m_page_table.LockForCodeMemory(out, address, size));
}
Result UnlockForCodeMemory(KProcessAddress address, size_t size, const KPageGroup& pg) {
R_RETURN(m_page_table.UnlockForCodeMemory(address, size, pg));
}
Result OpenMemoryRangeForProcessCacheOperation(KPageTableBase::MemoryRange* out,
KProcessAddress address, size_t size) {
R_RETURN(m_page_table.OpenMemoryRangeForProcessCacheOperation(out, address, size));
}
Result CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size,
KProcessAddress src_addr, KMemoryState src_state_mask,
KMemoryState src_state, KMemoryPermission src_test_perm,
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) {
R_RETURN(m_page_table.CopyMemoryFromLinearToUser(dst_addr, size, src_addr, src_state_mask,
src_state, src_test_perm, src_attr_mask,
src_attr));
}
Result CopyMemoryFromLinearToKernel(void* dst_addr, size_t size, KProcessAddress src_addr,
KMemoryState src_state_mask, KMemoryState src_state,
KMemoryPermission src_test_perm,
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) {
R_RETURN(m_page_table.CopyMemoryFromLinearToKernel(dst_addr, size, src_addr, src_state_mask,
src_state, src_test_perm, src_attr_mask,
src_attr));
}
Result CopyMemoryFromUserToLinear(KProcessAddress dst_addr, size_t size,
KMemoryState dst_state_mask, KMemoryState dst_state,
KMemoryPermission dst_test_perm,
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
KProcessAddress src_addr) {
R_RETURN(m_page_table.CopyMemoryFromUserToLinear(dst_addr, size, dst_state_mask, dst_state,
dst_test_perm, dst_attr_mask, dst_attr,
src_addr));
}
Result CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, size_t size,
KMemoryState dst_state_mask, KMemoryState dst_state,
KMemoryPermission dst_test_perm,
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
void* src_addr) {
R_RETURN(m_page_table.CopyMemoryFromKernelToLinear(dst_addr, size, dst_state_mask,
dst_state, dst_test_perm, dst_attr_mask,
dst_attr, src_addr));
}
Result CopyMemoryFromHeapToHeap(KProcessPageTable& dst_page_table, KProcessAddress dst_addr,
size_t size, KMemoryState dst_state_mask,
KMemoryState dst_state, KMemoryPermission dst_test_perm,
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
KProcessAddress src_addr, KMemoryState src_state_mask,
KMemoryState src_state, KMemoryPermission src_test_perm,
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) {
R_RETURN(m_page_table.CopyMemoryFromHeapToHeap(
dst_page_table.m_page_table, dst_addr, size, dst_state_mask, dst_state, dst_test_perm,
dst_attr_mask, dst_attr, src_addr, src_state_mask, src_state, src_test_perm,
src_attr_mask, src_attr));
}
Result CopyMemoryFromHeapToHeapWithoutCheckDestination(
KProcessPageTable& dst_page_table, KProcessAddress dst_addr, size_t size,
KMemoryState dst_state_mask, KMemoryState dst_state, KMemoryPermission dst_test_perm,
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, KProcessAddress src_addr,
KMemoryState src_state_mask, KMemoryState src_state, KMemoryPermission src_test_perm,
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) {
R_RETURN(m_page_table.CopyMemoryFromHeapToHeapWithoutCheckDestination(
dst_page_table.m_page_table, dst_addr, size, dst_state_mask, dst_state, dst_test_perm,
dst_attr_mask, dst_attr, src_addr, src_state_mask, src_state, src_test_perm,
src_attr_mask, src_attr));
}
Result SetupForIpc(KProcessAddress* out_dst_addr, size_t size, KProcessAddress src_addr,
KProcessPageTable& src_page_table, KMemoryPermission test_perm,
KMemoryState dst_state, bool send) {
R_RETURN(m_page_table.SetupForIpc(out_dst_addr, size, src_addr, src_page_table.m_page_table,
test_perm, dst_state, send));
}
Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state) {
R_RETURN(m_page_table.CleanupForIpcServer(address, size, dst_state));
}
Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state) {
R_RETURN(m_page_table.CleanupForIpcClient(address, size, dst_state));
}
Result MapPhysicalMemory(KProcessAddress address, size_t size) {
R_RETURN(m_page_table.MapPhysicalMemory(address, size));
}
Result UnmapPhysicalMemory(KProcessAddress address, size_t size) {
R_RETURN(m_page_table.UnmapPhysicalMemory(address, size));
}
Result MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size) {
R_RETURN(m_page_table.MapPhysicalMemoryUnsafe(address, size));
}
Result UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size) {
R_RETURN(m_page_table.UnmapPhysicalMemoryUnsafe(address, size));
}
Result UnmapProcessMemory(KProcessAddress dst_address, size_t size,
KProcessPageTable& src_page_table, KProcessAddress src_address) {
R_RETURN(m_page_table.UnmapProcessMemory(dst_address, size, src_page_table.m_page_table,
src_address));
}
bool GetPhysicalAddress(KPhysicalAddress* out, KProcessAddress address) {
return m_page_table.GetPhysicalAddress(out, address);
}
bool Contains(KProcessAddress addr, size_t size) const {
return m_page_table.Contains(addr, size);
}
bool IsInAliasRegion(KProcessAddress addr, size_t size) const {
return m_page_table.IsInAliasRegion(addr, size);
}
bool IsInHeapRegion(KProcessAddress addr, size_t size) const {
return m_page_table.IsInHeapRegion(addr, size);
}
bool IsInUnsafeAliasRegion(KProcessAddress addr, size_t size) const {
return m_page_table.IsInUnsafeAliasRegion(addr, size);
}
bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const {
return m_page_table.CanContain(addr, size, state);
}
KProcessAddress GetAddressSpaceStart() const {
return m_page_table.GetAddressSpaceStart();
}
KProcessAddress GetHeapRegionStart() const {
return m_page_table.GetHeapRegionStart();
}
KProcessAddress GetAliasRegionStart() const {
return m_page_table.GetAliasRegionStart();
}
KProcessAddress GetStackRegionStart() const {
return m_page_table.GetStackRegionStart();
}
KProcessAddress GetKernelMapRegionStart() const {
return m_page_table.GetKernelMapRegionStart();
}
KProcessAddress GetCodeRegionStart() const {
return m_page_table.GetCodeRegionStart();
}
KProcessAddress GetAliasCodeRegionStart() const {
return m_page_table.GetAliasCodeRegionStart();
}
size_t GetAddressSpaceSize() const {
return m_page_table.GetAddressSpaceSize();
}
size_t GetHeapRegionSize() const {
return m_page_table.GetHeapRegionSize();
}
size_t GetAliasRegionSize() const {
return m_page_table.GetAliasRegionSize();
}
size_t GetStackRegionSize() const {
return m_page_table.GetStackRegionSize();
}
size_t GetKernelMapRegionSize() const {
return m_page_table.GetKernelMapRegionSize();
}
size_t GetCodeRegionSize() const {
return m_page_table.GetCodeRegionSize();
}
size_t GetAliasCodeRegionSize() const {
return m_page_table.GetAliasCodeRegionSize();
}
size_t GetNormalMemorySize() const {
return m_page_table.GetNormalMemorySize();
}
size_t GetCodeSize() const {
return m_page_table.GetCodeSize();
}
size_t GetCodeDataSize() const {
return m_page_table.GetCodeDataSize();
}
size_t GetAliasCodeSize() const {
return m_page_table.GetAliasCodeSize();
}
size_t GetAliasCodeDataSize() const {
return m_page_table.GetAliasCodeDataSize();
}
u32 GetAllocateOption() const {
return m_page_table.GetAllocateOption();
}
u32 GetAddressSpaceWidth() const {
return m_page_table.GetAddressSpaceWidth();
}
KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress address) {
return m_page_table.GetHeapPhysicalAddress(address);
}
u8* GetHeapVirtualPointer(KPhysicalAddress address) {
return m_page_table.GetHeapVirtualPointer(address);
}
KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress address) {
return m_page_table.GetHeapVirtualAddress(address);
}
KBlockInfoManager* GetBlockInfoManager() {
return m_page_table.GetBlockInfoManager();
}
KPageTable& GetBasePageTable() {
return m_page_table;
}
const KPageTable& GetBasePageTable() const {
return m_page_table;
}
};
} // namespace Kernel

View File

@ -383,7 +383,7 @@ Result KServerSession::SendReply(bool is_hle) {
if (event != nullptr) { if (event != nullptr) {
// // Get the client process/page table. // // Get the client process/page table.
// KProcess *client_process = client_thread->GetOwnerProcess(); // KProcess *client_process = client_thread->GetOwnerProcess();
// KPageTable *client_page_table = std::addressof(client_process->PageTable()); // KProcessPageTable *client_page_table = std::addressof(client_process->PageTable());
// // If we need to, reply with an async error. // // If we need to, reply with an async error.
// if (R_FAILED(client_result)) { // if (R_FAILED(client_result)) {

View File

@ -40,7 +40,7 @@ Result KSecureSystemResource::Initialize(size_t size, KResourceLimit* resource_l
// Get resource pointer. // Get resource pointer.
KPhysicalAddress resource_paddr = KPhysicalAddress resource_paddr =
KPageTable::GetHeapPhysicalAddress(m_kernel.MemoryLayout(), m_resource_address); KPageTable::GetHeapPhysicalAddress(m_kernel, m_resource_address);
auto* resource = auto* resource =
m_kernel.System().DeviceMemory().GetPointer<KPageTableManager::RefCount>(resource_paddr); m_kernel.System().DeviceMemory().GetPointer<KPageTableManager::RefCount>(resource_paddr);

Some files were not shown because too many files have changed in this diff Show More