early-access version 3930

This commit is contained in:
pineappleEA 2023-10-13 17:25:20 +02:00
parent db958cf156
commit 72b22f183c
55 changed files with 1482 additions and 528 deletions

View file

@ -1,7 +1,7 @@
yuzu emulator early access yuzu emulator early access
============= =============
This is the source code for early-access 3929. This is the source code for early-access 3930.
## Legal Notice ## Legal Notice

View file

@ -15,13 +15,9 @@ import androidx.annotation.Keep
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import org.yuzu.yuzu_emu.YuzuApplication.Companion.appContext
import org.yuzu.yuzu_emu.activities.EmulationActivity import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath
import org.yuzu.yuzu_emu.utils.FileUtil.exists import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize
import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory
import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri
import org.yuzu.yuzu_emu.utils.Log import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
@ -75,7 +71,7 @@ object NativeLibrary {
return if (isNativePath(path!!)) { return if (isNativePath(path!!)) {
YuzuApplication.documentsTree!!.openContentUri(path, openmode) YuzuApplication.documentsTree!!.openContentUri(path, openmode)
} else { } else {
openContentUri(appContext, path, openmode) FileUtil.openContentUri(path, openmode)
} }
} }
@ -85,7 +81,7 @@ object NativeLibrary {
return if (isNativePath(path!!)) { return if (isNativePath(path!!)) {
YuzuApplication.documentsTree!!.getFileSize(path) YuzuApplication.documentsTree!!.getFileSize(path)
} else { } else {
getFileSize(appContext, path) FileUtil.getFileSize(path)
} }
} }
@ -95,7 +91,7 @@ object NativeLibrary {
return if (isNativePath(path!!)) { return if (isNativePath(path!!)) {
YuzuApplication.documentsTree!!.exists(path) YuzuApplication.documentsTree!!.exists(path)
} else { } else {
exists(appContext, path) FileUtil.exists(path)
} }
} }
@ -105,7 +101,7 @@ object NativeLibrary {
return if (isNativePath(path!!)) { return if (isNativePath(path!!)) {
YuzuApplication.documentsTree!!.isDirectory(path) YuzuApplication.documentsTree!!.isDirectory(path)
} else { } else {
isDirectory(appContext, path) FileUtil.isDirectory(path)
} }
} }

View file

@ -47,7 +47,7 @@ class YuzuApplication : Application() {
application = this application = this
documentsTree = DocumentsTree() documentsTree = DocumentsTree()
DirectoryInitialization.start() DirectoryInitialization.start()
GpuDriverHelper.initializeDriverParameters(applicationContext) GpuDriverHelper.initializeDriverParameters()
NativeLibrary.logDeviceInfo() NativeLibrary.logDeviceInfo()
createNotificationChannels() createNotificationChannels()

View file

@ -0,0 +1,117 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.adapters
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
class DriverAdapter(private val driverViewModel: DriverViewModel) :
ListAdapter<Pair<String, GpuDriverMetadata>, DriverAdapter.DriverViewHolder>(
AsyncDifferConfig.Builder(DiffCallback()).build()
) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DriverViewHolder {
val binding =
CardDriverOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return DriverViewHolder(binding)
}
override fun getItemCount(): Int = currentList.size
override fun onBindViewHolder(holder: DriverViewHolder, position: Int) =
holder.bind(currentList[position])
private fun onSelectDriver(position: Int) {
driverViewModel.setSelectedDriverIndex(position)
notifyItemChanged(driverViewModel.previouslySelectedDriver)
notifyItemChanged(driverViewModel.selectedDriver)
}
private fun onDeleteDriver(driverData: Pair<String, GpuDriverMetadata>, position: Int) {
if (driverViewModel.selectedDriver > position) {
driverViewModel.setSelectedDriverIndex(driverViewModel.selectedDriver - 1)
}
if (GpuDriverHelper.customDriverData == driverData.second) {
driverViewModel.setSelectedDriverIndex(0)
}
driverViewModel.driversToDelete.add(driverData.first)
driverViewModel.removeDriver(driverData)
notifyItemRemoved(position)
notifyItemChanged(driverViewModel.selectedDriver)
}
inner class DriverViewHolder(val binding: CardDriverOptionBinding) :
RecyclerView.ViewHolder(binding.root) {
private lateinit var driverData: Pair<String, GpuDriverMetadata>
fun bind(driverData: Pair<String, GpuDriverMetadata>) {
this.driverData = driverData
val driver = driverData.second
binding.apply {
radioButton.isChecked = driverViewModel.selectedDriver == bindingAdapterPosition
root.setOnClickListener {
onSelectDriver(bindingAdapterPosition)
}
buttonDelete.setOnClickListener {
onDeleteDriver(driverData, bindingAdapterPosition)
}
// Delay marquee by 3s
title.postDelayed(
{
title.isSelected = true
title.ellipsize = TextUtils.TruncateAt.MARQUEE
version.isSelected = true
version.ellipsize = TextUtils.TruncateAt.MARQUEE
description.isSelected = true
description.ellipsize = TextUtils.TruncateAt.MARQUEE
},
3000
)
if (driver.name == null) {
title.setText(R.string.system_gpu_driver)
description.text = ""
version.text = ""
version.visibility = View.GONE
description.visibility = View.GONE
buttonDelete.visibility = View.GONE
} else {
title.text = driver.name
version.text = driver.version
description.text = driver.description
version.visibility = View.VISIBLE
description.visibility = View.VISIBLE
buttonDelete.visibility = View.VISIBLE
}
}
}
}
private class DiffCallback : DiffUtil.ItemCallback<Pair<String, GpuDriverMetadata>>() {
override fun areItemsTheSame(
oldItem: Pair<String, GpuDriverMetadata>,
newItem: Pair<String, GpuDriverMetadata>
): Boolean {
return oldItem.first == newItem.first
}
override fun areContentsTheSame(
oldItem: Pair<String, GpuDriverMetadata>,
newItem: Pair<String, GpuDriverMetadata>
): Boolean {
return oldItem.second == newItem.second
}
}
}

View file

@ -0,0 +1,185 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.adapters.DriverAdapter
import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import java.io.IOException
class DriverManagerFragment : Fragment() {
private var _binding: FragmentDriverManagerBinding? = null
private val binding get() = _binding!!
private val homeViewModel: HomeViewModel by activityViewModels()
private val driverViewModel: DriverViewModel by activityViewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentDriverManagerBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setNavigationVisibility(visible = false, animated = true)
homeViewModel.setStatusBarShadeVisibility(visible = false)
if (!driverViewModel.isInteractionAllowed) {
DriversLoadingDialogFragment().show(
childFragmentManager,
DriversLoadingDialogFragment.TAG
)
}
binding.toolbarDrivers.setNavigationOnClickListener {
binding.root.findNavController().popBackStack()
}
binding.buttonInstall.setOnClickListener {
getDriver.launch(arrayOf("application/zip"))
}
binding.listDrivers.apply {
layoutManager = GridLayoutManager(
requireContext(),
resources.getInteger(R.integer.grid_columns)
)
adapter = DriverAdapter(driverViewModel)
}
viewLifecycleOwner.lifecycleScope.apply {
launch {
driverViewModel.driverList.collectLatest {
(binding.listDrivers.adapter as DriverAdapter).submitList(it)
}
}
launch {
driverViewModel.newDriverInstalled.collect {
if (_binding != null && it) {
(binding.listDrivers.adapter as DriverAdapter).apply {
notifyItemChanged(driverViewModel.previouslySelectedDriver)
notifyItemChanged(driverViewModel.selectedDriver)
driverViewModel.setNewDriverInstalled(false)
}
}
}
}
}
setInsets()
}
// Start installing requested driver
override fun onStop() {
super.onStop()
driverViewModel.onCloseDriverManager()
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
) { _: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.toolbarDrivers.layoutParams as ViewGroup.MarginLayoutParams
mlpAppBar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets
binding.toolbarDrivers.layoutParams = mlpAppBar
val mlplistDrivers = binding.listDrivers.layoutParams as ViewGroup.MarginLayoutParams
mlplistDrivers.leftMargin = leftInsets
mlplistDrivers.rightMargin = rightInsets
binding.listDrivers.layoutParams = mlplistDrivers
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
val mlpFab =
binding.buttonInstall.layoutParams as ViewGroup.MarginLayoutParams
mlpFab.leftMargin = leftInsets + fabSpacing
mlpFab.rightMargin = rightInsets + fabSpacing
mlpFab.bottomMargin = barInsets.bottom + fabSpacing
binding.buttonInstall.layoutParams = mlpFab
binding.listDrivers.updatePadding(
bottom = barInsets.bottom +
resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab)
)
windowInsets
}
private val getDriver =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result == null) {
return@registerForActivityResult
}
IndeterminateProgressDialogFragment.newInstance(
requireActivity(),
R.string.installing_driver,
false
) {
// Ignore file exceptions when a user selects an invalid zip
try {
GpuDriverHelper.copyDriverToInternalStorage(result)
} catch (_: IOException) {
return@newInstance getString(R.string.select_gpu_driver_error)
}
val driverData = GpuDriverHelper.customDriverData
if (driverData.name == null) {
return@newInstance getString(R.string.select_gpu_driver_error)
}
val driverInList =
driverViewModel.driverList.value.firstOrNull { it.second == driverData }
if (driverInList != null) {
return@newInstance getString(R.string.driver_already_installed)
} else {
driverViewModel.addDriver(
Pair(
"${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}",
driverData
)
)
driverViewModel.setNewDriverInstalled(true)
}
return@newInstance Any()
}.show(childFragmentManager, IndeterminateProgressDialogFragment.TAG)
}
}

View file

@ -0,0 +1,75 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.fragments
import android.app.Dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.model.DriverViewModel
class DriversLoadingDialogFragment : DialogFragment() {
private val driverViewModel: DriverViewModel by activityViewModels()
private lateinit var binding: DialogProgressBarBinding
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
binding = DialogProgressBarBinding.inflate(layoutInflater)
binding.progressBar.isIndeterminate = true
isCancelable = false
return MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.loading)
.setView(binding.root)
.create()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View = binding.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.apply {
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
driverViewModel.areDriversLoading.collect { checkForDismiss() }
}
}
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
driverViewModel.isDriverReady.collect { checkForDismiss() }
}
}
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
driverViewModel.isDeletingDrivers.collect { checkForDismiss() }
}
}
}
}
private fun checkForDismiss() {
if (driverViewModel.isInteractionAllowed) {
dismiss()
}
}
companion object {
const val TAG = "DriversLoadingDialogFragment"
}
}

View file

