Compare commits
115 Commits
android-12
...
android-13
Author | SHA1 | Date | |
---|---|---|---|
b5d0c6a58e | |||
67bb87db29 | |||
0543726586 | |||
12fba361bd | |||
6bcde572dd | |||
20a17607ae | |||
473caaff5b | |||
787552f832 | |||
5f945e2fcd | |||
c08da2d6ad | |||
4458920799 | |||
61fed8a3a6 | |||
efb3165e3d | |||
a493ba76b4 | |||
ae60a5657e | |||
feb60de5c3 | |||
c67644f1da | |||
9343b81afd | |||
71f53b4218 | |||
f131b0faeb | |||
6c64d5aff2 | |||
de594995da | |||
4c16a1a26f | |||
862e66202c | |||
2136a46ab7 | |||
b9c7e5c2c8 | |||
d86e88a622 | |||
7eac28e410 | |||
ea4c92f734 | |||
c9cd938dfd | |||
4c5e3d5f7a | |||
e2be180136 | |||
c9437e5244 | |||
24548b1f5c | |||
be0ecae108 | |||
fcd54c6479 | |||
08296f151e | |||
a134e924ff | |||
31ed6bae11 | |||
9a5ef835cc | |||
df0d3698ae | |||
51fc608f68 | |||
b30e19ba24 | |||
ec6b67d862 | |||
4d0b7f8496 | |||
e3b510a4b4 | |||
247d66a680 | |||
0047d8a01e | |||
efc0187537 | |||
b6fe8a0b3f | |||
ecaa038b4d | |||
4aac971864 | |||
6c93cdffb1 | |||
470714e2d1 | |||
6b888b0fa8 | |||
1a1393dad7 | |||
55412962c0 | |||
d920da2631 | |||
ff72bf2cb2 | |||
4efb9763d9 | |||
c600bc8652 | |||
f1806d237f | |||
ae57a99d7d | |||
767c4b5a99 | |||
904d03b01f | |||
9f1c9599a2 | |||
5f6666a7cd | |||
1906e2724f | |||
0c032d3f2f | |||
a6735cba5f | |||
3b872b89d1 | |||
40d4e9543b | |||
e588f341ed | |||
875246f5b2 | |||
b16fefa106 | |||
2a255b2d61 | |||
9e331f9957 | |||
9169cbf728 | |||
2f9487cd38 | |||
1d03a0fa75 | |||
09f993899e | |||
c9038af29e | |||
f3053920bf | |||
c7b31d24b9 | |||
edce713fc9 | |||
8d0d0e1c7a | |||
4b8b223db2 | |||
728aca7703 | |||
a872030a35 | |||
79e7d7f4ba | |||
7f62a48ab5 | |||
b5415b6872 | |||
b76a1d987f | |||
ae2130470e | |||
ac6290bea7 | |||
4051bbbed7 | |||
2a7edda70a | |||
59b6ada7b7 | |||
9908434c14 | |||
668a10f9b9 | |||
fc4b45ebd3 | |||
1afe6d51ee | |||
1ae0f0f3f6 | |||
de0b35b974 | |||
ae88d01d8d | |||
d759de9f96 | |||
89d3e81be8 | |||
71f264c498 | |||
26417da5d3 | |||
b3b458edf9 | |||
74961d4dfb | |||
9ffa1801c7 | |||
4d4fe69223 | |||
0a75519ab5 | |||
3062a35eb1 |
@ -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
3
.gitmodules
vendored
@ -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
|
||||||
|
10
README.md
10
README.md
@ -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
|
||||||
|
4
externals/CMakeLists.txt
vendored
4
externals/CMakeLists.txt
vendored
@ -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
1
externals/oaknut
vendored
Submodule
Submodule externals/oaknut added at 316d8869e8
@ -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())
|
||||||
|
@ -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,28 +96,34 @@ class GameAdapter(private val activity: AppCompatActivity) :
|
|||||||
data = Uri.parse(holder.game.path)
|
data = Uri.parse(holder.game.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
val layerDrawable = ResourcesCompat.getDrawable(
|
activity.lifecycleScope.launch {
|
||||||
YuzuApplication.appContext.resources,
|
withContext(Dispatchers.IO) {
|
||||||
R.drawable.shortcut,
|
val layerDrawable = ResourcesCompat.getDrawable(
|
||||||
null
|
YuzuApplication.appContext.resources,
|
||||||
) as LayerDrawable
|
R.drawable.shortcut,
|
||||||
layerDrawable.setDrawableByLayerId(
|
null
|
||||||
R.id.shortcut_foreground,
|
) as LayerDrawable
|
||||||
GameIconUtils.getGameIcon(holder.game).toDrawable(YuzuApplication.appContext.resources)
|
layerDrawable.setDrawableByLayerId(
|
||||||
)
|
R.id.shortcut_foreground,
|
||||||
val inset = YuzuApplication.appContext.resources
|
GameIconUtils.getGameIcon(activity, holder.game)
|
||||||
.getDimensionPixelSize(R.dimen.icon_inset)
|
.toDrawable(YuzuApplication.appContext.resources)
|
||||||
layerDrawable.setLayerInset(1, inset, inset, inset, inset)
|
|
||||||
val shortcut = ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path)
|
|
||||||
.setShortLabel(holder.game.title)
|
|
||||||
.setIcon(
|
|
||||||
IconCompat.createWithAdaptiveBitmap(
|
|
||||||
layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
|
|
||||||
)
|
)
|
||||||
)
|
val inset = YuzuApplication.appContext.resources
|
||||||
.setIntent(openIntent)
|
.getDimensionPixelSize(R.dimen.icon_inset)
|
||||||
.build()
|
layerDrawable.setLayerInset(1, inset, inset, inset, inset)
|
||||||
ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
|
val shortcut =
|
||||||
|
ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path)
|
||||||
|
.setShortLabel(holder.game.title)
|
||||||
|
.setIcon(
|
||||||
|
IconCompat.createWithAdaptiveBitmap(
|
||||||
|
layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.setIntent(openIntent)
|
||||||
|
.build()
|
||||||
|
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)
|
||||||
|
@ -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),
|
||||||
|
@ -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),
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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))
|
||||||
|
@ -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(
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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.
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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);
|
||||||
|
@ -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:
|
||||||
|
@ -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,
|
||||||
|
9
src/android/app/src/main/res/drawable/ic_audio.xml
Normal file
9
src/android/app/src/main/res/drawable/ic_audio.xml
Normal 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>
|
9
src/android/app/src/main/res/drawable/ic_code.xml
Normal file
9
src/android/app/src/main/res/drawable/ic_code.xml
Normal 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>
|
9
src/android/app/src/main/res/drawable/ic_graphics.xml
Normal file
9
src/android/app/src/main/res/drawable/ic_graphics.xml
Normal 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>
|
@ -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>
|
233
src/android/app/src/main/res/layout-w600dp/fragment_about.xml
Normal file
233
src/android/app/src/main/res/layout-w600dp/fragment_about.xml
Normal 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>
|
@ -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"
|
||||||
|
@ -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"
|
||||||
|
@ -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>
|
||||||
|
@ -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" />
|
||||||
|
|
||||||
|
@ -10,41 +10,59 @@
|
|||||||
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
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<ImageView
|
||||||
android:id="@+id/text_setting_name"
|
android:id="@+id/icon"
|
||||||
style="@style/TextAppearance.Material3.HeadlineMedium"
|
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
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textAlignment="viewStart"
|
android:orientation="vertical">
|
||||||
android:textSize="16sp"
|
|
||||||
app:lineHeight="22dp"
|
|
||||||
tools:text="Setting Name" />
|
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:id="@+id/text_setting_description"
|
android:id="@+id/text_setting_name"
|
||||||
style="@style/TextAppearance.Material3.BodySmall"
|
style="@style/TextAppearance.Material3.HeadlineMedium"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
android:textAlignment="viewStart"
|
||||||
android:textAlignment="viewStart"
|
android:textSize="17sp"
|
||||||
tools:text="@string/app_disclaimer" />
|
app:lineHeight="22dp"
|
||||||
|
tools:text="Setting Name" />
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:id="@+id/text_setting_value"
|
android:id="@+id/text_setting_description"
|
||||||
style="@style/TextAppearance.Material3.LabelMedium"
|
style="@style/TextAppearance.Material3.BodySmall"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
android:layout_marginTop="@dimen/spacing_small"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:textStyle="bold"
|
tools:text="@string/app_disclaimer" />
|
||||||
tools:text="1x" />
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/text_setting_value"
|
||||||
|
style="@style/TextAppearance.Material3.LabelMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/spacing_small"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="13sp"
|
||||||
|
tools:text="1x" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
@ -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" />
|
||||||
|
|
||||||
|
@ -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"
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
@ -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 &&
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
55
src/common/free_region_manager.h
Normal file
55
src/common/free_region_manager.h
Normal 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
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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`.
|
||||||
|
@ -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:
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ enum class Category : u32 {
|
|||||||
AddOns,
|
AddOns,
|
||||||
Controls,
|
Controls,
|
||||||
Ui,
|
Ui,
|
||||||
|
UiAudio,
|
||||||
UiGeneral,
|
UiGeneral,
|
||||||
UiLayout,
|
UiLayout,
|
||||||
UiGameList,
|
UiGameList,
|
||||||
|
@ -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);
|
||||||
|
@ -6,10 +6,11 @@
|
|||||||
namespace Settings {
|
namespace Settings {
|
||||||
namespace NativeButton {
|
namespace NativeButton {
|
||||||
const std::array<const char*, NumButtons> mapping = {{
|
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",
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,12 +29,15 @@ enum Values : int {
|
|||||||
DRight,
|
DRight,
|
||||||
DDown,
|
DDown,
|
||||||
|
|
||||||
SL,
|
SLLeft,
|
||||||
SR,
|
SRLeft,
|
||||||
|
|
||||||
Home,
|
Home,
|
||||||
Screenshot,
|
Screenshot,
|
||||||
|
|
||||||
|
SLRight,
|
||||||
|
SRRight,
|
||||||
|
|
||||||
NumButtons,
|
NumButtons,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
42
src/common/signal_chain.cpp
Normal file
42
src/common/signal_chain.cpp
Normal 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
19
src/common/signal_chain.h
Normal 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
|
@ -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
|
||||||
|
@ -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)) {
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
392
src/core/arm/nce/arm_nce.cpp
Normal file
392
src/core/arm/nce/arm_nce.cpp
Normal 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
108
src/core/arm/nce/arm_nce.h
Normal 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
222
src/core/arm/nce/arm_nce.s
Normal 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
|
29
src/core/arm/nce/arm_nce_asm_definitions.h
Normal file
29
src/core/arm/nce/arm_nce_asm_definitions.h
Normal 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
|
50
src/core/arm/nce/guest_context.h
Normal file
50
src/core/arm/nce/guest_context.h
Normal 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
|
147
src/core/arm/nce/instructions.h
Normal file
147
src/core/arm/nce/instructions.h
Normal 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
471
src/core/arm/nce/patch.cpp
Normal 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
101
src/core/arm/nce/patch.h
Normal 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
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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{};
|
||||||
|
@ -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();
|
||||||
|
@ -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(
|
||||||
" Alias: {:#012x} - {:#012x}\n"
|
"Layout:\n"
|
||||||
" Heap: {:#012x} - {:#012x}\n"
|
" Alias: {:#012x} - {:#012x}\n"
|
||||||
" Aslr: {:#012x} - {:#012x}\n"
|
" Heap: {:#012x} - {:#012x}\n"
|
||||||
" Stack: {:#012x} - {:#012x}\n"
|
" Aslr: {:#012x} - {:#012x}\n"
|
||||||
"Modules:\n",
|
" Stack: {:#012x} - {:#012x}\n"
|
||||||
GetInteger(page_table.GetAliasRegionStart()),
|
"Modules:\n",
|
||||||
GetInteger(page_table.GetAliasRegionEnd()),
|
GetInteger(page_table.GetAliasRegionStart()),
|
||||||
GetInteger(page_table.GetHeapRegionStart()),
|
GetInteger(page_table.GetAliasRegionStart()) + page_table.GetAliasRegionSize() - 1,
|
||||||
GetInteger(page_table.GetHeapRegionEnd()),
|
GetInteger(page_table.GetHeapRegionStart()),
|
||||||
GetInteger(page_table.GetAliasCodeRegionStart()),
|
GetInteger(page_table.GetHeapRegionStart()) + page_table.GetHeapRegionSize() - 1,
|
||||||
GetInteger(page_table.GetAliasCodeRegionEnd()),
|
GetInteger(page_table.GetAliasCodeRegionStart()),
|
||||||
GetInteger(page_table.GetStackRegionStart()),
|
GetInteger(page_table.GetAliasCodeRegionStart()) + page_table.GetAliasCodeRegionSize() -
|
||||||
GetInteger(page_table.GetStackRegionEnd()));
|
1,
|
||||||
|
GetInteger(page_table.GetStackRegionStart()),
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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{};
|
||||||
|
@ -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:
|
||||||
|
@ -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};
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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, },
|
||||||
|
@ -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();
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result KCapabilities::MapIoPage_(const u32 cap, KPageTable* page_table) {
|
Result KCapabilities::MapIoPage_(const u32 cap, KProcessPageTable* 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));
|
||||||
}
|
}
|
||||||
|
@ -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{};
|
||||||
|
@ -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 &&
|
||||||
|
@ -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:
|
||||||
|
@ -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{};
|
||||||
|
@ -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
@ -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
|
||||||
|
5746
src/core/hle/kernel/k_page_table_base.cpp
Normal file
5746
src/core/hle/kernel/k_page_table_base.cpp
Normal file
File diff suppressed because it is too large
Load Diff
760
src/core/hle/kernel/k_page_table_base.h
Normal file
760
src/core/hle/kernel/k_page_table_base.h
Normal 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
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
481
src/core/hle/kernel/k_process_page_table.h
Normal file
481
src/core/hle/kernel/k_process_page_table.h
Normal 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
|
@ -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)) {
|
||||||
|
@ -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
Reference in New Issue
Block a user