Compare commits
61 Commits
android-12
...
android-13
Author | SHA1 | Date | |
---|---|---|---|
3919622671 | |||
50108bdddb | |||
13f37654c2 | |||
d695cf1676 | |||
2136a46ab7 | |||
b9c7e5c2c8 | |||
d86e88a622 | |||
7eac28e410 | |||
ea4c92f734 | |||
c9cd938dfd | |||
4c5e3d5f7a | |||
e2be180136 | |||
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 | |||
edce713fc9 |
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -4,9 +4,6 @@
|
||||
[submodule "enet"]
|
||||
path = externals/enet
|
||||
url = https://github.com/lsalzman/enet.git
|
||||
[submodule "inih"]
|
||||
path = externals/inih/inih
|
||||
url = https://github.com/benhoyt/inih.git
|
||||
[submodule "cubeb"]
|
||||
path = externals/cubeb
|
||||
url = https://github.com/mozilla/cubeb.git
|
||||
@ -61,3 +58,6 @@
|
||||
[submodule "breakpad"]
|
||||
path = externals/breakpad
|
||||
url = https://github.com/yuzu-emu/breakpad.git
|
||||
[submodule "simpleini"]
|
||||
path = externals/simpleini
|
||||
url = https://github.com/brofield/simpleini.git
|
||||
|
@ -285,7 +285,6 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
find_package(Boost 1.79.0 REQUIRED context)
|
||||
find_package(enet 1.3 MODULE)
|
||||
find_package(fmt 9 REQUIRED)
|
||||
find_package(inih 52 MODULE COMPONENTS INIReader)
|
||||
find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle)
|
||||
find_package(lz4 REQUIRED)
|
||||
find_package(nlohmann_json 3.8 REQUIRED)
|
||||
|
@ -1,27 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2022 Alexandre Bouvier <contact@amb.tf>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_search_module(INIH QUIET IMPORTED_TARGET inih)
|
||||
if (INIReader IN_LIST inih_FIND_COMPONENTS)
|
||||
pkg_search_module(INIREADER QUIET IMPORTED_TARGET INIReader)
|
||||
if (INIREADER_FOUND)
|
||||
set(inih_INIReader_FOUND TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(inih
|
||||
REQUIRED_VARS INIH_LINK_LIBRARIES
|
||||
VERSION_VAR INIH_VERSION
|
||||
HANDLE_COMPONENTS
|
||||
)
|
||||
|
||||
if (inih_FOUND AND NOT TARGET inih::inih)
|
||||
add_library(inih::inih ALIAS PkgConfig::INIH)
|
||||
endif()
|
||||
|
||||
if (inih_FOUND AND inih_INIReader_FOUND AND NOT TARGET inih::INIReader)
|
||||
add_library(inih::INIReader ALIAS PkgConfig::INIREADER)
|
||||
endif()
|
11
README.md
11
README.md
@ -1,3 +1,14 @@
|
||||
| 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 |
|
||||
| [11889](https://github.com/yuzu-emu/yuzu//pull/11889) | [`44fa74715`](https://github.com/yuzu-emu/yuzu//pull/11889/files) | configuration: Unify config handling across frontends | [t895](https://github.com/t895/) | Yes |
|
||||
| [12036](https://github.com/yuzu-emu/yuzu//pull/12036) | [`c9437e524`](https://github.com/yuzu-emu/yuzu//pull/12036/files) | Query Cache: Disable write syncing on Android | [FernandoS27](https://github.com/FernandoS27/) | Yes |
|
||||
|
||||
|
||||
End of merge log. You can find the original README.md below the break.
|
||||
|
||||
-----
|
||||
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
8
externals/CMakeLists.txt
vendored
8
externals/CMakeLists.txt
vendored
@ -34,11 +34,6 @@ endif()
|
||||
# Glad
|
||||
add_subdirectory(glad)
|
||||
|
||||
# inih
|
||||
if (NOT TARGET inih::INIReader)
|
||||
add_subdirectory(inih)
|
||||
endif()
|
||||
|
||||
# mbedtls
|
||||
add_subdirectory(mbedtls)
|
||||
target_include_directories(mbedtls PUBLIC ./mbedtls/include)
|
||||
@ -295,3 +290,6 @@ if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client)
|
||||
target_link_libraries(dump_syms PRIVATE libbreakpad_client ZLIB::ZLIB)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# SimpleIni
|
||||
add_subdirectory(simpleini)
|
||||
|
13
externals/inih/CMakeLists.txt
vendored
13
externals/inih/CMakeLists.txt
vendored
@ -1,13 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2014 Gui Andrade <admin@archshift.com>
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
add_library(inih
|
||||
inih/ini.c
|
||||
inih/ini.h
|
||||
inih/cpp/INIReader.cpp
|
||||
inih/cpp/INIReader.h
|
||||
)
|
||||
|
||||
create_target_directory_groups(inih)
|
||||
target_include_directories(inih INTERFACE inih/cpp)
|
||||
add_library(inih::INIReader ALIAS inih)
|
1
externals/inih/inih
vendored
1
externals/inih/inih
vendored
Submodule externals/inih/inih deleted from 9cecf0643d
1
externals/simpleini
vendored
Submodule
1
externals/simpleini
vendored
Submodule
Submodule externals/simpleini added at 382ddbb4b9
@ -187,6 +187,7 @@ add_subdirectory(audio_core)
|
||||
add_subdirectory(video_core)
|
||||
add_subdirectory(network)
|
||||
add_subdirectory(input_common)
|
||||
add_subdirectory(frontend_common)
|
||||
add_subdirectory(shader_recompiler)
|
||||
|
||||
if (YUZU_ROOM)
|
||||
|
@ -219,7 +219,6 @@ dependencies {
|
||||
implementation("io.coil-kt:coil:2.2.2")
|
||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||
implementation("androidx.window:window:1.2.0-beta03")
|
||||
implementation("org.ini4j:ini4j:0.5.4")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
|
||||
|
@ -230,8 +230,6 @@ object NativeLibrary {
|
||||
*/
|
||||
external fun onTouchReleased(finger_id: Int)
|
||||
|
||||
external fun reloadSettings()
|
||||
|
||||
external fun initGameIni(gameID: String?)
|
||||
|
||||
external fun setAppDirectory(directory: String)
|
||||
|
@ -373,8 +373,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
|
||||
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
val isEmulationActive = emulationViewModel.emulationStarted.value &&
|
||||
!emulationViewModel.isEmulationStopping.value
|
||||
pictureInPictureParamsBuilder.setAutoEnterEnabled(
|
||||
BooleanSetting.PICTURE_IN_PICTURE.boolean
|
||||
BooleanSetting.PICTURE_IN_PICTURE.boolean && isEmulationActive
|
||||
)
|
||||
}
|
||||
setPictureInPictureParams(pictureInPictureParamsBuilder.build())
|
||||
|
@ -22,12 +22,16 @@ import androidx.core.graphics.drawable.toBitmap
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.AsyncDifferConfig
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
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.R
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
@ -92,28 +96,34 @@ class GameAdapter(private val activity: AppCompatActivity) :
|
||||
data = Uri.parse(holder.game.path)
|
||||
}
|
||||
|
||||
val layerDrawable = ResourcesCompat.getDrawable(
|
||||
YuzuApplication.appContext.resources,
|
||||
R.drawable.shortcut,
|
||||
null
|
||||
) as LayerDrawable
|
||||
layerDrawable.setDrawableByLayerId(
|
||||
R.id.shortcut_foreground,
|
||||
GameIconUtils.getGameIcon(holder.game).toDrawable(YuzuApplication.appContext.resources)
|
||||
)
|
||||
val inset = YuzuApplication.appContext.resources
|
||||
.getDimensionPixelSize(R.dimen.icon_inset)
|
||||
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)
|
||||
activity.lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
val layerDrawable = ResourcesCompat.getDrawable(
|
||||
YuzuApplication.appContext.resources,
|
||||
R.drawable.shortcut,
|
||||
null
|
||||
) as LayerDrawable
|
||||
layerDrawable.setDrawableByLayerId(
|
||||
R.id.shortcut_foreground,
|
||||
GameIconUtils.getGameIcon(activity, holder.game)
|
||||
.toDrawable(YuzuApplication.appContext.resources)
|
||||
)
|
||||
)
|
||||
.setIntent(openIntent)
|
||||
.build()
|
||||
ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
|
||||
val inset = YuzuApplication.appContext.resources
|
||||
.getDimensionPixelSize(R.dimen.icon_inset)
|
||||
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)
|
||||
)
|
||||
)
|
||||
.setIntent(openIntent)
|
||||
.build()
|
||||
ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
|
||||
}
|
||||
}
|
||||
|
||||
val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game)
|
||||
view.findNavController().navigate(action)
|
||||
|
@ -7,7 +7,7 @@ import android.text.TextUtils
|
||||
import android.widget.Toast
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
|
||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||
|
||||
object Settings {
|
||||
private val context get() = YuzuApplication.appContext
|
||||
@ -19,7 +19,7 @@ object Settings {
|
||||
context.getString(R.string.ini_saved),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
SettingsFile.saveFile(SettingsFile.FILE_NAME_CONFIG)
|
||||
NativeConfig.saveSettings()
|
||||
} else {
|
||||
// TODO: Save custom game settings
|
||||
Toast.makeText(
|
||||
@ -82,7 +82,6 @@ object Settings {
|
||||
|
||||
enum class MenuTag(val titleId: Int) {
|
||||
SECTION_ROOT(R.string.advanced_settings),
|
||||
SECTION_GENERAL(R.string.preferences_general),
|
||||
SECTION_SYSTEM(R.string.preferences_system),
|
||||
SECTION_RENDERER(R.string.preferences_graphics),
|
||||
SECTION_AUDIO(R.string.preferences_audio),
|
||||
|
@ -3,10 +3,13 @@
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.model.view
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
|
||||
class RunnableSetting(
|
||||
titleId: Int,
|
||||
descriptionId: Int,
|
||||
val isRuntimeRunnable: Boolean,
|
||||
@DrawableRes val iconId: Int = 0,
|
||||
val runnable: () -> Unit
|
||||
) : SettingsItem(emptySetting, titleId, descriptionId) {
|
||||
override val type = TYPE_RUNNABLE
|
||||
|
@ -3,11 +3,14 @@
|
||||
|
||||
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
|
||||
|
||||
class SubmenuSetting(
|
||||
titleId: Int,
|
||||
descriptionId: Int,
|
||||
@StringRes titleId: Int,
|
||||
@StringRes descriptionId: Int,
|
||||
@DrawableRes val iconId: Int,
|
||||
val menuKey: Settings.MenuTag
|
||||
) : SettingsItem(emptySetting, titleId, descriptionId) {
|
||||
override val type = TYPE_SUBMENU
|
||||
|
@ -21,7 +21,6 @@ import androidx.navigation.navArgs
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import java.io.IOException
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
|
||||
@ -165,11 +164,12 @@ class SettingsActivity : AppCompatActivity() {
|
||||
settingsViewModel.shouldSave = false
|
||||
|
||||
// Delete settings file because the user may have changed values that do not exist in the UI
|
||||
NativeConfig.unloadConfig()
|
||||
val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG)
|
||||
if (!settingsFile.delete()) {
|
||||
throw IOException("Failed to delete $settingsFile")
|
||||
}
|
||||
NativeLibrary.reloadSettings()
|
||||
NativeConfig.initializeConfig()
|
||||
|
||||
Toast.makeText(
|
||||
applicationContext,
|
||||
|
@ -20,7 +20,6 @@ import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.divider.MaterialDividerItemDecoration
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
@ -68,15 +67,9 @@ class SettingsFragment : Fragment() {
|
||||
)
|
||||
|
||||
binding.toolbarSettingsLayout.title = getString(args.menuTag.titleId)
|
||||
val dividerDecoration = MaterialDividerItemDecoration(
|
||||
requireContext(),
|
||||
LinearLayoutManager.VERTICAL
|
||||
)
|
||||
dividerDecoration.isLastItemDecorated = false
|
||||
binding.listSettings.apply {
|
||||
adapter = settingsAdapter
|
||||
layoutManager = LinearLayoutManager(requireContext())
|
||||
addItemDecoration(dividerDecoration)
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -112,8 +94,6 @@ class SettingsFragment : Fragment() {
|
||||
binding.toolbarSettings.setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.action_search -> {
|
||||
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
|
||||
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
|
||||
view.findNavController()
|
||||
.navigate(R.id.action_settingsFragment_to_settingsSearchFragment)
|
||||
true
|
||||
@ -129,11 +109,6 @@ class SettingsFragment : Fragment() {
|
||||
setInsets()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
settingsViewModel.setIsUsingSearch(false)
|
||||
}
|
||||
|
||||
private fun setInsets() {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
binding.root
|
||||
@ -144,10 +119,9 @@ class SettingsFragment : Fragment() {
|
||||
val leftInsets = barInsets.left + cutoutInsets.left
|
||||
val rightInsets = barInsets.right + cutoutInsets.right
|
||||
|
||||
val sideMargin = resources.getDimensionPixelSize(R.dimen.spacing_medlarge)
|
||||
val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams
|
||||
mlpSettingsList.leftMargin = sideMargin + leftInsets
|
||||
mlpSettingsList.rightMargin = sideMargin + rightInsets
|
||||
mlpSettingsList.leftMargin = leftInsets
|
||||
mlpSettingsList.rightMargin = rightInsets
|
||||
binding.listSettings.layoutParams = mlpSettingsList
|
||||
binding.listSettings.updatePadding(
|
||||
bottom = barInsets.bottom
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
import android.widget.Toast
|
||||
@ -32,8 +31,6 @@ class SettingsFragmentPresenter(
|
||||
private val preferences: SharedPreferences
|
||||
get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
||||
|
||||
private val context: Context get() = YuzuApplication.appContext
|
||||
|
||||
// Extension for populating settings list based on paired settings
|
||||
fun ArrayList<SettingsItem>.add(key: String) {
|
||||
val item = SettingsItem.settingsItems[key]!!
|
||||
@ -53,7 +50,6 @@ class SettingsFragmentPresenter(
|
||||
val sl = ArrayList<SettingsItem>()
|
||||
when (menuTag) {
|
||||
Settings.MenuTag.SECTION_ROOT -> addConfigSettings(sl)
|
||||
Settings.MenuTag.SECTION_GENERAL -> addGeneralSettings(sl)
|
||||
Settings.MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
|
||||
Settings.MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
|
||||
Settings.MenuTag.SECTION_AUDIO -> addAudioSettings(sl)
|
||||
@ -75,30 +71,53 @@ class SettingsFragmentPresenter(
|
||||
|
||||
private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
|
||||
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(
|
||||
RunnableSetting(R.string.reset_to_default, 0, false) {
|
||||
settingsViewModel.setShouldShowResetSettingsDialog(true)
|
||||
}
|
||||
SubmenuSetting(
|
||||
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>) {
|
||||
sl.apply {
|
||||
add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
|
||||
add(ShortSetting.RENDERER_SPEED_LIMIT.key)
|
||||
add(BooleanSetting.USE_DOCKED_MODE.key)
|
||||
add(IntSetting.REGION_INDEX.key)
|
||||
add(IntSetting.LANGUAGE_INDEX.key)
|
||||
@ -116,6 +135,7 @@ class SettingsFragmentPresenter(
|
||||
add(IntSetting.RENDERER_ANTI_ALIASING.key)
|
||||
add(IntSetting.RENDERER_SCREEN_LAYOUT.key)
|
||||
add(IntSetting.RENDERER_ASPECT_RATIO.key)
|
||||
add(BooleanSetting.PICTURE_IN_PICTURE.key)
|
||||
add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
|
||||
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
|
||||
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
|
||||
@ -249,6 +269,7 @@ class SettingsFragmentPresenter(
|
||||
add(BooleanSetting.RENDERER_DEBUG.key)
|
||||
|
||||
add(HeaderSetting(R.string.cpu))
|
||||
add(IntSetting.CPU_ACCURACY.key)
|
||||
add(BooleanSetting.CPU_DEBUG_MODE.key)
|
||||
add(SettingsItem.FASTMEM_COMBINED)
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
|
||||
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) {
|
||||
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)
|
||||
if (item.descriptionId != 0) {
|
||||
binding.textSettingDescription.setText(item.descriptionId)
|
||||
|
@ -4,6 +4,7 @@
|
||||
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
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.SubmenuSetting
|
||||
@ -15,6 +16,19 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd
|
||||
|
||||
override fun bind(item: SettingsItem) {
|
||||
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)
|
||||
if (item.descriptionId != 0) {
|
||||
binding.textSettingDescription.setText(item.descriptionId)
|
||||
|
@ -3,15 +3,8 @@
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.utils
|
||||
|
||||
import android.widget.Toast
|
||||
import java.io.*
|
||||
import org.ini4j.Wini
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import org.yuzu.yuzu_emu.features.settings.model.*
|
||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
||||
import org.yuzu.yuzu_emu.utils.Log
|
||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||
|
||||
/**
|
||||
* Contains static methods for interacting with .ini files in which settings are stored.
|
||||
@ -19,41 +12,6 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||
object SettingsFile {
|
||||
const val FILE_NAME_CONFIG = "config"
|
||||
|
||||
/**
|
||||
* Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error
|
||||
* telling why it failed.
|
||||
*
|
||||
* @param fileName The target filename without a path or extension.
|
||||
*/
|
||||
fun saveFile(fileName: String) {
|
||||
val ini = getSettingsFile(fileName)
|
||||
try {
|
||||
val wini = Wini(ini)
|
||||
for (specificCategory in Settings.Category.values()) {
|
||||
val categoryHeader = NativeConfig.getConfigHeader(specificCategory.ordinal)
|
||||
for (setting in Settings.settingsList) {
|
||||
if (setting.key!!.isEmpty()) continue
|
||||
|
||||
val settingCategoryHeader =
|
||||
NativeConfig.getConfigHeader(setting.category.ordinal)
|
||||
val iniSetting: String? = wini.get(categoryHeader, setting.key)
|
||||
if (iniSetting != null || settingCategoryHeader == categoryHeader) {
|
||||
wini.put(settingCategoryHeader, setting.key, setting.valueAsString)
|
||||
}
|
||||
}
|
||||
}
|
||||
wini.store()
|
||||
} catch (e: IOException) {
|
||||
Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.message)
|
||||
val context = YuzuApplication.appContext
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.error_saving, fileName, e.message),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
|
||||
fun getSettingsFile(fileName: String): File =
|
||||
File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini")
|
||||
}
|
||||
|
@ -114,10 +114,10 @@ class AboutFragment : Fragment() {
|
||||
val leftInsets = barInsets.left + cutoutInsets.left
|
||||
val rightInsets = barInsets.right + cutoutInsets.right
|
||||
|
||||
val mlpAppBar = binding.appbarAbout.layoutParams as MarginLayoutParams
|
||||
mlpAppBar.leftMargin = leftInsets
|
||||
mlpAppBar.rightMargin = rightInsets
|
||||
binding.appbarAbout.layoutParams = mlpAppBar
|
||||
val mlpToolbar = binding.toolbarAbout.layoutParams as MarginLayoutParams
|
||||
mlpToolbar.leftMargin = leftInsets
|
||||
mlpToolbar.rightMargin = rightInsets
|
||||
binding.toolbarAbout.layoutParams = mlpToolbar
|
||||
|
||||
val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams
|
||||
mlpScrollAbout.leftMargin = leftInsets
|
||||
|
@ -40,8 +40,10 @@ class SettingsSearchFragment : Fragment() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
|
||||
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
|
||||
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
@ -55,7 +57,6 @@ class SettingsSearchFragment : Fragment() {
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
settingsViewModel.setIsUsingSearch(true)
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT))
|
||||
|
@ -29,9 +29,6 @@ class SettingsViewModel : ViewModel() {
|
||||
val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList
|
||||
private val _shouldReloadSettingsList = MutableStateFlow(false)
|
||||
|
||||
val isUsingSearch: StateFlow<Boolean> get() = _isUsingSearch
|
||||
private val _isUsingSearch = MutableStateFlow(false)
|
||||
|
||||
val sliderProgress: StateFlow<Int> get() = _sliderProgress
|
||||
private val _sliderProgress = MutableStateFlow(-1)
|
||||
|
||||
@ -57,10 +54,6 @@ class SettingsViewModel : ViewModel() {
|
||||
_shouldReloadSettingsList.value = value
|
||||
}
|
||||
|
||||
fun setIsUsingSearch(value: Boolean) {
|
||||
_isUsingSearch.value = value
|
||||
}
|
||||
|
||||
fun setSliderTextValue(value: Float, units: String) {
|
||||
_sliderProgress.value = value.toInt()
|
||||
_sliderTextValue.value = String.format(
|
||||
|
@ -632,6 +632,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
}
|
||||
|
||||
// Clear existing user data
|
||||
NativeConfig.unloadConfig()
|
||||
File(DirectoryInitialization.userDirectory!!).deleteRecursively()
|
||||
|
||||
// Copy archive to internal storage
|
||||
@ -650,6 +651,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
|
||||
// Reinitialize relevant data
|
||||
NativeLibrary.initializeSystem(true)
|
||||
NativeConfig.initializeConfig()
|
||||
gamesViewModel.reloadGames(false)
|
||||
|
||||
return@newInstance getString(R.string.user_data_import_success)
|
||||
|
@ -16,6 +16,7 @@ object DirectoryInitialization {
|
||||
if (!areDirectoriesReady) {
|
||||
initializeInternalStorage()
|
||||
NativeLibrary.initializeSystem(false)
|
||||
NativeConfig.initializeConfig()
|
||||
areDirectoriesReady = true
|
||||
}
|
||||
}
|
||||
|
@ -8,9 +8,9 @@ import android.graphics.BitmapFactory
|
||||
import android.widget.ImageView
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import coil.ImageLoader
|
||||
import coil.decode.DataSource
|
||||
import coil.executeBlocking
|
||||
import coil.fetch.DrawableResult
|
||||
import coil.fetch.FetchResult
|
||||
import coil.fetch.Fetcher
|
||||
@ -76,12 +76,13 @@ object GameIconUtils {
|
||||
imageLoader.enqueue(request)
|
||||
}
|
||||
|
||||
fun getGameIcon(game: Game): Bitmap {
|
||||
suspend fun getGameIcon(lifecycleOwner: LifecycleOwner, game: Game): Bitmap {
|
||||
val request = ImageRequest.Builder(YuzuApplication.appContext)
|
||||
.data(game)
|
||||
.lifecycle(lifecycleOwner)
|
||||
.error(R.drawable.default_icon)
|
||||
.build()
|
||||
return imageLoader.executeBlocking(request)
|
||||
return imageLoader.execute(request)
|
||||
.drawable!!.toBitmap(config = Bitmap.Config.ARGB_8888)
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,30 @@
|
||||
package org.yuzu.yuzu_emu.utils
|
||||
|
||||
object NativeConfig {
|
||||
/**
|
||||
* Creates a Config object and opens the emulation config.
|
||||
*/
|
||||
@Synchronized
|
||||
external fun initializeConfig()
|
||||
|
||||
/**
|
||||
* Destroys the stored config object. This automatically saves the existing config.
|
||||
*/
|
||||
@Synchronized
|
||||
external fun unloadConfig()
|
||||
|
||||
/**
|
||||
* Reads values saved to the config file and saves them.
|
||||
*/
|
||||
@Synchronized
|
||||
external fun reloadSettings()
|
||||
|
||||
/**
|
||||
* Saves settings values in memory to disk.
|
||||
*/
|
||||
@Synchronized
|
||||
external fun saveSettings()
|
||||
|
||||
external fun getBoolean(key: String, getDefault: Boolean): Boolean
|
||||
external fun setBoolean(key: String, value: Boolean)
|
||||
|
||||
|
@ -6,9 +6,6 @@ add_library(yuzu-android SHARED
|
||||
android_common/android_common.h
|
||||
applets/software_keyboard.cpp
|
||||
applets/software_keyboard.h
|
||||
config.cpp
|
||||
config.h
|
||||
default_ini.h
|
||||
emu_window/emu_window.cpp
|
||||
emu_window/emu_window.h
|
||||
id_cache.cpp
|
||||
@ -16,15 +13,17 @@ add_library(yuzu-android SHARED
|
||||
native.cpp
|
||||
native.h
|
||||
native_config.cpp
|
||||
uisettings.cpp
|
||||
android_settings.cpp
|
||||
game_metadata.cpp
|
||||
native_log.cpp
|
||||
android_config.cpp
|
||||
android_config.h
|
||||
)
|
||||
|
||||
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
|
||||
|
||||
target_link_libraries(yuzu-android PRIVATE audio_core common core input_common)
|
||||
target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad inih jnigraphics log)
|
||||
target_link_libraries(yuzu-android PRIVATE audio_core common core input_common frontend_common)
|
||||
target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log)
|
||||
if (ARCHITECTURE_arm64)
|
||||
target_link_libraries(yuzu-android PRIVATE adrenotools)
|
||||
endif()
|
||||
|
70
src/android/app/src/main/jni/android_config.cpp
Normal file
70
src/android/app/src/main/jni/android_config.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "android_config.h"
|
||||
#include "android_settings.h"
|
||||
#include "common/settings_setting.h"
|
||||
|
||||
AndroidConfig::AndroidConfig(const std::string& config_name, ConfigType config_type)
|
||||
: Config(config_type) {
|
||||
Initialize(config_name);
|
||||
if (config_type != ConfigType::InputProfile) {
|
||||
ReadAndroidValues();
|
||||
SaveAndroidValues();
|
||||
}
|
||||
}
|
||||
|
||||
AndroidConfig::~AndroidConfig() {
|
||||
if (global) {
|
||||
AndroidConfig::SaveAllValues();
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidConfig::ReloadAllValues() {
|
||||
Reload();
|
||||
ReadAndroidValues();
|
||||
SaveAndroidValues();
|
||||
}
|
||||
|
||||
void AndroidConfig::SaveAllValues() {
|
||||
Save();
|
||||
SaveAndroidValues();
|
||||
}
|
||||
|
||||
void AndroidConfig::ReadAndroidValues() {
|
||||
if (global) {
|
||||
ReadAndroidUIValues();
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidConfig::ReadAndroidUIValues() {
|
||||
BeginGroup(Settings::TranslateCategory(Settings::Category::Android));
|
||||
|
||||
ReadCategory(Settings::Category::Android);
|
||||
|
||||
EndGroup();
|
||||
}
|
||||
|
||||
void AndroidConfig::SaveAndroidValues() {
|
||||
if (global) {
|
||||
SaveAndroidUIValues();
|
||||
}
|
||||
|
||||
WriteToIni();
|
||||
}
|
||||
|
||||
void AndroidConfig::SaveAndroidUIValues() {
|
||||
BeginGroup(Settings::TranslateCategory(Settings::Category::Android));
|
||||
|
||||
WriteCategory(Settings::Category::Android);
|
||||
|
||||
EndGroup();
|
||||
}
|
||||
|
||||
std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) {
|
||||
auto& map = Settings::values.linkage.by_category;
|
||||
if (map.contains(category)) {
|
||||
return Settings::values.linkage.by_category[category];
|
||||
}
|
||||
return AndroidSettings::values.linkage.by_category[category];
|
||||
}
|
41
src/android/app/src/main/jni/android_config.h
Normal file
41
src/android/app/src/main/jni/android_config.h
Normal file
@ -0,0 +1,41 @@
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "frontend_common/config.h"
|
||||
|
||||
class AndroidConfig final : public Config {
|
||||
public:
|
||||
explicit AndroidConfig(const std::string& config_name = "config",
|
||||
ConfigType config_type = ConfigType::GlobalConfig);
|
||||
~AndroidConfig() override;
|
||||
|
||||
void ReloadAllValues() override;
|
||||
void SaveAllValues() override;
|
||||
|
||||
protected:
|
||||
void ReadAndroidValues();
|
||||
void ReadAndroidUIValues();
|
||||
void ReadHidbusValues() override {}
|
||||
void ReadDebugControlValues() override {}
|
||||
void ReadPathValues() override {}
|
||||
void ReadShortcutValues() override {}
|
||||
void ReadUIValues() override {}
|
||||
void ReadUIGamelistValues() override {}
|
||||
void ReadUILayoutValues() override {}
|
||||
void ReadMultiplayerValues() override {}
|
||||
|
||||
void SaveAndroidValues();
|
||||
void SaveAndroidUIValues();
|
||||
void SaveHidbusValues() override {}
|
||||
void SaveDebugControlValues() override {}
|
||||
void SavePathValues() override {}
|
||||
void SaveShortcutValues() override {}
|
||||
void SaveUIValues() override {}
|
||||
void SaveUIGamelistValues() override {}
|
||||
void SaveUILayoutValues() override {}
|
||||
void SaveMultiplayerValues() override {}
|
||||
|
||||
std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) override;
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "uisettings.h"
|
||||
#include "android_settings.h"
|
||||
|
||||
namespace AndroidSettings {
|
||||
|
@ -13,7 +13,7 @@ struct Values {
|
||||
Settings::Linkage linkage;
|
||||
|
||||
// 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::Setting<s32> screen_layout{linkage,
|
||||
5,
|
@ -1,330 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
|
||||
#include <INIReader.h>
|
||||
#include "common/fs/file.h"
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "input_common/main.h"
|
||||
#include "jni/config.h"
|
||||
#include "jni/default_ini.h"
|
||||
#include "uisettings.h"
|
||||
|
||||
namespace FS = Common::FS;
|
||||
|
||||
Config::Config(const std::string& config_name, ConfigType config_type)
|
||||
: type(config_type), global{config_type == ConfigType::GlobalConfig} {
|
||||
Initialize(config_name);
|
||||
}
|
||||
|
||||
Config::~Config() = default;
|
||||
|
||||
bool Config::LoadINI(const std::string& default_contents, bool retry) {
|
||||
void(FS::CreateParentDir(config_loc));
|
||||
config = std::make_unique<INIReader>(FS::PathToUTF8String(config_loc));
|
||||
const auto config_loc_str = FS::PathToUTF8String(config_loc);
|
||||
if (config->ParseError() < 0) {
|
||||
if (retry) {
|
||||
LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...",
|
||||
config_loc_str);
|
||||
|
||||
void(FS::CreateParentDir(config_loc));
|
||||
void(FS::WriteStringToFile(config_loc, FS::FileType::TextFile, default_contents));
|
||||
|
||||
config = std::make_unique<INIReader>(config_loc_str);
|
||||
|
||||
return LoadINI(default_contents, false);
|
||||
}
|
||||
LOG_ERROR(Config, "Failed.");
|
||||
return false;
|
||||
}
|
||||
LOG_INFO(Config, "Successfully loaded {}", config_loc_str);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) {
|
||||
std::string setting_value = config->Get(group, setting.GetLabel(), setting.GetDefault());
|
||||
if (setting_value.empty()) {
|
||||
setting_value = setting.GetDefault();
|
||||
}
|
||||
setting = std::move(setting_value);
|
||||
}
|
||||
|
||||
template <>
|
||||
void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) {
|
||||
setting = config->GetBoolean(group, setting.GetLabel(), setting.GetDefault());
|
||||
}
|
||||
|
||||
template <typename Type, bool ranged>
|
||||
void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) {
|
||||
setting = static_cast<Type>(
|
||||
config->GetInteger(group, setting.GetLabel(), static_cast<long>(setting.GetDefault())));
|
||||
}
|
||||
|
||||
void Config::ReadValues() {
|
||||
ReadSetting("ControlsGeneral", Settings::values.mouse_enabled);
|
||||
ReadSetting("ControlsGeneral", Settings::values.touch_device);
|
||||
ReadSetting("ControlsGeneral", Settings::values.keyboard_enabled);
|
||||
ReadSetting("ControlsGeneral", Settings::values.debug_pad_enabled);
|
||||
ReadSetting("ControlsGeneral", Settings::values.vibration_enabled);
|
||||
ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations);
|
||||
ReadSetting("ControlsGeneral", Settings::values.motion_enabled);
|
||||
Settings::values.touchscreen.enabled =
|
||||
config->GetBoolean("ControlsGeneral", "touch_enabled", true);
|
||||
Settings::values.touchscreen.rotation_angle =
|
||||
config->GetInteger("ControlsGeneral", "touch_angle", 0);
|
||||
Settings::values.touchscreen.diameter_x =
|
||||
config->GetInteger("ControlsGeneral", "touch_diameter_x", 15);
|
||||
Settings::values.touchscreen.diameter_y =
|
||||
config->GetInteger("ControlsGeneral", "touch_diameter_y", 15);
|
||||
|
||||
int num_touch_from_button_maps =
|
||||
config->GetInteger("ControlsGeneral", "touch_from_button_map", 0);
|
||||
if (num_touch_from_button_maps > 0) {
|
||||
for (int i = 0; i < num_touch_from_button_maps; ++i) {
|
||||
Settings::TouchFromButtonMap map;
|
||||
map.name = config->Get("ControlsGeneral",
|
||||
std::string("touch_from_button_maps_") + std::to_string(i) +
|
||||
std::string("_name"),
|
||||
"default");
|
||||
const int num_touch_maps = config->GetInteger(
|
||||
"ControlsGeneral",
|
||||
std::string("touch_from_button_maps_") + std::to_string(i) + std::string("_count"),
|
||||
0);
|
||||
map.buttons.reserve(num_touch_maps);
|
||||
|
||||
for (int j = 0; j < num_touch_maps; ++j) {
|
||||
std::string touch_mapping =
|
||||
config->Get("ControlsGeneral",
|
||||
std::string("touch_from_button_maps_") + std::to_string(i) +
|
||||
std::string("_bind_") + std::to_string(j),
|
||||
"");
|
||||
map.buttons.emplace_back(std::move(touch_mapping));
|
||||
}
|
||||
|
||||
Settings::values.touch_from_button_maps.emplace_back(std::move(map));
|
||||
}
|
||||
} else {
|
||||
Settings::values.touch_from_button_maps.emplace_back(
|
||||
Settings::TouchFromButtonMap{"default", {}});
|
||||
num_touch_from_button_maps = 1;
|
||||
}
|
||||
Settings::values.touch_from_button_map_index = std::clamp(
|
||||
Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
|
||||
|
||||
ReadSetting("ControlsGeneral", Settings::values.udp_input_servers);
|
||||
|
||||
// Data Storage
|
||||
ReadSetting("Data Storage", Settings::values.use_virtual_sd);
|
||||
FS::SetYuzuPath(FS::YuzuPath::NANDDir,
|
||||
config->Get("Data Storage", "nand_directory",
|
||||
FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
|
||||
FS::SetYuzuPath(FS::YuzuPath::SDMCDir,
|
||||
config->Get("Data Storage", "sdmc_directory",
|
||||
FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
|
||||
FS::SetYuzuPath(FS::YuzuPath::LoadDir,
|
||||
config->Get("Data Storage", "load_directory",
|
||||
FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
|
||||
FS::SetYuzuPath(FS::YuzuPath::DumpDir,
|
||||
config->Get("Data Storage", "dump_directory",
|
||||
FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
|
||||
ReadSetting("Data Storage", Settings::values.gamecard_inserted);
|
||||
ReadSetting("Data Storage", Settings::values.gamecard_current_game);
|
||||
ReadSetting("Data Storage", Settings::values.gamecard_path);
|
||||
|
||||
// System
|
||||
ReadSetting("System", Settings::values.current_user);
|
||||
Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0,
|
||||
Service::Account::MAX_USERS - 1);
|
||||
|
||||
// Disable docked mode by default on Android
|
||||
Settings::values.use_docked_mode.SetValue(config->GetBoolean("System", "use_docked_mode", false)
|
||||
? Settings::ConsoleMode::Docked
|
||||
: Settings::ConsoleMode::Handheld);
|
||||
|
||||
const auto rng_seed_enabled = config->GetBoolean("System", "rng_seed_enabled", false);
|
||||
if (rng_seed_enabled) {
|
||||
Settings::values.rng_seed.SetValue(config->GetInteger("System", "rng_seed", 0));
|
||||
} else {
|
||||
Settings::values.rng_seed.SetValue(0);
|
||||
}
|
||||
Settings::values.rng_seed_enabled.SetValue(rng_seed_enabled);
|
||||
|
||||
const auto custom_rtc_enabled = config->GetBoolean("System", "custom_rtc_enabled", false);
|
||||
if (custom_rtc_enabled) {
|
||||
Settings::values.custom_rtc = config->GetInteger("System", "custom_rtc", 0);
|
||||
} else {
|
||||
Settings::values.custom_rtc = 0;
|
||||
}
|
||||
Settings::values.custom_rtc_enabled = custom_rtc_enabled;
|
||||
|
||||
ReadSetting("System", Settings::values.language_index);
|
||||
ReadSetting("System", Settings::values.region_index);
|
||||
ReadSetting("System", Settings::values.time_zone_index);
|
||||
ReadSetting("System", Settings::values.sound_index);
|
||||
|
||||
// Core
|
||||
ReadSetting("Core", Settings::values.use_multi_core);
|
||||
ReadSetting("Core", Settings::values.memory_layout_mode);
|
||||
|
||||
// Cpu
|
||||
ReadSetting("Cpu", Settings::values.cpu_accuracy);
|
||||
ReadSetting("Cpu", Settings::values.cpu_debug_mode);
|
||||
ReadSetting("Cpu", Settings::values.cpuopt_page_tables);
|
||||
ReadSetting("Cpu", Settings::values.cpuopt_block_linking);
|
||||
ReadSetting("Cpu", Settings::values.cpuopt_return_stack_buffer);
|
||||
ReadSetting("Cpu", Settings::values.cpuopt_fast_dispatcher);
|
||||
ReadSetting("Cpu", Settings::values.cpuopt_context_elimination);
|
||||
ReadSetting("Cpu", Settings::values.cpuopt_const_prop);
|
||||
ReadSetting("Cpu", Settings::values.cpuopt_misc_ir);
|
||||
ReadSetting("Cpu", Settings::values.cpuopt_reduce_misalign_checks);
|
||||
ReadSetting("Cpu", Settings::values.cpuopt_fastmem);
|
||||
ReadSetting("Cpu", Settings::values.cpuopt_fastmem_exclusives);
|
||||
ReadSetting("Cpu", Settings::values.cpuopt_recompile_exclusives);
|
||||
ReadSetting("Cpu", Settings::values.cpuopt_ignore_memory_aborts);
|
||||
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma);
|
||||
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error);
|
||||
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr);
|
||||
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_inaccurate_nan);
|
||||
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_fastmem_check);
|
||||
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_global_monitor);
|
||||
|
||||
// Renderer
|
||||
ReadSetting("Renderer", Settings::values.renderer_backend);
|
||||
ReadSetting("Renderer", Settings::values.renderer_debug);
|
||||
ReadSetting("Renderer", Settings::values.renderer_shader_feedback);
|
||||
ReadSetting("Renderer", Settings::values.enable_nsight_aftermath);
|
||||
ReadSetting("Renderer", Settings::values.disable_shader_loop_safety_checks);
|
||||
ReadSetting("Renderer", Settings::values.vulkan_device);
|
||||
|
||||
ReadSetting("Renderer", Settings::values.resolution_setup);
|
||||
ReadSetting("Renderer", Settings::values.scaling_filter);
|
||||
ReadSetting("Renderer", Settings::values.fsr_sharpening_slider);
|
||||
ReadSetting("Renderer", Settings::values.anti_aliasing);
|
||||
ReadSetting("Renderer", Settings::values.fullscreen_mode);
|
||||
ReadSetting("Renderer", Settings::values.aspect_ratio);
|
||||
ReadSetting("Renderer", Settings::values.max_anisotropy);
|
||||
ReadSetting("Renderer", Settings::values.use_speed_limit);
|
||||
ReadSetting("Renderer", Settings::values.speed_limit);
|
||||
ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
|
||||
ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation);
|
||||
ReadSetting("Renderer", Settings::values.vsync_mode);
|
||||
ReadSetting("Renderer", Settings::values.shader_backend);
|
||||
ReadSetting("Renderer", Settings::values.use_asynchronous_shaders);
|
||||
ReadSetting("Renderer", Settings::values.nvdec_emulation);
|
||||
ReadSetting("Renderer", Settings::values.use_fast_gpu_time);
|
||||
ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache);
|
||||
|
||||
ReadSetting("Renderer", Settings::values.bg_red);
|
||||
ReadSetting("Renderer", Settings::values.bg_green);
|
||||
ReadSetting("Renderer", Settings::values.bg_blue);
|
||||
|
||||
// Use GPU accuracy normal by default on Android
|
||||
Settings::values.gpu_accuracy = static_cast<Settings::GpuAccuracy>(config->GetInteger(
|
||||
"Renderer", "gpu_accuracy", static_cast<u32>(Settings::GpuAccuracy::Normal)));
|
||||
|
||||
// Use GPU default anisotropic filtering on Android
|
||||
Settings::values.max_anisotropy =
|
||||
static_cast<Settings::AnisotropyMode>(config->GetInteger("Renderer", "max_anisotropy", 1));
|
||||
|
||||
// Disable ASTC compute by default on Android
|
||||
Settings::values.accelerate_astc.SetValue(
|
||||
config->GetBoolean("Renderer", "accelerate_astc", false) ? Settings::AstcDecodeMode::Gpu
|
||||
: Settings::AstcDecodeMode::Cpu);
|
||||
|
||||
// Enable asynchronous presentation by default on Android
|
||||
Settings::values.async_presentation =
|
||||
config->GetBoolean("Renderer", "async_presentation", true);
|
||||
|
||||
// Disable force_max_clock by default on Android
|
||||
Settings::values.renderer_force_max_clock =
|
||||
config->GetBoolean("Renderer", "force_max_clock", false);
|
||||
|
||||
// Disable use_reactive_flushing by default on Android
|
||||
Settings::values.use_reactive_flushing =
|
||||
config->GetBoolean("Renderer", "use_reactive_flushing", false);
|
||||
|
||||
// Audio
|
||||
ReadSetting("Audio", Settings::values.sink_id);
|
||||
ReadSetting("Audio", Settings::values.audio_output_device_id);
|
||||
ReadSetting("Audio", Settings::values.volume);
|
||||
|
||||
// Miscellaneous
|
||||
// log_filter has a different default here than from common
|
||||
Settings::values.log_filter = "*:Info";
|
||||
ReadSetting("Miscellaneous", Settings::values.use_dev_keys);
|
||||
|
||||
// Debugging
|
||||
Settings::values.record_frame_times =
|
||||
config->GetBoolean("Debugging", "record_frame_times", false);
|
||||
ReadSetting("Debugging", Settings::values.dump_exefs);
|
||||
ReadSetting("Debugging", Settings::values.dump_nso);
|
||||
ReadSetting("Debugging", Settings::values.enable_fs_access_log);
|
||||
ReadSetting("Debugging", Settings::values.reporting_services);
|
||||
ReadSetting("Debugging", Settings::values.quest_flag);
|
||||
ReadSetting("Debugging", Settings::values.use_debug_asserts);
|
||||
ReadSetting("Debugging", Settings::values.use_auto_stub);
|
||||
ReadSetting("Debugging", Settings::values.disable_macro_jit);
|
||||
ReadSetting("Debugging", Settings::values.disable_macro_hle);
|
||||
ReadSetting("Debugging", Settings::values.use_gdbstub);
|
||||
ReadSetting("Debugging", Settings::values.gdbstub_port);
|
||||
|
||||
const auto title_list = config->Get("AddOns", "title_ids", "");
|
||||
std::stringstream ss(title_list);
|
||||
std::string line;
|
||||
while (std::getline(ss, line, '|')) {
|
||||
const auto title_id = std::strtoul(line.c_str(), nullptr, 16);
|
||||
const auto disabled_list = config->Get("AddOns", "disabled_" + line, "");
|
||||
|
||||
std::stringstream inner_ss(disabled_list);
|
||||
std::string inner_line;
|
||||
std::vector<std::string> out;
|
||||
while (std::getline(inner_ss, inner_line, '|')) {
|
||||
out.push_back(inner_line);
|
||||
}
|
||||
|
||||
Settings::values.disabled_addons.insert_or_assign(title_id, out);
|
||||
}
|
||||
|
||||
// Web Service
|
||||
ReadSetting("WebService", Settings::values.enable_telemetry);
|
||||
ReadSetting("WebService", Settings::values.web_api_url);
|
||||
ReadSetting("WebService", Settings::values.yuzu_username);
|
||||
ReadSetting("WebService", Settings::values.yuzu_token);
|
||||
|
||||
// Network
|
||||
ReadSetting("Network", Settings::values.network_interface);
|
||||
|
||||
// Android
|
||||
ReadSetting("Android", AndroidSettings::values.picture_in_picture);
|
||||
ReadSetting("Android", AndroidSettings::values.screen_layout);
|
||||
}
|
||||
|
||||
void Config::Initialize(const std::string& config_name) {
|
||||
const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir);
|
||||
const auto config_file = fmt::format("{}.ini", config_name);
|
||||
|
||||
switch (type) {
|
||||
case ConfigType::GlobalConfig:
|
||||
config_loc = FS::PathToUTF8String(fs_config_loc / config_file);
|
||||
break;
|
||||
case ConfigType::PerGameConfig:
|
||||
config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file));
|
||||
break;
|
||||
case ConfigType::InputProfile:
|
||||
config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file);
|
||||
LoadINI(DefaultINI::android_config_file);
|
||||
return;
|
||||
}
|
||||
LoadINI(DefaultINI::android_config_file);
|
||||
ReadValues();
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "common/settings.h"
|
||||
|
||||
class INIReader;
|
||||
|
||||
class Config {
|
||||
bool LoadINI(const std::string& default_contents = "", bool retry = true);
|
||||
|
||||
public:
|
||||
enum class ConfigType {
|
||||
GlobalConfig,
|
||||
PerGameConfig,
|
||||
InputProfile,
|
||||
};
|
||||
|
||||
explicit Config(const std::string& config_name = "config",
|
||||
ConfigType config_type = ConfigType::GlobalConfig);
|
||||
~Config();
|
||||
|
||||
void Initialize(const std::string& config_name);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Applies a value read from the config to a Setting.
|
||||
*
|
||||
* @param group The name of the INI group
|
||||
* @param setting The yuzu setting to modify
|
||||
*/
|
||||
template <typename Type, bool ranged>
|
||||
void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting);
|
||||
|
||||
void ReadValues();
|
||||
|
||||
const ConfigType type;
|
||||
std::unique_ptr<INIReader> config;
|
||||
std::string config_loc;
|
||||
const bool global;
|
||||
};
|
@ -1,511 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace DefaultINI {
|
||||
|
||||
const char* android_config_file = R"(
|
||||
|
||||
[ControlsP0]
|
||||
# The input devices and parameters for each Switch native input
|
||||
# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ...
|
||||
# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..."
|
||||
# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
|
||||
|
||||
# Indicates if this player should be connected at boot
|
||||
connected=
|
||||
|
||||
# for button input, the following devices are available:
|
||||
# - "keyboard" (default) for keyboard input. Required parameters:
|
||||
# - "code": the code of the key to bind
|
||||
# - "sdl" for joystick input using SDL. Required parameters:
|
||||
# - "guid": SDL identification GUID of the joystick
|
||||
# - "port": the index of the joystick to bind
|
||||
# - "button"(optional): the index of the button to bind
|
||||
# - "hat"(optional): the index of the hat to bind as direction buttons
|
||||
# - "axis"(optional): the index of the axis to bind
|
||||
# - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right"
|
||||
# - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
|
||||
# triggered if the axis value crosses
|
||||
# - "direction"(only used for axis): "+" means the button is triggered when the axis value
|
||||
# is greater than the threshold; "-" means the button is triggered when the axis value
|
||||
# is smaller than the threshold
|
||||
button_a=
|
||||
button_b=
|
||||
button_x=
|
||||
button_y=
|
||||
button_lstick=
|
||||
button_rstick=
|
||||
button_l=
|
||||
button_r=
|
||||
button_zl=
|
||||
button_zr=
|
||||
button_plus=
|
||||
button_minus=
|
||||
button_dleft=
|
||||
button_dup=
|
||||
button_dright=
|
||||
button_ddown=
|
||||
button_lstick_left=
|
||||
button_lstick_up=
|
||||
button_lstick_right=
|
||||
button_lstick_down=
|
||||
button_sl=
|
||||
button_sr=
|
||||
button_home=
|
||||
button_screenshot=
|
||||
|
||||
# for analog input, the following devices are available:
|
||||
# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters:
|
||||
# - "up", "down", "left", "right": sub-devices for each direction.
|
||||
# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00"
|
||||
# - "modifier": sub-devices as a modifier.
|
||||
# - "modifier_scale": a float number representing the applied modifier scale to the analog input.
|
||||
# Must be in range of 0.0-1.0. Defaults to 0.5
|
||||
# - "sdl" for joystick input using SDL. Required parameters:
|
||||
# - "guid": SDL identification GUID of the joystick
|
||||
# - "port": the index of the joystick to bind
|
||||
# - "axis_x": the index of the axis to bind as x-axis (default to 0)
|
||||
# - "axis_y": the index of the axis to bind as y-axis (default to 1)
|
||||
lstick=
|
||||
rstick=
|
||||
|
||||
# for motion input, the following devices are available:
|
||||
# - "keyboard" (default) for emulating random motion input from buttons. Required parameters:
|
||||
# - "code": the code of the key to bind
|
||||
# - "sdl" for motion input using SDL. Required parameters:
|
||||
# - "guid": SDL identification GUID of the joystick
|
||||
# - "port": the index of the joystick to bind
|
||||
# - "motion": the index of the motion sensor to bind
|
||||
# - "cemuhookudp" for motion input using Cemu Hook protocol. Required parameters:
|
||||
# - "guid": the IP address of the cemu hook server encoded to a hex string. for example 192.168.0.1 = "c0a80001"
|
||||
# - "port": the port of the cemu hook server
|
||||
# - "pad": the index of the joystick
|
||||
# - "motion": the index of the motion sensor of the joystick to bind
|
||||
motionleft=
|
||||
motionright=
|
||||
|
||||
[ControlsGeneral]
|
||||
# To use the debug_pad, prepend `debug_pad_` before each button setting above.
|
||||
# i.e. debug_pad_button_a=
|
||||
|
||||
# Enable debug pad inputs to the guest
|
||||
# 0 (default): Disabled, 1: Enabled
|
||||
debug_pad_enabled =
|
||||
|
||||
# Whether to enable or disable vibration
|
||||
# 0: Disabled, 1 (default): Enabled
|
||||
vibration_enabled=
|
||||
|
||||
# Whether to enable or disable accurate vibrations
|
||||
# 0 (default): Disabled, 1: Enabled
|
||||
enable_accurate_vibrations=
|
||||
|
||||
# Enables controller motion inputs
|
||||
# 0: Disabled, 1 (default): Enabled
|
||||
motion_enabled =
|
||||
|
||||
# Defines the udp device's touch screen coordinate system for cemuhookudp devices
|
||||
# - "min_x", "min_y", "max_x", "max_y"
|
||||
touch_device=
|
||||
|
||||
# for mapping buttons to touch inputs.
|
||||
#touch_from_button_map=1
|
||||
#touch_from_button_maps_0_name=default
|
||||
#touch_from_button_maps_0_count=2
|
||||
#touch_from_button_maps_0_bind_0=foo
|
||||
#touch_from_button_maps_0_bind_1=bar
|
||||
# etc.
|
||||
|
||||
# List of Cemuhook UDP servers, delimited by ','.
|
||||
# Default: 127.0.0.1:26760
|
||||
# Example: 127.0.0.1:26760,123.4.5.67:26761
|
||||
udp_input_servers =
|
||||
|
||||
# Enable controlling an axis via a mouse input.
|
||||
# 0 (default): Off, 1: On
|
||||
mouse_panning =
|
||||
|
||||
# Set mouse sensitivity.
|
||||
# Default: 1.0
|
||||
mouse_panning_sensitivity =
|
||||
|
||||
# Emulate an analog control stick from keyboard inputs.
|
||||
# 0 (default): Disabled, 1: Enabled
|
||||
emulate_analog_keyboard =
|
||||
|
||||
# Enable mouse inputs to the guest
|
||||
# 0 (default): Disabled, 1: Enabled
|
||||
mouse_enabled =
|
||||
|
||||
# Enable keyboard inputs to the guest
|
||||
# 0 (default): Disabled, 1: Enabled
|
||||
keyboard_enabled =
|
||||
|
||||
[Core]
|
||||
# Whether to use multi-core for CPU emulation
|
||||
# 0: Disabled, 1 (default): Enabled
|
||||
use_multi_core =
|
||||
|
||||
# Enable unsafe extended guest system memory layout (8GB DRAM)
|
||||
# 0 (default): Disabled, 1: Enabled
|
||||
use_unsafe_extended_memory_layout =
|
||||
|
||||
[Cpu]
|
||||
# Adjusts various optimizations.
|
||||
# Auto-select mode enables choice unsafe optimizations.
|
||||
# Accurate enables only safe optimizations.
|
||||
# Unsafe allows any unsafe optimizations.
|
||||
# 0 (default): Auto-select, 1: Accurate, 2: Enable unsafe optimizations
|
||||
cpu_accuracy =
|
||||
|
||||
# Allow disabling safe optimizations.
|
||||
# 0 (default): Disabled, 1: Enabled
|
||||
cpu_debug_mode =
|
||||
|
||||
# Enable inline page tables optimization (faster guest memory access)
|
||||
# 0: Disabled, 1 (default): Enabled
|
||||
cpuopt_page_tables =
|
||||
|
||||
# Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps)
|
||||
# 0: Disabled, 1 (default): Enabled
|
||||
cpuopt_block_linking =
|
||||
|
||||
# Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns)
|
||||
# 0: Disabled, 1 (default): Enabled
|
||||
cpuopt_return_stack_buffer =
|
||||
|
||||
# Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture)
|
||||
# 0: Disabled, 1 (default): Enabled
|
||||
cpuopt_fast_dispatcher =
|
||||
|
||||
# Enable context elimination CPU Optimization (reduce host memory use for guest context)
|
||||
# 0: Disabled, 1 (default): Enabled
|
||||
cpuopt_context_elimination =
|
||||
|
||||
# Enable constant propagation CPU optimization (basic IR optimization)
|
||||
# 0: Disabled, 1 (default): Enabled
|
||||
cpuopt_const_prop =
|
||||
|
||||
# Enable miscellaneous CPU optimizations (basic IR optimization)
|
||||
# 0: Disabled, 1 (default): Enabled
|
||||
cpuopt_misc_ir =
|
||||
|
||||
# Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access)
|
||||
# 0: Disabled, 1 (default): Enabled
|
||||
cpuopt_reduce_misalign_checks =
|
||||
|
||||
# Enable Host MMU Emulation (faster guest memory access)
|
||||
# 0: Disabled, 1 (default): Enabled
|
||||
cpuopt_fastmem =
|
||||
|
||||
# Enable Host MMU Emulation for exclusive memory instructions (faster guest memory access)
|
||||
# 0: Disabled, 1 (default): Enabled
|
||||
cpuopt_fastmem_exclusives =
|
||||
|
||||
# Enable fallback on failure of fastmem of exclusive memory instructions (faster guest memory access)
|
||||
# 0: Disabled, 1 (default): Enabled
|
||||
cpuopt_recompile_exclusives =
|
||||
|
||||
# Enable optimization to ignore invalid memory accesses (faster guest memory access)
|
||||
# 0: Disabled, 1 (default): Enabled
|
||||
cpuopt_ignore_memory_aborts =
|
||||
|
||||
# Enable unfuse FMA (improve performance on CPUs without FMA)
|
||||
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
|
||||
# 0: Disabled, 1 (default): Enabled
|
||||
cpuopt_unsafe_unfuse_fma =
|
||||
|
||||
# Enable faster FRSQRTE and FRECPE
|
||||
# Only enabled if cpu_accuracy is set to Unsafe.
|
||||
# 0: Disabled, 1 (default): Enabled
|
||||
cpuopt_unsafe_reduce_fp_error =
|
||||
|
||||
# Enable faster ASIMD instructions (32 bits only)
|
||||
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
|
||||
# 0: Disabled, 1 (default): Enabled
|
||||
cpuopt_unsafe_ignore_standard_fpcr =
|
||||
|
||||
# Enable inaccurate NaN handling
|
||||
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
|
||||
# 0: Disabled, 1 (default): Enabled
|
||||
cpuopt_unsafe_inaccurate_nan =
|
||||
|
||||
# Disable address space checks (64 bits only)
|
||||
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
|
||||
# 0: Disabled, 1 (default): Enabled
|
||||
cpuopt_unsafe_fastmem_check =
|
||||
|
||||
# Enable faster exclusive instructions
|
||||
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
|
||||
# 0: Disabled, 1 (default): Enabled
|
||||
cpuopt_unsafe_ignore_global_monitor =
|
||||
|
||||
[Renderer]
|
||||
# Which backend API to use.
|
||||
# 0: OpenGL (unsupported), 1 (default): Vulkan, 2: Null
|
||||
backend =
|
||||
|
||||
# Whether to enable asynchronous presentation (Vulkan only)
|
||||
# 0: Off, 1 (default): On
|
||||
async_presentation =
|
||||
|
||||
# Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).
|
||||
# 0 (default): Disabled, 1: Enabled
|
||||
force_max_clock =
|
||||
|
||||
# Enable graphics API debugging mode.
|
||||
# 0 (default): Disabled, 1: Enabled
|
||||
debug =
|
||||
|
||||
# Enable shader feedback.
|
||||
# 0 (default): Disabled, 1: Enabled
|
||||
renderer_shader_feedback =
|
||||
|
||||
# Enable Nsight Aftermath crash dumps
|
||||
# 0 (default): Disabled, 1: Enabled
|
||||
nsight_aftermath =
|
||||
|
||||
# Disable shader loop safety checks, executing the shader without loop logic changes
|
||||
# 0 (default): Disabled, 1: Enabled
|
||||
disable_shader_loop_safety_checks =
|
||||
|
||||
# Which Vulkan physical device to use (defaults to 0)
|
||||
vulkan_device =
|
||||
|
||||
# 0: 0.5x (360p/540p) [EXPERIMENTAL]
|
||||
# 1: 0.75x (540p/810p) [EXPERIMENTAL]
|
||||
# 2 (default): 1x (720p/1080p)
|
||||
# 3: 2x (1440p/2160p)
|
||||
# 4: 3x (2160p/3240p)
|
||||
# 5: 4x (2880p/4320p)
|
||||
# 6: 5x (3600p/5400p)
|
||||
# 7: 6x (4320p/6480p)
|
||||
resolution_setup =
|
||||
|
||||
# Pixel filter to use when up- or down-sampling rendered frames.
|
||||
# 0: Nearest Neighbor
|
||||
# 1 (default): Bilinear
|
||||
# 2: Bicubic
|
||||
# 3: Gaussian
|
||||
# 4: ScaleForce
|
||||
# 5: AMD FidelityFX™️ Super Resolution [Vulkan Only]
|
||||
scaling_filter =
|
||||
|
||||
# Anti-Aliasing (AA)
|
||||
# 0 (default): None, 1: FXAA
|
||||
anti_aliasing =
|
||||
|
||||
# Whether to use fullscreen or borderless window mode
|
||||
# 0 (Windows default): Borderless window, 1 (All other default): Exclusive fullscreen
|
||||
fullscreen_mode =
|
||||
|
||||
# Aspect ratio
|
||||
# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Force 16:10, 4: Stretch to Window
|
||||
aspect_ratio =
|
||||
|
||||
# Anisotropic filtering
|
||||
# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x
|
||||
max_anisotropy =
|
||||
|
||||
# Whether to enable VSync or not.
|
||||
# OpenGL: Values other than 0 enable VSync
|
||||
# Vulkan: FIFO is selected if the requested mode is not supported by the driver.
|
||||
# FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate.
|
||||
# FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down.
|
||||
# Mailbox can have lower latency than FIFO and does not tear but may drop frames.
|
||||
# Immediate (no synchronization) just presents whatever is available and can exhibit tearing.
|
||||
# 0: Immediate (Off), 1 (Default): Mailbox (On), 2: FIFO, 3: FIFO Relaxed
|
||||
use_vsync =
|
||||
|
||||
# Selects the OpenGL shader backend. NV_gpu_program5 is required for GLASM. If NV_gpu_program5 is
|
||||
# not available and GLASM is selected, GLSL will be used.
|
||||
# 0: GLSL, 1 (default): GLASM, 2: SPIR-V
|
||||
shader_backend =
|
||||
|
||||
# Whether to allow asynchronous shader building.
|
||||
# 0 (default): Off, 1: On
|
||||
use_asynchronous_shaders =
|
||||
|
||||
# Uses reactive flushing instead of predictive flushing. Allowing a more accurate syncing of memory.
|
||||
# 0 (default): Off, 1: On
|
||||
use_reactive_flushing =
|
||||
|
||||
# NVDEC emulation.
|
||||
# 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding
|
||||
nvdec_emulation =
|
||||
|
||||
# Accelerate ASTC texture decoding.
|
||||
# 0 (default): Off, 1: On
|
||||
accelerate_astc =
|
||||
|
||||
# Turns on the speed limiter, which will limit the emulation speed to the desired speed limit value
|
||||
# 0: Off, 1: On (default)
|
||||
use_speed_limit =
|
||||
|
||||
# Limits the speed of the game to run no faster than this value as a percentage of target speed
|
||||
# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default)
|
||||
speed_limit =
|
||||
|
||||
# Whether to use disk based shader cache
|
||||
# 0: Off, 1 (default): On
|
||||
use_disk_shader_cache =
|
||||
|
||||
# Which gpu accuracy level to use
|
||||
# 0 (default): Normal, 1: High, 2: Extreme (Very slow)
|
||||
gpu_accuracy =
|
||||
|
||||
# Whether to use asynchronous GPU emulation
|
||||
# 0 : Off (slow), 1 (default): On (fast)
|
||||
use_asynchronous_gpu_emulation =
|
||||
|
||||
# Inform the guest that GPU operations completed more quickly than they did.
|
||||
# 0: Off, 1 (default): On
|
||||
use_fast_gpu_time =
|
||||
|
||||
# Force unmodified buffers to be flushed, which can cost performance.
|
||||
# 0: Off (default), 1: On
|
||||
use_pessimistic_flushes =
|
||||
|
||||
# Whether to use garbage collection or not for GPU caches.
|
||||
# 0 (default): Off, 1: On
|
||||
use_caches_gc =
|
||||
|
||||
# The clear color for the renderer. What shows up on the sides of the bottom screen.
|
||||
# Must be in range of 0-255. Defaults to 0 for all.
|
||||
bg_red =
|
||||
bg_blue =
|
||||
bg_green =
|
||||
|
||||
[Audio]
|
||||
# Which audio output engine to use.
|
||||
# auto (default): Auto-select
|
||||
# cubeb: Cubeb audio engine (if available)
|
||||
# sdl2: SDL2 audio engine (if available)
|
||||
# null: No audio output
|
||||
output_engine =
|
||||
|
||||
# Which audio device to use.
|
||||
# auto (default): Auto-select
|
||||
output_device =
|
||||
|
||||
# Output volume.
|
||||
# 100 (default): 100%, 0; mute
|
||||
volume =
|
||||
|
||||
[Data Storage]
|
||||
# Whether to create a virtual SD card.
|
||||
# 1: Yes, 0 (default): No
|
||||
use_virtual_sd =
|
||||
|
||||
# Whether or not to enable gamecard emulation
|
||||
# 1: Yes, 0 (default): No
|
||||
gamecard_inserted =
|
||||
|
||||
# Whether or not the gamecard should be emulated as the current game
|
||||
# If 'gamecard_inserted' is 0 this setting is irrelevant
|
||||
# 1: Yes, 0 (default): No
|
||||
gamecard_current_game =
|
||||
|
||||
# Path to an XCI file to use as the gamecard
|
||||
# If 'gamecard_inserted' is 0 this setting is irrelevant
|
||||
# If 'gamecard_current_game' is 1 this setting is irrelevant
|
||||
gamecard_path =
|
||||
|
||||
[System]
|
||||
# Whether the system is docked
|
||||
# 1 (default): Yes, 0: No
|
||||
use_docked_mode =
|
||||
|
||||
# Sets the seed for the RNG generator built into the switch
|
||||
# rng_seed will be ignored and randomly generated if rng_seed_enabled is false
|
||||
rng_seed_enabled =
|
||||
rng_seed =
|
||||
|
||||
# Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service
|
||||
# This will auto-increment, with the time set being the time the game is started
|
||||
# This override will only occur if custom_rtc_enabled is true, otherwise the current time is used
|
||||
custom_rtc_enabled =
|
||||
custom_rtc =
|
||||
|
||||
# Sets the systems language index
|
||||
# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese,
|
||||
# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French,
|
||||
# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese, 17: Brazilian Portuguese
|
||||
language_index =
|
||||
|
||||
# The system region that yuzu will use during emulation
|
||||
# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
|
||||
region_index =
|
||||
|
||||
# The system time zone that yuzu will use during emulation
|
||||
# 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone
|
||||
time_zone_index =
|
||||
|
||||
# Sets the sound output mode.
|
||||
# 0: Mono, 1 (default): Stereo, 2: Surround
|
||||
sound_index =
|
||||
|
||||
[Miscellaneous]
|
||||
# A filter which removes logs below a certain logging level.
|
||||
# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
|
||||
log_filter = *:Trace
|
||||
|
||||
# Use developer keys
|
||||
# 0 (default): Disabled, 1: Enabled
|
||||
use_dev_keys =
|
||||
|
||||
[Debugging]
|
||||
# Record frame time data, can be found in the log directory. Boolean value
|
||||
record_frame_times =
|
||||
# Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them
|
||||
dump_exefs=false
|
||||
# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
|
||||
dump_nso=false
|
||||
# Determines whether or not yuzu will save the filesystem access log.
|
||||
enable_fs_access_log=false
|
||||
# Enables verbose reporting services
|
||||
reporting_services =
|
||||
# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode
|
||||
# false: Retail/Normal Mode (default), true: Kiosk Mode
|
||||
quest_flag =
|
||||
# Determines whether debug asserts should be enabled, which will throw an exception on asserts.
|
||||
# false: Disabled (default), true: Enabled
|
||||
use_debug_asserts =
|
||||
# Determines whether unimplemented HLE service calls should be automatically stubbed.
|
||||
# false: Disabled (default), true: Enabled
|
||||
use_auto_stub =
|
||||
# Enables/Disables the macro JIT compiler
|
||||
disable_macro_jit=false
|
||||
# Determines whether to enable the GDB stub and wait for the debugger to attach before running.
|
||||
# false: Disabled (default), true: Enabled
|
||||
use_gdbstub=false
|
||||
# The port to use for the GDB server, if it is enabled.
|
||||
gdbstub_port=6543
|
||||
|
||||
[WebService]
|
||||
# Whether or not to enable telemetry
|
||||
# 0: No, 1 (default): Yes
|
||||
enable_telemetry =
|
||||
# URL for Web API
|
||||
web_api_url = https://api.yuzu-emu.org
|
||||
# Username and token for yuzu Web Service
|
||||
# See https://profile.yuzu-emu.org/ for more info
|
||||
yuzu_username =
|
||||
yuzu_token =
|
||||
|
||||
[Network]
|
||||
# Name of the network interface device to use with yuzu LAN play.
|
||||
# e.g. On *nix: 'enp7s0', 'wlp6s0u1u3u3', 'lo'
|
||||
# e.g. On Windows: 'Ethernet', 'Wi-Fi'
|
||||
network_interface =
|
||||
|
||||
[AddOns]
|
||||
# Used to disable add-ons
|
||||
# List of title IDs of games that will have add-ons disabled (separated by '|'):
|
||||
title_ids =
|
||||
# For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|')
|
||||
# e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey
|
||||
)";
|
||||
} // namespace DefaultINI
|
@ -9,6 +9,7 @@
|
||||
#include "input_common/drivers/virtual_gamepad.h"
|
||||
#include "input_common/main.h"
|
||||
#include "jni/emu_window/emu_window.h"
|
||||
#include "jni/native.h"
|
||||
|
||||
void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
|
||||
m_window_width = ANativeWindow_getWidth(surface);
|
||||
@ -57,6 +58,13 @@ void EmuWindow_Android::OnRemoveNfcTag() {
|
||||
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,
|
||||
ANativeWindow* surface,
|
||||
std::shared_ptr<Common::DynamicLibrary> driver_library)
|
||||
|
@ -45,7 +45,7 @@ public:
|
||||
float gyro_z, float accel_x, float accel_y, float accel_z);
|
||||
void OnReadNfcTag(std::span<u8> data);
|
||||
void OnRemoveNfcTag();
|
||||
void OnFrameDisplayed() override {}
|
||||
void OnFrameDisplayed() override;
|
||||
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override {
|
||||
return {std::make_unique<GraphicsContext_Android>(m_driver_library)};
|
||||
@ -61,4 +61,6 @@ private:
|
||||
float m_window_height{};
|
||||
|
||||
std::shared_ptr<Common::DynamicLibrary> m_driver_library;
|
||||
|
||||
bool m_first_frame = false;
|
||||
};
|
||||
|
@ -52,8 +52,8 @@
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "frontend_common/config.h"
|
||||
#include "jni/android_common/android_common.h"
|
||||
#include "jni/config.h"
|
||||
#include "jni/id_cache.h"
|
||||
#include "jni/native.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
@ -372,8 +372,6 @@ void EmulationSession::RunEmulation() {
|
||||
m_system.InitializeDebugger();
|
||||
}
|
||||
|
||||
OnEmulationStarted();
|
||||
|
||||
while (true) {
|
||||
{
|
||||
[[maybe_unused]] std::unique_lock lock(m_mutex);
|
||||
@ -666,8 +664,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz,
|
||||
jboolean reload) {
|
||||
// Create the default config.ini.
|
||||
Config{};
|
||||
// Initialize the emulated system.
|
||||
if (!reload) {
|
||||
EmulationSession::GetInstance().System().Initialize();
|
||||
@ -682,17 +678,6 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass cl
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z(
|
||||
JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) {
|
||||
Config{};
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz,
|
||||
jstring j_game_id) {
|
||||
std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
|
||||
|
||||
env->ReleaseStringUTFChars(j_game_id, game_id.data());
|
||||
}
|
||||
|
||||
jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) {
|
||||
jdoubleArray j_stats = env->NewDoubleArray(4);
|
||||
|
||||
|
@ -52,9 +52,10 @@ public:
|
||||
void OnGamepadDisconnectEvent([[maybe_unused]] int index);
|
||||
SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard();
|
||||
|
||||
static void OnEmulationStarted();
|
||||
|
||||
private:
|
||||
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
|
||||
static void OnEmulationStarted();
|
||||
static void OnEmulationStopped(Core::SystemResultStatus result);
|
||||
|
||||
private:
|
||||
|
@ -5,11 +5,14 @@
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "android_config.h"
|
||||
#include "android_settings.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "frontend_common/config.h"
|
||||
#include "jni/android_common/android_common.h"
|
||||
#include "jni/config.h"
|
||||
#include "uisettings.h"
|
||||
|
||||
std::unique_ptr<AndroidConfig> config;
|
||||
|
||||
template <typename T>
|
||||
Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
|
||||
@ -28,6 +31,22 @@ Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
|
||||
|
||||
extern "C" {
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializeConfig(JNIEnv* env, jobject obj) {
|
||||
config = std::make_unique<AndroidConfig>();
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadConfig(JNIEnv* env, jobject obj) {
|
||||
config.reset();
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_reloadSettings(JNIEnv* env, jobject obj) {
|
||||
config->AndroidConfig::ReloadAllValues();
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveSettings(JNIEnv* env, jobject obj) {
|
||||
config->AndroidConfig::SaveAllValues();
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj,
|
||||
jstring jkey, jboolean getDefault) {
|
||||
auto setting = getSetting<bool>(env, jkey);
|
||||
|
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:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="12dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:layout_marginHorizontal="12dp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:backgroundTint="?attr/colorSurfaceVariant"
|
||||
android:clickable="true"
|
||||
|
@ -38,17 +38,17 @@
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_logo"
|
||||
android:layout_width="250dp"
|
||||
android:layout_height="250dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="150dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginBottom="28dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:src="@drawable/ic_yuzu_title" />
|
||||
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:layout_marginTop="28dp" />
|
||||
android:layout_marginHorizontal="20dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
@ -139,7 +139,7 @@
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/show_fps_text"
|
||||
style="@style/TextAppearance.Material3.BodyMedium"
|
||||
style="@style/TextAppearance.Material3.BodySmall"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="left"
|
||||
@ -147,7 +147,8 @@
|
||||
android:focusable="false"
|
||||
android:paddingHorizontal="20dp"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="12sp"
|
||||
android:shadowColor="@android:color/black"
|
||||
android:shadowRadius="3"
|
||||
tools:ignore="RtlHardcoded" />
|
||||
|
||||
</FrameLayout>
|
||||
|
@ -14,13 +14,14 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="?attr/colorSurface">
|
||||
android:background="?attr/colorSurface"
|
||||
android:paddingHorizontal="8dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/logo_image"
|
||||
android:layout_width="128dp"
|
||||
android:layout_height="128dp"
|
||||
android:layout_margin="64dp"
|
||||
android:layout_width="96dp"
|
||||
android:layout_height="96dp"
|
||||
android:layout_marginVertical="32dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:src="@drawable/ic_yuzu_full" />
|
||||
|
||||
|
@ -10,41 +10,59 @@
|
||||
android:focusable="true"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="72dp"
|
||||
android:padding="@dimen/spacing_large">
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/text_setting_name"
|
||||
style="@style/TextAppearance.Material3.HeadlineMedium"
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:visibility="gone"
|
||||
app:tint="?attr/colorOnSurface" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="viewStart"
|
||||
android:textSize="16sp"
|
||||
app:lineHeight="22dp"
|
||||
tools:text="Setting Name" />
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/text_setting_description"
|
||||
style="@style/TextAppearance.Material3.BodySmall"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/spacing_small"
|
||||
android:textAlignment="viewStart"
|
||||
tools:text="@string/app_disclaimer" />
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/text_setting_name"
|
||||
style="@style/TextAppearance.Material3.HeadlineMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="viewStart"
|
||||
android:textSize="17sp"
|
||||
app:lineHeight="22dp"
|
||||
tools:text="Setting Name" />
|
||||
|
||||
<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"
|
||||
tools:text="1x" />
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/text_setting_description"
|
||||
style="@style/TextAppearance.Material3.BodySmall"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/spacing_small"
|
||||
android:textAlignment="viewStart"
|
||||
tools:text="@string/app_disclaimer" />
|
||||
|
||||
<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>
|
||||
|
||||
|
@ -8,9 +8,7 @@
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:minHeight="72dp"
|
||||
android:paddingVertical="@dimen/spacing_large"
|
||||
android:paddingStart="@dimen/spacing_large"
|
||||
android:paddingEnd="24dp">
|
||||
android:padding="16dp">
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/switch_widget"
|
||||
@ -24,7 +22,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="@dimen/spacing_large"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:layout_toStartOf="@+id/switch_widget"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
@ -35,7 +33,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="viewStart"
|
||||
android:textSize="16sp"
|
||||
android:textSize="17sp"
|
||||
app:lineHeight="28dp"
|
||||
tools:text="@string/frame_limit_enable" />
|
||||
|
||||
|
@ -7,7 +7,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:paddingHorizontal="@dimen/spacing_large"
|
||||
android:paddingVertical="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="?attr/colorPrimary"
|
||||
android:textStyle="bold"
|
||||
|
@ -2,7 +2,6 @@
|
||||
<resources>
|
||||
|
||||
<string-array name="regionNames">
|
||||
<item>@string/auto</item>
|
||||
<item>@string/region_australia</item>
|
||||
<item>@string/region_china</item>
|
||||
<item>@string/region_europe</item>
|
||||
@ -13,7 +12,6 @@
|
||||
</string-array>
|
||||
|
||||
<integer-array name="regionValues">
|
||||
<item>-1</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
<item>2</item>
|
||||
|
@ -240,6 +240,7 @@
|
||||
<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_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_description">All advanced settings will be reset to their default configuration. This can not be undone.</string>
|
||||
<string name="settings_reset">Settings reset</string>
|
||||
@ -271,10 +272,14 @@
|
||||
<string name="preferences_settings">Settings</string>
|
||||
<string name="preferences_general">General</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_description">Accuracy level, resolution, shader cache</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_debug">Debug</string>
|
||||
<string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string>
|
||||
|
||||
<!-- ROM loading errors -->
|
||||
<string name="loader_error_encrypted">Your ROM is encrypted</string>
|
||||
|
@ -146,7 +146,7 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
paused = true;
|
||||
SignalPause();
|
||||
if (cubeb_stream_stop(stream_backend) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream");
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ public:
|
||||
if (device == 0 || paused) {
|
||||
return;
|
||||
}
|
||||
paused = true;
|
||||
SignalPause();
|
||||
SDL_PauseAudioDevice(device, 1);
|
||||
}
|
||||
|
||||
|
@ -282,11 +282,19 @@ u64 SinkStream::GetExpectedPlayedSampleCount() {
|
||||
void SinkStream::WaitFreeSpace(std::stop_token stop_token) {
|
||||
std::unique_lock lk{release_mutex};
|
||||
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) {
|
||||
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
|
||||
|
@ -213,6 +213,12 @@ public:
|
||||
*/
|
||||
void WaitFreeSpace(std::stop_token stop_token);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Unblocks the ADSP if the stream is paused.
|
||||
*/
|
||||
void SignalPause();
|
||||
|
||||
protected:
|
||||
/// Core system
|
||||
Core::System& system;
|
||||
|
@ -9,12 +9,12 @@ PageTable::PageTable() = default;
|
||||
|
||||
PageTable::~PageTable() noexcept = default;
|
||||
|
||||
bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context,
|
||||
u64 address) const {
|
||||
bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
|
||||
Common::ProcessAddress address) const {
|
||||
// Setup invalid defaults.
|
||||
out_entry.phys_addr = 0;
|
||||
out_entry.block_size = page_size;
|
||||
out_context.next_page = 0;
|
||||
out_entry->phys_addr = 0;
|
||||
out_entry->block_size = page_size;
|
||||
out_context->next_page = 0;
|
||||
|
||||
// Validate that we can read the actual entry.
|
||||
const auto page = address / page_size;
|
||||
@ -29,20 +29,20 @@ bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_
|
||||
}
|
||||
|
||||
// Populate the results.
|
||||
out_entry.phys_addr = phys_addr + address;
|
||||
out_context.next_page = page + 1;
|
||||
out_context.next_offset = address + page_size;
|
||||
out_entry->phys_addr = phys_addr + GetInteger(address);
|
||||
out_context->next_page = page + 1;
|
||||
out_context->next_offset = GetInteger(address) + page_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const {
|
||||
bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const {
|
||||
// Setup invalid defaults.
|
||||
out_entry.phys_addr = 0;
|
||||
out_entry.block_size = page_size;
|
||||
out_entry->phys_addr = 0;
|
||||
out_entry->block_size = page_size;
|
||||
|
||||
// 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()) {
|
||||
return false;
|
||||
}
|
||||
@ -54,9 +54,9 @@ bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& c
|
||||
}
|
||||
|
||||
// Populate the results.
|
||||
out_entry.phys_addr = phys_addr + context.next_offset;
|
||||
context.next_page = page + 1;
|
||||
context.next_offset += page_size;
|
||||
out_entry->phys_addr = phys_addr + context->next_offset;
|
||||
context->next_page = page + 1;
|
||||
context->next_offset += page_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <atomic>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/typed_address.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
namespace Common {
|
||||
@ -100,9 +101,9 @@ struct PageTable {
|
||||
PageTable(PageTable&&) noexcept = default;
|
||||
PageTable& operator=(PageTable&&) noexcept = default;
|
||||
|
||||
bool BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context,
|
||||
u64 address) const;
|
||||
bool ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const;
|
||||
bool BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
|
||||
Common::ProcessAddress address) const;
|
||||
bool ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
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
|
||||
* corresponding attribute element is of type `Memory`.
|
||||
|
@ -203,10 +203,12 @@ const char* TranslateCategory(Category category) {
|
||||
case Category::Ui:
|
||||
case Category::UiGeneral:
|
||||
return "UI";
|
||||
case Category::UiAudio:
|
||||
return "UiAudio";
|
||||
case Category::UiLayout:
|
||||
return "UiLayout";
|
||||
return "UILayout";
|
||||
case Category::UiGameList:
|
||||
return "UiGameList";
|
||||
return "UIGameList";
|
||||
case Category::Screenshots:
|
||||
return "Screenshots";
|
||||
case Category::Shortcuts:
|
||||
|
@ -153,7 +153,7 @@ struct Values {
|
||||
true,
|
||||
true};
|
||||
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{
|
||||
linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false};
|
||||
|
||||
@ -232,7 +232,11 @@ struct Values {
|
||||
SwitchableSetting<bool> use_asynchronous_gpu_emulation{
|
||||
linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer};
|
||||
SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage,
|
||||
#ifdef ANDROID
|
||||
AstcDecodeMode::Cpu,
|
||||
#else
|
||||
AstcDecodeMode::Gpu,
|
||||
#endif
|
||||
AstcDecodeMode::Cpu,
|
||||
AstcDecodeMode::CpuAsynchronous,
|
||||
"accelerate_astc",
|
||||
@ -304,7 +308,11 @@ struct Values {
|
||||
linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true};
|
||||
|
||||
SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage,
|
||||
#ifdef ANDROID
|
||||
GpuAccuracy::Normal,
|
||||
#else
|
||||
GpuAccuracy::High,
|
||||
#endif
|
||||
GpuAccuracy::Normal,
|
||||
GpuAccuracy::Extreme,
|
||||
"gpu_accuracy",
|
||||
@ -313,20 +321,38 @@ struct Values {
|
||||
true,
|
||||
true};
|
||||
GpuAccuracy current_gpu_accuracy{GpuAccuracy::High};
|
||||
SwitchableSetting<AnisotropyMode, true> max_anisotropy{
|
||||
linkage, AnisotropyMode::Automatic, AnisotropyMode::Automatic, AnisotropyMode::X16,
|
||||
"max_anisotropy", Category::RendererAdvanced};
|
||||
SwitchableSetting<AnisotropyMode, true> max_anisotropy{linkage,
|
||||
#ifdef ANDROID
|
||||
AnisotropyMode::Default,
|
||||
#else
|
||||
AnisotropyMode::Automatic,
|
||||
#endif
|
||||
AnisotropyMode::Automatic,
|
||||
AnisotropyMode::X16,
|
||||
"max_anisotropy",
|
||||
Category::RendererAdvanced};
|
||||
SwitchableSetting<AstcRecompression, true> astc_recompression{linkage,
|
||||
AstcRecompression::Uncompressed,
|
||||
AstcRecompression::Uncompressed,
|
||||
AstcRecompression::Bc3,
|
||||
"astc_recompression",
|
||||
Category::RendererAdvanced};
|
||||
SwitchableSetting<bool> async_presentation{linkage, false, "async_presentation",
|
||||
Category::RendererAdvanced};
|
||||
SwitchableSetting<bool> async_presentation{linkage,
|
||||
#ifdef ANDROID
|
||||
true,
|
||||
#else
|
||||
false,
|
||||
#endif
|
||||
"async_presentation", Category::RendererAdvanced};
|
||||
SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock",
|
||||
Category::RendererAdvanced};
|
||||
SwitchableSetting<bool> use_reactive_flushing{linkage, true, "use_reactive_flushing",
|
||||
SwitchableSetting<bool> use_reactive_flushing{linkage,
|
||||
#ifdef ANDROID
|
||||
false,
|
||||
#else
|
||||
true,
|
||||
#endif
|
||||
"use_reactive_flushing",
|
||||
Category::RendererAdvanced};
|
||||
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
||||
Category::RendererAdvanced};
|
||||
@ -358,6 +384,8 @@ struct Values {
|
||||
Category::RendererDebug};
|
||||
// TODO: remove this once AMDVLK supports VK_EXT_depth_bias_control
|
||||
bool renderer_amdvlk_depth_bias_workaround{};
|
||||
Setting<bool> disable_buffer_reorder{linkage, false, "disable_buffer_reorder",
|
||||
Category::RendererDebug};
|
||||
|
||||
// System
|
||||
SwitchableSetting<Language, true> language_index{linkage,
|
||||
@ -390,7 +418,11 @@ struct Values {
|
||||
Setting<s32> current_user{linkage, 0, "current_user", Category::System};
|
||||
|
||||
SwitchableSetting<ConsoleMode> use_docked_mode{linkage,
|
||||
#ifdef ANDROID
|
||||
ConsoleMode::Handheld,
|
||||
#else
|
||||
ConsoleMode::Docked,
|
||||
#endif
|
||||
"use_docked_mode",
|
||||
Category::System,
|
||||
Specialization::Radio,
|
||||
|
@ -32,6 +32,7 @@ enum class Category : u32 {
|
||||
AddOns,
|
||||
Controls,
|
||||
Ui,
|
||||
UiAudio,
|
||||
UiGeneral,
|
||||
UiLayout,
|
||||
UiGameList,
|
||||
|
@ -6,10 +6,11 @@
|
||||
namespace Settings {
|
||||
namespace NativeButton {
|
||||
const std::array<const char*, NumButtons> mapping = {{
|
||||
"button_a", "button_b", "button_x", "button_y", "button_lstick",
|
||||
"button_rstick", "button_l", "button_r", "button_zl", "button_zr",
|
||||
"button_plus", "button_minus", "button_dleft", "button_dup", "button_dright",
|
||||
"button_ddown", "button_sl", "button_sr", "button_home", "button_screenshot",
|
||||
"button_a", "button_b", "button_x", "button_y", "button_lstick",
|
||||
"button_rstick", "button_l", "button_r", "button_zl", "button_zr",
|
||||
"button_plus", "button_minus", "button_dleft", "button_dup", "button_dright",
|
||||
"button_ddown", "button_slleft", "button_srleft", "button_home", "button_screenshot",
|
||||
"button_slright", "button_srright",
|
||||
}};
|
||||
}
|
||||
|
||||
|
@ -29,12 +29,15 @@ enum Values : int {
|
||||
DRight,
|
||||
DDown,
|
||||
|
||||
SL,
|
||||
SR,
|
||||
SLLeft,
|
||||
SRLeft,
|
||||
|
||||
Home,
|
||||
Screenshot,
|
||||
|
||||
SLRight,
|
||||
SRRight,
|
||||
|
||||
NumButtons,
|
||||
};
|
||||
|
||||
|
@ -271,8 +271,9 @@ add_library(core STATIC
|
||||
hle/kernel/k_page_heap.h
|
||||
hle/kernel/k_page_group.cpp
|
||||
hle/kernel/k_page_group.h
|
||||
hle/kernel/k_page_table.cpp
|
||||
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_slab_heap.h
|
||||
hle/kernel/k_port.cpp
|
||||
@ -280,6 +281,7 @@ add_library(core STATIC
|
||||
hle/kernel/k_priority_queue.h
|
||||
hle/kernel/k_process.cpp
|
||||
hle/kernel/k_process.h
|
||||
hle/kernel/k_process_page_table.h
|
||||
hle/kernel/k_readable_event.cpp
|
||||
hle/kernel/k_readable_event.h
|
||||
hle/kernel/k_resource_limit.cpp
|
||||
@ -330,8 +332,6 @@ add_library(core STATIC
|
||||
hle/kernel/physical_core.cpp
|
||||
hle/kernel/physical_core.h
|
||||
hle/kernel/physical_memory.h
|
||||
hle/kernel/process_capability.cpp
|
||||
hle/kernel/process_capability.h
|
||||
hle/kernel/slab_helpers.h
|
||||
hle/kernel/svc.cpp
|
||||
hle/kernel/svc.h
|
||||
@ -521,11 +521,19 @@ add_library(core STATIC
|
||||
hle/service/grc/grc.h
|
||||
hle/service/hid/hid.cpp
|
||||
hle/service/hid/hid.h
|
||||
hle/service/hid/hid_debug_server.cpp
|
||||
hle/service/hid/hid_debug_server.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.h
|
||||
hle/service/hid/irs.cpp
|
||||
hle/service/hid/irs.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/xcd.cpp
|
||||
hle/service/hid/xcd.h
|
||||
|
@ -153,6 +153,14 @@ void ARM_Interface::Run() {
|
||||
Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())};
|
||||
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
|
||||
// and this thread has been scheduled again.
|
||||
if (current_thread->GetStepState() == StepState::StepPerformed) {
|
||||
@ -174,14 +182,6 @@ void ARM_Interface::Run() {
|
||||
}
|
||||
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,
|
||||
// or if the thread is unable to continue for any reason.
|
||||
if (True(hr & HaltReason::InstructionBreakpoint) || True(hr & HaltReason::PrefetchAbort)) {
|
||||
|
@ -76,6 +76,7 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
|
||||
}
|
||||
|
||||
void CoreTiming::ClearPendingEvents() {
|
||||
std::scoped_lock lock{basic_lock};
|
||||
event_queue.clear();
|
||||
}
|
||||
|
||||
@ -113,6 +114,7 @@ bool CoreTiming::IsRunning() const {
|
||||
}
|
||||
|
||||
bool CoreTiming::HasPendingEvents() const {
|
||||
std::scoped_lock lock{basic_lock};
|
||||
return !(wait_set && event_queue.empty());
|
||||
}
|
||||
|
||||
|
@ -161,7 +161,7 @@ private:
|
||||
std::shared_ptr<EventType> ev_lost;
|
||||
Common::Event event{};
|
||||
Common::Event pause_event{};
|
||||
std::mutex basic_lock;
|
||||
mutable std::mutex basic_lock;
|
||||
std::mutex advance_lock;
|
||||
std::unique_ptr<std::jthread> timer_thread;
|
||||
std::atomic<bool> paused{};
|
||||
|
@ -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) {
|
||||
if (command.starts_with("TStatus")) {
|
||||
// no tracepoint support
|
||||
@ -573,21 +687,10 @@ void GDBStub::HandleQuery(std::string_view command) {
|
||||
const auto target_xml{arch->GetTargetXML()};
|
||||
SendReply(PaginateBuffer(target_xml, command.substr(30)));
|
||||
} else if (command.starts_with("Offsets")) {
|
||||
Loader::AppLoader::Modules modules;
|
||||
system.GetAppLoader().ReadNSOModules(modules);
|
||||
|
||||
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())));
|
||||
}
|
||||
const auto main_offset = FindMainModuleEntrypoint(system);
|
||||
SendReply(fmt::format("TextSeg={:x}", main_offset));
|
||||
} else if (command.starts_with("Xfer:libraries:read::")) {
|
||||
Loader::AppLoader::Modules modules;
|
||||
system.GetAppLoader().ReadNSOModules(modules);
|
||||
auto modules = FindModules(system);
|
||||
|
||||
std::string buffer;
|
||||
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) {
|
||||
std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
|
||||
std::string reply;
|
||||
@ -767,7 +844,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
|
||||
|
||||
if (command_str == "get fastmem") {
|
||||
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_bits = impl.current_address_space_width_in_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";
|
||||
}
|
||||
} else if (command_str == "get info") {
|
||||
Loader::AppLoader::Modules modules;
|
||||
system.GetAppLoader().ReadNSOModules(modules);
|
||||
auto modules = FindModules(system);
|
||||
|
||||
reply = fmt::format("Process: {:#x} ({})\n"
|
||||
"Program Id: {:#018x}\n",
|
||||
process->GetProcessId(), process->GetName(), process->GetProgramId());
|
||||
reply += fmt::format("Layout:\n"
|
||||
" Alias: {:#012x} - {:#012x}\n"
|
||||
" Heap: {:#012x} - {:#012x}\n"
|
||||
" Aslr: {:#012x} - {:#012x}\n"
|
||||
" Stack: {:#012x} - {:#012x}\n"
|
||||
"Modules:\n",
|
||||
GetInteger(page_table.GetAliasRegionStart()),
|
||||
GetInteger(page_table.GetAliasRegionEnd()),
|
||||
GetInteger(page_table.GetHeapRegionStart()),
|
||||
GetInteger(page_table.GetHeapRegionEnd()),
|
||||
GetInteger(page_table.GetAliasCodeRegionStart()),
|
||||
GetInteger(page_table.GetAliasCodeRegionEnd()),
|
||||
GetInteger(page_table.GetStackRegionStart()),
|
||||
GetInteger(page_table.GetStackRegionEnd()));
|
||||
reply += fmt::format(
|
||||
"Layout:\n"
|
||||
" Alias: {:#012x} - {:#012x}\n"
|
||||
" Heap: {:#012x} - {:#012x}\n"
|
||||
" Aslr: {:#012x} - {:#012x}\n"
|
||||
" Stack: {:#012x} - {:#012x}\n"
|
||||
"Modules:\n",
|
||||
GetInteger(page_table.GetAliasRegionStart()),
|
||||
GetInteger(page_table.GetAliasRegionStart()) + page_table.GetAliasRegionSize() - 1,
|
||||
GetInteger(page_table.GetHeapRegionStart()),
|
||||
GetInteger(page_table.GetHeapRegionStart()) + page_table.GetHeapRegionSize() - 1,
|
||||
GetInteger(page_table.GetAliasCodeRegionStart()),
|
||||
GetInteger(page_table.GetAliasCodeRegionStart()) + page_table.GetAliasCodeRegionSize() -
|
||||
1,
|
||||
GetInteger(page_table.GetStackRegionStart()),
|
||||
GetInteger(page_table.GetStackRegionStart()) + page_table.GetStackRegionSize() - 1);
|
||||
|
||||
for (const auto& [vaddr, name] : modules) {
|
||||
reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr,
|
||||
@ -811,27 +889,34 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
|
||||
while (true) {
|
||||
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 ||
|
||||
mem_info.base_address + mem_info.size - 1 != std::numeric_limits<u64>::max()) {
|
||||
const char* state = GetMemoryStateName(mem_info.state);
|
||||
const char* perm = GetMemoryPermissionString(mem_info);
|
||||
if (svc_mem_info.state != Kernel::Svc::MemoryState::Inaccessible ||
|
||||
svc_mem_info.base_address + svc_mem_info.size - 1 !=
|
||||
std::numeric_limits<u64>::max()) {
|
||||
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 i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
|
||||
const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
|
||||
const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
|
||||
const char l = True(svc_mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-';
|
||||
const char i =
|
||||
True(svc_mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
|
||||
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 =
|
||||
True(mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-';
|
||||
True(svc_mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-';
|
||||
|
||||
reply += fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n",
|
||||
mem_info.base_address,
|
||||
mem_info.base_address + mem_info.size - 1, perm, state, l, i,
|
||||
d, u, p, mem_info.ipc_count, mem_info.device_count);
|
||||
reply += fmt::format(
|
||||
" {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n", svc_mem_info.base_address,
|
||||
svc_mem_info.base_address + svc_mem_info.size - 1, perm, state, l, i, d, u, p,
|
||||
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) {
|
||||
break;
|
||||
}
|
||||
|
@ -38,14 +38,6 @@ using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>;
|
||||
using ConsoleMotionValues = ConsoleMotionInfo;
|
||||
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
|
||||
struct ConsoleMotion {
|
||||
Common::Vec3f accel{};
|
||||
|
@ -243,10 +243,12 @@ void EmulatedController::LoadTASParams() {
|
||||
tas_button_params[Settings::NativeButton::DUp].Set("button", 13);
|
||||
tas_button_params[Settings::NativeButton::DRight].Set("button", 14);
|
||||
tas_button_params[Settings::NativeButton::DDown].Set("button", 15);
|
||||
tas_button_params[Settings::NativeButton::SL].Set("button", 16);
|
||||
tas_button_params[Settings::NativeButton::SR].Set("button", 17);
|
||||
tas_button_params[Settings::NativeButton::SLLeft].Set("button", 16);
|
||||
tas_button_params[Settings::NativeButton::SRLeft].Set("button", 17);
|
||||
tas_button_params[Settings::NativeButton::Home].Set("button", 18);
|
||||
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_y", 1);
|
||||
@ -296,10 +298,12 @@ void EmulatedController::LoadVirtualGamepadParams() {
|
||||
virtual_button_params[Settings::NativeButton::DUp].Set("button", 13);
|
||||
virtual_button_params[Settings::NativeButton::DRight].Set("button", 14);
|
||||
virtual_button_params[Settings::NativeButton::DDown].Set("button", 15);
|
||||
virtual_button_params[Settings::NativeButton::SL].Set("button", 16);
|
||||
virtual_button_params[Settings::NativeButton::SR].Set("button", 17);
|
||||
virtual_button_params[Settings::NativeButton::SLLeft].Set("button", 16);
|
||||
virtual_button_params[Settings::NativeButton::SRLeft].Set("button", 17);
|
||||
virtual_button_params[Settings::NativeButton::Home].Set("button", 18);
|
||||
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_y", 1);
|
||||
@ -867,12 +871,16 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
|
||||
controller.npad_button_state.down.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.d_down.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::SL:
|
||||
case Settings::NativeButton::SLLeft:
|
||||
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);
|
||||
break;
|
||||
case Settings::NativeButton::SR:
|
||||
case Settings::NativeButton::SRLeft:
|
||||
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);
|
||||
break;
|
||||
case Settings::NativeButton::Home:
|
||||
@ -1890,12 +1898,16 @@ NpadButton EmulatedController::GetTurboButtonMask() const {
|
||||
case Settings::NativeButton::DDown:
|
||||
button_mask.down.Assign(1);
|
||||
break;
|
||||
case Settings::NativeButton::SL:
|
||||
case Settings::NativeButton::SLLeft:
|
||||
button_mask.left_sl.Assign(1);
|
||||
break;
|
||||
case Settings::NativeButton::SLRight:
|
||||
button_mask.right_sl.Assign(1);
|
||||
break;
|
||||
case Settings::NativeButton::SR:
|
||||
case Settings::NativeButton::SRLeft:
|
||||
button_mask.left_sr.Assign(1);
|
||||
break;
|
||||
case Settings::NativeButton::SRRight:
|
||||
button_mask.right_sr.Assign(1);
|
||||
break;
|
||||
default:
|
||||
|
@ -356,6 +356,14 @@ struct TouchState {
|
||||
};
|
||||
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
|
||||
struct TouchScreenConfigurationForNx {
|
||||
TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting};
|
||||
|
@ -5,13 +5,14 @@
|
||||
#include "core/hid/hid_types.h"
|
||||
#include "core/hid/input_interpreter.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"
|
||||
|
||||
InputInterpreter::InputInterpreter(Core::System& system)
|
||||
: npad{system.ServiceManager()
|
||||
.GetService<Service::HID::Hid>("hid")
|
||||
->GetAppletResource()
|
||||
.GetService<Service::HID::IHidServer>("hid")
|
||||
->GetResourceManager()
|
||||
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} {
|
||||
ResetButtonStates();
|
||||
}
|
||||
|
@ -222,7 +222,7 @@ Result KSystemControl::AllocateSecureMemory(KernelCore& kernel, KVirtualAddress*
|
||||
};
|
||||
|
||||
// We succeeded.
|
||||
*out = KPageTable::GetHeapVirtualAddress(kernel.MemoryLayout(), paddr);
|
||||
*out = KPageTable::GetHeapVirtualAddress(kernel, paddr);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@ -238,8 +238,17 @@ void KSystemControl::FreeSecureMemory(KernelCore& kernel, KVirtualAddress addres
|
||||
ASSERT(Common::IsAligned(size, alignment));
|
||||
|
||||
// Close the secure region's pages.
|
||||
kernel.MemoryManager().Close(KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), address),
|
||||
kernel.MemoryManager().Close(KPageTable::GetHeapPhysicalAddress(kernel, address),
|
||||
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
|
||||
|
@ -8,7 +8,8 @@
|
||||
|
||||
namespace Kernel {
|
||||
class KernelCore;
|
||||
}
|
||||
class KResourceLimit;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Kernel::Board::Nintendo::Nx {
|
||||
|
||||
@ -40,6 +41,10 @@ public:
|
||||
u32 pool);
|
||||
static void FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size,
|
||||
u32 pool);
|
||||
|
||||
// Insecure Memory.
|
||||
static KResourceLimit* GetInsecureMemoryResourceLimit(KernelCore& kernel);
|
||||
static u32 GetInsecureMemoryPool();
|
||||
};
|
||||
|
||||
} // namespace Kernel::Board::Nintendo::Nx
|
||||
|
@ -4,14 +4,16 @@
|
||||
#include "core/hardware_properties.h"
|
||||
#include "core/hle/kernel/k_capabilities.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/svc_results.h"
|
||||
#include "core/hle/kernel/svc_version.h"
|
||||
|
||||
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.
|
||||
m_svc_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));
|
||||
}
|
||||
|
||||
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.
|
||||
m_svc_access_flags.reset();
|
||||
m_irq_access_flags.reset();
|
||||
@ -121,7 +124,7 @@ Result KCapabilities::SetSyscallMaskCapability(const u32 cap, u32& set_svc) {
|
||||
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 size_pack = MapRangeSize{size_cap};
|
||||
|
||||
@ -142,16 +145,13 @@ Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KPageTable* p
|
||||
? KMemoryPermission::UserRead
|
||||
: KMemoryPermission::UserReadWrite;
|
||||
if (MapRangeSize{size_cap}.normal) {
|
||||
// R_RETURN(page_table->MapStatic(phys_addr, size, perm));
|
||||
R_RETURN(page_table->MapStatic(phys_addr, size, perm));
|
||||
} 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
|
||||
const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize;
|
||||
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);
|
||||
|
||||
// Do the mapping.
|
||||
// R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission_UserReadWrite));
|
||||
|
||||
UNIMPLEMENTED();
|
||||
R_SUCCEED();
|
||||
R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission::UserReadWrite));
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
@ -200,13 +197,11 @@ Result KCapabilities::ProcessMapRegionCapability(const u32 cap, F f) {
|
||||
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.
|
||||
return ProcessMapRegionCapability(
|
||||
cap, [](KMemoryRegionType region_type, KMemoryPermission perm) -> Result {
|
||||
// R_RETURN(page_table->MapRegion(region_type, perm));
|
||||
UNIMPLEMENTED();
|
||||
R_SUCCEED();
|
||||
cap, [page_table](KMemoryRegionType region_type, KMemoryPermission perm) -> Result {
|
||||
R_RETURN(page_table->MapRegion(region_type, perm));
|
||||
});
|
||||
}
|
||||
|
||||
@ -280,7 +275,7 @@ Result KCapabilities::SetDebugFlagsCapability(const u32 cap) {
|
||||
}
|
||||
|
||||
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.
|
||||
const auto type = GetCapabilityType(cap);
|
||||
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;
|
||||
|
||||
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.
|
||||
R_TRY(this->MapRange_(cap, size_cap, page_table));
|
||||
} else if (GetCapabilityType(cap) == CapabilityType::MapRegion && !IsKTraceEnabled) {
|
||||
continue;
|
||||
} else {
|
||||
R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table));
|
||||
}
|
||||
|
@ -15,15 +15,15 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KPageTable;
|
||||
class KProcessPageTable;
|
||||
class KernelCore;
|
||||
|
||||
class KCapabilities {
|
||||
public:
|
||||
constexpr explicit KCapabilities() = default;
|
||||
|
||||
Result InitializeForKip(std::span<const u32> kern_caps, KPageTable* page_table);
|
||||
Result InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table);
|
||||
Result InitializeForKip(std::span<const u32> kern_caps, KProcessPageTable* page_table);
|
||||
Result InitializeForUser(std::span<const u32> user_caps, KProcessPageTable* page_table);
|
||||
|
||||
static Result CheckCapabilities(KernelCore& kernel, std::span<const u32> user_caps);
|
||||
|
||||
@ -264,9 +264,9 @@ private:
|
||||
|
||||
Result SetCorePriorityCapability(const u32 cap);
|
||||
Result SetSyscallMaskCapability(const u32 cap, u32& set_svc);
|
||||
Result MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table);
|
||||
Result MapIoPage_(const u32 cap, KPageTable* page_table);
|
||||
Result MapRegion_(const u32 cap, KPageTable* page_table);
|
||||
Result MapRange_(const u32 cap, const u32 size_cap, KProcessPageTable* page_table);
|
||||
Result MapIoPage_(const u32 cap, KProcessPageTable* page_table);
|
||||
Result MapRegion_(const u32 cap, KProcessPageTable* page_table);
|
||||
Result SetInterruptPairCapability(const u32 cap);
|
||||
Result SetProgramTypeCapability(const u32 cap);
|
||||
Result SetKernelVersionCapability(const u32 cap);
|
||||
@ -277,8 +277,9 @@ private:
|
||||
static Result ProcessMapRegionCapability(const u32 cap, F f);
|
||||
static Result CheckMapRegion(KernelCore& kernel, const u32 cap);
|
||||
|
||||
Result SetCapability(const u32 cap, u32& set_flags, u32& set_svc, KPageTable* page_table);
|
||||
Result SetCapabilities(std::span<const u32> caps, KPageTable* page_table);
|
||||
Result SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
|
||||
KProcessPageTable* page_table);
|
||||
Result SetCapabilities(std::span<const u32> caps, KProcessPageTable* page_table);
|
||||
|
||||
private:
|
||||
Svc::SvcAccessFlagSet m_svc_access_flags{};
|
||||
|
@ -54,7 +54,7 @@ Result KDeviceAddressSpace::Detach(Svc::DeviceName device_name) {
|
||||
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) {
|
||||
// Check that the address falls within the space.
|
||||
R_UNLESS((m_space_address <= device_address &&
|
||||
@ -113,7 +113,7 @@ Result KDeviceAddressSpace::Map(KPageTable* page_table, KProcessAddress process_
|
||||
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) {
|
||||
// Check that the address falls within the space.
|
||||
R_UNLESS((m_space_address <= device_address &&
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
#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/slab_helpers.h"
|
||||
#include "core/hle/result.h"
|
||||
@ -31,23 +31,23 @@ public:
|
||||
Result Attach(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) {
|
||||
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) {
|
||||
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);
|
||||
|
||||
static void Initialize();
|
||||
|
||||
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);
|
||||
|
||||
private:
|
||||
|
@ -394,6 +394,14 @@ private:
|
||||
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:
|
||||
u64 m_linear_phys_to_virt_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) {
|
||||
auto optimize_pa =
|
||||
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
|
||||
auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
|
||||
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
|
||||
|
||||
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,
|
||||
size_t num_pages) {
|
||||
auto optimize_pa =
|
||||
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
|
||||
auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
|
||||
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
|
||||
|
||||
// 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,
|
||||
size_t num_pages) {
|
||||
auto optimize_pa =
|
||||
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
|
||||
auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
|
||||
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
|
||||
|
||||
// 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,
|
||||
size_t num_pages, u8 fill_pattern) {
|
||||
auto& device_memory = kernel.System().DeviceMemory();
|
||||
auto optimize_pa =
|
||||
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
|
||||
auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
|
||||
auto* optimize_map = device_memory.GetPointer<u64>(optimize_pa);
|
||||
|
||||
// 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
|
||||
|
||||
#include <memory>
|
||||
|
||||
#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;
|
||||
}
|
||||
#include "core/hle/kernel/k_page_table_base.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,
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
class KPageTable final : public KPageTableBase {
|
||||
public:
|
||||
enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll };
|
||||
|
||||
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{};
|
||||
explicit KPageTable(KernelCore& kernel) : KPageTableBase(kernel) {}
|
||||
~KPageTable() = default;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
5716
src/core/hle/kernel/k_page_table_base.cpp
Normal file
5716
src/core/hle/kernel/k_page_table_base.cpp
Normal file
File diff suppressed because it is too large
Load Diff
759
src/core/hle/kernel/k_page_table_base.h
Normal file
759
src/core/hle/kernel/k_page_table_base.h
Normal file
@ -0,0 +1,759 @@
|
||||
// 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);
|
||||
|
||||
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_das_merge =
|
||||
False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
|
||||
R_TRY(m_page_table.InitializeForProcess(
|
||||
as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address,
|
||||
params.code_num_pages * PageSize, m_system_resource, res_limit, this->GetMemory()));
|
||||
R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
|
||||
params.code_address, params.code_num_pages * PageSize,
|
||||
m_system_resource, res_limit, this->GetMemory()));
|
||||
}
|
||||
ON_RESULT_FAILURE_2 {
|
||||
m_page_table.Finalize();
|
||||
@ -391,9 +391,9 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params,
|
||||
const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr);
|
||||
const bool enable_das_merge =
|
||||
False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
|
||||
R_TRY(m_page_table.InitializeForProcess(as_type, enable_aslr, enable_das_merge,
|
||||
!enable_aslr, pool, params.code_address, code_size,
|
||||
m_system_resource, res_limit, this->GetMemory()));
|
||||
R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
|
||||
params.code_address, code_size, m_system_resource, res_limit,
|
||||
this->GetMemory()));
|
||||
}
|
||||
ON_RESULT_FAILURE_2 {
|
||||
m_page_table.Finalize();
|
||||
@ -1122,9 +1122,9 @@ Result KProcess::GetThreadList(s32* out_num_threads, KProcessAddress out_thread_
|
||||
void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {}
|
||||
|
||||
KProcess::KProcess(KernelCore& kernel)
|
||||
: KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel.System()},
|
||||
m_state_lock{kernel}, m_list_lock{kernel}, m_cond_var{kernel.System()},
|
||||
m_address_arbiter{kernel.System()}, m_handle_table{kernel} {}
|
||||
: KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel}, m_state_lock{kernel},
|
||||
m_list_lock{kernel}, m_cond_var{kernel.System()}, m_address_arbiter{kernel.System()},
|
||||
m_handle_table{kernel} {}
|
||||
KProcess::~KProcess() = default;
|
||||
|
||||
Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
|
||||
|
@ -5,13 +5,14 @@
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/hle/kernel/code_set.h"
|
||||
#include "core/hle/kernel/k_address_arbiter.h"
|
||||
#include "core/hle/kernel/k_capabilities.h"
|
||||
#include "core/hle/kernel/k_condition_variable.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_process_page_table.h"
|
||||
#include "core/hle/kernel/k_system_resource.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_thread_local_page.h"
|
||||
@ -65,7 +66,7 @@ private:
|
||||
using TLPIterator = TLPTree::iterator;
|
||||
|
||||
private:
|
||||
KPageTable m_page_table;
|
||||
KProcessPageTable m_page_table;
|
||||
std::atomic<size_t> m_used_kernel_memory_size{};
|
||||
TLPTree m_fully_used_tlp_tree{};
|
||||
TLPTree m_partially_used_tlp_tree{};
|
||||
@ -254,9 +255,8 @@ public:
|
||||
return m_is_hbl;
|
||||
}
|
||||
|
||||
Kernel::KMemoryManager::Direction GetAllocateOption() const {
|
||||
// TODO: property of the KPageTableBase
|
||||
return KMemoryManager::Direction::FromFront;
|
||||
u32 GetAllocateOption() const {
|
||||
return m_page_table.GetAllocateOption();
|
||||
}
|
||||
|
||||
ThreadList& GetThreadList() {
|
||||
@ -295,10 +295,10 @@ public:
|
||||
return m_list_lock;
|
||||
}
|
||||
|
||||
KPageTable& GetPageTable() {
|
||||
KProcessPageTable& GetPageTable() {
|
||||
return m_page_table;
|
||||
}
|
||||
const KPageTable& GetPageTable() const {
|
||||
const KProcessPageTable& GetPageTable() const {
|
||||
return m_page_table;
|
||||
}
|
||||
|
||||
|
480
src/core/hle/kernel/k_process_page_table.h
Normal file
480
src/core/hle/kernel/k_process_page_table.h
Normal file
@ -0,0 +1,480 @@
|
||||
// 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) {
|
||||
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));
|
||||
}
|
||||
|
||||
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) {
|
||||
// // Get the client process/page table.
|
||||
// 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 (R_FAILED(client_result)) {
|
||||
|
@ -40,7 +40,7 @@ Result KSecureSystemResource::Initialize(size_t size, KResourceLimit* resource_l
|
||||
|
||||
// Get resource pointer.
|
||||
KPhysicalAddress resource_paddr =
|
||||
KPageTable::GetHeapPhysicalAddress(m_kernel.MemoryLayout(), m_resource_address);
|
||||
KPageTable::GetHeapPhysicalAddress(m_kernel, m_resource_address);
|
||||
auto* resource =
|
||||
m_kernel.System().DeviceMemory().GetPointer<KPageTableManager::RefCount>(resource_paddr);
|
||||
|
||||
|
@ -37,8 +37,8 @@ Result KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) {
|
||||
|
||||
Result KThreadLocalPage::Finalize() {
|
||||
// Get the physical address of the page.
|
||||
const KPhysicalAddress phys_addr = m_owner->GetPageTable().GetPhysicalAddr(m_virt_addr);
|
||||
ASSERT(phys_addr);
|
||||
KPhysicalAddress phys_addr{};
|
||||
ASSERT(m_owner->GetPageTable().GetPhysicalAddress(std::addressof(phys_addr), m_virt_addr));
|
||||
|
||||
// Unmap the page.
|
||||
R_TRY(m_owner->GetPageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal));
|
||||
|
@ -1,389 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <bit>
|
||||
|
||||
#include "common/bit_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/kernel/k_handle_table.h"
|
||||
#include "core/hle/kernel/k_page_table.h"
|
||||
#include "core/hle/kernel/process_capability.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
|
||||
namespace Kernel {
|
||||
namespace {
|
||||
|
||||
// clang-format off
|
||||
|
||||
// Shift offsets for kernel capability types.
|
||||
enum : u32 {
|
||||
CapabilityOffset_PriorityAndCoreNum = 3,
|
||||
CapabilityOffset_Syscall = 4,
|
||||
CapabilityOffset_MapPhysical = 6,
|
||||
CapabilityOffset_MapIO = 7,
|
||||
CapabilityOffset_MapRegion = 10,
|
||||
CapabilityOffset_Interrupt = 11,
|
||||
CapabilityOffset_ProgramType = 13,
|
||||
CapabilityOffset_KernelVersion = 14,
|
||||
CapabilityOffset_HandleTableSize = 15,
|
||||
CapabilityOffset_Debug = 16,
|
||||
};
|
||||
|
||||
// Combined mask of all parameters that may be initialized only once.
|
||||
constexpr u32 InitializeOnceMask = (1U << CapabilityOffset_PriorityAndCoreNum) |
|
||||
(1U << CapabilityOffset_ProgramType) |
|
||||
(1U << CapabilityOffset_KernelVersion) |
|
||||
(1U << CapabilityOffset_HandleTableSize) |
|
||||
(1U << CapabilityOffset_Debug);
|
||||
|
||||
// Packed kernel version indicating 10.4.0
|
||||
constexpr u32 PackedKernelVersion = 0x520000;
|
||||
|
||||
// Indicates possible types of capabilities that can be specified.
|
||||
enum class CapabilityType : u32 {
|
||||
Unset = 0U,
|
||||
PriorityAndCoreNum = (1U << CapabilityOffset_PriorityAndCoreNum) - 1,
|
||||
Syscall = (1U << CapabilityOffset_Syscall) - 1,
|
||||
MapPhysical = (1U << CapabilityOffset_MapPhysical) - 1,
|
||||
MapIO = (1U << CapabilityOffset_MapIO) - 1,
|
||||
MapRegion = (1U << CapabilityOffset_MapRegion) - 1,
|
||||
Interrupt = (1U << CapabilityOffset_Interrupt) - 1,
|
||||
ProgramType = (1U << CapabilityOffset_ProgramType) - 1,
|
||||
KernelVersion = (1U << CapabilityOffset_KernelVersion) - 1,
|
||||
HandleTableSize = (1U << CapabilityOffset_HandleTableSize) - 1,
|
||||
Debug = (1U << CapabilityOffset_Debug) - 1,
|
||||
Ignorable = 0xFFFFFFFFU,
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
|
||||
constexpr CapabilityType GetCapabilityType(u32 value) {
|
||||
return static_cast<CapabilityType>((~value & (value + 1)) - 1);
|
||||
}
|
||||
|
||||
u32 GetFlagBitOffset(CapabilityType type) {
|
||||
const auto value = static_cast<u32>(type);
|
||||
return static_cast<u32>(Common::BitSize<u32>() - static_cast<u32>(std::countl_zero(value)));
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
Result ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities,
|
||||
std::size_t num_capabilities,
|
||||
KPageTable& page_table) {
|
||||
Clear();
|
||||
|
||||
// Allow all cores and priorities.
|
||||
core_mask = 0xF;
|
||||
priority_mask = 0xFFFFFFFFFFFFFFFF;
|
||||
kernel_version = PackedKernelVersion;
|
||||
|
||||
return ParseCapabilities(capabilities, num_capabilities, page_table);
|
||||
}
|
||||
|
||||
Result ProcessCapabilities::InitializeForUserProcess(const u32* capabilities,
|
||||
std::size_t num_capabilities,
|
||||
KPageTable& page_table) {
|
||||
Clear();
|
||||
|
||||
return ParseCapabilities(capabilities, num_capabilities, page_table);
|
||||
}
|
||||
|
||||
void ProcessCapabilities::InitializeForMetadatalessProcess() {
|
||||
// Allow all cores and priorities
|
||||
core_mask = 0xF;
|
||||
priority_mask = 0xFFFFFFFFFFFFFFFF;
|
||||
kernel_version = PackedKernelVersion;
|
||||
|
||||
// Allow all system calls and interrupts.
|
||||
svc_capabilities.set();
|
||||
interrupt_capabilities.set();
|
||||
|
||||
// Allow using the maximum possible amount of handles
|
||||
handle_table_size = static_cast<s32>(KHandleTable::MaxTableSize);
|
||||
|
||||
// Allow all debugging capabilities.
|
||||
is_debuggable = true;
|
||||
can_force_debug = true;
|
||||
}
|
||||
|
||||
Result ProcessCapabilities::ParseCapabilities(const u32* capabilities, std::size_t num_capabilities,
|
||||
KPageTable& page_table) {
|
||||
u32 set_flags = 0;
|
||||
u32 set_svc_bits = 0;
|
||||
|
||||
for (std::size_t i = 0; i < num_capabilities; ++i) {
|
||||
const u32 descriptor = capabilities[i];
|
||||
const auto type = GetCapabilityType(descriptor);
|
||||
|
||||
if (type == CapabilityType::MapPhysical) {
|
||||
i++;
|
||||
|
||||
// The MapPhysical type uses two descriptor flags for its parameters.
|
||||
// If there's only one, then there's a problem.
|
||||
if (i >= num_capabilities) {
|
||||
LOG_ERROR(Kernel, "Invalid combination! i={}", i);
|
||||
return ResultInvalidCombination;
|
||||
}
|
||||
|
||||
const auto size_flags = capabilities[i];
|
||||
if (GetCapabilityType(size_flags) != CapabilityType::MapPhysical) {
|
||||
LOG_ERROR(Kernel, "Invalid capability type! size_flags={}", size_flags);
|
||||
return ResultInvalidCombination;
|
||||
}
|
||||
|
||||
const auto result = HandleMapPhysicalFlags(descriptor, size_flags, page_table);
|
||||
if (result.IsError()) {
|
||||
LOG_ERROR(Kernel, "Failed to map physical flags! descriptor={}, size_flags={}",
|
||||
descriptor, size_flags);
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
const auto result =
|
||||
ParseSingleFlagCapability(set_flags, set_svc_bits, descriptor, page_table);
|
||||
if (result.IsError()) {
|
||||
LOG_ERROR(
|
||||
Kernel,
|
||||
"Failed to parse capability flag! set_flags={}, set_svc_bits={}, descriptor={}",
|
||||
set_flags, set_svc_bits, descriptor);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag,
|
||||
KPageTable& page_table) {
|
||||
const auto type = GetCapabilityType(flag);
|
||||
|
||||
if (type == CapabilityType::Unset) {
|
||||
return ResultInvalidArgument;
|
||||
}
|
||||
|
||||
// Bail early on ignorable entries, as one would expect,
|
||||
// ignorable descriptors can be ignored.
|
||||
if (type == CapabilityType::Ignorable) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
// Ensure that the give flag hasn't already been initialized before.
|
||||
// If it has been, then bail.
|
||||
const u32 flag_length = GetFlagBitOffset(type);
|
||||
const u32 set_flag = 1U << flag_length;
|
||||
if ((set_flag & set_flags & InitializeOnceMask) != 0) {
|
||||
LOG_ERROR(Kernel,
|
||||
"Attempted to initialize flags that may only be initialized once. set_flags={}",
|
||||
set_flags);
|
||||
return ResultInvalidCombination;
|
||||
}
|
||||
set_flags |= set_flag;
|
||||
|
||||
switch (type) {
|
||||
case CapabilityType::PriorityAndCoreNum:
|
||||
return HandlePriorityCoreNumFlags(flag);
|
||||
case CapabilityType::Syscall:
|
||||
return HandleSyscallFlags(set_svc_bits, flag);
|
||||
case CapabilityType::MapIO:
|
||||
return HandleMapIOFlags(flag, page_table);
|
||||
case CapabilityType::MapRegion:
|
||||
return HandleMapRegionFlags(flag, page_table);
|
||||
case CapabilityType::Interrupt:
|
||||
return HandleInterruptFlags(flag);
|
||||
case CapabilityType::ProgramType:
|
||||
return HandleProgramTypeFlags(flag);
|
||||
case CapabilityType::KernelVersion:
|
||||
return HandleKernelVersionFlags(flag);
|
||||
case CapabilityType::HandleTableSize:
|
||||
return HandleHandleTableFlags(flag);
|
||||
case CapabilityType::Debug:
|
||||
return HandleDebugFlags(flag);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_ERROR(Kernel, "Invalid capability type! type={}", type);
|
||||
return ResultInvalidArgument;
|
||||
}
|
||||
|
||||
void ProcessCapabilities::Clear() {
|
||||
svc_capabilities.reset();
|
||||
interrupt_capabilities.reset();
|
||||
|
||||
core_mask = 0;
|
||||
priority_mask = 0;
|
||||
|
||||
handle_table_size = 0;
|
||||
kernel_version = 0;
|
||||
|
||||
program_type = ProgramType::SysModule;
|
||||
|
||||
is_debuggable = false;
|
||||
can_force_debug = false;
|
||||
}
|
||||
|
||||
Result ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
|
||||
if (priority_mask != 0 || core_mask != 0) {
|
||||
LOG_ERROR(Kernel, "Core or priority mask are not zero! priority_mask={}, core_mask={}",
|
||||
priority_mask, core_mask);
|
||||
return ResultInvalidArgument;
|
||||
}
|
||||
|
||||
const u32 core_num_min = (flags >> 16) & 0xFF;
|
||||
const u32 core_num_max = (flags >> 24) & 0xFF;
|
||||
if (core_num_min > core_num_max) {
|
||||
LOG_ERROR(Kernel, "Core min is greater than core max! core_num_min={}, core_num_max={}",
|
||||
core_num_min, core_num_max);
|
||||
return ResultInvalidCombination;
|
||||
}
|
||||
|
||||
const u32 priority_min = (flags >> 10) & 0x3F;
|
||||
const u32 priority_max = (flags >> 4) & 0x3F;
|
||||
if (priority_min > priority_max) {
|
||||
LOG_ERROR(Kernel,
|
||||
"Priority min is greater than priority max! priority_min={}, priority_max={}",
|
||||
core_num_min, priority_max);
|
||||
return ResultInvalidCombination;
|
||||
}
|
||||
|
||||
// The switch only has 4 usable cores.
|
||||
if (core_num_max >= 4) {
|
||||
LOG_ERROR(Kernel, "Invalid max cores specified! core_num_max={}", core_num_max);
|
||||
return ResultInvalidCoreId;
|
||||
}
|
||||
|
||||
const auto make_mask = [](u64 min, u64 max) {
|
||||
const u64 range = max - min + 1;
|
||||
const u64 mask = (1ULL << range) - 1;
|
||||
|
||||
return mask << min;
|
||||
};
|
||||
|
||||
core_mask = make_mask(core_num_min, core_num_max);
|
||||
priority_mask = make_mask(priority_min, priority_max);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) {
|
||||
const u32 index = flags >> 29;
|
||||
const u32 svc_bit = 1U << index;
|
||||
|
||||
// If we've already set this svc before, bail.
|
||||
if ((set_svc_bits & svc_bit) != 0) {
|
||||
return ResultInvalidCombination;
|
||||
}
|
||||
set_svc_bits |= svc_bit;
|
||||
|
||||
const u32 svc_mask = (flags >> 5) & 0xFFFFFF;
|
||||
for (u32 i = 0; i < 24; ++i) {
|
||||
const u32 svc_number = index * 24 + i;
|
||||
|
||||
if ((svc_mask & (1U << i)) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
svc_capabilities[svc_number] = true;
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags,
|
||||
KPageTable& page_table) {
|
||||
// TODO(Lioncache): Implement once the memory manager can handle this.
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ProcessCapabilities::HandleMapIOFlags(u32 flags, KPageTable& page_table) {
|
||||
// TODO(Lioncache): Implement once the memory manager can handle this.
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ProcessCapabilities::HandleMapRegionFlags(u32 flags, KPageTable& page_table) {
|
||||
// TODO(Lioncache): Implement once the memory manager can handle this.
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ProcessCapabilities::HandleInterruptFlags(u32 flags) {
|
||||
constexpr u32 interrupt_ignore_value = 0x3FF;
|
||||
const u32 interrupt0 = (flags >> 12) & 0x3FF;
|
||||
const u32 interrupt1 = (flags >> 22) & 0x3FF;
|
||||
|
||||
for (u32 interrupt : {interrupt0, interrupt1}) {
|
||||
if (interrupt == interrupt_ignore_value) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE:
|
||||
// This should be checking a generic interrupt controller value
|
||||
// as part of the calculation, however, given we don't currently
|
||||
// emulate that, it's sufficient to mark every interrupt as defined.
|
||||
|
||||
if (interrupt >= interrupt_capabilities.size()) {
|
||||
LOG_ERROR(Kernel, "Process interrupt capability is out of range! svc_number={}",
|
||||
interrupt);
|
||||
return ResultOutOfRange;
|
||||
}
|
||||
|
||||
interrupt_capabilities[interrupt] = true;
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ProcessCapabilities::HandleProgramTypeFlags(u32 flags) {
|
||||
const u32 reserved = flags >> 17;
|
||||
if (reserved != 0) {
|
||||
LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
|
||||
return ResultReservedUsed;
|
||||
}
|
||||
|
||||
program_type = static_cast<ProgramType>((flags >> 14) & 0b111);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ProcessCapabilities::HandleKernelVersionFlags(u32 flags) {
|
||||
// Yes, the internal member variable is checked in the actual kernel here.
|
||||
// This might look odd for options that are only allowed to be initialized
|
||||
// just once, however the kernel has a separate initialization function for
|
||||
// kernel processes and userland processes. The kernel variant sets this
|
||||
// member variable ahead of time.
|
||||
|
||||
const u32 major_version = kernel_version >> 19;
|
||||
|
||||
if (major_version != 0 || flags < 0x80000) {
|
||||
LOG_ERROR(Kernel,
|
||||
"Kernel version is non zero or flags are too small! major_version={}, flags={}",
|
||||
major_version, flags);
|
||||
return ResultInvalidArgument;
|
||||
}
|
||||
|
||||
kernel_version = flags;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
|
||||
const u32 reserved = flags >> 26;
|
||||
if (reserved != 0) {
|
||||
LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
|
||||
return ResultReservedUsed;
|
||||
}
|
||||
|
||||
handle_table_size = static_cast<s32>((flags >> 16) & 0x3FF);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ProcessCapabilities::HandleDebugFlags(u32 flags) {
|
||||
const u32 reserved = flags >> 19;
|
||||
if (reserved != 0) {
|
||||
LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
|
||||
return ResultReservedUsed;
|
||||
}
|
||||
|
||||
is_debuggable = (flags & 0x20000) != 0;
|
||||
can_force_debug = (flags & 0x40000) != 0;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
@ -1,266 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
union Result;
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KPageTable;
|
||||
|
||||
/// The possible types of programs that may be indicated
|
||||
/// by the program type capability descriptor.
|
||||
enum class ProgramType {
|
||||
SysModule,
|
||||
Application,
|
||||
Applet,
|
||||
};
|
||||
|
||||
/// Handles kernel capability descriptors that are provided by
|
||||
/// application metadata. These descriptors provide information
|
||||
/// that alters certain parameters for kernel process instance
|
||||
/// that will run said application (or applet).
|
||||
///
|
||||
/// Capabilities are a sequence of flag descriptors, that indicate various
|
||||
/// configurations and constraints for a particular process.
|
||||
///
|
||||
/// Flag types are indicated by a sequence of set low bits. E.g. the
|
||||
/// types are indicated with the low bits as follows (where x indicates "don't care"):
|
||||
///
|
||||
/// - Priority and core mask : 0bxxxxxxxxxxxx0111
|
||||
/// - Allowed service call mask: 0bxxxxxxxxxxx01111
|
||||
/// - Map physical memory : 0bxxxxxxxxx0111111
|
||||
/// - Map IO memory : 0bxxxxxxxx01111111
|
||||
/// - Interrupts : 0bxxxx011111111111
|
||||
/// - Application type : 0bxx01111111111111
|
||||
/// - Kernel version : 0bx011111111111111
|
||||
/// - Handle table size : 0b0111111111111111
|
||||
/// - Debugger flags : 0b1111111111111111
|
||||
///
|
||||
/// These are essentially a bit offset subtracted by 1 to create a mask.
|
||||
/// e.g. The first entry in the above list is simply bit 3 (value 8 -> 0b1000)
|
||||
/// subtracted by one (7 -> 0b0111)
|
||||
///
|
||||
/// An example of a bit layout (using the map physical layout):
|
||||
/// <example>
|
||||
/// The MapPhysical type indicates a sequence entry pair of:
|
||||
///
|
||||
/// [initial, memory_flags], where:
|
||||
///
|
||||
/// initial:
|
||||
/// bits:
|
||||
/// 7-24: Starting page to map memory at.
|
||||
/// 25 : Indicates if the memory should be mapped as read only.
|
||||
///
|
||||
/// memory_flags:
|
||||
/// bits:
|
||||
/// 7-20 : Number of pages to map
|
||||
/// 21-25: Seems to be reserved (still checked against though)
|
||||
/// 26 : Whether or not the memory being mapped is IO memory, or physical memory
|
||||
/// </example>
|
||||
///
|
||||
class ProcessCapabilities {
|
||||
public:
|
||||
using InterruptCapabilities = std::bitset<1024>;
|
||||
using SyscallCapabilities = std::bitset<192>;
|
||||
|
||||
ProcessCapabilities() = default;
|
||||
ProcessCapabilities(const ProcessCapabilities&) = delete;
|
||||
ProcessCapabilities(ProcessCapabilities&&) = default;
|
||||
|
||||
ProcessCapabilities& operator=(const ProcessCapabilities&) = delete;
|
||||
ProcessCapabilities& operator=(ProcessCapabilities&&) = default;
|
||||
|
||||
/// Initializes this process capabilities instance for a kernel process.
|
||||
///
|
||||
/// @param capabilities The capabilities to parse
|
||||
/// @param num_capabilities The number of capabilities to parse.
|
||||
/// @param page_table The memory manager to use for handling any mapping-related
|
||||
/// operations (such as mapping IO memory, etc).
|
||||
///
|
||||
/// @returns ResultSuccess if this capabilities instance was able to be initialized,
|
||||
/// otherwise, an error code upon failure.
|
||||
///
|
||||
Result InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities,
|
||||
KPageTable& page_table);
|
||||
|
||||
/// Initializes this process capabilities instance for a userland process.
|
||||
///
|
||||
/// @param capabilities The capabilities to parse.
|
||||
/// @param num_capabilities The total number of capabilities to parse.
|
||||
/// @param page_table The memory manager to use for handling any mapping-related
|
||||
/// operations (such as mapping IO memory, etc).
|
||||
///
|
||||
/// @returns ResultSuccess if this capabilities instance was able to be initialized,
|
||||
/// otherwise, an error code upon failure.
|
||||
///
|
||||
Result InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities,
|
||||
KPageTable& page_table);
|
||||
|
||||
/// Initializes this process capabilities instance for a process that does not
|
||||
/// have any metadata to parse.
|
||||
///
|
||||
/// This is necessary, as we allow running raw executables, and the internal
|
||||
/// kernel process capabilities also determine what CPU cores the process is
|
||||
/// allowed to run on, and what priorities are allowed for threads. It also
|
||||
/// determines the max handle table size, what the program type is, whether or
|
||||
/// not the process can be debugged, or whether it's possible for a process to
|
||||
/// forcibly debug another process.
|
||||
///
|
||||
/// Given the above, this essentially enables all capabilities across the board
|
||||
/// for the process. It allows the process to:
|
||||
///
|
||||
/// - Run on any core
|
||||
/// - Use any thread priority
|
||||
/// - Use the maximum amount of handles a process is allowed to.
|
||||
/// - Be debuggable
|
||||
/// - Forcibly debug other processes.
|
||||
///
|
||||
/// Note that this is not a behavior that the kernel allows a process to do via
|
||||
/// a single function like this. This is yuzu-specific behavior to handle
|
||||
/// executables with no capability descriptors whatsoever to derive behavior from.
|
||||
/// It being yuzu-specific is why this is also not the default behavior and not
|
||||
/// done by default in the constructor.
|
||||
///
|
||||
void InitializeForMetadatalessProcess();
|
||||
|
||||
/// Gets the allowable core mask
|
||||
u64 GetCoreMask() const {
|
||||
return core_mask;
|
||||
}
|
||||
|
||||
/// Gets the allowable priority mask
|
||||
u64 GetPriorityMask() const {
|
||||
return priority_mask;
|
||||
}
|
||||
|
||||
/// Gets the SVC access permission bits
|
||||
const SyscallCapabilities& GetServiceCapabilities() const {
|
||||
return svc_capabilities;
|
||||
}
|
||||
|
||||
/// Gets the valid interrupt bits.
|
||||
const InterruptCapabilities& GetInterruptCapabilities() const {
|
||||
return interrupt_capabilities;
|
||||
}
|
||||
|
||||
/// Gets the program type for this process.
|
||||
ProgramType GetProgramType() const {
|
||||
return program_type;
|
||||
}
|
||||
|
||||
/// Gets the number of total allowable handles for the process' handle table.
|
||||
s32 GetHandleTableSize() const {
|
||||
return handle_table_size;
|
||||
}
|
||||
|
||||
/// Gets the kernel version value.
|
||||
u32 GetKernelVersion() const {
|
||||
return kernel_version;
|
||||
}
|
||||
|
||||
/// Whether or not this process can be debugged.
|
||||
bool IsDebuggable() const {
|
||||
return is_debuggable;
|
||||
}
|
||||
|
||||
/// Whether or not this process can forcibly debug another
|
||||
/// process, even if that process is not considered debuggable.
|
||||
bool CanForceDebug() const {
|
||||
return can_force_debug;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Attempts to parse a given sequence of capability descriptors.
|
||||
///
|
||||
/// @param capabilities The sequence of capability descriptors to parse.
|
||||
/// @param num_capabilities The number of descriptors within the given sequence.
|
||||
/// @param page_table The memory manager that will perform any memory
|
||||
/// mapping if necessary.
|
||||
///
|
||||
/// @return ResultSuccess if no errors occur, otherwise an error code.
|
||||
///
|
||||
Result ParseCapabilities(const u32* capabilities, std::size_t num_capabilities,
|
||||
KPageTable& page_table);
|
||||
|
||||
/// Attempts to parse a capability descriptor that is only represented by a
|
||||
/// single flag set.
|
||||
///
|
||||
/// @param set_flags Running set of flags that are used to catch
|
||||
/// flags being initialized more than once when they shouldn't be.
|
||||
/// @param set_svc_bits Running set of bits representing the allowed supervisor calls mask.
|
||||
/// @param flag The flag to attempt to parse.
|
||||
/// @param page_table The memory manager that will perform any memory
|
||||
/// mapping if necessary.
|
||||
///
|
||||
/// @return ResultSuccess if no errors occurred, otherwise an error code.
|
||||
///
|
||||
Result ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag,
|
||||
KPageTable& page_table);
|
||||
|
||||
/// Clears the internal state of this process capability instance. Necessary,
|
||||
/// to have a sane starting point due to us allowing running executables without
|
||||
/// configuration metadata. We assume a process is not going to have metadata,
|
||||
/// and if it turns out that the process does, in fact, have metadata, then
|
||||
/// we attempt to parse it. Thus, we need this to reset data members back to
|
||||
/// a good state.
|
||||
///
|
||||
/// DO NOT ever make this a public member function. This isn't an invariant
|
||||
/// anything external should depend upon (and if anything comes to rely on it,
|
||||
/// you should immediately be questioning the design of that thing, not this
|
||||
/// class. If the kernel itself can run without depending on behavior like that,
|
||||
/// then so can yuzu).
|
||||
///
|
||||
void Clear();
|
||||
|
||||
/// Handles flags related to the priority and core number capability flags.
|
||||
Result HandlePriorityCoreNumFlags(u32 flags);
|
||||
|
||||
/// Handles flags related to determining the allowable SVC mask.
|
||||
Result HandleSyscallFlags(u32& set_svc_bits, u32 flags);
|
||||
|
||||
/// Handles flags related to mapping physical memory pages.
|
||||
Result HandleMapPhysicalFlags(u32 flags, u32 size_flags, KPageTable& page_table);
|
||||
|
||||
/// Handles flags related to mapping IO pages.
|
||||
Result HandleMapIOFlags(u32 flags, KPageTable& page_table);
|
||||
|
||||
/// Handles flags related to mapping physical memory regions.
|
||||
Result HandleMapRegionFlags(u32 flags, KPageTable& page_table);
|
||||
|
||||
/// Handles flags related to the interrupt capability flags.
|
||||
Result HandleInterruptFlags(u32 flags);
|
||||
|
||||
/// Handles flags related to the program type.
|
||||
Result HandleProgramTypeFlags(u32 flags);
|
||||
|
||||
/// Handles flags related to the handle table size.
|
||||
Result HandleHandleTableFlags(u32 flags);
|
||||
|
||||
/// Handles flags related to the kernel version capability flags.
|
||||
Result HandleKernelVersionFlags(u32 flags);
|
||||
|
||||
/// Handles flags related to debug-specific capabilities.
|
||||
Result HandleDebugFlags(u32 flags);
|
||||
|
||||
SyscallCapabilities svc_capabilities;
|
||||
InterruptCapabilities interrupt_capabilities;
|
||||
|
||||
u64 core_mask = 0;
|
||||
u64 priority_mask = 0;
|
||||
|
||||
s32 handle_table_size = 0;
|
||||
u32 kernel_version = 0;
|
||||
|
||||
ProgramType program_type = ProgramType::SysModule;
|
||||
|
||||
bool is_debuggable = false;
|
||||
bool can_force_debug = false;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
@ -29,7 +29,8 @@ constexpr bool IsValidAddressRange(u64 address, u64 size) {
|
||||
// Helper function that performs the common sanity checks for svcMapMemory
|
||||
// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
|
||||
// in the same order.
|
||||
Result MapUnmapMemorySanityChecks(const KPageTable& manager, u64 dst_addr, u64 src_addr, u64 size) {
|
||||
Result MapUnmapMemorySanityChecks(const KProcessPageTable& manager, u64 dst_addr, u64 src_addr,
|
||||
u64 size) {
|
||||
if (!Common::Is4KBAligned(dst_addr)) {
|
||||
LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
|
||||
R_THROW(ResultInvalidAddress);
|
||||
@ -123,7 +124,8 @@ Result SetMemoryAttribute(Core::System& system, u64 address, u64 size, u32 mask,
|
||||
R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
|
||||
|
||||
// Set the memory attribute.
|
||||
R_RETURN(page_table.SetMemoryAttribute(address, size, mask, attr));
|
||||
R_RETURN(page_table.SetMemoryAttribute(address, size, static_cast<KMemoryAttribute>(mask),
|
||||
static_cast<KMemoryAttribute>(attr)));
|
||||
}
|
||||
|
||||
/// Maps a memory range into a different range.
|
||||
|
@ -16,7 +16,14 @@ Result SetHeapSize(Core::System& system, u64* out_address, u64 size) {
|
||||
R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize);
|
||||
|
||||
// Set the heap size.
|
||||
R_RETURN(GetCurrentProcess(system.Kernel()).GetPageTable().SetHeapSize(out_address, size));
|
||||
KProcessAddress address{};
|
||||
R_TRY(GetCurrentProcess(system.Kernel())
|
||||
.GetPageTable()
|
||||
.SetHeapSize(std::addressof(address), size));
|
||||
|
||||
// We succeeded.
|
||||
*out_address = GetInteger(address);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/// Maps memory at a desired address
|
||||
|
@ -247,8 +247,7 @@ Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 d
|
||||
R_THROW(ResultInvalidCurrentMemory);
|
||||
}
|
||||
|
||||
R_RETURN(page_table.UnmapCodeMemory(dst_address, src_address, size,
|
||||
KPageTable::ICacheInvalidationStrategy::InvalidateAll));
|
||||
R_RETURN(page_table.UnmapCodeMemory(dst_address, src_address, size));
|
||||
}
|
||||
|
||||
Result SetProcessMemoryPermission64(Core::System& system, Handle process_handle, uint64_t address,
|
||||
|
@ -31,12 +31,12 @@ Result QueryProcessMemory(Core::System& system, uint64_t out_memory_info, PageIn
|
||||
}
|
||||
|
||||
auto& current_memory{GetCurrentMemory(system.Kernel())};
|
||||
const auto memory_info{process->GetPageTable().QueryInfo(address).GetSvcMemoryInfo()};
|
||||
|
||||
current_memory.WriteBlock(out_memory_info, std::addressof(memory_info), sizeof(memory_info));
|
||||
KMemoryInfo mem_info;
|
||||
R_TRY(process->GetPageTable().QueryInfo(std::addressof(mem_info), out_page_info, address));
|
||||
|
||||
//! This is supposed to be part of the QueryInfo call.
|
||||
*out_page_info = {};
|
||||
const auto svc_mem_info = mem_info.GetSvcMemoryInfo();
|
||||
current_memory.WriteBlock(out_memory_info, std::addressof(svc_mem_info), sizeof(svc_mem_info));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user