@ -39,6 +39,7 @@ import androidx.window.layout.WindowLayoutInfo
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider import com.google.android.material.slider.Slider
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.HomeNavigationDirections
@ -50,6 +51,7 @@ import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding
import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
import org.yuzu.yuzu_emu.features.settings.model.IntSetting import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.EmulationViewModel import org.yuzu.yuzu_emu.model.EmulationViewModel
import org.yuzu.yuzu_emu.overlay.InputOverlay import org.yuzu.yuzu_emu.overlay.InputOverlay
@ -70,6 +72,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private lateinit var game: Game private lateinit var game: Game
private val emulationViewModel: EmulationViewModel by activityViewModels() private val emulationViewModel: EmulationViewModel by activityViewModels()
private val driverViewModel: DriverViewModel by activityViewModels()
private var isInFoldableLayout = false private var isInFoldableLayout = false
@ -299,6 +302,21 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
} }
} }
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
driverViewModel.isDriverReady.collect {
if (it && !emulationState.isRunning) {
if (!DirectoryInitialization.areDirectoriesReady) {
DirectoryInitialization.start()
}
updateScreenLayout()
emulationState.run(emulationActivity!!.isActivityRecreated)
}
}
}
}
} }
} }
@ -332,17 +350,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
} }
override fun onResume() {
super.onResume()
if (!DirectoryInitialization.areDirectoriesReady) {
DirectoryInitialization.start()
}
updateScreenLayout()
emulationState.run(emulationActivity!!.isActivityRecreated)
}
override fun onPause() { override fun onPause() {
if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) { if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) {
emulationState.pause() emulationState.pause()

View file

@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.fragments
import android.Manifest import android.Manifest
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
@ -28,7 +27,6 @@ import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
import org.yuzu.yuzu_emu.BuildConfig import org.yuzu.yuzu_emu.BuildConfig
import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.HomeNavigationDirections
@ -37,6 +35,7 @@ import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter
import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
import org.yuzu.yuzu_emu.features.DocumentProvider import org.yuzu.yuzu_emu.features.DocumentProvider
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.HomeSetting import org.yuzu.yuzu_emu.model.HomeSetting
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity import org.yuzu.yuzu_emu.ui.main.MainActivity
@ -50,6 +49,7 @@ class HomeSettingsFragment : Fragment() {
private lateinit var mainActivity: MainActivity private lateinit var mainActivity: MainActivity
private val homeViewModel: HomeViewModel by activityViewModels() private val homeViewModel: HomeViewModel by activityViewModels()
private val driverViewModel: DriverViewModel by activityViewModels()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -107,13 +107,17 @@ class HomeSettingsFragment : Fragment() {
) )
add( add(
HomeSetting( HomeSetting(
R.string.install_gpu_driver, R.string.gpu_driver_manager,
R.string.install_gpu_driver_description, R.string.install_gpu_driver_description,
R.drawable.ic_exit, R.drawable.ic_build,
{ driverInstaller() }, {
binding.root.findNavController()
.navigate(R.id.action_homeSettingsFragment_to_driverManagerFragment)
},
{ GpuDriverHelper.supportsCustomDriverLoading() }, { GpuDriverHelper.supportsCustomDriverLoading() },
R.string.custom_driver_not_supported, R.string.custom_driver_not_supported,
R.string.custom_driver_not_supported_description R.string.custom_driver_not_supported_description,
driverViewModel.selectedDriverMetadata
) )
) )
add( add(
@ -292,31 +296,6 @@ class HomeSettingsFragment : Fragment() {
} }
} }
private fun driverInstaller() {
// Get the driver name for the dialog message.
var driverName = GpuDriverHelper.customDriverName
if (driverName == null) {
driverName = getString(R.string.system_gpu_driver)
}
MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.select_gpu_driver_title))
.setMessage(driverName)
.setNegativeButton(android.R.string.cancel, null)
.setNeutralButton(R.string.select_gpu_driver_default) { _: DialogInterface?, _: Int ->
GpuDriverHelper.installDefaultDriver(requireContext())
Toast.makeText(
requireContext(),
R.string.select_gpu_driver_use_default,
Toast.LENGTH_SHORT
).show()
}
.setPositiveButton(R.string.select_gpu_driver_install) { _: DialogInterface?, _: Int ->
mainActivity.getDriver.launch(arrayOf("application/zip"))
}
.show()
}
private fun shareLog() { private fun shareLog() {
val file = DocumentFile.fromSingleUri( val file = DocumentFile.fromSingleUri(
mainActivity, mainActivity,

View file

@ -10,8 +10,8 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
@ -78,6 +78,10 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
requireActivity().supportFragmentManager, requireActivity().supportFragmentManager,
MessageDialogFragment.TAG MessageDialogFragment.TAG
) )
else -> {
// Do nothing
}
} }
taskViewModel.clear() taskViewModel.clear()
} }
@ -115,7 +119,7 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
private const val CANCELLABLE = "Cancellable" private const val CANCELLABLE = "Cancellable"
fun newInstance( fun newInstance(
activity: AppCompatActivity, activity: FragmentActivity,
titleId: Int, titleId: Int,
cancellable: Boolean = false, cancellable: Boolean = false,
task: () -> Any task: () -> Any

View file

@ -0,0 +1,158 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.model
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
import java.io.BufferedOutputStream
import java.io.File
class DriverViewModel : ViewModel() {
private val _areDriversLoading = MutableStateFlow(false)
val areDriversLoading: StateFlow<Boolean> get() = _areDriversLoading
private val _isDriverReady = MutableStateFlow(true)
val isDriverReady: StateFlow<Boolean> get() = _isDriverReady
private val _isDeletingDrivers = MutableStateFlow(false)
val isDeletingDrivers: StateFlow<Boolean> get() = _isDeletingDrivers
private val _driverList = MutableStateFlow(mutableListOf<Pair<String, GpuDriverMetadata>>())
val driverList: StateFlow<MutableList<Pair<String, GpuDriverMetadata>>> get() = _driverList
var previouslySelectedDriver = 0
var selectedDriver = -1
private val _selectedDriverMetadata =
MutableStateFlow(
GpuDriverHelper.customDriverData.name
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
)
val selectedDriverMetadata: StateFlow<String> get() = _selectedDriverMetadata
private val _newDriverInstalled = MutableStateFlow(false)
val newDriverInstalled: StateFlow<Boolean> get() = _newDriverInstalled
val driversToDelete = mutableListOf<String>()
val isInteractionAllowed
get() = !areDriversLoading.value && isDriverReady.value && !isDeletingDrivers.value
init {
_areDriversLoading.value = true
viewModelScope.launch {
withContext(Dispatchers.IO) {
val drivers = GpuDriverHelper.getDrivers()
val currentDriverMetadata = GpuDriverHelper.customDriverData
for (i in drivers.indices) {
if (drivers[i].second == currentDriverMetadata) {
setSelectedDriverIndex(i)
break
}
}
// If a user had installed a driver before the manager was implemented, this zips
// the installed driver to UserData/gpu_drivers/CustomDriver.zip so that it can
// be indexed and exported as expected.
if (selectedDriver == -1) {
val driverToSave =
File(GpuDriverHelper.driverStoragePath, "CustomDriver.zip")
driverToSave.createNewFile()
FileUtil.zipFromInternalStorage(
File(GpuDriverHelper.driverInstallationPath!!),
GpuDriverHelper.driverInstallationPath!!,
BufferedOutputStream(driverToSave.outputStream())
)
drivers.add(Pair(driverToSave.path, currentDriverMetadata))
setSelectedDriverIndex(drivers.size - 1)
}
_driverList.value = drivers
_areDriversLoading.value = false
}
}
}
fun setSelectedDriverIndex(value: Int) {
if (selectedDriver != -1) {
previouslySelectedDriver = selectedDriver
}
selectedDriver = value
}
fun setNewDriverInstalled(value: Boolean) {
_newDriverInstalled.value = value
}
fun addDriver(driverData: Pair<String, GpuDriverMetadata>) {
val driverIndex = _driverList.value.indexOfFirst { it == driverData }
if (driverIndex == -1) {
setSelectedDriverIndex(_driverList.value.size)
_driverList.value.add(driverData)
_selectedDriverMetadata.value = driverData.second.name
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
} else {
setSelectedDriverIndex(driverIndex)
}
}
fun removeDriver(driverData: Pair<String, GpuDriverMetadata>) {
_driverList.value.remove(driverData)
}
fun onCloseDriverManager() {
_isDeletingDrivers.value = true
viewModelScope.launch {
withContext(Dispatchers.IO) {
driversToDelete.forEach {
val driver = File(it)
if (driver.exists()) {
driver.delete()
}
}
driversToDelete.clear()
_isDeletingDrivers.value = false
}
}
if (GpuDriverHelper.customDriverData == driverList.value[selectedDriver].second) {
return
}
_isDriverReady.value = false
viewModelScope.launch {
withContext(Dispatchers.IO) {
if (selectedDriver == 0) {
GpuDriverHelper.installDefaultDriver()
setDriverReady()
return@withContext
}
val driverToInstall = File(driverList.value[selectedDriver].first)
if (driverToInstall.exists()) {
GpuDriverHelper.installCustomDriver(driverToInstall)
} else {
GpuDriverHelper.installDefaultDriver()
}
setDriverReady()
}
}
}
private fun setDriverReady() {
_isDriverReady.value = true
_selectedDriverMetadata.value = GpuDriverHelper.customDriverData.name
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
}
}

View file

@ -29,12 +29,10 @@ import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.navigation.NavigationBarView import com.google.android.material.navigation.NavigationBarView
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import java.io.File import java.io.File
import java.io.FilenameFilter import java.io.FilenameFilter
import java.io.IOException
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -43,7 +41,6 @@ import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.activities.EmulationActivity import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.features.DocumentProvider import org.yuzu.yuzu_emu.features.DocumentProvider
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
@ -343,11 +340,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val dstPath = DirectoryInitialization.userDirectory + "/keys/" val dstPath = DirectoryInitialization.userDirectory + "/keys/"
if (FileUtil.copyUriToInternalStorage( if (FileUtil.copyUriToInternalStorage(
applicationContext,
result, result,
dstPath, dstPath,
"prod.keys" "prod.keys"
) ) != null
) { ) {
if (NativeLibrary.reloadKeys()) { if (NativeLibrary.reloadKeys()) {
Toast.makeText( Toast.makeText(
@ -446,11 +442,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val dstPath = DirectoryInitialization.userDirectory + "/keys/" val dstPath = DirectoryInitialization.userDirectory + "/keys/"
if (FileUtil.copyUriToInternalStorage( if (FileUtil.copyUriToInternalStorage(
applicationContext,
result, result,
dstPath, dstPath,
"key_retail.bin" "key_retail.bin"
) ) != null
) { ) {
if (NativeLibrary.reloadKeys()) { if (NativeLibrary.reloadKeys()) {
Toast.makeText( Toast.makeText(
@ -469,59 +464,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
} }
} }
val getDriver =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result == null) {
return@registerForActivityResult
}
val takeFlags =
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
contentResolver.takePersistableUriPermission(
result,
takeFlags
)
val progressBinding = DialogProgressBarBinding.inflate(layoutInflater)
progressBinding.progressBar.isIndeterminate = true
val installationDialog = MaterialAlertDialogBuilder(this)
.setTitle(R.string.installing_driver)
.setView(progressBinding.root)
.show()
lifecycleScope.launch {
withContext(Dispatchers.IO) {
// Ignore file exceptions when a user selects an invalid zip
try {
GpuDriverHelper.installCustomDriver(applicationContext, result)
} catch (_: IOException) {
}
withContext(Dispatchers.Main) {
installationDialog.dismiss()
val driverName = GpuDriverHelper.customDriverName
if (driverName != null) {
Toast.makeText(
applicationContext,
getString(
R.string.select_gpu_driver_install_success,
driverName
),
Toast.LENGTH_SHORT
).show()
} else {
Toast.makeText(
applicationContext,
R.string.select_gpu_driver_error,
Toast.LENGTH_LONG
).show()
}
}
}
}
}
val installGameUpdate = registerForActivityResult( val installGameUpdate = registerForActivityResult(
ActivityResultContracts.OpenMultipleDocuments() ActivityResultContracts.OpenMultipleDocuments()
) { documents: List<Uri> -> ) { documents: List<Uri> ->

View file

@ -7,7 +7,6 @@ import android.net.Uri
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import java.io.File import java.io.File
import java.util.* import java.util.*
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.model.MinimalDocumentFile import org.yuzu.yuzu_emu.model.MinimalDocumentFile
class DocumentsTree { class DocumentsTree {
@ -22,7 +21,7 @@ class DocumentsTree {
fun openContentUri(filepath: String, openMode: String?): Int { fun openContentUri(filepath: String, openMode: String?): Int {
val node = resolvePath(filepath) ?: return -1 val node = resolvePath(filepath) ?: return -1
return FileUtil.openContentUri(YuzuApplication.appContext, node.uri.toString(), openMode) return FileUtil.openContentUri(node.uri.toString(), openMode)
} }
fun getFileSize(filepath: String): Long { fun getFileSize(filepath: String): Long {
@ -30,7 +29,7 @@ class DocumentsTree {
return if (node == null || node.isDirectory) { return if (node == null || node.isDirectory) {
0 0
} else { } else {
FileUtil.getFileSize(YuzuApplication.appContext, node.uri.toString()) FileUtil.getFileSize(node.uri.toString())
} }
} }
@ -67,7 +66,7 @@ class DocumentsTree {
* @param parent parent node of this level * @param parent parent node of this level
*/ */
private fun structTree(parent: DocumentsNode) { private fun structTree(parent: DocumentsNode) {
val documents = FileUtil.listFiles(YuzuApplication.appContext, parent.uri!!) val documents = FileUtil.listFiles(parent.uri!!)
for (document in documents) { for (document in documents) {
val node = DocumentsNode(document) val node = DocumentsNode(document)
node.parent = parent node.parent = parent

View file

@ -3,7 +3,6 @@
package org.yuzu.yuzu_emu.utils package org.yuzu.yuzu_emu.utils
import android.content.Context
import android.database.Cursor import android.database.Cursor
import android.net.Uri import android.net.Uri
import android.provider.DocumentsContract import android.provider.DocumentsContract
@ -11,7 +10,6 @@ import androidx.documentfile.provider.DocumentFile
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.File import java.io.File
import java.io.FileOutputStream
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.net.URLDecoder import java.net.URLDecoder
@ -21,6 +19,8 @@ import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.model.MinimalDocumentFile import org.yuzu.yuzu_emu.model.MinimalDocumentFile
import org.yuzu.yuzu_emu.model.TaskState import org.yuzu.yuzu_emu.model.TaskState
import java.io.BufferedOutputStream import java.io.BufferedOutputStream
import java.lang.NullPointerException
import java.nio.charset.StandardCharsets
import java.util.zip.ZipOutputStream import java.util.zip.ZipOutputStream
object FileUtil { object FileUtil {
@ -29,6 +29,8 @@ object FileUtil {
const val APPLICATION_OCTET_STREAM = "application/octet-stream" const val APPLICATION_OCTET_STREAM = "application/octet-stream"
const val TEXT_PLAIN = "text/plain" const val TEXT_PLAIN = "text/plain"
private val context get() = YuzuApplication.appContext
/** /**
* Create a file from directory with filename. * Create a file from directory with filename.
* @param context Application context * @param context Application context
@ -36,11 +38,11 @@ object FileUtil {
* @param filename file display name. * @param filename file display name.
* @return boolean * @return boolean
*/ */
fun createFile(context: Context?, directory: String?, filename: String): DocumentFile? { fun createFile(directory: String?, filename: String): DocumentFile? {
var decodedFilename = filename var decodedFilename = filename
try { try {
val directoryUri = Uri.parse(directory) val directoryUri = Uri.parse(directory)
val parent = DocumentFile.fromTreeUri(context!!, directoryUri) ?: return null val parent = DocumentFile.fromTreeUri(context, directoryUri) ?: return null
decodedFilename = URLDecoder.decode(decodedFilename, DECODE_METHOD) decodedFilename = URLDecoder.decode(decodedFilename, DECODE_METHOD)
var mimeType = APPLICATION_OCTET_STREAM var mimeType = APPLICATION_OCTET_STREAM
if (decodedFilename.endsWith(".txt")) { if (decodedFilename.endsWith(".txt")) {
@ -56,16 +58,15 @@ object FileUtil {
/** /**
* Create a directory from directory with filename. * Create a directory from directory with filename.
* @param context Application context
* @param directory parent path for directory. * @param directory parent path for directory.
* @param directoryName directory display name. * @param directoryName directory display name.
* @return boolean * @return boolean
*/ */
fun createDir(context: Context?, directory: String?, directoryName: String?): DocumentFile? { fun createDir(directory: String?, directoryName: String?): DocumentFile? {
var decodedDirectoryName = directoryName var decodedDirectoryName = directoryName
try { try {
val directoryUri = Uri.parse(directory) val directoryUri = Uri.parse(directory)
val parent = DocumentFile.fromTreeUri(context!!, directoryUri) ?: return null val parent = DocumentFile.fromTreeUri(context, directoryUri) ?: return null
decodedDirectoryName = URLDecoder.decode(decodedDirectoryName, DECODE_METHOD) decodedDirectoryName = URLDecoder.decode(decodedDirectoryName, DECODE_METHOD)
val isExist = parent.findFile(decodedDirectoryName) val isExist = parent.findFile(decodedDirectoryName)
return isExist ?: parent.createDirectory(decodedDirectoryName) return isExist ?: parent.createDirectory(decodedDirectoryName)
@ -77,13 +78,12 @@ object FileUtil {
/** /**
* Open content uri and return file descriptor to JNI. * Open content uri and return file descriptor to JNI.
* @param context Application context
* @param path Native content uri path * @param path Native content uri path
* @param openMode will be one of "r", "r", "rw", "wa", "rwa" * @param openMode will be one of "r", "r", "rw", "wa", "rwa"
* @return file descriptor * @return file descriptor
*/ */
@JvmStatic @JvmStatic
fun openContentUri(context: Context, path: String, openMode: String?): Int { fun openContentUri(path: String, openMode: String?): Int {
try { try {
val uri = Uri.parse(path) val uri = Uri.parse(path)
val parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, openMode!!) val parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, openMode!!)
@ -103,11 +103,10 @@ object FileUtil {
/** /**
* Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow * Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow
* This function will be faster than DoucmentFile.listFiles * This function will be faster than DoucmentFile.listFiles
* @param context Application context
* @param uri Directory uri. * @param uri Directory uri.
* @return CheapDocument lists. * @return CheapDocument lists.
*/ */
fun listFiles(context: Context, uri: Uri): Array<MinimalDocumentFile> { fun listFiles(uri: Uri): Array<MinimalDocumentFile> {
val resolver = context.contentResolver val resolver = context.contentResolver
val columns = arrayOf( val columns = arrayOf(
DocumentsContract.Document.COLUMN_DOCUMENT_ID, DocumentsContract.Document.COLUMN_DOCUMENT_ID,
@ -145,7 +144,7 @@ object FileUtil {
* @param path Native content uri path * @param path Native content uri path
* @return bool * @return bool
*/ */
fun exists(context: Context, path: String?): Boolean { fun exists(path: String?): Boolean {
var c: Cursor? = null var c: Cursor? = null
try { try {
val mUri = Uri.parse(path) val mUri = Uri.parse(path)
@ -165,7 +164,7 @@ object FileUtil {
* @param path content uri path * @param path content uri path
* @return bool * @return bool
*/ */
fun isDirectory(context: Context, path: String): Boolean { fun isDirectory(path: String): Boolean {
val resolver = context.contentResolver val resolver = context.contentResolver
val columns = arrayOf( val columns = arrayOf(
DocumentsContract.Document.COLUMN_MIME_TYPE DocumentsContract.Document.COLUMN_MIME_TYPE
@ -210,10 +209,10 @@ object FileUtil {
return filename return filename
} }
fun getFilesName(context: Context, path: String): Array<String> { fun getFilesName(path: String): Array<String> {
val uri = Uri.parse(path) val uri = Uri.parse(path)
val files: MutableList<String> = ArrayList() val files: MutableList<String> = ArrayList()
for (file in listFiles(context, uri)) { for (file in listFiles(uri)) {
files.add(file.filename) files.add(file.filename)
} }
return files.toTypedArray() return files.toTypedArray()
@ -225,7 +224,7 @@ object FileUtil {
* @return long file size * @return long file size
*/ */
@JvmStatic @JvmStatic
fun getFileSize(context: Context, path: String): Long { fun getFileSize(path: String): Long {
val resolver = context.contentResolver val resolver = context.contentResolver
val columns = arrayOf( val columns = arrayOf(
DocumentsContract.Document.COLUMN_SIZE DocumentsContract.Document.COLUMN_SIZE
@ -245,44 +244,38 @@ object FileUtil {
return size return size
} }
/**
* Creates an input stream with a given [Uri] and copies its data to the given path. This will
* overwrite any pre-existing files.
*
* @param sourceUri The [Uri] to copy data from
* @param destinationParentPath Destination directory
* @param destinationFilename Optionally renames the file once copied
*/
fun copyUriToInternalStorage( fun copyUriToInternalStorage(
context: Context, sourceUri: Uri,
sourceUri: Uri?,
destinationParentPath: String, destinationParentPath: String,
destinationFilename: String destinationFilename: String = ""
): Boolean { ): File? =
var input: InputStream? = null
var output: FileOutputStream? = null
try { try {
input = context.contentResolver.openInputStream(sourceUri!!) val fileName =
output = FileOutputStream("$destinationParentPath/$destinationFilename") if (destinationFilename == "") getFilename(sourceUri) else "/$destinationFilename"
val buffer = ByteArray(1024) val inputStream = context.contentResolver.openInputStream(sourceUri)!!
var len: Int
while (input!!.read(buffer).also { len = it } != -1) { val destinationFile = File("$destinationParentPath$fileName")
output.write(buffer, 0, len) if (destinationFile.exists()) {
destinationFile.delete()
} }
output.flush()
return true destinationFile.outputStream().use { fos ->
} catch (e: Exception) { inputStream.use { it.copyTo(fos) }
Log.error("[FileUtil]: Cannot copy file, error: " + e.message)
} finally {
if (input != null) {
try {
input.close()
} catch (e: IOException) {
Log.error("[FileUtil]: Cannot close input file, error: " + e.message)
}
}
if (output != null) {
try {
output.close()
} catch (e: IOException) {
Log.error("[FileUtil]: Cannot close output file, error: " + e.message)
}
} }
destinationFile
} catch (e: IOException) {
null
} catch (e: NullPointerException) {
null
} }
return false
}
/** /**
* Extracts the given zip file into the given directory. * Extracts the given zip file into the given directory.
@ -368,4 +361,12 @@ object FileUtil {
return fileName.substring(fileName.lastIndexOf(".") + 1) return fileName.substring(fileName.lastIndexOf(".") + 1)
.lowercase() .lowercase()
} }
@Throws(IOException::class)
fun getStringFromFile(file: File): String =
String(file.readBytes(), StandardCharsets.UTF_8)
@Throws(IOException::class)
fun getStringFromInputStream(stream: InputStream): String =
String(stream.readBytes(), StandardCharsets.UTF_8)
} }

View file

@ -30,7 +30,7 @@ object GameHelper {
// Ensure keys are loaded so that ROM metadata can be decrypted. // Ensure keys are loaded so that ROM metadata can be decrypted.
NativeLibrary.reloadKeys() NativeLibrary.reloadKeys()
addGamesRecursive(games, FileUtil.listFiles(context, gamesUri), 3) addGamesRecursive(games, FileUtil.listFiles(gamesUri), 3)
// Cache list of games found on disk // Cache list of games found on disk
val serializedGames = mutableSetOf<String>() val serializedGames = mutableSetOf<String>()
@ -58,7 +58,7 @@ object GameHelper {
if (it.isDirectory) { if (it.isDirectory) {
addGamesRecursive( addGamesRecursive(
games, games,
FileUtil.listFiles(YuzuApplication.appContext, it.uri), FileUtil.listFiles(it.uri),
depth - 1 depth - 1
) )
} else { } else {

View file

@ -3,64 +3,33 @@
package org.yuzu.yuzu_emu.utils package org.yuzu.yuzu_emu.utils
import android.content.Context
import android.net.Uri import android.net.Uri
import android.os.Build
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.File import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException import java.io.IOException
import java.util.zip.ZipInputStream
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.utils.FileUtil.copyUriToInternalStorage import org.yuzu.yuzu_emu.YuzuApplication
import java.util.zip.ZipException
import java.util.zip.ZipFile
object GpuDriverHelper { object GpuDriverHelper {
private const val META_JSON_FILENAME = "meta.json" private const val META_JSON_FILENAME = "meta.json"
private const val DRIVER_INTERNAL_FILENAME = "gpu_driver.zip"
private var fileRedirectionPath: String? = null private var fileRedirectionPath: String? = null
private var driverInstallationPath: String? = null var driverInstallationPath: String? = null
private var hookLibPath: String? = null private var hookLibPath: String? = null
@Throws(IOException::class) val driverStoragePath get() = DirectoryInitialization.userDirectory!! + "/gpu_drivers/"
private fun unzip(zipFilePath: String, destDir: String) {
val dir = File(destDir)
// Create output directory if it doesn't exist fun initializeDriverParameters() {
if (!dir.exists()) dir.mkdirs()
// Unpack the files.
val inputStream = FileInputStream(zipFilePath)
val zis = ZipInputStream(BufferedInputStream(inputStream))
val buffer = ByteArray(1024)
var ze = zis.nextEntry
while (ze != null) {
val newFile = File(destDir, ze.name)
val canonicalPath = newFile.canonicalPath
if (!canonicalPath.startsWith(destDir + ze.name)) {
throw SecurityException("Zip file attempted path traversal! " + ze.name)
}
newFile.parentFile!!.mkdirs()
val fos = FileOutputStream(newFile)
var len: Int
while (zis.read(buffer).also { len = it } > 0) {
fos.write(buffer, 0, len)
}
fos.close()
zis.closeEntry()
ze = zis.nextEntry
}
zis.closeEntry()
}
fun initializeDriverParameters(context: Context) {
try { try {
// Initialize the file redirection directory. // Initialize the file redirection directory.
fileRedirectionPath = fileRedirectionPath = YuzuApplication.appContext
context.getExternalFilesDir(null)!!.canonicalPath + "/gpu/vk_file_redirect/" .getExternalFilesDir(null)!!.canonicalPath + "/gpu/vk_file_redirect/"
// Initialize the driver installation directory. // Initialize the driver installation directory.
driverInstallationPath = context.filesDir.canonicalPath + "/gpu_driver/" driverInstallationPath = YuzuApplication.appContext
.filesDir.canonicalPath + "/gpu_driver/"
} catch (e: IOException) { } catch (e: IOException) {
throw RuntimeException(e) throw RuntimeException(e)
} }
@ -69,68 +38,169 @@ object GpuDriverHelper {
initializeDirectories() initializeDirectories()
// Initialize hook libraries directory. // Initialize hook libraries directory.
hookLibPath = context.applicationInfo.nativeLibraryDir + "/" hookLibPath = YuzuApplication.appContext.applicationInfo.nativeLibraryDir + "/"
// Initialize GPU driver. // Initialize GPU driver.
NativeLibrary.initializeGpuDriver( NativeLibrary.initializeGpuDriver(
hookLibPath, hookLibPath,
driverInstallationPath, driverInstallationPath,
customDriverLibraryName, customDriverData.libraryName,
fileRedirectionPath fileRedirectionPath
) )
} }
fun installDefaultDriver(context: Context) { fun getDrivers(): MutableList<Pair<String, GpuDriverMetadata>> {
// Removing the installed driver will result in the backend using the default system driver. val driverZips = File(driverStoragePath).listFiles()
val driverInstallationDir = File(driverInstallationPath!!) val drivers: MutableList<Pair<String, GpuDriverMetadata>> =
deleteRecursive(driverInstallationDir) driverZips
initializeDriverParameters(context) ?.mapNotNull {
val metadata = getMetadataFromZip(it)
metadata.name?.let { _ -> Pair(it.path, metadata) }
}
?.sortedByDescending { it: Pair<String, GpuDriverMetadata> -> it.second.name }
?.distinct()
?.toMutableList() ?: mutableListOf()
// TODO: Get system driver information
drivers.add(0, Pair("", GpuDriverMetadata()))
return drivers
} }
fun installCustomDriver(context: Context, driverPathUri: Uri?) { fun installDefaultDriver() {
// Removing the installed driver will result in the backend using the default system driver.
File(driverInstallationPath!!).deleteRecursively()
initializeDriverParameters()
}
fun copyDriverToInternalStorage(driverUri: Uri): Boolean {
// Ensure we have directories.
initializeDirectories()
// Copy the zip file URI to user data
val copiedFile =
FileUtil.copyUriToInternalStorage(driverUri, driverStoragePath) ?: return false
// Validate driver
val metadata = getMetadataFromZip(copiedFile)
if (metadata.name == null) {
copiedFile.delete()
return false
}
if (metadata.minApi > Build.VERSION.SDK_INT) {
copiedFile.delete()
return false
}
return true
}
/**
* Copies driver zip into user data directory so that it can be exported along with
* other user data and also unzipped into the installation directory
*/
fun installCustomDriver(driverUri: Uri): Boolean {
// Revert to system default in the event the specified driver is bad. // Revert to system default in the event the specified driver is bad.
installDefaultDriver(context) installDefaultDriver()
// Ensure we have directories. // Ensure we have directories.
initializeDirectories() initializeDirectories()
// Copy the zip file URI into our private storage. // Copy the zip file URI to user data
copyUriToInternalStorage( val copiedFile =
context, FileUtil.copyUriToInternalStorage(driverUri, driverStoragePath) ?: return false
driverPathUri,
driverInstallationPath!!, // Validate driver
DRIVER_INTERNAL_FILENAME val metadata = getMetadataFromZip(copiedFile)
) if (metadata.name == null) {
copiedFile.delete()
return false
}
if (metadata.minApi > Build.VERSION.SDK_INT) {
copiedFile.delete()
return false
}
// Unzip the driver. // Unzip the driver.
try { try {
unzip(driverInstallationPath + DRIVER_INTERNAL_FILENAME, driverInstallationPath!!) FileUtil.unzipToInternalStorage(
BufferedInputStream(copiedFile.inputStream()),
File(driverInstallationPath!!)
)
} catch (e: SecurityException) { } catch (e: SecurityException) {
return return false
} }
// Initialize the driver parameters. // Initialize the driver parameters.
initializeDriverParameters(context) initializeDriverParameters()
return true
}
/**
* Unzips driver into installation directory
*/
fun installCustomDriver(driver: File): Boolean {
// Revert to system default in the event the specified driver is bad.
installDefaultDriver()
// Ensure we have directories.
initializeDirectories()
// Validate driver
val metadata = getMetadataFromZip(driver)
if (metadata.name == null) {
driver.delete()
return false
}
// Unzip the driver to the private installation directory
try {
FileUtil.unzipToInternalStorage(
BufferedInputStream(driver.inputStream()),
File(driverInstallationPath!!)
)
} catch (e: SecurityException) {
return false
}
// Initialize the driver parameters.
initializeDriverParameters()
return true
}
/**
* Takes in a zip file and reads the meta.json file for presentation to the UI
*
* @param driver Zip containing driver and meta.json file
* @return A non-null [GpuDriverMetadata] instance that may have null members
*/
fun getMetadataFromZip(driver: File): GpuDriverMetadata {
try {
ZipFile(driver).use { zf ->
val entries = zf.entries()
while (entries.hasMoreElements()) {
val entry = entries.nextElement()
if (!entry.isDirectory && entry.name.lowercase().contains(".json")) {
zf.getInputStream(entry).use {
return GpuDriverMetadata(it, entry.size)
}
}
}
}
} catch (_: ZipException) {
}
return GpuDriverMetadata()
} }
external fun supportsCustomDriverLoading(): Boolean external fun supportsCustomDriverLoading(): Boolean
// Parse the custom driver metadata to retrieve the name. // Parse the custom driver metadata to retrieve the name.
val customDriverName: String? val customDriverData: GpuDriverMetadata
get() { get() = GpuDriverMetadata(File(driverInstallationPath + META_JSON_FILENAME))
val metadata = GpuDriverMetadata(driverInstallationPath + META_JSON_FILENAME)
return metadata.name
}
// Parse the custom driver metadata to retrieve the library name. fun initializeDirectories() {
private val customDriverLibraryName: String?
get() {
// Parse the custom driver metadata to retrieve the library name.
val metadata = GpuDriverMetadata(driverInstallationPath + META_JSON_FILENAME)
return metadata.libraryName
}
private fun initializeDirectories() {
// Ensure the file redirection directory exists. // Ensure the file redirection directory exists.
val fileRedirectionDir = File(fileRedirectionPath!!) val fileRedirectionDir = File(fileRedirectionPath!!)
if (!fileRedirectionDir.exists()) { if (!fileRedirectionDir.exists()) {
@ -141,14 +211,10 @@ object GpuDriverHelper {
if (!driverInstallationDir.exists()) { if (!driverInstallationDir.exists()) {
driverInstallationDir.mkdirs() driverInstallationDir.mkdirs()
} }
} // Ensure the driver storage directory exists
val driverStorageDirectory = File(driverStoragePath)
private fun deleteRecursive(fileOrDirectory: File) { if (!driverStorageDirectory.exists()) {
if (fileOrDirectory.isDirectory) { driverStorageDirectory.mkdirs()
for (child in fileOrDirectory.listFiles()!!) {
deleteRecursive(child)
}
} }
fileOrDirectory.delete()
} }
} }

View file

@ -4,29 +4,29 @@
package org.yuzu.yuzu_emu.utils package org.yuzu.yuzu_emu.utils
import java.io.IOException import java.io.IOException
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Paths
import org.json.JSONException import org.json.JSONException
import org.json.JSONObject import org.json.JSONObject
import java.io.File
import java.io.InputStream
class GpuDriverMetadata(metadataFilePath: String) { class GpuDriverMetadata {
var name: String? = null /**
var description: String? = null * Tries to get driver metadata information from a meta.json [File]
var author: String? = null *
var vendor: String? = null * @param metadataFile meta.json file provided with a GPU driver
var driverVersion: String? = null */
var minApi = 0 constructor(metadataFile: File) {
var libraryName: String? = null if (metadataFile.length() > MAX_META_SIZE_BYTES) {
return
}
init {
try { try {
val json = JSONObject(getStringFromFile(metadataFilePath)) val json = JSONObject(FileUtil.getStringFromFile(metadataFile))
name = json.getString("name") name = json.getString("name")
description = json.getString("description") description = json.getString("description")
author = json.getString("author") author = json.getString("author")
vendor = json.getString("vendor") vendor = json.getString("vendor")
driverVersion = json.getString("driverVersion") version = json.getString("driverVersion")
minApi = json.getInt("minApi") minApi = json.getInt("minApi")
libraryName = json.getString("libraryName") libraryName = json.getString("libraryName")
} catch (e: JSONException) { } catch (e: JSONException) {
@ -36,12 +36,84 @@ class GpuDriverMetadata(metadataFilePath: String) {
} }
} }
companion object { /**
@Throws(IOException::class) * Tries to get driver metadata information from an input stream that's intended to be
private fun getStringFromFile(filePath: String): String { * from a zip file
val path = Paths.get(filePath) *
val bytes = Files.readAllBytes(path) * @param metadataStream ZipEntry input stream
return String(bytes, StandardCharsets.UTF_8) * @param size Size of the file in bytes
*/
constructor(metadataStream: InputStream, size: Long) {
if (size > MAX_META_SIZE_BYTES) {
return
}
try {
val json = JSONObject(FileUtil.getStringFromInputStream(metadataStream))
name = json.getString("name")
description = json.getString("description")
author = json.getString("author")
vendor = json.getString("vendor")
version = json.getString("driverVersion")
minApi = json.getInt("minApi")
libraryName = json.getString("libraryName")
} catch (e: JSONException) {
// JSON is malformed, ignore and treat as unsupported metadata.
} catch (e: IOException) {
// File is inaccessible, ignore and treat as unsupported metadata.
} }
} }
/**
* Creates an empty metadata instance
*/
constructor()
override fun equals(other: Any?): Boolean {
if (other !is GpuDriverMetadata) {
return false
}
return other.name == name &&
other.description == description &&
other.author == author &&
other.vendor == vendor &&
other.version == version &&
other.minApi == minApi &&
other.libraryName == libraryName
}
override fun hashCode(): Int {
var result = name?.hashCode() ?: 0
result = 31 * result + (description?.hashCode() ?: 0)
result = 31 * result + (author?.hashCode() ?: 0)
result = 31 * result + (vendor?.hashCode() ?: 0)
result = 31 * result + (version?.hashCode() ?: 0)
result = 31 * result + minApi
result = 31 * result + (libraryName?.hashCode() ?: 0)
return result
}
override fun toString(): String =
"""
Name - $name
Description - $description
Author - $author
Vendor - $vendor
Version - $version
Min API - $minApi
Library Name - $libraryName
""".trimMargin().trimIndent()
var name: String? = null
var description: String? = null
var author: String? = null
var vendor: String? = null
var version: String? = null
var minApi = 0
var libraryName: String? = null
companion object {
private const val MAX_META_SIZE_BYTES = 500000
}
} }

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M22.7,19l-9.1,-9.1c0.9,-2.3 0.4,-5 -1.5,-6.9 -2,-2 -5,-2.4 -7.4,-1.3L9,6 6,9 1.6,4.7C0.4,7.1 0.9,10.1 2.9,12.1c1.9,1.9 4.6,2.4 6.9,1.5l9.1,9.1c0.4,0.4 1,0.4 1.4,0l2.3,-2.3c0.5,-0.4 0.5,-1.1 0.1,-1.4z" />
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z" />
</vector>

View file

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView 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"
style="?attr/materialCardViewOutlinedStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginVertical="12dp"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center"
android:padding="16dp">
<RadioButton
android:id="@+id/radio_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:clickable="false"
android:checked="false" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:layout_gravity="center_vertical">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/title"
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="none"
android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="viewStart"
tools:text="@string/select_gpu_driver_default" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/version"
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:ellipsize="none"
android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="viewStart"
tools:text="@string/install_gpu_driver_description" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/description"
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:ellipsize="none"
android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="viewStart"
tools:text="@string/install_gpu_driver_description" />
</LinearLayout>
<Button
android:id="@+id/button_delete"
style="@style/Widget.Material3.Button.IconButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:contentDescription="@string/delete"
android:tooltipText="@string/delete"
app:icon="@drawable/ic_delete"
app:iconTint="?attr/colorControlNormal" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>

View file

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/coordinator_licenses"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_drivers"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:liftOnScrollTargetViewId="@id/list_drivers">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_drivers"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:navigationIcon="@drawable/ic_back"
app:title="@string/gpu_driver_manager" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list_drivers"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/button_install"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:text="@string/install"
app:icon="@drawable/ic_add"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -22,6 +22,9 @@
<action <action
android:id="@+id/action_homeSettingsFragment_to_installableFragment" android:id="@+id/action_homeSettingsFragment_to_installableFragment"
app:destination="@id/installableFragment" /> app:destination="@id/installableFragment" />
<action
android:id="@+id/action_homeSettingsFragment_to_driverManagerFragment"
app:destination="@id/driverManagerFragment" />
</fragment> </fragment>
<fragment <fragment
@ -95,5 +98,9 @@
android:id="@+id/installableFragment" android:id="@+id/installableFragment"
android:name="org.yuzu.yuzu_emu.fragments.InstallableFragment" android:name="org.yuzu.yuzu_emu.fragments.InstallableFragment"
android:label="InstallableFragment" /> android:label="InstallableFragment" />
<fragment
android:id="@+id/driverManagerFragment"
android:name="org.yuzu.yuzu_emu.fragments.DriverManagerFragment"
android:label="DriverManagerFragment" />
</navigation> </navigation>

View file

@ -168,9 +168,7 @@
<string name="select_gpu_driver_title">Möchtest du deinen aktuellen GPU-Treiber ersetzen?</string> <string name="select_gpu_driver_title">Möchtest du deinen aktuellen GPU-Treiber ersetzen?</string>
<string name="select_gpu_driver_install">Installieren</string> <string name="select_gpu_driver_install">Installieren</string>
<string name="select_gpu_driver_default">Standard</string> <string name="select_gpu_driver_default">Standard</string>
<string name="select_gpu_driver_install_success">%s wurde installiert</string>
<string name="select_gpu_driver_use_default">Standard GPU-Treiber wird verwendet</string> <string name="select_gpu_driver_use_default">Standard GPU-Treiber wird verwendet</string>
<string name="select_gpu_driver_error">Ungültiger Treiber ausgewählt, Standard-Treiber wird verwendet!</string>
<string name="system_gpu_driver">System GPU-Treiber</string> <string name="system_gpu_driver">System GPU-Treiber</string>
<string name="installing_driver">Treiber wird installiert...</string> <string name="installing_driver">Treiber wird installiert...</string>

View file

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">¿Quiere reemplazar el driver de GPU actual?</string> <string name="select_gpu_driver_title">¿Quiere reemplazar el driver de GPU actual?</string>
<string name="select_gpu_driver_install">Instalar</string> <string name="select_gpu_driver_install">Instalar</string>
<string name="select_gpu_driver_default">Predeterminado</string> <string name="select_gpu_driver_default">Predeterminado</string>
<string name="select_gpu_driver_install_success">Instalado %s</string>
<string name="select_gpu_driver_use_default">Usando el driver de GPU por defecto </string> <string name="select_gpu_driver_use_default">Usando el driver de GPU por defecto </string>
<string name="select_gpu_driver_error">¡Driver no válido, utilizando el predeterminado del sistema!</string>
<string name="system_gpu_driver">Driver GPU del sistema</string> <string name="system_gpu_driver">Driver GPU del sistema</string>
<string name="installing_driver">Instalando driver...</string> <string name="installing_driver">Instalando driver...</string>

View file

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Souhaitez vous remplacer votre pilote actuel ?</string> <string name="select_gpu_driver_title">Souhaitez vous remplacer votre pilote actuel ?</string>
<string name="select_gpu_driver_install">Installer</string> <string name="select_gpu_driver_install">Installer</string>
<string name="select_gpu_driver_default">Défaut</string> <string name="select_gpu_driver_default">Défaut</string>
<string name="select_gpu_driver_install_success">%s Installé</string>
<string name="select_gpu_driver_use_default">Utilisation du pilote de GPU par défaut</string> <string name="select_gpu_driver_use_default">Utilisation du pilote de GPU par défaut</string>
<string name="select_gpu_driver_error">Pilote non valide sélectionné, utilisation du paramètre par défaut du système !</string>
<string name="system_gpu_driver">Pilote du GPU du système</string> <string name="system_gpu_driver">Pilote du GPU du système</string>
<string name="installing_driver">Installation du pilote...</string> <string name="installing_driver">Installation du pilote...</string>

View file

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Vuoi sostituire il driver della tua GPU attuale?</string> <string name="select_gpu_driver_title">Vuoi sostituire il driver della tua GPU attuale?</string>
<string name="select_gpu_driver_install">Installa</string> <string name="select_gpu_driver_install">Installa</string>
<string name="select_gpu_driver_default">Predefinito</string> <string name="select_gpu_driver_default">Predefinito</string>
<string name="select_gpu_driver_install_success">Installato%s</string>
<string name="select_gpu_driver_use_default">Utilizza il driver predefinito della GPU.</string> <string name="select_gpu_driver_use_default">Utilizza il driver predefinito della GPU.</string>
<string name="select_gpu_driver_error">Il driver selezionato è invalido, è in utilizzo quello predefinito di sistema!</string>
<string name="system_gpu_driver">Driver GPU del sistema</string> <string name="system_gpu_driver">Driver GPU del sistema</string>
<string name="installing_driver">Installando i driver...</string> <string name="installing_driver">Installando i driver...</string>

View file

@ -170,9 +170,7 @@
<string name="select_gpu_driver_title">現在のGPUドライバーを置き換えますか?</string> <string name="select_gpu_driver_title">現在のGPUドライバーを置き換えますか?</string>
<string name="select_gpu_driver_install">インストール</string> <string name="select_gpu_driver_install">インストール</string>
<string name="select_gpu_driver_default">デフォルト</string> <string name="select_gpu_driver_default">デフォルト</string>
<string name="select_gpu_driver_install_success">%s をインストールしました</string>
<string name="select_gpu_driver_use_default">デフォルトのGPUドライバーを使用します</string> <string name="select_gpu_driver_use_default">デフォルトのGPUドライバーを使用します</string>
<string name="select_gpu_driver_error">選択されたドライバが無効なため、システムのデフォルトを使用します!</string>
<string name="system_gpu_driver">システムのGPUドライバ</string> <string name="system_gpu_driver">システムのGPUドライバ</string>
<string name="installing_driver">インストール中…</string> <string name="installing_driver">インストール中…</string>

View file

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">현재 사용 중인 GPU 드라이버를 교체하겠습니까?</string> <string name="select_gpu_driver_title">현재 사용 중인 GPU 드라이버를 교체하겠습니까?</string>
<string name="select_gpu_driver_install">설치</string> <string name="select_gpu_driver_install">설치</string>
<string name="select_gpu_driver_default">기본값</string> <string name="select_gpu_driver_default">기본값</string>
<string name="select_gpu_driver_install_success">설치된 %s</string>
<string name="select_gpu_driver_use_default">기본 GPU 드라이버 사용</string> <string name="select_gpu_driver_use_default">기본 GPU 드라이버 사용</string>
<string name="select_gpu_driver_error">시스템 기본값을 사용하여 잘못된 드라이버를 선택했습니다!</string>
<string name="system_gpu_driver">시스템 GPU 드라이버</string> <string name="system_gpu_driver">시스템 GPU 드라이버</string>
<string name="installing_driver">드라이버 설치 중...</string> <string name="installing_driver">드라이버 설치 중...</string>

View file

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Ønsker du å bytte ut din nåværende GPU-driver?</string> <string name="select_gpu_driver_title">Ønsker du å bytte ut din nåværende GPU-driver?</string>
<string name="select_gpu_driver_install">Installer</string> <string name="select_gpu_driver_install">Installer</string>
<string name="select_gpu_driver_default">Standard</string> <string name="select_gpu_driver_default">Standard</string>
<string name="select_gpu_driver_install_success">Installert %s</string>
<string name="select_gpu_driver_use_default">Bruk av standard GPU-driver</string> <string name="select_gpu_driver_use_default">Bruk av standard GPU-driver</string>
<string name="select_gpu_driver_error">Ugyldig driver valgt, bruker systemstandard!</string>
<string name="system_gpu_driver">Systemets GPU-driver</string> <string name="system_gpu_driver">Systemets GPU-driver</string>
<string name="installing_driver">Installerer driver...</string> <string name="installing_driver">Installerer driver...</string>

View file

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Chcesz zastąpić obecny sterownik układu graficznego?</string> <string name="select_gpu_driver_title">Chcesz zastąpić obecny sterownik układu graficznego?</string>
<string name="select_gpu_driver_install">Zainstaluj</string> <string name="select_gpu_driver_install">Zainstaluj</string>
<string name="select_gpu_driver_default">Domyślne</string> <string name="select_gpu_driver_default">Domyślne</string>
<string name="select_gpu_driver_install_success">Zainstalowano %s</string>
<string name="select_gpu_driver_use_default">Aktywny domyślny sterownik GPU</string> <string name="select_gpu_driver_use_default">Aktywny domyślny sterownik GPU</string>
<string name="select_gpu_driver_error">Wybrano błędny sterownik, powrót do domyślnego. </string>
<string name="system_gpu_driver">Systemowy sterownik GPU</string> <string name="system_gpu_driver">Systemowy sterownik GPU</string>
<string name="installing_driver">Instalowanie sterownika...</string> <string name="installing_driver">Instalowanie sterownika...</string>

View file

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string> <string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string>
<string name="select_gpu_driver_install">Instalar</string> <string name="select_gpu_driver_install">Instalar</string>
<string name="select_gpu_driver_default">Padrão</string> <string name="select_gpu_driver_default">Padrão</string>
<string name="select_gpu_driver_install_success">Instalado%s</string>
<string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string> <string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string>
<string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string>
<string name="system_gpu_driver">Driver do GPU padrão</string> <string name="system_gpu_driver">Driver do GPU padrão</string>
<string name="installing_driver">A instalar o Driver...</string> <string name="installing_driver">A instalar o Driver...</string>

View file

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string> <string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string>
<string name="select_gpu_driver_install">Instalar</string> <string name="select_gpu_driver_install">Instalar</string>
<string name="select_gpu_driver_default">Padrão</string> <string name="select_gpu_driver_default">Padrão</string>
<string name="select_gpu_driver_install_success">Instalado%s</string>
<string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string> <string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string>
<string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string>
<string name="system_gpu_driver">Driver do GPU padrão</string> <string name="system_gpu_driver">Driver do GPU padrão</string>
<string name="installing_driver">A instalar o Driver...</string> <string name="installing_driver">A instalar o Driver...</string>

View file

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Хотите заменить текущий драйвер ГП?</string> <string name="select_gpu_driver_title">Хотите заменить текущий драйвер ГП?</string>
<string name="select_gpu_driver_install">Установить</string> <string name="select_gpu_driver_install">Установить</string>
<string name="select_gpu_driver_default">По умолчанию</string> <string name="select_gpu_driver_default">По умолчанию</string>
<string name="select_gpu_driver_install_success">Установлено %s</string>
<string name="select_gpu_driver_use_default">Используется стандартный драйвер ГП </string> <string name="select_gpu_driver_use_default">Используется стандартный драйвер ГП </string>
<string name="select_gpu_driver_error">Выбран неверный драйвер, используется стандартный системный!</string>
<string name="system_gpu_driver">Системный драйвер ГП</string> <string name="system_gpu_driver">Системный драйвер ГП</string>
<string name="installing_driver">Установка драйвера...</string> <string name="installing_driver">Установка драйвера...</string>

View file

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Хочете замінити поточний драйвер ГП?</string> <string name="select_gpu_driver_title">Хочете замінити поточний драйвер ГП?</string>
<string name="select_gpu_driver_install">Встановити</string> <string name="select_gpu_driver_install">Встановити</string>
<string name="select_gpu_driver_default">За замовчуванням</string> <string name="select_gpu_driver_default">За замовчуванням</string>
<string name="select_gpu_driver_install_success">Встановлено %s</string>
<string name="select_gpu_driver_use_default">Використовується стандартний драйвер ГП</string> <string name="select_gpu_driver_use_default">Використовується стандартний драйвер ГП</string>
<string name="select_gpu_driver_error">Обрано неправильний драйвер, використовується стандартний системний!</string>
<string name="system_gpu_driver">Системний драйвер ГП</string> <string name="system_gpu_driver">Системний драйвер ГП</string>
<string name="installing_driver">Встановлення драйвера...</string> <string name="installing_driver">Встановлення драйвера...</string>

View file

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">要取代您当前的 GPU 驱动程序吗?</string> <string name="select_gpu_driver_title">要取代您当前的 GPU 驱动程序吗?</string>
<string name="select_gpu_driver_install">安装</string> <string name="select_gpu_driver_install">安装</string>
<string name="select_gpu_driver_default">系统默认</string> <string name="select_gpu_driver_default">系统默认</string>
<string name="select_gpu_driver_install_success">已安装 %s</string>
<string name="select_gpu_driver_use_default">使用默认 GPU 驱动程序</string> <string name="select_gpu_driver_use_default">使用默认 GPU 驱动程序</string>
<string name="select_gpu_driver_error">选择的驱动程序无效,将使用系统默认的驱动程序!</string>
<string name="system_gpu_driver">系统 GPU 驱动程序</string> <string name="system_gpu_driver">系统 GPU 驱动程序</string>
<string name="installing_driver">正在安装驱动程序…</string> <string name="installing_driver">正在安装驱动程序…</string>

View file

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">要取代您目前的 GPU 驅動程式嗎?</string> <string name="select_gpu_driver_title">要取代您目前的 GPU 驅動程式嗎?</string>
<string name="select_gpu_driver_install">安裝</string> <string name="select_gpu_driver_install">安裝</string>
<string name="select_gpu_driver_default">預設</string> <string name="select_gpu_driver_default">預設</string>
<string name="select_gpu_driver_install_success">已安裝 %s</string>
<string name="select_gpu_driver_use_default">使用預設 GPU 驅動程式</string> <string name="select_gpu_driver_use_default">使用預設 GPU 驅動程式</string>
<string name="select_gpu_driver_error">選取的驅動程式無效,將使用系統預設驅動程式!</string>
<string name="system_gpu_driver">系統 GPU 驅動程式</string> <string name="system_gpu_driver">系統 GPU 驅動程式</string>
<string name="installing_driver">正在安裝驅動程式…</string> <string name="installing_driver">正在安裝驅動程式…</string>

View file

@ -13,6 +13,8 @@
<dimen name="menu_width">256dp</dimen> <dimen name="menu_width">256dp</dimen>
<dimen name="card_width">165dp</dimen> <dimen name="card_width">165dp</dimen>
<dimen name="icon_inset">24dp</dimen> <dimen name="icon_inset">24dp</dimen>
<dimen name="spacing_bottom_list_fab">72dp</dimen>
<dimen name="spacing_fab">24dp</dimen>
<dimen name="dialog_margin">20dp</dimen> <dimen name="dialog_margin">20dp</dimen>
<dimen name="elevated_app_bar">3dp</dimen> <dimen name="elevated_app_bar">3dp</dimen>

View file

@ -72,6 +72,7 @@
<string name="invalid_keys_error">Invalid encryption keys</string> <string name="invalid_keys_error">Invalid encryption keys</string>
<string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
<string name="install_keys_failure_description">The selected file is incorrect or corrupt. Please redump your keys.</string> <string name="install_keys_failure_description">The selected file is incorrect or corrupt. Please redump your keys.</string>
<string name="gpu_driver_manager">GPU Driver Manager</string>
<string name="install_gpu_driver">Install GPU driver</string> <string name="install_gpu_driver">Install GPU driver</string>
<string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string> <string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string>
<string name="advanced_settings">Advanced settings</string> <string name="advanced_settings">Advanced settings</string>
@ -234,15 +235,17 @@
<string name="export_failed">Export failed</string> <string name="export_failed">Export failed</string>
<string name="import_failed">Import failed</string> <string name="import_failed">Import failed</string>
<string name="cancelling">Cancelling</string> <string name="cancelling">Cancelling</string>
<string name="install">Install</string>
<string name="delete">Delete</string>
<!-- GPU driver installation --> <!-- GPU driver installation -->
<string name="select_gpu_driver">Select GPU driver</string> <string name="select_gpu_driver">Select GPU driver</string>
<string name="select_gpu_driver_title">Would you like to replace your current GPU driver?</string> <string name="select_gpu_driver_title">Would you like to replace your current GPU driver?</string>
<string name="select_gpu_driver_install">Install</string> <string name="select_gpu_driver_install">Install</string>
<string name="select_gpu_driver_default">Default</string> <string name="select_gpu_driver_default">Default</string>
<string name="select_gpu_driver_install_success">Installed %s</string>
<string name="select_gpu_driver_use_default">Using default GPU driver</string> <string name="select_gpu_driver_use_default">Using default GPU driver</string>
<string name="select_gpu_driver_error">Invalid driver selected, using system default!</string> <string name="select_gpu_driver_error">Invalid driver selected</string>
<string name="driver_already_installed">Driver already installed</string>
<string name="system_gpu_driver">System GPU driver</string> <string name="system_gpu_driver">System GPU driver</string>
<string name="installing_driver">Installing driver…</string> <string name="installing_driver">Installing driver…</string>

View file

@ -106,7 +106,7 @@ static_assert(KernelPageBufferAdditionalSize ==
/// memory. /// memory.
static KPhysicalAddress TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout, static KPhysicalAddress TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout,
KVirtualAddress slab_addr) { KVirtualAddress slab_addr) {
slab_addr -= GetInteger(memory_layout.GetSlabRegionAddress()); slab_addr -= memory_layout.GetSlabRegion().GetAddress();
return GetInteger(slab_addr) + Core::DramMemoryMap::SlabHeapBase; return GetInteger(slab_addr) + Core::DramMemoryMap::SlabHeapBase;
} }
@ -196,7 +196,12 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
auto& kernel = system.Kernel(); auto& kernel = system.Kernel();
// Get the start of the slab region, since that's where we'll be working. // Get the start of the slab region, since that's where we'll be working.
KVirtualAddress address = memory_layout.GetSlabRegionAddress(); const KMemoryRegion& slab_region = memory_layout.GetSlabRegion();
KVirtualAddress address = slab_region.GetAddress();
// Clear the slab region.
// TODO: implement access to kernel VAs.
// std::memset(device_ptr, 0, slab_region.GetSize());
// Initialize slab type array to be in sorted order. // Initialize slab type array to be in sorted order.
std::array<KSlabType, KSlabType_Count> slab_types; std::array<KSlabType, KSlabType_Count> slab_types;

View file

@ -19,4 +19,8 @@ static inline KPhysicalAddress GetInitialProcessBinaryPhysicalAddress() {
MainMemoryAddress); MainMemoryAddress);
} }
static inline size_t GetInitialProcessBinarySize() {
return InitialProcessBinarySizeMax;
}
} // namespace Kernel } // namespace Kernel

View file

@ -36,6 +36,7 @@ enum class KMemoryState : u32 {
FlagCanChangeAttribute = (1 << 24), FlagCanChangeAttribute = (1 << 24),
FlagCanCodeMemory = (1 << 25), FlagCanCodeMemory = (1 << 25),
FlagLinearMapped = (1 << 26), FlagLinearMapped = (1 << 26),
FlagCanPermissionLock = (1 << 27),
FlagsData = FlagCanReprotect | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc | FlagsData = FlagCanReprotect | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc |
FlagMapped | FlagCanAlias | FlagCanTransfer | FlagCanQueryPhysical | FlagMapped | FlagCanAlias | FlagCanTransfer | FlagCanQueryPhysical |
@ -50,12 +51,16 @@ enum class KMemoryState : u32 {
FlagLinearMapped, FlagLinearMapped,
Free = static_cast<u32>(Svc::MemoryState::Free), Free = static_cast<u32>(Svc::MemoryState::Free),
Io = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped | FlagCanDeviceMap |
FlagCanAlignedDeviceMap, IoMemory = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped | FlagCanDeviceMap |
FlagCanAlignedDeviceMap,
IoRegister =
static_cast<u32>(Svc::MemoryState::Io) | FlagCanDeviceMap | FlagCanAlignedDeviceMap,
Static = static_cast<u32>(Svc::MemoryState::Static) | FlagMapped | FlagCanQueryPhysical, Static = static_cast<u32>(Svc::MemoryState::Static) | FlagMapped | FlagCanQueryPhysical,
Code = static_cast<u32>(Svc::MemoryState::Code) | FlagsCode | FlagCanMapProcess, Code = static_cast<u32>(Svc::MemoryState::Code) | FlagsCode | FlagCanMapProcess,
CodeData = static_cast<u32>(Svc::MemoryState::CodeData) | FlagsData | FlagCanMapProcess | CodeData = static_cast<u32>(Svc::MemoryState::CodeData) | FlagsData | FlagCanMapProcess |
FlagCanCodeMemory, FlagCanCodeMemory | FlagCanPermissionLock,
Normal = static_cast<u32>(Svc::MemoryState::Normal) | FlagsData | FlagCanCodeMemory, Normal = static_cast<u32>(Svc::MemoryState::Normal) | FlagsData | FlagCanCodeMemory,
Shared = static_cast<u32>(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted | Shared = static_cast<u32>(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted |
FlagLinearMapped, FlagLinearMapped,
@ -65,7 +70,8 @@ enum class KMemoryState : u32 {
AliasCode = static_cast<u32>(Svc::MemoryState::AliasCode) | FlagsCode | FlagCanMapProcess | AliasCode = static_cast<u32>(Svc::MemoryState::AliasCode) | FlagsCode | FlagCanMapProcess |
FlagCanCodeAlias, FlagCanCodeAlias,
AliasCodeData = static_cast<u32>(Svc::MemoryState::AliasCodeData) | FlagsData | AliasCodeData = static_cast<u32>(Svc::MemoryState::AliasCodeData) | FlagsData |
FlagCanMapProcess | FlagCanCodeAlias | FlagCanCodeMemory, FlagCanMapProcess | FlagCanCodeAlias | FlagCanCodeMemory |
FlagCanPermissionLock,
Ipc = static_cast<u32>(Svc::MemoryState::Ipc) | FlagsMisc | FlagCanAlignedDeviceMap | Ipc = static_cast<u32>(Svc::MemoryState::Ipc) | FlagsMisc | FlagCanAlignedDeviceMap |
FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
@ -73,7 +79,7 @@ enum class KMemoryState : u32 {
Stack = static_cast<u32>(Svc::MemoryState::Stack) | FlagsMisc | FlagCanAlignedDeviceMap | Stack = static_cast<u32>(Svc::MemoryState::Stack) | FlagsMisc | FlagCanAlignedDeviceMap |
FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
ThreadLocal = static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagLinearMapped, ThreadLocal = static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagLinearMapped,
Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc | Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc |
FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc | FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
@ -94,7 +100,7 @@ enum class KMemoryState : u32 {
NonDeviceIpc = NonDeviceIpc =
static_cast<u32>(Svc::MemoryState::NonDeviceIpc) | FlagsMisc | FlagCanUseNonDeviceIpc, static_cast<u32>(Svc::MemoryState::NonDeviceIpc) | FlagsMisc | FlagCanUseNonDeviceIpc,
Kernel = static_cast<u32>(Svc::MemoryState::Kernel) | FlagMapped, Kernel = static_cast<u32>(Svc::MemoryState::Kernel),
GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped | GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped |
FlagReferenceCounted | FlagCanDebug | FlagLinearMapped, FlagReferenceCounted | FlagCanDebug | FlagLinearMapped,
@ -105,34 +111,36 @@ enum class KMemoryState : u32 {
Insecure = static_cast<u32>(Svc::MemoryState::Insecure) | FlagMapped | FlagReferenceCounted | Insecure = static_cast<u32>(Svc::MemoryState::Insecure) | FlagMapped | FlagReferenceCounted |
FlagLinearMapped | FlagCanChangeAttribute | FlagCanDeviceMap | FlagLinearMapped | FlagCanChangeAttribute | FlagCanDeviceMap |
FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, FlagCanAlignedDeviceMap | FlagCanQueryPhysical | FlagCanUseNonSecureIpc |
FlagCanUseNonDeviceIpc,
}; };
DECLARE_ENUM_FLAG_OPERATORS(KMemoryState); DECLARE_ENUM_FLAG_OPERATORS(KMemoryState);
static_assert(static_cast<u32>(KMemoryState::Free) == 0x00000000); static_assert(static_cast<u32>(KMemoryState::Free) == 0x00000000);
static_assert(static_cast<u32>(KMemoryState::Io) == 0x00182001); static_assert(static_cast<u32>(KMemoryState::IoMemory) == 0x00182001);
static_assert(static_cast<u32>(KMemoryState::IoRegister) == 0x00180001);
static_assert(static_cast<u32>(KMemoryState::Static) == 0x00042002); static_assert(static_cast<u32>(KMemoryState::Static) == 0x00042002);
static_assert(static_cast<u32>(KMemoryState::Code) == 0x04DC7E03); static_assert(static_cast<u32>(KMemoryState::Code) == 0x04DC7E03);
static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x07FEBD04); static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x0FFEBD04);
static_assert(static_cast<u32>(KMemoryState::Normal) == 0x077EBD05); static_assert(static_cast<u32>(KMemoryState::Normal) == 0x077EBD05);
static_assert(static_cast<u32>(KMemoryState::Shared) == 0x04402006); static_assert(static_cast<u32>(KMemoryState::Shared) == 0x04402006);
static_assert(static_cast<u32>(KMemoryState::AliasCode) == 0x04DD7E08); static_assert(static_cast<u32>(KMemoryState::AliasCode) == 0x04DD7E08);
static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x07FFBD09); static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x0FFFBD09);
static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x045C3C0A); static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x045C3C0A);
static_assert(static_cast<u32>(KMemoryState::Stack) == 0x045C3C0B); static_assert(static_cast<u32>(KMemoryState::Stack) == 0x045C3C0B);
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0400200C); static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0400000C);
static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x055C3C0D); static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x055C3C0D);
static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x045C380E); static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x045C380E);
static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0440380F); static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0440380F);
static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010); static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x045C3811); static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x045C3811);
static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x044C2812); static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x044C2812);
static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00002013); static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00000013);
static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x04402214); static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x04402214);
static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x04402015); static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x04402015);
static_assert(static_cast<u32>(KMemoryState::Coverage) == 0x00002016); static_assert(static_cast<u32>(KMemoryState::Coverage) == 0x00002016);
static_assert(static_cast<u32>(KMemoryState::Insecure) == 0x05583817); static_assert(static_cast<u32>(KMemoryState::Insecure) == 0x055C3817);
enum class KMemoryPermission : u8 { enum class KMemoryPermission : u8 {
None = 0, None = 0,
@ -182,8 +190,9 @@ enum class KMemoryAttribute : u8 {
IpcLocked = static_cast<u8>(Svc::MemoryAttribute::IpcLocked), IpcLocked = static_cast<u8>(Svc::MemoryAttribute::IpcLocked),
DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared), DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared),
Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached), Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached),
PermissionLocked = static_cast<u8>(Svc::MemoryAttribute::PermissionLocked),
SetMask = Uncached, SetMask = Uncached | PermissionLocked,
}; };
DECLARE_ENUM_FLAG_OPERATORS(KMemoryAttribute); DECLARE_ENUM_FLAG_OPERATORS(KMemoryAttribute);
@ -261,6 +270,10 @@ struct KMemoryInfo {
return m_state; return m_state;
} }
constexpr Svc::MemoryState GetSvcState() const {
return static_cast<Svc::MemoryState>(m_state & KMemoryState::Mask);
}
constexpr KMemoryPermission GetPermission() const { constexpr KMemoryPermission GetPermission() const {
return m_permission; return m_permission;
} }
@ -326,6 +339,10 @@ public:
return this->GetEndAddress() - 1; return this->GetEndAddress() - 1;
} }
constexpr KMemoryState GetState() const {
return m_memory_state;
}
constexpr u16 GetIpcLockCount() const { constexpr u16 GetIpcLockCount() const {
return m_ipc_lock_count; return m_ipc_lock_count;
} }
@ -443,6 +460,13 @@ public:
} }
} }
constexpr void UpdateAttribute(KMemoryAttribute mask, KMemoryAttribute attr) {
ASSERT(False(mask & KMemoryAttribute::IpcLocked));
ASSERT(False(mask & KMemoryAttribute::DeviceShared));
m_attribute = (m_attribute & ~mask) | attr;
}
constexpr void Split(KMemoryBlock* block, KProcessAddress addr) { constexpr void Split(KMemoryBlock* block, KProcessAddress addr) {
ASSERT(this->GetAddress() < addr); ASSERT(this->GetAddress() < addr);
ASSERT(this->Contains(addr)); ASSERT(this->Contains(addr));

View file

@ -160,8 +160,8 @@ void KMemoryBlockManager::Update(KMemoryBlockManagerUpdateAllocator* allocator,
} }
// Update block state. // Update block state.
it->Update(state, perm, attr, cur_address == address, static_cast<u8>(set_disable_attr), it->Update(state, perm, attr, it->GetAddress() == address,
static_cast<u8>(clear_disable_attr)); static_cast<u8>(set_disable_attr), static_cast<u8>(clear_disable_attr));
cur_address += cur_info.GetSize(); cur_address += cur_info.GetSize();
remaining_pages -= cur_info.GetNumPages(); remaining_pages -= cur_info.GetNumPages();
} }
@ -175,7 +175,9 @@ void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allo
KProcessAddress address, size_t num_pages, KProcessAddress address, size_t num_pages,
KMemoryState test_state, KMemoryPermission test_perm, KMemoryState test_state, KMemoryPermission test_perm,
KMemoryAttribute test_attr, KMemoryState state, KMemoryAttribute test_attr, KMemoryState state,
KMemoryPermission perm, KMemoryAttribute attr) { KMemoryPermission perm, KMemoryAttribute attr,
KMemoryBlockDisableMergeAttribute set_disable_attr,
KMemoryBlockDisableMergeAttribute clear_disable_attr) {
// Ensure for auditing that we never end up with an invalid tree. // Ensure for auditing that we never end up with an invalid tree.
KScopedMemoryBlockManagerAuditor auditor(this); KScopedMemoryBlockManagerAuditor auditor(this);
ASSERT(Common::IsAligned(GetInteger(address), PageSize)); ASSERT(Common::IsAligned(GetInteger(address), PageSize));
@ -214,7 +216,8 @@ void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allo
} }
// Update block state. // Update block state.
it->Update(state, perm, attr, false, 0, 0); it->Update(state, perm, attr, false, static_cast<u8>(set_disable_attr),
static_cast<u8>(clear_disable_attr));
cur_address += cur_info.GetSize(); cur_address += cur_info.GetSize();
remaining_pages -= cur_info.GetNumPages(); remaining_pages -= cur_info.GetNumPages();
} else { } else {
@ -284,6 +287,65 @@ void KMemoryBlockManager::UpdateLock(KMemoryBlockManagerUpdateAllocator* allocat
this->CoalesceForUpdate(allocator, address, num_pages); this->CoalesceForUpdate(allocator, address, num_pages);
} }
void KMemoryBlockManager::UpdateAttribute(KMemoryBlockManagerUpdateAllocator* allocator,
KProcessAddress address, size_t num_pages,
KMemoryAttribute mask, KMemoryAttribute attr) {
// Ensure for auditing that we never end up with an invalid tree.
KScopedMemoryBlockManagerAuditor auditor(this);
ASSERT(Common::IsAligned(GetInteger(address), PageSize));
KProcessAddress cur_address = address;
size_t remaining_pages = num_pages;
iterator it = this->FindIterator(address);
while (remaining_pages > 0) {
const size_t remaining_size = remaining_pages * PageSize;
KMemoryInfo cur_info = it->GetMemoryInfo();
if ((it->GetAttribute() & mask) != attr) {
// If we need to, create a new block before and insert it.
if (cur_info.GetAddress() != GetInteger(cur_address)) {
KMemoryBlock* new_block = allocator->Allocate();
it->Split(new_block, cur_address);
it = m_memory_block_tree.insert(*new_block);
it++;
cur_info = it->GetMemoryInfo();
cur_address = cur_info.GetAddress();
}
// If we need to, create a new block after and insert it.
if (cur_info.GetSize() > remaining_size) {
KMemoryBlock* new_block = allocator->Allocate();
it->Split(new_block, cur_address + remaining_size);
it = m_memory_block_tree.insert(*new_block);
cur_info = it->GetMemoryInfo();
}
// Update block state.
it->UpdateAttribute(mask, attr);
cur_address += cur_info.GetSize();
remaining_pages -= cur_info.GetNumPages();
} else {
// If we already have the right attributes, just advance.
if (cur_address + remaining_size < cur_info.GetEndAddress()) {
remaining_pages = 0;
cur_address += remaining_size;
} else {
remaining_pages =
(cur_address + remaining_size - cur_info.GetEndAddress()) / PageSize;
cur_address = cur_info.GetEndAddress();
}
}
it++;
}
this->CoalesceForUpdate(allocator, address, num_pages);
}
// Debug. // Debug.
bool KMemoryBlockManager::CheckState() const { bool KMemoryBlockManager::CheckState() const {
// Loop over every block, ensuring that we are sorted and coalesced. // Loop over every block, ensuring that we are sorted and coalesced.

View file

@ -115,7 +115,11 @@ public:
void UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allocator, KProcessAddress address, void UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allocator, KProcessAddress address,
size_t num_pages, KMemoryState test_state, KMemoryPermission test_perm, size_t num_pages, KMemoryState test_state, KMemoryPermission test_perm,
KMemoryAttribute test_attr, KMemoryState state, KMemoryPermission perm, KMemoryAttribute test_attr, KMemoryState state, KMemoryPermission perm,
KMemoryAttribute attr); KMemoryAttribute attr, KMemoryBlockDisableMergeAttribute set_disable_attr,
KMemoryBlockDisableMergeAttribute clear_disable_attr);
void UpdateAttribute(KMemoryBlockManagerUpdateAllocator* allocator, KProcessAddress address,
size_t num_pages, KMemoryAttribute mask, KMemoryAttribute attr);
iterator FindIterator(KProcessAddress address) const { iterator FindIterator(KProcessAddress address) const {
return m_memory_block_tree.find(KMemoryBlock( return m_memory_block_tree.find(KMemoryBlock(

View file

@ -137,11 +137,9 @@ public:
return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscExceptionStack); return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscExceptionStack);
} }
KVirtualAddress GetSlabRegionAddress() const { const KMemoryRegion& GetSlabRegion() const {
return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelSlab)) return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelSlab));
.GetAddress();
} }
const KMemoryRegion& GetDeviceRegion(KMemoryRegionType type) const { const KMemoryRegion& GetDeviceRegion(KMemoryRegionType type) const {
return Dereference(GetPhysicalMemoryRegionTree().FindFirstDerived(type)); return Dereference(GetPhysicalMemoryRegionTree().FindFirstDerived(type));
} }

View file

@ -119,7 +119,8 @@ void KMemoryManager::Initialize(KVirtualAddress management_region, size_t manage
// Free each region to its corresponding heap. // Free each region to its corresponding heap.
size_t reserved_sizes[MaxManagerCount] = {}; size_t reserved_sizes[MaxManagerCount] = {};
const KPhysicalAddress ini_start = GetInitialProcessBinaryPhysicalAddress(); const KPhysicalAddress ini_start = GetInitialProcessBinaryPhysicalAddress();
const KPhysicalAddress ini_end = ini_start + InitialProcessBinarySizeMax; const size_t ini_size = GetInitialProcessBinarySize();
const KPhysicalAddress ini_end = ini_start + ini_size;
const KPhysicalAddress ini_last = ini_end - 1; const KPhysicalAddress ini_last = ini_end - 1;
for (const auto& it : m_system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) { for (const auto& it : m_system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) { if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
@ -137,13 +138,13 @@ void KMemoryManager::Initialize(KVirtualAddress management_region, size_t manage
} }
// Open/reserve the ini memory. // Open/reserve the ini memory.
manager.OpenFirst(ini_start, InitialProcessBinarySizeMax / PageSize); manager.OpenFirst(ini_start, ini_size / PageSize);
reserved_sizes[it.GetAttributes()] += InitialProcessBinarySizeMax; reserved_sizes[it.GetAttributes()] += ini_size;
// Free memory after the ini to the heap. // Free memory after the ini to the heap.
if (ini_last != cur_last) { if (ini_last != cur_last) {
ASSERT(cur_end != 0); ASSERT(cur_end != 0);
manager.Free(ini_end, cur_end - ini_end); manager.Free(ini_end, (cur_end - ini_end) / PageSize);
} }
} else { } else {
// Ensure there's no partial overlap with the ini image. // Ensure there's no partial overlap with the ini image.

View file

@ -190,9 +190,15 @@ static_assert(KMemoryRegionType_DramKernelInitPt.GetValue() ==
constexpr inline auto KMemoryRegionType_DramKernelSecureAppletMemory = constexpr inline auto KMemoryRegionType_DramKernelSecureAppletMemory =
KMemoryRegionType_DramKernelBase.DeriveSparse(1, 3, 0).SetAttribute( KMemoryRegionType_DramKernelBase.DeriveSparse(1, 3, 0).SetAttribute(
KMemoryRegionAttr_LinearMapped); KMemoryRegionAttr_LinearMapped);
constexpr inline const auto KMemoryRegionType_DramKernelSecureUnknown =
KMemoryRegionType_DramKernelBase.DeriveSparse(1, 3, 1).SetAttribute(
KMemoryRegionAttr_LinearMapped);
static_assert(KMemoryRegionType_DramKernelSecureAppletMemory.GetValue() == static_assert(KMemoryRegionType_DramKernelSecureAppletMemory.GetValue() ==
(0x18E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap | (0x18E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
KMemoryRegionAttr_LinearMapped)); KMemoryRegionAttr_LinearMapped));
static_assert(KMemoryRegionType_DramKernelSecureUnknown.GetValue() ==
(0x28E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
KMemoryRegionAttr_LinearMapped));
constexpr inline auto KMemoryRegionType_DramReservedEarly = constexpr inline auto KMemoryRegionType_DramReservedEarly =
KMemoryRegionType_DramReservedBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap); KMemoryRegionType_DramReservedBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
@ -217,16 +223,18 @@ constexpr inline auto KMemoryRegionType_DramPoolPartition =
static_assert(KMemoryRegionType_DramPoolPartition.GetValue() == static_assert(KMemoryRegionType_DramPoolPartition.GetValue() ==
(0x26 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap)); (0x26 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
constexpr inline auto KMemoryRegionType_DramPoolManagement = // UNUSED: .Derive(4, 1);
KMemoryRegionType_DramPoolPartition.DeriveTransition(0, 2).DeriveTransition().SetAttribute( // UNUSED: .Derive(4, 2);
constexpr inline const auto KMemoryRegionType_DramPoolManagement =
KMemoryRegionType_DramPoolPartition.Derive(4, 0).SetAttribute(
KMemoryRegionAttr_CarveoutProtected); KMemoryRegionAttr_CarveoutProtected);
constexpr inline auto KMemoryRegionType_DramUserPool = constexpr inline const auto KMemoryRegionType_DramUserPool =
KMemoryRegionType_DramPoolPartition.DeriveTransition(1, 2).DeriveTransition(); KMemoryRegionType_DramPoolPartition.Derive(4, 3);
static_assert(KMemoryRegionType_DramPoolManagement.GetValue() == static_assert(KMemoryRegionType_DramPoolManagement.GetValue() ==
(0x166 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap | (0xE6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
KMemoryRegionAttr_CarveoutProtected)); KMemoryRegionAttr_CarveoutProtected));
static_assert(KMemoryRegionType_DramUserPool.GetValue() == static_assert(KMemoryRegionType_DramUserPool.GetValue() ==
(0x1A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap)); (0x266 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
constexpr inline auto KMemoryRegionType_DramApplicationPool = constexpr inline auto KMemoryRegionType_DramApplicationPool =
KMemoryRegionType_DramUserPool.Derive(4, 0); KMemoryRegionType_DramUserPool.Derive(4, 0);
@ -237,60 +245,63 @@ constexpr inline auto KMemoryRegionType_DramSystemNonSecurePool =
constexpr inline auto KMemoryRegionType_DramSystemPool = constexpr inline auto KMemoryRegionType_DramSystemPool =
KMemoryRegionType_DramUserPool.Derive(4, 3).SetAttribute(KMemoryRegionAttr_CarveoutProtected); KMemoryRegionType_DramUserPool.Derive(4, 3).SetAttribute(KMemoryRegionAttr_CarveoutProtected);
static_assert(KMemoryRegionType_DramApplicationPool.GetValue() == static_assert(KMemoryRegionType_DramApplicationPool.GetValue() ==
(0x7A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap)); (0xE66 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_DramAppletPool.GetValue() == static_assert(KMemoryRegionType_DramAppletPool.GetValue() ==
(0xBA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap)); (0x1666 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_DramSystemNonSecurePool.GetValue() == static_assert(KMemoryRegionType_DramSystemNonSecurePool.GetValue() ==
(0xDA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap)); (0x1A66 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_DramSystemPool.GetValue() == static_assert(KMemoryRegionType_DramSystemPool.GetValue() ==
(0x13A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap | (0x2666 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
KMemoryRegionAttr_CarveoutProtected)); KMemoryRegionAttr_CarveoutProtected));
constexpr inline auto KMemoryRegionType_VirtualDramHeapBase = constexpr inline auto KMemoryRegionType_VirtualDramHeapBase =
KMemoryRegionType_Dram.DeriveSparse(1, 3, 0); KMemoryRegionType_Dram.DeriveSparse(1, 4, 0);
constexpr inline auto KMemoryRegionType_VirtualDramKernelPtHeap = constexpr inline auto KMemoryRegionType_VirtualDramKernelPtHeap =
KMemoryRegionType_Dram.DeriveSparse(1, 3, 1); KMemoryRegionType_Dram.DeriveSparse(1, 4, 1);
constexpr inline auto KMemoryRegionType_VirtualDramKernelTraceBuffer = constexpr inline auto KMemoryRegionType_VirtualDramKernelTraceBuffer =
KMemoryRegionType_Dram.DeriveSparse(1, 3, 2); KMemoryRegionType_Dram.DeriveSparse(1, 4, 2);
static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A); static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A);
static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A); static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A);
static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A); static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A);
// UNUSED: .DeriveSparse(2, 2, 0); // UNUSED: .Derive(4, 2);
constexpr inline auto KMemoryRegionType_VirtualDramUnknownDebug = constexpr inline const auto KMemoryRegionType_VirtualDramUnknownDebug =
KMemoryRegionType_Dram.DeriveSparse(2, 2, 1); KMemoryRegionType_Dram.Advance(2).Derive(4, 0);
static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52)); constexpr inline const auto KMemoryRegionType_VirtualDramKernelSecureAppletMemory =
KMemoryRegionType_Dram.Advance(2).Derive(4, 1);
constexpr inline const auto KMemoryRegionType_VirtualDramKernelSecureUnknown =
KMemoryRegionType_Dram.Advance(2).Derive(4, 3);
static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x32));
static_assert(KMemoryRegionType_VirtualDramKernelSecureAppletMemory.GetValue() == (0x52));
static_assert(KMemoryRegionType_VirtualDramKernelSecureUnknown.GetValue() == (0x92));
constexpr inline auto KMemoryRegionType_VirtualDramKernelSecureAppletMemory = // UNUSED: .Derive(4, 3);
KMemoryRegionType_Dram.DeriveSparse(3, 1, 0); constexpr inline const auto KMemoryRegionType_VirtualDramKernelInitPt =
static_assert(KMemoryRegionType_VirtualDramKernelSecureAppletMemory.GetValue() == (0x62)); KMemoryRegionType_VirtualDramHeapBase.Derive(4, 0);
constexpr inline const auto KMemoryRegionType_VirtualDramPoolManagement =
constexpr inline auto KMemoryRegionType_VirtualDramKernelInitPt = KMemoryRegionType_VirtualDramHeapBase.Derive(4, 1);
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0); constexpr inline const auto KMemoryRegionType_VirtualDramUserPool =
constexpr inline auto KMemoryRegionType_VirtualDramPoolManagement = KMemoryRegionType_VirtualDramHeapBase.Derive(4, 2);
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 1); static_assert(KMemoryRegionType_VirtualDramKernelInitPt.GetValue() == 0x31A);
constexpr inline auto KMemoryRegionType_VirtualDramUserPool = static_assert(KMemoryRegionType_VirtualDramPoolManagement.GetValue() == 0x51A);
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 2); static_assert(KMemoryRegionType_VirtualDramUserPool.GetValue() == 0x61A);
static_assert(KMemoryRegionType_VirtualDramKernelInitPt.GetValue() == 0x19A);
static_assert(KMemoryRegionType_VirtualDramPoolManagement.GetValue() == 0x29A);
static_assert(KMemoryRegionType_VirtualDramUserPool.GetValue() == 0x31A);
// NOTE: For unknown reason, the pools are derived out-of-order here. // NOTE: For unknown reason, the pools are derived out-of-order here.
// It's worth eventually trying to understand why Nintendo made this choice. // It's worth eventually trying to understand why Nintendo made this choice.
// UNUSED: .Derive(6, 0); // UNUSED: .Derive(6, 0);
// UNUSED: .Derive(6, 1); // UNUSED: .Derive(6, 1);
constexpr inline auto KMemoryRegionType_VirtualDramAppletPool = constexpr inline const auto KMemoryRegionType_VirtualDramApplicationPool =
KMemoryRegionType_VirtualDramUserPool.Derive(6, 2); KMemoryRegionType_VirtualDramUserPool.Derive(4, 0);
constexpr inline auto KMemoryRegionType_VirtualDramApplicationPool = constexpr inline const auto KMemoryRegionType_VirtualDramAppletPool =
KMemoryRegionType_VirtualDramUserPool.Derive(6, 3); KMemoryRegionType_VirtualDramUserPool.Derive(4, 1);
constexpr inline auto KMemoryRegionType_VirtualDramSystemNonSecurePool = constexpr inline const auto KMemoryRegionType_VirtualDramSystemNonSecurePool =
KMemoryRegionType_VirtualDramUserPool.Derive(6, 4); KMemoryRegionType_VirtualDramUserPool.Derive(4, 2);
constexpr inline auto KMemoryRegionType_VirtualDramSystemPool = constexpr inline const auto KMemoryRegionType_VirtualDramSystemPool =
KMemoryRegionType_VirtualDramUserPool.Derive(6, 5); KMemoryRegionType_VirtualDramUserPool.Derive(4, 3);
static_assert(KMemoryRegionType_VirtualDramAppletPool.GetValue() == 0x1B1A); static_assert(KMemoryRegionType_VirtualDramApplicationPool.GetValue() == 0x361A);
static_assert(KMemoryRegionType_VirtualDramApplicationPool.GetValue() == 0x271A); static_assert(KMemoryRegionType_VirtualDramAppletPool.GetValue() == 0x561A);
static_assert(KMemoryRegionType_VirtualDramSystemNonSecurePool.GetValue() == 0x2B1A); static_assert(KMemoryRegionType_VirtualDramSystemNonSecurePool.GetValue() == 0x661A);
static_assert(KMemoryRegionType_VirtualDramSystemPool.GetValue() == 0x331A); static_assert(KMemoryRegionType_VirtualDramSystemPool.GetValue() == 0x961A);
constexpr inline auto KMemoryRegionType_ArchDeviceBase = constexpr inline auto KMemoryRegionType_ArchDeviceBase =
KMemoryRegionType_Kernel.DeriveTransition(0, 1).SetSparseOnly(); KMemoryRegionType_Kernel.DeriveTransition(0, 1).SetSparseOnly();
@ -354,12 +365,14 @@ constexpr inline auto KMemoryRegionType_KernelTemp =
static_assert(KMemoryRegionType_KernelTemp.GetValue() == 0x31); static_assert(KMemoryRegionType_KernelTemp.GetValue() == 0x31);
constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) { constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
if (KMemoryRegionType_KernelTraceBuffer.IsAncestorOf(type_id)) { if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
return KMemoryRegionType_VirtualDramKernelTraceBuffer;
} else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
return KMemoryRegionType_VirtualDramKernelPtHeap; return KMemoryRegionType_VirtualDramKernelPtHeap;
} else if (KMemoryRegionType_DramKernelSecureAppletMemory.IsAncestorOf(type_id)) { } else if (KMemoryRegionType_DramKernelSecureAppletMemory.IsAncestorOf(type_id)) {
return KMemoryRegionType_VirtualDramKernelSecureAppletMemory; return KMemoryRegionType_VirtualDramKernelSecureAppletMemory;
} else if (KMemoryRegionType_DramKernelSecureUnknown.IsAncestorOf(type_id)) {
return KMemoryRegionType_VirtualDramKernelSecureUnknown;
} else if (KMemoryRegionType_KernelTraceBuffer.IsAncestorOf(type_id)) {
return KMemoryRegionType_VirtualDramKernelTraceBuffer;
} else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) { } else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) {
return KMemoryRegionType_VirtualDramUnknownDebug; return KMemoryRegionType_VirtualDramUnknownDebug;
} else { } else {

View file

@ -183,12 +183,17 @@ private:
class KScopedPageGroup { class KScopedPageGroup {
public: public:
explicit KScopedPageGroup(const KPageGroup* gp) : m_pg(gp) { explicit KScopedPageGroup(const KPageGroup* gp, bool not_first = true) : m_pg(gp) {
if (m_pg) { if (m_pg) {
m_pg->Open(); if (not_first) {
m_pg->Open();
} else {
m_pg->OpenFirst();
}
} }
} }
explicit KScopedPageGroup(const KPageGroup& gp) : KScopedPageGroup(std::addressof(gp)) {} explicit KScopedPageGroup(const KPageGroup& gp, bool not_first = true)
: KScopedPageGroup(std::addressof(gp), not_first) {}
~KScopedPageGroup() { ~KScopedPageGroup() {
if (m_pg) { if (m_pg) {
m_pg->Close(); m_pg->Close();

View file

@ -505,7 +505,7 @@ Result KPageTable::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress
R_TRY(this->CheckMemoryStateContiguous( R_TRY(this->CheckMemoryStateContiguous(
std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias, std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias,
KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None, KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::All, KMemoryAttribute::None)); KMemoryAttribute::All & ~KMemoryAttribute::PermissionLocked, KMemoryAttribute::None));
// Determine whether any pages being unmapped are code. // Determine whether any pages being unmapped are code.
bool any_code_pages = false; bool any_code_pages = false;
@ -1724,29 +1724,43 @@ Result KPageTable::MapPhysicalMemory(KProcessAddress address, size_t size) {
PageSize; PageSize;
// While we have pages to map, map them. // While we have pages to map, map them.
while (map_pages > 0) { {
// Check if we're at the end of the physical block. // Create a page group for the current mapping range.
if (pg_pages == 0) { KPageGroup cur_pg(m_kernel, m_block_info_manager);
// Ensure there are more pages to map. {
ASSERT(pg_it != pg.end()); ON_RESULT_FAILURE_2 {
cur_pg.OpenFirst();
cur_pg.Close();
};
// Advance our physical block. size_t remain_pages = map_pages;
++pg_it; while (remain_pages > 0) {
pg_phys_addr = pg_it->GetAddress(); // Check if we're at the end of the physical block.
pg_pages = pg_it->GetNumPages(); if (pg_pages == 0) {
// Ensure there are more pages to map.
ASSERT(pg_it != pg.end());
// Advance our physical block.
++pg_it;
pg_phys_addr = pg_it->GetAddress();
pg_pages = pg_it->GetNumPages();
}
// Add whatever we can to the current block.
const size_t cur_pages = std::min(pg_pages, remain_pages);
R_TRY(cur_pg.AddBlock(pg_phys_addr +
((pg_pages - cur_pages) * PageSize),
cur_pages));
// Advance.
remain_pages -= cur_pages;
pg_pages -= cur_pages;
}
} }
// Map whatever we can. // Map the pages.
const size_t cur_pages = std::min(pg_pages, map_pages); R_TRY(this->Operate(cur_address, map_pages, cur_pg,
R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite, OperationType::MapFirstGroup));
OperationType::MapFirst, pg_phys_addr));
// Advance.
cur_address += cur_pages * PageSize;
map_pages -= cur_pages;
pg_phys_addr += cur_pages * PageSize;
pg_pages -= cur_pages;
} }
} }
@ -1770,7 +1784,11 @@ Result KPageTable::MapPhysicalMemory(KProcessAddress address, size_t size) {
m_memory_block_manager.UpdateIfMatch( m_memory_block_manager.UpdateIfMatch(
std::addressof(allocator), address, size / PageSize, KMemoryState::Free, std::addressof(allocator), address, size / PageSize, KMemoryState::Free,
KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal, KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal,
KMemoryPermission::UserReadWrite, KMemoryAttribute::None); KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
address == this->GetAliasRegionStart()
? KMemoryBlockDisableMergeAttribute::Normal
: KMemoryBlockDisableMergeAttribute::None,
KMemoryBlockDisableMergeAttribute::None);
R_SUCCEED(); R_SUCCEED();
} }
@ -1868,6 +1886,13 @@ Result KPageTable::UnmapPhysicalMemory(KProcessAddress address, size_t size) {
// Iterate over the memory, unmapping as we go. // Iterate over the memory, unmapping as we go.
auto it = m_memory_block_manager.FindIterator(cur_address); auto it = m_memory_block_manager.FindIterator(cur_address);
const auto clear_merge_attr =
(it->GetState() == KMemoryState::Normal &&
it->GetAddress() == this->GetAliasRegionStart() && it->GetAddress() == address)
? KMemoryBlockDisableMergeAttribute::Normal
: KMemoryBlockDisableMergeAttribute::None;
while (true) { while (true) {
// Check that the iterator is valid. // Check that the iterator is valid.
ASSERT(it != m_memory_block_manager.end()); ASSERT(it != m_memory_block_manager.end());
@ -1905,7 +1930,7 @@ Result KPageTable::UnmapPhysicalMemory(KProcessAddress address, size_t size) {
m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize, m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize,
KMemoryState::Free, KMemoryPermission::None, KMemoryState::Free, KMemoryPermission::None,
KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
KMemoryBlockDisableMergeAttribute::None); clear_merge_attr);
// We succeeded. // We succeeded.
R_SUCCEED(); R_SUCCEED();
@ -2379,8 +2404,7 @@ Result KPageTable::MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
KScopedPageTableUpdater updater(this); KScopedPageTableUpdater updater(this);
// Perform mapping operation. // Perform mapping operation.
const KPageProperties properties = {perm, state == KMemoryState::Io, false, const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
DisableMergeAttribute::DisableHead};
R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false)); R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
// Update the blocks. // Update the blocks.
@ -2422,8 +2446,7 @@ Result KPageTable::MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMem
KScopedPageTableUpdater updater(this); KScopedPageTableUpdater updater(this);
// Perform mapping operation. // Perform mapping operation.
const KPageProperties properties = {perm, state == KMemoryState::Io, false, const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
DisableMergeAttribute::DisableHead};
R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false)); R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
// Update the blocks. // Update the blocks.
@ -2652,11 +2675,18 @@ Result KPageTable::SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mas
size_t num_allocator_blocks; size_t num_allocator_blocks;
constexpr auto AttributeTestMask = constexpr auto AttributeTestMask =
~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared); ~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared);
R_TRY(this->CheckMemoryState( const KMemoryState state_test_mask =
std::addressof(old_state), std::addressof(old_perm), std::addressof(old_attr), static_cast<KMemoryState>(((mask & static_cast<u32>(KMemoryAttribute::Uncached))
std::addressof(num_allocator_blocks), addr, size, KMemoryState::FlagCanChangeAttribute, ? static_cast<u32>(KMemoryState::FlagCanChangeAttribute)
KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None, : 0) |
AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask)); ((mask & static_cast<u32>(KMemoryAttribute::PermissionLocked))
? static_cast<u32>(KMemoryState::FlagCanPermissionLock)
: 0));
R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
std::addressof(old_attr), std::addressof(num_allocator_blocks),
addr, size, state_test_mask, state_test_mask,
KMemoryPermission::None, KMemoryPermission::None,
AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask));
// Create an update allocator. // Create an update allocator.
Result allocator_result{ResultSuccess}; Result allocator_result{ResultSuccess};
@ -2664,18 +2694,17 @@ Result KPageTable::SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mas
m_memory_block_slab_manager, num_allocator_blocks); m_memory_block_slab_manager, num_allocator_blocks);
R_TRY(allocator_result); R_TRY(allocator_result);
// Determine the new attribute. // If we need to, perform a change attribute operation.
const KMemoryAttribute new_attr = if (True(KMemoryAttribute::Uncached & static_cast<KMemoryAttribute>(mask))) {
static_cast<KMemoryAttribute>(((old_attr & static_cast<KMemoryAttribute>(~mask)) | // Perform operation.
static_cast<KMemoryAttribute>(attr & mask))); R_TRY(this->Operate(addr, num_pages, old_perm,
OperationType::ChangePermissionsAndRefreshAndFlush, 0));
// Perform operation. }
this->Operate(addr, num_pages, old_perm, OperationType::ChangePermissionsAndRefresh);
// Update the blocks. // Update the blocks.
m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, old_perm, m_memory_block_manager.UpdateAttribute(std::addressof(allocator), addr, num_pages,
new_attr, KMemoryBlockDisableMergeAttribute::None, static_cast<KMemoryAttribute>(mask),
KMemoryBlockDisableMergeAttribute::None); static_cast<KMemoryAttribute>(attr));
R_SUCCEED(); R_SUCCEED();
} }
@ -2863,7 +2892,8 @@ Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress
&KMemoryBlock::ShareToDevice, KMemoryPermission::None); &KMemoryBlock::ShareToDevice, KMemoryPermission::None);
// Set whether the locked memory was io. // Set whether the locked memory was io.
*out_is_io = old_state == KMemoryState::Io; *out_is_io =
static_cast<Svc::MemoryState>(old_state & KMemoryState::Mask) == Svc::MemoryState::Io;
R_SUCCEED(); R_SUCCEED();
} }
@ -3021,9 +3051,10 @@ Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, const KPageGr
ASSERT(num_pages == page_group.GetNumPages()); ASSERT(num_pages == page_group.GetNumPages());
switch (operation) { switch (operation) {
case OperationType::MapGroup: { case OperationType::MapGroup:
case OperationType::MapFirstGroup: {
// We want to maintain a new reference to every page in the group. // We want to maintain a new reference to every page in the group.
KScopedPageGroup spg(page_group); KScopedPageGroup spg(page_group, operation != OperationType::MapFirstGroup);
for (const auto& node : page_group) { for (const auto& node : page_group) {
const size_t size{node.GetNumPages() * PageSize}; const size_t size{node.GetNumPages() * PageSize};
@ -3065,7 +3096,6 @@ Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, KMemoryPermis
m_memory->UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize); m_memory->UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize);
break; break;
} }
case OperationType::MapFirst:
case OperationType::Map: { case OperationType::Map: {
ASSERT(map_addr); ASSERT(map_addr);
ASSERT(Common::IsAligned(GetInteger(map_addr), PageSize)); ASSERT(Common::IsAligned(GetInteger(map_addr), PageSize));
@ -3073,11 +3103,7 @@ Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, KMemoryPermis
// Open references to pages, if we should. // Open references to pages, if we should.
if (IsHeapPhysicalAddress(m_kernel.MemoryLayout(), map_addr)) { if (IsHeapPhysicalAddress(m_kernel.MemoryLayout(), map_addr)) {
if (operation == OperationType::MapFirst) { m_kernel.MemoryManager().Open(map_addr, num_pages);
m_kernel.MemoryManager().OpenFirst(map_addr, num_pages);
} else {
m_kernel.MemoryManager().Open(map_addr, num_pages);
}
} }
break; break;
} }
@ -3087,6 +3113,7 @@ Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, KMemoryPermis
} }
case OperationType::ChangePermissions: case OperationType::ChangePermissions:
case OperationType::ChangePermissionsAndRefresh: case OperationType::ChangePermissionsAndRefresh:
case OperationType::ChangePermissionsAndRefreshAndFlush:
break; break;
default: default:
ASSERT(false); ASSERT(false);
@ -3106,79 +3133,79 @@ void KPageTable::FinalizeUpdate(PageLinkedList* page_list) {
} }
} }
KProcessAddress KPageTable::GetRegionAddress(KMemoryState state) const { KProcessAddress KPageTable::GetRegionAddress(Svc::MemoryState state) const {
switch (state) { switch (state) {
case KMemoryState::Free: case Svc::MemoryState::Free:
case KMemoryState::Kernel: case Svc::MemoryState::Kernel:
return m_address_space_start; return m_address_space_start;
case KMemoryState::Normal: case Svc::MemoryState::Normal:
return m_heap_region_start; return m_heap_region_start;
case KMemoryState::Ipc: case Svc::MemoryState::Ipc:
case KMemoryState::NonSecureIpc: case Svc::MemoryState::NonSecureIpc:
case KMemoryState::NonDeviceIpc: case Svc::MemoryState::NonDeviceIpc:
return m_alias_region_start; return m_alias_region_start;
case KMemoryState::Stack: case Svc::MemoryState::Stack:
return m_stack_region_start; return m_stack_region_start;
case KMemoryState::Static: case Svc::MemoryState::Static:
case KMemoryState::ThreadLocal: case Svc::MemoryState::ThreadLocal:
return m_kernel_map_region_start; return m_kernel_map_region_start;
case KMemoryState::Io: case Svc::MemoryState::Io:
case KMemoryState::Shared: case Svc::MemoryState::Shared:
case KMemoryState::AliasCode: case Svc::MemoryState::AliasCode:
case KMemoryState::AliasCodeData: case Svc::MemoryState::AliasCodeData:
case KMemoryState::Transfered: case Svc::MemoryState::Transfered:
case KMemoryState::SharedTransfered: case Svc::MemoryState::SharedTransfered:
case KMemoryState::SharedCode: case Svc::MemoryState::SharedCode:
case KMemoryState::GeneratedCode: case Svc::MemoryState::GeneratedCode:
case KMemoryState::CodeOut: case Svc::MemoryState::CodeOut:
case KMemoryState::Coverage: case Svc::MemoryState::Coverage:
case KMemoryState::Insecure: case Svc::MemoryState::Insecure:
return m_alias_code_region_start; return m_alias_code_region_start;
case KMemoryState::Code: case Svc::MemoryState::Code:
case KMemoryState::CodeData: case Svc::MemoryState::CodeData:
return m_code_region_start; return m_code_region_start;
default: default:
UNREACHABLE(); UNREACHABLE();
} }
} }
size_t KPageTable::GetRegionSize(KMemoryState state) const { size_t KPageTable::GetRegionSize(Svc::MemoryState state) const {
switch (state) { switch (state) {
case KMemoryState::Free: case Svc::MemoryState::Free:
case KMemoryState::Kernel: case Svc::MemoryState::Kernel:
return m_address_space_end - m_address_space_start; return m_address_space_end - m_address_space_start;
case KMemoryState::Normal: case Svc::MemoryState::Normal:
return m_heap_region_end - m_heap_region_start; return m_heap_region_end - m_heap_region_start;
case KMemoryState::Ipc: case Svc::MemoryState::Ipc:
case KMemoryState::NonSecureIpc: case Svc::MemoryState::NonSecureIpc:
case KMemoryState::NonDeviceIpc: case Svc::MemoryState::NonDeviceIpc:
return m_alias_region_end - m_alias_region_start; return m_alias_region_end - m_alias_region_start;
case KMemoryState::Stack: case Svc::MemoryState::Stack:
return m_stack_region_end - m_stack_region_start; return m_stack_region_end - m_stack_region_start;
case KMemoryState::Static: case Svc::MemoryState::Static:
case KMemoryState::ThreadLocal: case Svc::MemoryState::ThreadLocal:
return m_kernel_map_region_end - m_kernel_map_region_start; return m_kernel_map_region_end - m_kernel_map_region_start;
case KMemoryState::Io: case Svc::MemoryState::Io:
case KMemoryState::Shared: case Svc::MemoryState::Shared:
case KMemoryState::AliasCode: case Svc::MemoryState::AliasCode:
case KMemoryState::AliasCodeData: case Svc::MemoryState::AliasCodeData:
case KMemoryState::Transfered: case Svc::MemoryState::Transfered:
case KMemoryState::SharedTransfered: case Svc::MemoryState::SharedTransfered:
case KMemoryState::SharedCode: case Svc::MemoryState::SharedCode:
case KMemoryState::GeneratedCode: case Svc::MemoryState::GeneratedCode:
case KMemoryState::CodeOut: case Svc::MemoryState::CodeOut:
case KMemoryState::Coverage: case Svc::MemoryState::Coverage:
case KMemoryState::Insecure: case Svc::MemoryState::Insecure:
return m_alias_code_region_end - m_alias_code_region_start; return m_alias_code_region_end - m_alias_code_region_start;
case KMemoryState::Code: case Svc::MemoryState::Code:
case KMemoryState::CodeData: case Svc::MemoryState::CodeData:
return m_code_region_end - m_code_region_start; return m_code_region_end - m_code_region_start;
default: default:
UNREACHABLE(); UNREACHABLE();
} }
} }
bool KPageTable::CanContain(KProcessAddress addr, size_t size, KMemoryState state) const { bool KPageTable::CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const {
const KProcessAddress end = addr + size; const KProcessAddress end = addr + size;
const KProcessAddress last = end - 1; const KProcessAddress last = end - 1;
@ -3192,32 +3219,32 @@ bool KPageTable::CanContain(KProcessAddress addr, size_t size, KMemoryState stat
const bool is_in_alias = !(end <= m_alias_region_start || m_alias_region_end <= addr || const bool is_in_alias = !(end <= m_alias_region_start || m_alias_region_end <= addr ||
m_alias_region_start == m_alias_region_end); m_alias_region_start == m_alias_region_end);
switch (state) { switch (state) {
case KMemoryState::Free: case Svc::MemoryState::Free:
case KMemoryState::Kernel: case Svc::MemoryState::Kernel:
return is_in_region; return is_in_region;
case KMemoryState::Io: case Svc::MemoryState::Io:
case KMemoryState::Static: case Svc::MemoryState::Static:
case KMemoryState::Code: case Svc::MemoryState::Code:
case KMemoryState::CodeData: case Svc::MemoryState::CodeData:
case KMemoryState::Shared: case Svc::MemoryState::Shared:
case KMemoryState::AliasCode: case Svc::MemoryState::AliasCode:
case KMemoryState::AliasCodeData: case Svc::MemoryState::AliasCodeData:
case KMemoryState::Stack: case Svc::MemoryState::Stack:
case KMemoryState::ThreadLocal: case Svc::MemoryState::ThreadLocal:
case KMemoryState::Transfered: case Svc::MemoryState::Transfered:
case KMemoryState::SharedTransfered: case Svc::MemoryState::SharedTransfered:
case KMemoryState::SharedCode: case Svc::MemoryState::SharedCode:
case KMemoryState::GeneratedCode: case Svc::MemoryState::GeneratedCode:
case KMemoryState::CodeOut: case Svc::MemoryState::CodeOut:
case KMemoryState::Coverage: case Svc::MemoryState::Coverage:
case KMemoryState::Insecure: case Svc::MemoryState::Insecure:
return is_in_region && !is_in_heap && !is_in_alias; return is_in_region && !is_in_heap && !is_in_alias;
case KMemoryState::Normal: case Svc::MemoryState::Normal:
ASSERT(is_in_heap); ASSERT(is_in_heap);
return is_in_region && !is_in_alias; return is_in_region && !is_in_alias;
case KMemoryState::Ipc: case Svc::MemoryState::Ipc:
case KMemoryState::NonSecureIpc: case Svc::MemoryState::NonSecureIpc:
case KMemoryState::NonDeviceIpc: case Svc::MemoryState::NonDeviceIpc:
ASSERT(is_in_alias); ASSERT(is_in_alias);
return is_in_region && !is_in_heap; return is_in_region && !is_in_heap;
default: default:
@ -3281,21 +3308,16 @@ Result KPageTable::CheckMemoryStateContiguous(size_t* out_blocks_needed, KProces
Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
KMemoryAttribute* out_attr, size_t* out_blocks_needed, KMemoryAttribute* out_attr, size_t* out_blocks_needed,
KProcessAddress addr, size_t size, KMemoryState state_mask, KMemoryBlockManager::const_iterator it,
KProcessAddress last_addr, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask, KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr, KMemoryAttribute ignore_attr) const { KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
ASSERT(this->IsLockedByCurrentThread()); ASSERT(this->IsLockedByCurrentThread());
// Get information about the first block. // Get information about the first block.
const KProcessAddress last_addr = addr + size - 1;
KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr);
KMemoryInfo info = it->GetMemoryInfo(); KMemoryInfo info = it->GetMemoryInfo();
// If the start address isn't aligned, we need a block.
const size_t blocks_for_start_align =
(Common::AlignDown(GetInteger(addr), PageSize) != info.GetAddress()) ? 1 : 0;
// Validate all blocks in the range have correct state. // Validate all blocks in the range have correct state.
const KMemoryState first_state = info.m_state; const KMemoryState first_state = info.m_state;
const KMemoryPermission first_perm = info.m_permission; const KMemoryPermission first_perm = info.m_permission;
@ -3321,10 +3343,6 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission*
info = it->GetMemoryInfo(); info = it->GetMemoryInfo();
} }
// If the end address isn't aligned, we need a block.
const size_t blocks_for_end_align =
(Common::AlignUp(GetInteger(addr) + size, PageSize) != info.GetEndAddress()) ? 1 : 0;
// Write output state. // Write output state.
if (out_state != nullptr) { if (out_state != nullptr) {
*out_state = first_state; *out_state = first_state;
@ -3335,9 +3353,39 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission*
if (out_attr != nullptr) { if (out_attr != nullptr) {
*out_attr = static_cast<KMemoryAttribute>(first_attr & ~ignore_attr); *out_attr = static_cast<KMemoryAttribute>(first_attr & ~ignore_attr);
} }
// If the end address isn't aligned, we need a block.
if (out_blocks_needed != nullptr) { if (out_blocks_needed != nullptr) {
*out_blocks_needed = blocks_for_start_align + blocks_for_end_align; const size_t blocks_for_end_align =
(Common::AlignDown(GetInteger(last_addr), PageSize) + PageSize != info.GetEndAddress())
? 1
: 0;
*out_blocks_needed = blocks_for_end_align;
} }
R_SUCCEED();
}
Result KPageTable::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) const {
ASSERT(this->IsLockedByCurrentThread());
// Check memory state.
const KProcessAddress last_addr = addr + size - 1;
KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr);
R_TRY(this->CheckMemoryState(out_state, out_perm, out_attr, out_blocks_needed, it, last_addr,
state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr));
// If the start address isn't aligned, we need a block.
if (out_blocks_needed != nullptr &&
Common::AlignDown(GetInteger(addr), PageSize) != it->GetAddress()) {
++(*out_blocks_needed);
}
R_SUCCEED(); R_SUCCEED();
} }

View file

@ -126,8 +126,6 @@ public:
return m_block_info_manager; return m_block_info_manager;
} }
bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const;
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
KPhysicalAddress phys_addr, KProcessAddress region_start, KPhysicalAddress phys_addr, KProcessAddress region_start,
size_t region_num_pages, KMemoryState state, KMemoryPermission perm) { size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
@ -162,6 +160,21 @@ public:
void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size, void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
const KPageGroup& pg); 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: protected:
struct PageLinkedList { struct PageLinkedList {
private: private:
@ -204,12 +217,13 @@ protected:
private: private:
enum class OperationType : u32 { enum class OperationType : u32 {
Map = 0, Map = 0,
MapFirst = 1, MapGroup = 1,
MapGroup = 2, MapFirstGroup = 2,
Unmap = 3, Unmap = 3,
ChangePermissions = 4, ChangePermissions = 4,
ChangePermissionsAndRefresh = 5, ChangePermissionsAndRefresh = 5,
Separate = 6, ChangePermissionsAndRefreshAndFlush = 6,
Separate = 7,
}; };
static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr = static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
@ -228,8 +242,6 @@ private:
Result Operate(KProcessAddress addr, size_t num_pages, KMemoryPermission perm, Result Operate(KProcessAddress addr, size_t num_pages, KMemoryPermission perm,
OperationType operation, KPhysicalAddress map_addr = 0); OperationType operation, KPhysicalAddress map_addr = 0);
void FinalizeUpdate(PageLinkedList* page_list); void FinalizeUpdate(PageLinkedList* page_list);
KProcessAddress GetRegionAddress(KMemoryState state) const;
size_t GetRegionSize(KMemoryState state) const;
KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages, KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages,
size_t num_pages, size_t alignment, size_t offset, size_t num_pages, size_t alignment, size_t offset,
@ -250,6 +262,13 @@ private:
Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state, Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr) const; 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, Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
KMemoryAttribute* out_attr, size_t* out_blocks_needed, KMemoryAttribute* out_attr, size_t* out_blocks_needed,
KProcessAddress addr, size_t size, KMemoryState state_mask, KProcessAddress addr, size_t size, KMemoryState state_mask,

View file

@ -623,14 +623,33 @@ struct KernelCore::Impl {
ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert( ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
GetInteger(slab_start_phys_addr), slab_region_size, KMemoryRegionType_DramKernelSlab)); GetInteger(slab_start_phys_addr), slab_region_size, KMemoryRegionType_DramKernelSlab));
// Insert a physical region for the secure applet memory.
const auto secure_applet_end_phys_addr =
slab_end_phys_addr + KSystemControl::SecureAppletMemorySize;
if constexpr (KSystemControl::SecureAppletMemorySize > 0) {
ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
GetInteger(slab_end_phys_addr), KSystemControl::SecureAppletMemorySize,
KMemoryRegionType_DramKernelSecureAppletMemory));
}
// Insert a physical region for the unknown debug2 region.
constexpr size_t SecureUnknownRegionSize = 0;
const size_t secure_unknown_size = SecureUnknownRegionSize;
const auto secure_unknown_end_phys_addr = secure_applet_end_phys_addr + secure_unknown_size;
if constexpr (SecureUnknownRegionSize > 0) {
ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
GetInteger(secure_applet_end_phys_addr), secure_unknown_size,
KMemoryRegionType_DramKernelSecureUnknown));
}
// Determine size available for kernel page table heaps, requiring > 8 MB. // Determine size available for kernel page table heaps, requiring > 8 MB.
const KPhysicalAddress resource_end_phys_addr = slab_start_phys_addr + resource_region_size; const KPhysicalAddress resource_end_phys_addr = slab_start_phys_addr + resource_region_size;
const size_t page_table_heap_size = resource_end_phys_addr - slab_end_phys_addr; const size_t page_table_heap_size = resource_end_phys_addr - secure_unknown_end_phys_addr;
ASSERT(page_table_heap_size / 4_MiB > 2); ASSERT(page_table_heap_size / 4_MiB > 2);
// Insert a physical region for the kernel page table heap region // Insert a physical region for the kernel page table heap region
ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert( ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
GetInteger(slab_end_phys_addr), page_table_heap_size, GetInteger(secure_unknown_end_phys_addr), page_table_heap_size,
KMemoryRegionType_DramKernelPtHeap)); KMemoryRegionType_DramKernelPtHeap));
// All DRAM regions that we haven't tagged by this point will be mapped under the linear // All DRAM regions that we haven't tagged by this point will be mapped under the linear

View file

@ -108,10 +108,16 @@ Result SetMemoryAttribute(Core::System& system, u64 address, u64 size, u32 mask,
R_UNLESS((address < address + size), ResultInvalidCurrentMemory); R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
// Validate the attribute and mask. // Validate the attribute and mask.
constexpr u32 SupportedMask = static_cast<u32>(MemoryAttribute::Uncached); constexpr u32 SupportedMask =
static_cast<u32>(MemoryAttribute::Uncached | MemoryAttribute::PermissionLocked);
R_UNLESS((mask | attr) == mask, ResultInvalidCombination); R_UNLESS((mask | attr) == mask, ResultInvalidCombination);
R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination); R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination);
// Check that permission locked is either being set or not masked.
R_UNLESS((static_cast<Svc::MemoryAttribute>(mask) & Svc::MemoryAttribute::PermissionLocked) ==
(static_cast<Svc::MemoryAttribute>(attr) & Svc::MemoryAttribute::PermissionLocked),
ResultInvalidCombination);
// Validate that the region is in range for the current process. // Validate that the region is in range for the current process.
auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()}; auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()};
R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);

View file

@ -46,6 +46,7 @@ enum class MemoryAttribute : u32 {
IpcLocked = (1 << 1), IpcLocked = (1 << 1),
DeviceShared = (1 << 2), DeviceShared = (1 << 2),
Uncached = (1 << 3), Uncached = (1 << 3),
PermissionLocked = (1 << 4),
}; };
DECLARE_ENUM_FLAG_OPERATORS(MemoryAttribute); DECLARE_ENUM_FLAG_OPERATORS(MemoryAttribute);

View file

@ -46,7 +46,7 @@ Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address,
// Get bounds of where mapping is possible. // Get bounds of where mapping is possible.
const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart()); const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart());
const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE; const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE;
const auto state = Kernel::KMemoryState::Io; const auto state = Kernel::KMemoryState::IoMemory;
const auto perm = Kernel::KMemoryPermission::UserReadWrite; const auto perm = Kernel::KMemoryPermission::UserReadWrite;
std::mt19937_64 rng{process->GetRandomEntropy(0)}; std::mt19937_64 rng{process->GetRandomEntropy(0)};

View file

@ -826,12 +826,13 @@ void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) {
tree_view->setColumnHidden(COLUMN_SIZE, !UISettings::values.show_size); tree_view->setColumnHidden(COLUMN_SIZE, !UISettings::values.show_size);
tree_view->setColumnHidden(COLUMN_PLAY_TIME, !UISettings::values.show_play_time); tree_view->setColumnHidden(COLUMN_PLAY_TIME, !UISettings::values.show_play_time);
// Before deleting rows, cancel the worker so that it is not using them
emit ShouldCancelWorker();
// Delete any rows that might already exist if we're repopulating // Delete any rows that might already exist if we're repopulating
item_model->removeRows(0, item_model->rowCount()); item_model->removeRows(0, item_model->rowCount());
search_field->clear(); search_field->clear();
emit ShouldCancelWorker();
GameListWorker* worker = GameListWorker* worker =
new GameListWorker(vfs, provider, game_dirs, compatibility_list, play_time_manager, system); new GameListWorker(vfs, provider, game_dirs, compatibility_list, play_time_manager, system);

View file

@ -293,7 +293,7 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path, bool deep_scan, void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path, bool deep_scan,
GameListDir* parent_dir) { GameListDir* parent_dir) {
const auto callback = [this, target, parent_dir](const std::filesystem::path& path) -> bool { const auto callback = [this, target, parent_dir](const std::filesystem::path& path) -> bool {
if (stop_processing) { if (stop_requested) {
// Breaks the callback loop. // Breaks the callback loop.
return false; return false;
} }
@ -399,7 +399,6 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
} }
void GameListWorker::run() { void GameListWorker::run() {
stop_processing = false;
provider->ClearAllEntries(); provider->ClearAllEntries();
for (UISettings::GameDir& game_dir : game_dirs) { for (UISettings::GameDir& game_dir : game_dirs) {
@ -427,9 +426,11 @@ void GameListWorker::run() {
} }
emit Finished(watch_list); emit Finished(watch_list);
processing_completed.Set();
} }
void GameListWorker::Cancel() { void GameListWorker::Cancel() {
this->disconnect(); this->disconnect();
stop_processing = true; stop_requested.store(true);
processing_completed.Wait();
} }

View file

@ -12,6 +12,7 @@
#include <QRunnable> #include <QRunnable>
#include <QString> #include <QString>
#include "common/thread.h"
#include "yuzu/compatibility_list.h" #include "yuzu/compatibility_list.h"
#include "yuzu/play_time_manager.h" #include "yuzu/play_time_manager.h"
@ -82,7 +83,9 @@ private:
const PlayTime::PlayTimeManager& play_time_manager; const PlayTime::PlayTimeManager& play_time_manager;
QStringList watch_list; QStringList watch_list;
std::atomic_bool stop_processing;
Common::Event processing_completed;
std::atomic_bool stop_requested = false;
Core::System& system; Core::System& system;
}; };