Compare commits

...

87 Commits

Author SHA1 Message Date
yuzubot
fb36f6d5e4 Android #138 2023-11-22 00:57:40 +00:00
yuzubot
bc5af008ff Merge PR 12074 2023-11-22 00:57:40 +00:00
yuzubot
14a1598a34 Merge PR 11535 2023-11-22 00:57:40 +00:00
liamwhite
cddb28cf26 Merge pull request #12107 from daisymlleung/patch-1
Stub CheckBlockedUserListAvailability for Super Bomberman R 2
2023-11-21 09:19:41 -05:00
liamwhite
538e137bca Merge pull request #12100 from t895/android-translations
translations: Add android translations to transifex config
2023-11-21 09:19:34 -05:00
liamwhite
e69118042f Merge pull request #12045 from liamwhite/codec-refactor
video_core: refactor video frame and packet parsing
2023-11-21 09:19:26 -05:00
liamwhite
a6b8d85b34 Merge pull request #11984 from lat9nq/lupdate
shared_translation: Call tr more directly
2023-11-21 09:19:13 -05:00
Narr the Reg
8da5bd27e9 Merge pull request #12011 from Macj0rdan/controller-applet
Implemented qlaunch version of the controller applet
2023-11-20 16:53:33 -06:00
daisymlleung
0216aa55b4 Stub CheckBlockedUserListAvailability 2023-11-21 01:57:58 +08:00
t895
0298c2cc5d translations: Add android translations to transifex config 2023-11-20 10:37:09 -05:00
liamwhite
00dcf69ce8 Merge pull request #12093 from t895/zip-intent
android: Use file picker intent for save exporter
2023-11-20 09:38:44 -05:00
liamwhite
1ea0890a36 Merge pull request #12092 from t895/recent-games-default
android: Select recently played games by default in search tab
2023-11-20 09:38:36 -05:00
liamwhite
a77feca086 Merge pull request #12091 from t895/silly-speed
android: Allow up to 400% for the limit speed percent setting
2023-11-20 09:38:21 -05:00
Narr the Reg
99a8f7fb72 Merge pull request #12097 from Squall-Leonhart/ResourceToGetResource
service: hid: Ensure resource manager is initialized
2023-11-19 20:10:38 -06:00
Squall-Leonhart
8ae26df15c service: hid: Ensure resource manager is initialized
Ensures the proper initialization of the IActiveVibrationDeviceList. By using GetResourceManager() instead of resource_manager, we make sure that the IActiveVibrationDeviceListis initialized before it's used, preventing potential null issues.

Fixes #12088
2023-11-20 11:43:56 +11:00
t895
82a4a67f6b android: Use file picker intent for save exporter 2023-11-19 17:52:53 -05:00
t895
8e93a9a9ef android: Use path instead of programId for recently added / played key 2023-11-19 16:00:41 -05:00
t895
23c1f7c72f android: Select recently played games by default in search tab 2023-11-19 15:57:05 -05:00
t895
d3ed771f39 android: Allow up to 400% for the limit speed percent setting 2023-11-19 15:36:25 -05:00
liamwhite
12fba361bd Merge pull request #12083 from liamwhite/viewport
renderer_vulkan: ignore viewport stores on non-supporting drivers
2023-11-19 14:09:25 -05:00
liamwhite
6bcde572dd Merge pull request #12085 from liamwhite/build-fix
qt: fix linux build
2023-11-19 14:09:18 -05:00
FrozenAra
cb004d1ba1 Implemented qlaunch version of the controller applet 2023-11-19 18:38:58 +01:00
Liam
20a17607ae qt: fix linux build 2023-11-19 11:49:51 -05:00
Liam
473caaff5b renderer_vulkan: ignore viewport stores on non-supporting drivers 2023-11-19 11:27:12 -05:00
liamwhite
787552f832 Merge pull request #12081 from FernandoS27/check-out-on-your-broke-crypto-friends
Vulkan: Be more generous with pipeline workers for Android
2023-11-19 11:16:19 -05:00
liamwhite
5f945e2fcd Merge pull request #12036 from FernandoS27/you-should-have-more-than-one-towel
Query Cache: Disable write syncing on Android
2023-11-19 09:52:22 -05:00
liamwhite
c08da2d6ad Merge pull request #11792 from boludoz/new-shortcut
Improved shortcut: add games in applist for Windows, question for sta…
2023-11-19 09:50:49 -05:00
liamwhite
4458920799 Merge pull request #12066 from ameerj/nvidia-nsanity
shader_recompiler: add byteswap pattern workaround for Nvidia
2023-11-19 09:49:45 -05:00
liamwhite
61fed8a3a6 Merge pull request #12068 from ameerj/glasm-xfb-fixes
gl_graphics_pipeline: GLASM: Fix transform feedback attribs buffer mode
2023-11-19 09:49:38 -05:00
liamwhite
efb3165e3d Merge pull request #12072 from FernandoS27/winter-is-2-seconds-too-soon
Buffer Cache: Eliminate clears on Indirect buffers
2023-11-19 09:49:30 -05:00
Fernando Sahmkow
a493ba76b4 Vulkan: Be more generous with pipeline workers for Android 2023-11-19 14:54:15 +01:00
Fernando Sahmkow
ae60a5657e Buffer Cache: Eliminate clears on Indirect buffers 2023-11-18 19:26:14 +01:00
Ameer J
feb60de5c3 shader_recompiler: Fix spelling of "derivate" (#12067) 2023-11-18 13:39:47 +01:00
Ameer J
c67644f1da gl_graphics_pipeline: GLASM: Fix transform feedback attribs buffer mode
GL_SEPARATE_ATTRIBS only applies when multiple buffers are being used, else GL_INTERLEAVED_ATTRIBS handles the cases for a single buffer with potentially more than one attribute
2023-11-18 00:44:05 -05:00
Ameer J
9343b81afd shader_recompiler: add byteswap pattern workaround for Nvidia 2023-11-17 22:29:12 -05:00
liamwhite
71f53b4218 Merge pull request #12058 from liamwhite/opus-again
opus: fix stereo stream count check in multistream decoder object
2023-11-17 20:41:50 -05:00
liamwhite
f131b0faeb Merge pull request #11912 from liamwhite/nv-graphic-buffer
nvnflinger: use graphic buffer lifetime for map handle
2023-11-17 20:41:34 -05:00
liamwhite
6c64d5aff2 Merge pull request #11917 from liamwhite/abandonment
nvnflinger: implement consumer abandonment
2023-11-17 20:41:23 -05:00
Liam
de594995da opus: fix stereo stream count check in multistream decoder object 2023-11-17 08:26:22 -05:00
liamwhite
4c16a1a26f Merge pull request #12055 from german77/activate
service: hid: Introduce firmware settings and update activate controller calls
2023-11-16 21:28:06 -05:00
Narr the Reg
862e66202c service: hid: Introduce firmware settings and update activate controller calls 2023-11-16 18:51:14 -06:00
Liam
4055a476aa video_core: refactor video frame and packet parsing 2023-11-16 17:01:38 -05:00
liamwhite
2136a46ab7 Merge pull request #12053 from german77/no_functional
service: hid: Implement xpad calls
2023-11-16 16:54:01 -05:00
Narr the Reg
b9c7e5c2c8 service: hid: Implement xpad calls 2023-11-16 11:18:11 -06:00
liamwhite
d86e88a622 Merge pull request #11995 from FernandoS27/you-dont-need-the-new-iphone
Revert PR #11806 and do a proper fix to the memory handling.
2023-11-16 09:17:13 -05:00
liamwhite
7eac28e410 Merge pull request #12038 from german77/no_implement
service: hid: Split hid.cpp into individual interfaces
2023-11-16 09:13:56 -05:00
liamwhite
ea4c92f734 Merge pull request #12007 from german77/moar_buttons
core: hid: Split SL and SR buttons
2023-11-16 09:13:39 -05:00
Narr the Reg
c9cd938dfd service: hid: Split hid.cpp into individual interfaces 2023-11-15 09:59:54 -06:00
Fernando Sahmkow
c9437e5244 Query Cache: Disable write syncing on Android 2023-11-15 02:23:39 +01:00
Fernando Sahmkow
f1806d237f Memory: Fix invalidation handling from the CPU/Services 2023-11-12 14:10:40 +01:00
german77
ae57a99d7d core: hid: Split SL and SR buttons 2023-11-11 21:03:15 -06:00
Fernando Sahmkow
1d03a0fa75 Revert "renderer_vulkan: add locks to avoid scheduler flushes from CPU"
This reverts commit d9dde7e6f3.
2023-11-10 15:40:48 +01:00
Franco M
c9038af29e Fix out_icon_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::IconsDir); 2023-11-09 04:53:10 +00:00
Franco M
f3053920bf Minor changes 2023-11-09 03:37:06 +00:00
Franco M
c7b31d24b9 Final change, i think 2023-11-08 21:04:30 +00:00
lat9nq
cb3559539a CMakeLists: Add option to call lupdate directly
qt_create_translation silently fails to run at all on my system. Since
there is no error, I was unable to determine a fix. This sidesteps the
convenience function by setting up the rules ourselves.

This is left as an option since this path likely does not work on
Windows.
2023-11-08 11:54:05 -05:00
lat9nq
71cdfa6ad5 shared_translation: Call tr for each string
Qt can't parse tr called within a macro, so we must call it on each
string.

shared_translation: Remove redundant include
2023-11-08 11:54:01 -05:00
Franco M
8d0d0e1c7a Fixed clang 2023-11-07 02:32:19 +00:00
Franco M
4b8b223db2 modified: src/yuzu/main.cpp 2023-11-05 00:39:43 +00:00
Franco M
728aca7703 Merge branch 'master' into new-shortcut 2023-11-04 21:28:16 -03:00
Liam
a872030a35 nvnflinger: implement consumer abandonment 2023-10-29 23:38:24 -04:00
Liam
79e7d7f4ba nvnflinger: use graphic buffer lifetime for map handle 2023-10-29 22:12:16 -04:00
boludoz
7f62a48ab5 We dont need that 2023-10-27 00:30:35 -03:00
Franco M
b5415b6872 Merge branch 'yuzu-emu:master' into new-shortcut 2023-10-26 19:11:15 -03:00
Franco M
b76a1d987f Merge branch 'yuzu-emu:master' into new-shortcut 2023-10-21 02:25:27 -03:00
boludoz
ae2130470e Reverted dirty code in main. 2023-10-18 19:30:21 -03:00
boludoz
ac6290bea7 TODO: Implement shortcut creation for Apple. 2023-10-18 02:35:23 -03:00
boludoz
4051bbbed7 Useless code removed related to admin privileges, if it is not an error we can add it later, that is what git is for. 2023-10-18 01:26:50 -03:00
boludoz
2a7edda70a Deleted admin requisite (maybe it was another mistake). 2023-10-18 01:20:46 -03:00
Franco M
59b6ada7b7 Merge branch 'yuzu-emu:master' into new-shortcut 2023-10-18 01:06:58 -03:00
boludoz
9908434c14 Final refactorization 2023-10-17 02:57:35 -03:00
Franco M
668a10f9b9 Merge branch 'yuzu-emu:master' into new-shortcut 2023-10-17 01:01:41 -03:00
boludoz
fc4b45ebd3 Moved check. 2023-10-16 23:50:09 -03:00
boludoz
1afe6d51ee More @liamwhite suggestions applied. 2023-10-16 23:42:45 -03:00
boludoz
1ae0f0f3f6 shortcut_stream.close(); fixed 2023-10-16 18:59:21 -03:00
boludoz
de0b35b974 Comment using fmt instead qt. 2023-10-16 16:54:51 -03:00
boludoz
ae88d01d8d .clear() instead = ""; and switch improved. 2023-10-16 16:47:21 -03:00
boludoz
d759de9f96 More missed suggestions 2023-10-16 16:11:24 -03:00
boludoz
89d3e81be8 Sugestions and fixes. 2023-10-16 16:01:46 -03:00
boludoz
71f264c498 Merge branch 'new-shortcut' of https://github.com/boludoz/yuzu into new-shortcut 2023-10-16 03:27:10 -03:00
boludoz
26417da5d3 Some improvements (suggestions) 2023-10-16 03:26:40 -03:00
Franco M
b3b458edf9 Merge branch 'yuzu-emu:master' into new-shortcut 2023-10-16 03:25:11 -03:00
boludoz
74961d4dfb Less code, simpler, better. 2023-10-15 21:40:10 -03:00
boludoz
9ffa1801c7 Typing and formatting errors fixed. 2023-10-15 20:57:06 -03:00
boludoz
4d4fe69223 Unnecessary feature removed 2023-10-15 14:44:23 -03:00
boludoz
0a75519ab5 Fixes and improvements 2023-10-15 03:16:29 -03:00
boludoz
3062a35eb1 Improved shortcut: add games in applist for Windows, question for start game at fullscreen & better unicode support for some Windows path funcs. 2023-10-15 02:02:22 -03:00
187 changed files with 8902 additions and 4711 deletions

View File

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

3
.gitmodules vendored
View File

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

View File

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

View File

@@ -6,3 +6,8 @@ file_filter = <lang>.ts
source_file = en.ts
source_lang = en
type = QT
[o:yuzu-emulator:p:yuzu:r:yuzu-android]
file_filter = ../../src/android/app/src/main/res/values-<lang>/strings.xml
source_file = ../../src/android/app/src/main/res/values/strings.xml
type = ANDROID

View File

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

1
externals/oaknut vendored Submodule

Submodule externals/oaknut added at 316d8869e8

View File

@@ -301,6 +301,11 @@ object NativeLibrary {
*/
external fun getPerfStats(): DoubleArray
/**
* Returns the current CPU backend.
*/
external fun getCpuBackend(): String
/**
* Notifies the core emulation that the orientation has changed.
*/

View File

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

View File

@@ -73,10 +73,19 @@ abstract class SettingsItem(
R.string.frame_limit_slider,
R.string.frame_limit_slider_description,
1,
200,
400,
"%"
)
)
put(
SingleChoiceSetting(
IntSetting.CPU_BACKEND,
R.string.cpu_backend,
0,
R.array.cpuBackendNames,
R.array.cpuBackendValues
)
)
put(
SingleChoiceSetting(
IntSetting.CPU_ACCURACY,

View File

@@ -269,6 +269,7 @@ class SettingsFragmentPresenter(
add(BooleanSetting.RENDERER_DEBUG.key)
add(HeaderSetting(R.string.cpu))
add(IntSetting.CPU_BACKEND.key)
add(IntSetting.CPU_ACCURACY.key)
add(BooleanSetting.CPU_DEBUG_MODE.key)
add(SettingsItem.FASTMEM_COMBINED)

View File

@@ -414,8 +414,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
perfStatsUpdater = {
if (emulationViewModel.emulationStarted.value) {
val perfStats = NativeLibrary.getPerfStats()
val cpuBackend = NativeLibrary.getCpuBackend()
if (_binding != null) {
binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS])
binding.showFpsText.text =
String.format("FPS: %.1f\n%s", perfStats[FPS], cpuBackend)
}
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
}

View File

@@ -21,6 +21,8 @@ import org.yuzu.yuzu_emu.databinding.FragmentInstallablesBinding
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.Installable
import org.yuzu.yuzu_emu.ui.main.MainActivity
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
class InstallableFragment : Fragment() {
private var _binding: FragmentInstallablesBinding? = null
@@ -78,7 +80,15 @@ class InstallableFragment : Fragment() {
R.string.manage_save_data,
R.string.import_export_saves_description,
install = { mainActivity.importSaves.launch(arrayOf("application/zip")) },
export = { mainActivity.exportSave() }
export = {
mainActivity.exportSaves.launch(
"yuzu saves - ${
LocalDateTime.now().format(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
)
}.zip"
)
}
)
} else {
Installable(

View File

@@ -18,8 +18,8 @@ class Game(
val version: String = "",
val isHomebrew: Boolean = false
) : Parcelable {
val keyAddedToLibraryTime get() = "${programId}_AddedToLibraryTime"
val keyLastPlayedTime get() = "${programId}_LastPlayed"
val keyAddedToLibraryTime get() = "${path}_AddedToLibraryTime"
val keyLastPlayedTime get() = "${path}_LastPlayed"
override fun equals(other: Any?): Boolean {
if (other !is Game) {

View File

@@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.ui.main
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.DocumentsContract
import android.view.View
import android.view.ViewGroup.MarginLayoutParams
import android.view.WindowManager
@@ -20,7 +19,6 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
@@ -41,7 +39,6 @@ import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
import org.yuzu.yuzu_emu.features.DocumentProvider
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
@@ -53,9 +50,6 @@ import org.yuzu.yuzu_emu.model.TaskViewModel
import org.yuzu.yuzu_emu.utils.*
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.FileOutputStream
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
@@ -73,7 +67,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
// Get first subfolder in saves folder (should be the user folder)
val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: ""
private var lastZipCreated: File? = null
override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen()
@@ -656,75 +649,31 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
}
/**
* Zips the save files located in the given folder path and creates a new zip file with the current date and time.
* @return true if the zip file is successfully created, false otherwise.
*/
private fun zipSave(): Boolean {
try {
val tempFolder = File(getPublicFilesDir().canonicalPath, "temp")
tempFolder.mkdirs()
val saveFolder = File(savesFolderRoot)
val outputZipFile = File(
tempFolder,
"yuzu saves - ${
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
}.zip"
)
outputZipFile.createNewFile()
val result = FileUtil.zipFromInternalStorage(
saveFolder,
savesFolderRoot,
BufferedOutputStream(FileOutputStream(outputZipFile))
)
if (result == TaskState.Failed) {
return false
}
lastZipCreated = outputZipFile
} catch (e: Exception) {
return false
}
return true
}
/**
* Exports the save file located in the given folder path by creating a zip file and sharing it via intent.
*/
fun exportSave() {
CoroutineScope(Dispatchers.IO).launch {
val wasZipCreated = zipSave()
val lastZipFile = lastZipCreated
if (!wasZipCreated || lastZipFile == null) {
withContext(Dispatchers.Main) {
Toast.makeText(
this@MainActivity,
getString(R.string.export_save_failed),
Toast.LENGTH_LONG
).show()
}
return@launch
}
withContext(Dispatchers.Main) {
val file = DocumentFile.fromSingleUri(
this@MainActivity,
DocumentsContract.buildDocumentUri(
DocumentProvider.AUTHORITY,
"${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}"
)
)!!
val intent = Intent(Intent.ACTION_SEND)
.setDataAndType(file.uri, "application/zip")
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
.putExtra(Intent.EXTRA_STREAM, file.uri)
startForResultExportSave.launch(
Intent.createChooser(
intent,
getString(R.string.share_save_file)
)
)
}
val exportSaves = registerForActivityResult(
ActivityResultContracts.CreateDocument("application/zip")
) { result ->
if (result == null) {
return@registerForActivityResult
}
IndeterminateProgressDialogFragment.newInstance(
this,
R.string.save_files_exporting,
false
) {
val zipResult = FileUtil.zipFromInternalStorage(
File(savesFolderRoot),
savesFolderRoot,
BufferedOutputStream(contentResolver.openOutputStream(result))
)
return@newInstance when (zipResult) {
TaskState.Completed -> getString(R.string.export_success)
TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
}
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
}
private val startForResultExportSave =

View File

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

View File

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

View File

@@ -707,6 +707,14 @@ jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jcl
return j_stats;
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuBackend(JNIEnv* env, jclass clazz) {
if (Settings::IsNceEnabled()) {
return ToJString(env, "NCE");
}
return ToJString(env, "JIT");
}
void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory(JNIEnv* env,
jclass clazz,
jstring j_path) {}

View File

@@ -127,6 +127,7 @@
android:layout_height="wrap_content"
android:clipToPadding="false"
android:paddingVertical="4dp"
app:checkedChip="@id/chip_recently_played"
app:chipSpacingHorizontal="12dp"
app:singleLine="true"
app:singleSelection="true">

View File

@@ -175,6 +175,16 @@
<item>2</item>
</integer-array>
<string-array name="cpuBackendNames">
<item>@string/cpu_backend_dynarmic</item>
<item>@string/cpu_backend_nce</item>
</string-array>
<integer-array name="cpuBackendValues">
<item>0</item>
<item>1</item>
</integer-array>
<string-array name="cpuAccuracyNames">
<item>@string/auto</item>
<item>@string/cpu_accuracy_accurate</item>

View File

@@ -91,6 +91,7 @@
<string name="manage_save_data">Manage save data</string>
<string name="manage_save_data_description">Save data found. Please select an option below.</string>
<string name="import_export_saves_description">Import or export save files</string>
<string name="save_files_exporting">Exporting save files…</string>
<string name="save_file_imported_success">Imported successfully</string>
<string name="save_file_invalid_zip_structure">Invalid save directory structure</string>
<string name="save_file_invalid_zip_structure_description">The first subfolder name must be the title ID of the game.</string>
@@ -184,6 +185,7 @@
<string name="frame_limit_enable_description">Limits emulation speed to a specified percentage of normal speed.</string>
<string name="frame_limit_slider">Limit speed percent</string>
<string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. 100% is the normal speed. Values higher or lower will increase or decrease the speed limit.</string>
<string name="cpu_backend">CPU Backend</string>
<string name="cpu_accuracy">CPU accuracy</string>
<string name="value_with_units">%1$s%2$s</string>
@@ -256,6 +258,7 @@
<string name="cancelling">Cancelling</string>
<string name="install">Install</string>
<string name="delete">Delete</string>
<string name="export_success">Exported successfully</string>
<!-- GPU driver installation -->
<string name="select_gpu_driver">Select GPU driver</string>
@@ -414,6 +417,10 @@
<string name="ratio_force_sixteen_ten">Force 16:10</string>
<string name="ratio_stretch">Stretch to window</string>
<!-- CPU Backend -->
<string name="cpu_backend_dynarmic">Dynarmic (Slow)</string>
<string name="cpu_backend_nce">Native code execution (NCE)</string>
<!-- CPU Accuracy -->
<string name="cpu_accuracy_accurate">Accurate</string>
<string name="cpu_accuracy_unsafe">Unsafe</string>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

@@ -521,11 +521,21 @@ add_library(core STATIC
hle/service/grc/grc.h
hle/service/hid/hid.cpp
hle/service/hid/hid.h
hle/service/hid/hid_debug_server.cpp
hle/service/hid/hid_debug_server.h
hle/service/hid/hid_firmware_settings.cpp
hle/service/hid/hid_firmware_settings.h
hle/service/hid/hid_server.cpp
hle/service/hid/hid_server.h
hle/service/hid/hid_system_server.cpp
hle/service/hid/hid_system_server.h
hle/service/hid/hidbus.cpp
hle/service/hid/hidbus.h
hle/service/hid/irs.cpp
hle/service/hid/irs.h
hle/service/hid/irs_ring_lifo.h
hle/service/hid/resource_manager.cpp
hle/service/hid/resource_manager.h
hle/service/hid/ring_lifo.h
hle/service/hid/xcd.cpp
hle/service/hid/xcd.h
@@ -715,6 +725,7 @@ add_library(core STATIC
hle/service/nvnflinger/producer_listener.h
hle/service/nvnflinger/status.h
hle/service/nvnflinger/ui/fence.h
hle/service/nvnflinger/ui/graphic_buffer.cpp
hle/service/nvnflinger/ui/graphic_buffer.h
hle/service/nvnflinger/window.h
hle/service/olsc/olsc.cpp
@@ -910,6 +921,23 @@ if (ENABLE_WEB_SERVICE)
target_link_libraries(core PRIVATE web_service)
endif()
if (ARCHITECTURE_arm64 AND (ANDROID OR ${CMAKE_SYSTEM_NAME} STREQUAL "Linux"))
target_compile_definitions(core PRIVATE -DHAS_NCE)
enable_language(C ASM)
set(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp")
target_sources(core PRIVATE
arm/nce/arm_nce.cpp
arm/nce/arm_nce.h
arm/nce/arm_nce.s
arm/nce/guest_context.h
arm/nce/patch.cpp
arm/nce/patch.h
arm/nce/instructions.h
)
target_link_libraries(core PRIVATE merry::oaknut)
endif()
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
target_sources(core PRIVATE
arm/dynarmic/arm_dynarmic.h

View File

@@ -201,6 +201,8 @@ void ARM_Interface::Run() {
if (True(hr & HaltReason::DataAbort)) {
if (system.DebuggerEnabled()) {
system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint());
} else {
LogBacktrace();
}
current_thread->RequestSuspend(SuspendType::Debug);
break;

View File

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

View File

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

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

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

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

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

View File

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

View File

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

View File

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

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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -218,6 +218,13 @@ enum class NpadIdType : u32 {
Invalid = 0xFFFFFFFF,
};
enum class NpadInterfaceType : u8 {
Bluetooth = 1,
Rail = 2,
Usb = 3,
Embedded = 4,
};
// This is nn::hid::NpadStyleIndex
enum class NpadStyleIndex : u8 {
None = 0,
@@ -356,6 +363,14 @@ struct TouchState {
};
static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
struct TouchFinger {
u64 last_touch{};
Common::Point<float> position{};
u32 id{};
TouchAttribute attribute{};
bool pressed{};
};
// This is nn::hid::TouchScreenConfigurationForNx
struct TouchScreenConfigurationForNx {
TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting};

View File

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

View File

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

View File

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

View File

@@ -88,6 +88,22 @@ Result FlushDataCache(AddressType addr, u64 size) {
R_SUCCEED();
}
constexpr Common::MemoryPermission ConvertToMemoryPermission(KMemoryPermission perm) {
Common::MemoryPermission perms{};
if (True(perm & KMemoryPermission::UserRead)) {
perms |= Common::MemoryPermission::Read;
}
if (True(perm & KMemoryPermission::UserWrite)) {
perms |= Common::MemoryPermission::Write;
}
#ifdef HAS_NCE
if (True(perm & KMemoryPermission::UserExecute)) {
perms |= Common::MemoryPermission::Execute;
}
#endif
return perms;
}
} // namespace
void KPageTableBase::MemoryRange::Open() {
@@ -170,7 +186,8 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
KMemoryManager::Pool pool, KProcessAddress code_address,
size_t code_size, KSystemResource* system_resource,
KResourceLimit* resource_limit,
Core::Memory::Memory& memory) {
Core::Memory::Memory& memory,
KProcessAddress aslr_space_start) {
// Calculate region extents.
const size_t as_width = GetAddressSpaceWidth(as_type);
const KProcessAddress start = 0;
@@ -211,7 +228,8 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Heap);
stack_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Stack);
kernel_map_region_size = GetSpaceSize(KAddressSpaceInfo::Type::MapSmall);
m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::Map39Bit);
m_code_region_start = m_address_space_start + aslr_space_start +
GetSpaceStart(KAddressSpaceInfo::Type::Map39Bit);
m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::Map39Bit);
m_alias_code_region_start = m_code_region_start;
m_alias_code_region_end = m_code_region_end;
@@ -5643,7 +5661,8 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
case OperationType::Map: {
ASSERT(virt_addr != 0);
ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize));
m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr);
m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr,
ConvertToMemoryPermission(properties.perm));
// Open references to pages, if we should.
if (this->IsHeapPhysicalAddress(phys_addr)) {
@@ -5658,8 +5677,18 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
}
case OperationType::ChangePermissions:
case OperationType::ChangePermissionsAndRefresh:
case OperationType::ChangePermissionsAndRefreshAndFlush:
case OperationType::ChangePermissionsAndRefreshAndFlush: {
const bool read = True(properties.perm & Kernel::KMemoryPermission::UserRead);
const bool write = True(properties.perm & Kernel::KMemoryPermission::UserWrite);
// todo: this doesn't really belong here and should go into m_memory to handle rasterizer
// access todo: ignore exec on non-direct-mapped case
const bool exec = True(properties.perm & Kernel::KMemoryPermission::UserExecute);
if (Settings::IsFastmemEnabled()) {
m_system.DeviceMemory().buffer.Protect(GetInteger(virt_addr), num_pages * PageSize,
read, write, exec);
}
R_SUCCEED();
}
default:
UNREACHABLE();
}
@@ -5687,7 +5716,8 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
const size_t size{node.GetNumPages() * PageSize};
// Map the pages.
m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress());
m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress(),
ConvertToMemoryPermission(properties.perm));
virt_addr += size;
}

View File

@@ -235,7 +235,8 @@ public:
bool enable_device_address_space_merge, bool from_back,
KMemoryManager::Pool pool, KProcessAddress code_address,
size_t code_size, KSystemResource* system_resource,
KResourceLimit* resource_limit, Core::Memory::Memory& memory);
KResourceLimit* resource_limit, Core::Memory::Memory& memory,
KProcessAddress aslr_space_start);
void Finalize();

View File

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

View File

@@ -120,6 +120,9 @@ private:
std::atomic<s64> m_num_ipc_messages{};
std::atomic<s64> m_num_ipc_replies{};
std::atomic<s64> m_num_ipc_receives{};
#ifdef HAS_NCE
std::unordered_map<u64, u64> m_post_handlers{};
#endif
private:
Result StartTermination();
@@ -150,7 +153,8 @@ public:
std::span<const u32> caps, KResourceLimit* res_limit,
KMemoryManager::Pool pool, bool immortal);
Result Initialize(const Svc::CreateProcessParameter& params, std::span<const u32> user_caps,
KResourceLimit* res_limit, KMemoryManager::Pool pool);
KResourceLimit* res_limit, KMemoryManager::Pool pool,
KProcessAddress aslr_space_start);
void Exit();
const char* GetName() const {
@@ -466,6 +470,12 @@ public:
static void Switch(KProcess* cur_process, KProcess* next_process);
#ifdef HAS_NCE
std::unordered_map<u64, u64>& GetPostHandlers() noexcept {
return m_post_handlers;
}
#endif
public:
// Attempts to insert a watchpoint into a free slot. Returns false if none are available.
bool InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type);
@@ -479,7 +489,7 @@ public:
public:
Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
bool is_hbl);
KProcessAddress aslr_space_start, bool is_hbl);
void LoadModule(CodeSet code_set, KProcessAddress base_addr);

View File

@@ -23,10 +23,11 @@ public:
Result Initialize(Svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge,
bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address,
size_t code_size, KSystemResource* system_resource,
KResourceLimit* resource_limit, Core::Memory::Memory& memory) {
R_RETURN(m_page_table.InitializeForProcess(as_type, enable_aslr, enable_das_merge,
from_back, pool, code_address, code_size,
system_resource, resource_limit, memory));
KResourceLimit* resource_limit, Core::Memory::Memory& memory,
KProcessAddress aslr_space_start) {
R_RETURN(m_page_table.InitializeForProcess(
as_type, enable_aslr, enable_das_merge, from_back, pool, code_address, code_size,
system_resource, resource_limit, memory, aslr_space_start));
}
void Finalize() {

View File

@@ -655,6 +655,21 @@ public:
return m_stack_top;
}
public:
// TODO: This shouldn't be defined in kernel namespace
struct NativeExecutionParameters {
u64 tpidr_el0{};
u64 tpidrro_el0{};
void* native_context{};
std::atomic<u32> lock{1};
bool is_running{};
u32 magic{Common::MakeMagic('Y', 'U', 'Z', 'U')};
};
NativeExecutionParameters& GetNativeExecutionParameters() {
return m_native_execution_parameters;
}
private:
KThread* RemoveWaiterByKey(bool* out_has_waiters, KProcessAddress key,
bool is_kernel_address_key);
@@ -914,6 +929,7 @@ private:
ThreadWaitReasonForDebugging m_wait_reason_for_debugging{};
uintptr_t m_argument{};
KProcessAddress m_stack_top{};
NativeExecutionParameters m_native_execution_parameters{};
public:
using ConditionVariableThreadTreeType = ConditionVariableThreadTree;

View File

@@ -1,8 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/settings.h"
#include "core/arm/dynarmic/arm_dynarmic_32.h"
#include "core/arm/dynarmic/arm_dynarmic_64.h"
#ifdef HAS_NCE
#include "core/arm/nce/arm_nce.h"
#endif
#include "core/core.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/kernel.h"
@@ -14,7 +18,8 @@ PhysicalCore::PhysicalCore(std::size_t core_index, Core::System& system, KSchedu
: m_core_index{core_index}, m_system{system}, m_scheduler{scheduler} {
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
// TODO(bunnei): Initialization relies on a core being available. We may later replace this with
// a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.
// an NCE interface or a 32-bit instance of Dynarmic. This should be abstracted out to a CPU
// manager.
auto& kernel = system.Kernel();
m_arm_interface = std::make_unique<Core::ARM_Dynarmic_64>(
system, kernel.IsMulticore(),
@@ -28,6 +33,13 @@ PhysicalCore::PhysicalCore(std::size_t core_index, Core::System& system, KSchedu
PhysicalCore::~PhysicalCore() = default;
void PhysicalCore::Initialize(bool is_64_bit) {
#if defined(HAS_NCE)
if (Settings::IsNceEnabled()) {
m_arm_interface = std::make_unique<Core::ARM_NCE>(m_system, m_system.Kernel().IsMulticore(),
m_core_index);
return;
}
#endif
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
auto& kernel = m_system.Kernel();
if (!is_64_bit) {

View File

@@ -13,6 +13,7 @@
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/savedata_factory.h"
#include "core/hid/hid_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/result.h"
@@ -21,6 +22,7 @@
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/am/applets/applet_cabinet.h"
#include "core/hle/service/am/applets/applet_controller.h"
#include "core/hle/service/am/applets/applet_mii_edit_types.h"
#include "core/hle/service/am/applets/applet_profile_select.h"
#include "core/hle/service/am/applets/applet_software_keyboard_types.h"
@@ -35,6 +37,7 @@
#include "core/hle/service/caps/caps_su.h"
#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
@@ -73,7 +76,7 @@ IWindowController::IWindowController(Core::System& system_)
static const FunctionInfo functions[] = {
{0, nullptr, "CreateWindow"},
{1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"},
{2, nullptr, "GetAppletResourceUserIdOfCallerApplet"},
{2, &IWindowController::GetAppletResourceUserIdOfCallerApplet, "GetAppletResourceUserIdOfCallerApplet"},
{10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"},
{11, nullptr, "ReleaseForegroundRights"},
{12, nullptr, "RejectToChangeIntoBackground"},
@@ -97,6 +100,16 @@ void IWindowController::GetAppletResourceUserId(HLERequestContext& ctx) {
rb.Push<u64>(process_id);
}
void IWindowController::GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx) {
const u64 process_id = 0;
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u64>(process_id);
}
void IWindowController::AcquireForegroundRights(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
@@ -1565,7 +1578,7 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_)
{6, nullptr, "GetPopInteractiveInDataEvent"},
{10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"},
{11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"},
{12, nullptr, "GetMainAppletIdentityInfo"},
{12, &ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo, "GetMainAppletIdentityInfo"},
{13, nullptr, "CanUseApplicationCore"},
{14, &ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo, "GetCallerAppletIdentityInfo"},
{15, nullptr, "GetMainAppletApplicationControlProperty"},
@@ -1609,6 +1622,9 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_)
case Applets::AppletId::SoftwareKeyboard:
PushInShowSoftwareKeyboard();
break;
case Applets::AppletId::Controller:
PushInShowController();
break;
default:
break;
}
@@ -1666,13 +1682,33 @@ void ILibraryAppletSelfAccessor::GetLibraryAppletInfo(HLERequestContext& ctx) {
rb.PushRaw(applet_info);
}
void ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo(HLERequestContext& ctx) {
struct AppletIdentityInfo {
Applets::AppletId applet_id;
INSERT_PADDING_BYTES(0x4);
u64 application_id;
};
static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size.");
LOG_WARNING(Service_AM, "(STUBBED) called");
const AppletIdentityInfo applet_info{
.applet_id = Applets::AppletId::QLaunch,
.application_id = 0x0100000000001000ull,
};
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(applet_info);
}
void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) {
struct AppletIdentityInfo {
Applets::AppletId applet_id;
INSERT_PADDING_BYTES(0x4);
u64 application_id;
};
static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size.");
LOG_WARNING(Service_AM, "(STUBBED) called");
const AppletIdentityInfo applet_info{
@@ -1737,6 +1773,55 @@ void ILibraryAppletSelfAccessor::PushInShowAlbum() {
queue_data.emplace_back(std::move(settings_data));
}
void ILibraryAppletSelfAccessor::PushInShowController() {
const Applets::CommonArguments common_args = {
.arguments_version = Applets::CommonArgumentVersion::Version3,
.size = Applets::CommonArgumentSize::Version3,
.library_version = static_cast<u32>(Applets::ControllerAppletVersion::Version8),
.theme_color = Applets::ThemeColor::BasicBlack,
.play_startup_sound = true,
.system_tick = system.CoreTiming().GetClockTicks(),
};
Applets::ControllerSupportArgNew user_args = {
.header = {.player_count_min = 1,
.player_count_max = 4,
.enable_take_over_connection = true,
.enable_left_justify = false,
.enable_permit_joy_dual = true,
.enable_single_mode = false,
.enable_identification_color = false},
.identification_colors = {},
.enable_explain_text = false,
.explain_text = {},
};
Applets::ControllerSupportArgPrivate private_args = {
.arg_private_size = sizeof(Applets::ControllerSupportArgPrivate),
.arg_size = sizeof(Applets::ControllerSupportArgNew),
.is_home_menu = true,
.flag_1 = true,
.mode = Applets::ControllerSupportMode::ShowControllerSupport,
.caller = Applets::ControllerSupportCaller::
Application, // switchbrew: Always zero except with
// ShowControllerFirmwareUpdateForSystem/ShowControllerKeyRemappingForSystem,
// which sets this to the input param
.style_set = Core::HID::NpadStyleSet::None,
.joy_hold_type = 0,
};
std::vector<u8> common_args_data(sizeof(common_args));
std::vector<u8> private_args_data(sizeof(private_args));
std::vector<u8> user_args_data(sizeof(user_args));
std::memcpy(common_args_data.data(), &common_args, sizeof(common_args));
std::memcpy(private_args_data.data(), &private_args, sizeof(private_args));
std::memcpy(user_args_data.data(), &user_args, sizeof(user_args));
queue_data.emplace_back(std::move(common_args_data));
queue_data.emplace_back(std::move(private_args_data));
queue_data.emplace_back(std::move(user_args_data));
}
void ILibraryAppletSelfAccessor::PushInShowCabinetData() {
const Applets::CommonArguments arguments{
.arguments_version = Applets::CommonArgumentVersion::Version3,

View File

@@ -87,6 +87,7 @@ public:
private:
void GetAppletResourceUserId(HLERequestContext& ctx);
void GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx);
void AcquireForegroundRights(HLERequestContext& ctx);
};
@@ -345,6 +346,7 @@ private:
void PopInData(HLERequestContext& ctx);
void PushOutData(HLERequestContext& ctx);
void GetLibraryAppletInfo(HLERequestContext& ctx);
void GetMainAppletIdentityInfo(HLERequestContext& ctx);
void ExitProcessAndReturn(HLERequestContext& ctx);
void GetCallerAppletIdentityInfo(HLERequestContext& ctx);
void GetDesirableKeyboardLayout(HLERequestContext& ctx);
@@ -355,6 +357,7 @@ private:
void PushInShowCabinetData();
void PushInShowMiiEditData();
void PushInShowSoftwareKeyboard();
void PushInShowController();
std::deque<std::vector<u8>> queue_data;
};

View File

@@ -56,7 +56,7 @@ enum class ControllerSupportResult : u32 {
struct ControllerSupportArgPrivate {
u32 arg_private_size{};
u32 arg_size{};
bool flag_0{};
bool is_home_menu{};
bool flag_1{};
ControllerSupportMode mode{};
ControllerSupportCaller caller{};

View File

@@ -127,7 +127,7 @@ public:
private:
void GetCore(HLERequestContext& ctx) {
LOG_DEBUG(Service_BTM, "called");
LOG_WARNING(Service_BTM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
@@ -263,13 +263,13 @@ public:
explicit IBtmSystemCore(Core::System& system_) : ServiceFramework{system_, "IBtmSystemCore"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "StartGamepadPairing"},
{1, nullptr, "CancelGamepadPairing"},
{0, &IBtmSystemCore::StartGamepadPairing, "StartGamepadPairing"},
{1, &IBtmSystemCore::CancelGamepadPairing, "CancelGamepadPairing"},
{2, nullptr, "ClearGamepadPairingDatabase"},
{3, nullptr, "GetPairedGamepadCount"},
{4, nullptr, "EnableRadio"},
{5, nullptr, "DisableRadio"},
{6, nullptr, "GetRadioOnOff"},
{6, &IBtmSystemCore::IsRadioEnabled, "IsRadioEnabled"},
{7, nullptr, "AcquireRadioEvent"},
{8, nullptr, "AcquireGamepadPairingEvent"},
{9, nullptr, "IsGamepadPairingStarted"},
@@ -280,18 +280,58 @@ public:
{14, nullptr, "AcquireAudioDeviceConnectionEvent"},
{15, nullptr, "ConnectAudioDevice"},
{16, nullptr, "IsConnectingAudioDevice"},
{17, nullptr, "GetConnectedAudioDevices"},
{17, &IBtmSystemCore::GetConnectedAudioDevices, "GetConnectedAudioDevices"},
{18, nullptr, "DisconnectAudioDevice"},
{19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"},
{20, nullptr, "GetPairedAudioDevices"},
{21, nullptr, "RemoveAudioDevicePairing"},
{22, nullptr, "RequestAudioDeviceConnectionRejection"},
{23, nullptr, "CancelAudioDeviceConnectionRejection"}
{22, &IBtmSystemCore::RequestAudioDeviceConnectionRejection, "RequestAudioDeviceConnectionRejection"},
{23, &IBtmSystemCore::CancelAudioDeviceConnectionRejection, "CancelAudioDeviceConnectionRejection"}
};
// clang-format on
RegisterHandlers(functions);
}
private:
void IsRadioEnabled(HLERequestContext& ctx) {
LOG_DEBUG(Service_BTM, "(STUBBED) called"); // Spams a lot when controller applet is running
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(true);
}
void StartGamepadPairing(HLERequestContext& ctx) {
LOG_WARNING(Service_BTM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void CancelGamepadPairing(HLERequestContext& ctx) {
LOG_WARNING(Service_BTM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void CancelAudioDeviceConnectionRejection(HLERequestContext& ctx) {
LOG_WARNING(Service_BTM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void GetConnectedAudioDevices(HLERequestContext& ctx) {
LOG_WARNING(Service_BTM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(0);
}
void RequestAudioDeviceConnectionRejection(HLERequestContext& ctx) {
LOG_WARNING(Service_BTM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
};
class BTM_SYS final : public ServiceFramework<BTM_SYS> {
@@ -308,7 +348,7 @@ public:
private:
void GetCore(HLERequestContext& ctx) {
LOG_DEBUG(Service_BTM, "called");
LOG_WARNING(Service_BTM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);

View File

@@ -32,7 +32,7 @@ public:
{10200, nullptr, "SendFriendRequestForApplication"},
{10211, nullptr, "AddFacedFriendRequestForApplication"},
{10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"},
{10420, nullptr, "IsBlockedUserListCacheAvailable"},
{10420, &IFriendService::CheckBlockedUserListAvailability, "CheckBlockedUserListAvailability"},
{10421, nullptr, "EnsureBlockedUserListAvailable"},
{10500, nullptr, "GetProfileList"},
{10600, nullptr, "DeclareOpenOnlinePlaySession"},
@@ -206,6 +206,17 @@ private:
rb.Push(true);
}
void CheckBlockedUserListAvailability(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto uuid{rp.PopRaw<Common::UUID>()};
LOG_WARNING(Service_Friend, "(STUBBED) called, uuid=0x{}", uuid.RawString());
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(true);
}
KernelHelpers::ServiceContext service_context;
Kernel::KEvent* completion_event;

View File

@@ -8,12 +8,17 @@ namespace Service::HID {
ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {}
ControllerBase::~ControllerBase() = default;
void ControllerBase::ActivateController() {
Result ControllerBase::Activate() {
if (is_activated) {
return;
return ResultSuccess;
}
is_activated = true;
OnInit();
return ResultSuccess;
}
Result ControllerBase::Activate(u64 aruid) {
return Activate();
}
void ControllerBase::DeactivateController() {

View File

@@ -4,6 +4,7 @@
#pragma once
#include "common/common_types.h"
#include "core/hle/result.h"
namespace Core::Timing {
class CoreTiming;
@@ -31,7 +32,8 @@ public:
// When the controller is requesting a motion update for the shared memory
virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {}
void ActivateController();
Result Activate();
Result Activate(u64 aruid);
void DeactivateController();

View File

@@ -344,6 +344,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices,
Common::Input::PollingMode::Active);
}
SignalStyleSetChangedEvent(npad_id);
WriteEmptyEntry(controller.shared_memory);
hid_core.SetLastActiveController(npad_id);
@@ -457,12 +458,14 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
pad_entry.l_stick = stick_state.left;
}
if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft) {
if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft ||
controller_type == Core::HID::NpadStyleIndex::JoyconDual) {
pad_entry.npad_buttons.left_sl.Assign(button_state.left_sl);
pad_entry.npad_buttons.left_sr.Assign(button_state.left_sr);
}
if (controller_type == Core::HID::NpadStyleIndex::JoyconRight) {
if (controller_type == Core::HID::NpadStyleIndex::JoyconRight ||
controller_type == Core::HID::NpadStyleIndex::JoyconDual) {
pad_entry.npad_buttons.right_sl.Assign(button_state.right_sl);
pad_entry.npad_buttons.right_sr.Assign(button_state.right_sr);
}
@@ -1724,4 +1727,19 @@ const Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState(
}
}
Controller_NPad::AppletDetailedUiType Controller_NPad::GetAppletDetailedUiType(
Core::HID::NpadIdType npad_id) {
auto controller = GetControllerFromNpadIdType(npad_id);
auto shared_memory = controller.shared_memory;
Service::HID::Controller_NPad::AppletFooterUiType applet_footer_type =
shared_memory->applet_footer_type;
Controller_NPad::AppletDetailedUiType detailed_ui_type{
.ui_variant = 0,
.footer = applet_footer_type,
};
return detailed_ui_type;
}
} // namespace Service::HID

View File

@@ -78,6 +78,46 @@ public:
MaxActivationMode = 3,
};
// This is nn::hid::system::AppletFooterUiAttributesSet
struct AppletFooterUiAttributes {
INSERT_PADDING_BYTES(0x4);
};
// This is nn::hid::system::AppletFooterUiType
enum class AppletFooterUiType : u8 {
None = 0,
HandheldNone = 1,
HandheldJoyConLeftOnly = 2,
HandheldJoyConRightOnly = 3,
HandheldJoyConLeftJoyConRight = 4,
JoyDual = 5,
JoyDualLeftOnly = 6,
JoyDualRightOnly = 7,
JoyLeftHorizontal = 8,
JoyLeftVertical = 9,
JoyRightHorizontal = 10,
JoyRightVertical = 11,
SwitchProController = 12,
CompatibleProController = 13,
CompatibleJoyCon = 14,
LarkHvc1 = 15,
LarkHvc2 = 16,
LarkNesLeft = 17,
LarkNesRight = 18,
Lucia = 19,
Verification = 20,
Lagon = 21,
};
using AppletFooterUiVariant = u8;
// This is "nn::hid::system::AppletDetailedUiType".
struct AppletDetailedUiType {
AppletFooterUiVariant ui_variant;
INSERT_PADDING_BYTES(0x2);
AppletFooterUiType footer;
};
static_assert(sizeof(AppletDetailedUiType) == 0x4, "AppletDetailedUiType is an invalid size");
// This is nn::hid::NpadCommunicationMode
enum class NpadCommunicationMode : u64 {
Mode_5ms = 0,
@@ -86,6 +126,13 @@ public:
Default = 3,
};
enum class NpadRevision : u32 {
Revision0 = 0,
Revision1 = 1,
Revision2 = 2,
Revision3 = 3,
};
void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
Core::HID::NpadStyleTag GetSupportedStyleSet() const;
@@ -196,6 +243,7 @@ public:
static Result IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
static Result VerifyValidSixAxisSensorHandle(
const Core::HID::SixAxisSensorHandle& device_handle);
AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id);
private:
static constexpr std::size_t NPAD_COUNT = 10;
@@ -353,37 +401,6 @@ private:
static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
"NfcXcdDeviceHandleStateImpl is an invalid size");
// This is nn::hid::system::AppletFooterUiAttributesSet
struct AppletFooterUiAttributes {
INSERT_PADDING_BYTES(0x4);
};
// This is nn::hid::system::AppletFooterUiType
enum class AppletFooterUiType : u8 {
None = 0,
HandheldNone = 1,
HandheldJoyConLeftOnly = 2,
HandheldJoyConRightOnly = 3,
HandheldJoyConLeftJoyConRight = 4,
JoyDual = 5,
JoyDualLeftOnly = 6,
JoyDualRightOnly = 7,
JoyLeftHorizontal = 8,
JoyLeftVertical = 9,
JoyRightHorizontal = 10,
JoyRightVertical = 11,
SwitchProController = 12,
CompatibleProController = 13,
CompatibleJoyCon = 14,
LarkHvc1 = 15,
LarkHvc2 = 16,
LarkNesLeft = 17,
LarkNesRight = 18,
Lucia = 19,
Verification = 20,
Lagon = 21,
};
// This is nn::hid::NpadLarkType
enum class NpadLarkType : u32 {
Invalid,

View File

@@ -44,7 +44,7 @@ Result Controller_Palma::InitializePalma(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
ActivateController();
Activate();
return ResultSuccess;
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,220 +3,12 @@
#pragma once
#include <chrono>
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
namespace Core::Timing {
struct EventType;
}
namespace Service::SM {
class ServiceManager;
namespace Core {
class System;
}
namespace Service::HID {
enum class HidController : std::size_t {
DebugPad,
Touchscreen,
Mouse,
Keyboard,
XPad,
HomeButton,
SleepButton,
CaptureButton,
InputDetector,
UniquePad,
NPad,
Gesture,
ConsoleSixAxisSensor,
DebugMouse,
Palma,
MaxControllers,
};
class IAppletResource final : public ServiceFramework<IAppletResource> {
public:
explicit IAppletResource(Core::System& system_,
KernelHelpers::ServiceContext& service_context_);
~IAppletResource() override;
void ActivateController(HidController controller);
void DeactivateController(HidController controller);
template <typename T>
T& GetController(HidController controller) {
return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
}
template <typename T>
const T& GetController(HidController controller) const {
return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
}
private:
template <typename T>
void MakeController(HidController controller, u8* shared_memory) {
if constexpr (std::is_constructible_v<T, Core::System&, u8*>) {
controllers[static_cast<std::size_t>(controller)] =
std::make_unique<T>(system, shared_memory);
} else {
controllers[static_cast<std::size_t>(controller)] =
std::make_unique<T>(system.HIDCore(), shared_memory);
}
}
template <typename T>
void MakeControllerWithServiceContext(HidController controller, u8* shared_memory) {
controllers[static_cast<std::size_t>(controller)] =
std::make_unique<T>(system.HIDCore(), shared_memory, service_context);
}
void GetSharedMemoryHandle(HLERequestContext& ctx);
void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
KernelHelpers::ServiceContext& service_context;
std::shared_ptr<Core::Timing::EventType> npad_update_event;
std::shared_ptr<Core::Timing::EventType> default_update_event;
std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
std::shared_ptr<Core::Timing::EventType> motion_update_event;
std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
controllers{};
};
class Hid final : public ServiceFramework<Hid> {
public:
explicit Hid(Core::System& system_, std::shared_ptr<IAppletResource> applet_resource_);
~Hid() override;
std::shared_ptr<IAppletResource> GetAppletResource();
private:
void CreateAppletResource(HLERequestContext& ctx);
void ActivateDebugPad(HLERequestContext& ctx);
void ActivateTouchScreen(HLERequestContext& ctx);
void ActivateMouse(HLERequestContext& ctx);
void ActivateKeyboard(HLERequestContext& ctx);
void SendKeyboardLockKeyEvent(HLERequestContext& ctx);
void ActivateXpad(HLERequestContext& ctx);
void GetXpadIDs(HLERequestContext& ctx);
void ActivateSixAxisSensor(HLERequestContext& ctx);
void DeactivateSixAxisSensor(HLERequestContext& ctx);
void StartSixAxisSensor(HLERequestContext& ctx);
void StopSixAxisSensor(HLERequestContext& ctx);
void IsSixAxisSensorFusionEnabled(HLERequestContext& ctx);
void EnableSixAxisSensorFusion(HLERequestContext& ctx);
void SetSixAxisSensorFusionParameters(HLERequestContext& ctx);
void GetSixAxisSensorFusionParameters(HLERequestContext& ctx);
void ResetSixAxisSensorFusionParameters(HLERequestContext& ctx);
void SetGyroscopeZeroDriftMode(HLERequestContext& ctx);
void GetGyroscopeZeroDriftMode(HLERequestContext& ctx);
void ResetGyroscopeZeroDriftMode(HLERequestContext& ctx);
void IsSixAxisSensorAtRest(HLERequestContext& ctx);
void IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx);
void EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx);
void IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx);
void LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx);
void GetSixAxisSensorIcInformation(HLERequestContext& ctx);
void ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx);
void ActivateGesture(HLERequestContext& ctx);
void SetSupportedNpadStyleSet(HLERequestContext& ctx);
void GetSupportedNpadStyleSet(HLERequestContext& ctx);
void SetSupportedNpadIdType(HLERequestContext& ctx);
void ActivateNpad(HLERequestContext& ctx);
void DeactivateNpad(HLERequestContext& ctx);
void AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx);
void DisconnectNpad(HLERequestContext& ctx);
void GetPlayerLedPattern(HLERequestContext& ctx);
void ActivateNpadWithRevision(HLERequestContext& ctx);
void SetNpadJoyHoldType(HLERequestContext& ctx);
void GetNpadJoyHoldType(HLERequestContext& ctx);
void SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx);
void SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx);
void SetNpadJoyAssignmentModeDual(HLERequestContext& ctx);
void MergeSingleJoyAsDualJoy(HLERequestContext& ctx);
void StartLrAssignmentMode(HLERequestContext& ctx);
void StopLrAssignmentMode(HLERequestContext& ctx);
void SetNpadHandheldActivationMode(HLERequestContext& ctx);
void GetNpadHandheldActivationMode(HLERequestContext& ctx);
void SwapNpadAssignment(HLERequestContext& ctx);
void IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx);
void EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx);
void SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx);
void SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx);
void SetNpadCaptureButtonAssignment(HLERequestContext& ctx);
void ClearNpadCaptureButtonAssignment(HLERequestContext& ctx);
void GetVibrationDeviceInfo(HLERequestContext& ctx);
void SendVibrationValue(HLERequestContext& ctx);
void GetActualVibrationValue(HLERequestContext& ctx);
void CreateActiveVibrationDeviceList(HLERequestContext& ctx);
void PermitVibration(HLERequestContext& ctx);
void IsVibrationPermitted(HLERequestContext& ctx);
void SendVibrationValues(HLERequestContext& ctx);
void SendVibrationGcErmCommand(HLERequestContext& ctx);
void GetActualVibrationGcErmCommand(HLERequestContext& ctx);
void BeginPermitVibrationSession(HLERequestContext& ctx);
void EndPermitVibrationSession(HLERequestContext& ctx);
void IsVibrationDeviceMounted(HLERequestContext& ctx);
void ActivateConsoleSixAxisSensor(HLERequestContext& ctx);
void StartConsoleSixAxisSensor(HLERequestContext& ctx);
void StopConsoleSixAxisSensor(HLERequestContext& ctx);
void ActivateSevenSixAxisSensor(HLERequestContext& ctx);
void StartSevenSixAxisSensor(HLERequestContext& ctx);
void StopSevenSixAxisSensor(HLERequestContext& ctx);
void InitializeSevenSixAxisSensor(HLERequestContext& ctx);
void FinalizeSevenSixAxisSensor(HLERequestContext& ctx);
void ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx);
void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
void GetPalmaConnectionHandle(HLERequestContext& ctx);
void InitializePalma(HLERequestContext& ctx);
void AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx);
void GetPalmaOperationInfo(HLERequestContext& ctx);
void PlayPalmaActivity(HLERequestContext& ctx);
void SetPalmaFrModeType(HLERequestContext& ctx);
void ReadPalmaStep(HLERequestContext& ctx);
void EnablePalmaStep(HLERequestContext& ctx);
void ResetPalmaStep(HLERequestContext& ctx);
void ReadPalmaApplicationSection(HLERequestContext& ctx);
void WritePalmaApplicationSection(HLERequestContext& ctx);
void ReadPalmaUniqueCode(HLERequestContext& ctx);
void SetPalmaUniqueCodeInvalid(HLERequestContext& ctx);
void WritePalmaActivityEntry(HLERequestContext& ctx);
void WritePalmaRgbLedPatternEntry(HLERequestContext& ctx);
void WritePalmaWaveEntry(HLERequestContext& ctx);
void SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
void GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
void SuspendPalmaFeature(HLERequestContext& ctx);
void GetPalmaOperationResult(HLERequestContext& ctx);
void ReadPalmaPlayLog(HLERequestContext& ctx);
void ResetPalmaPlayLog(HLERequestContext& ctx);
void SetIsPalmaAllConnectable(HLERequestContext& ctx);
void SetIsPalmaPairedConnectable(HLERequestContext& ctx);
void PairPalma(HLERequestContext& ctx);
void SetPalmaBoostMode(HLERequestContext& ctx);
void CancelWritePalmaWaveEntry(HLERequestContext& ctx);
void EnablePalmaBoostMode(HLERequestContext& ctx);
void GetPalmaBluetoothAddress(HLERequestContext& ctx);
void SetDisallowedPalmaConnection(HLERequestContext& ctx);
void SetNpadCommunicationMode(HLERequestContext& ctx);
void GetNpadCommunicationMode(HLERequestContext& ctx);
void SetTouchScreenConfiguration(HLERequestContext& ctx);
void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx);
std::shared_ptr<IAppletResource> applet_resource;
KernelHelpers::ServiceContext service_context;
};
void LoopProcess(Core::System& system);
} // namespace Service::HID

View File

@@ -0,0 +1,159 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/hid/hid_debug_server.h"
#include "core/hle/service/hid/resource_manager.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::HID {
IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource)
: ServiceFramework{system_, "hid:dbg"}, resource_manager{resource} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "DeactivateDebugPad"},
{1, nullptr, "SetDebugPadAutoPilotState"},
{2, nullptr, "UnsetDebugPadAutoPilotState"},
{10, nullptr, "DeactivateTouchScreen"},
{11, nullptr, "SetTouchScreenAutoPilotState"},
{12, nullptr, "UnsetTouchScreenAutoPilotState"},
{13, nullptr, "GetTouchScreenConfiguration"},
{14, nullptr, "ProcessTouchScreenAutoTune"},
{15, nullptr, "ForceStopTouchScreenManagement"},
{16, nullptr, "ForceRestartTouchScreenManagement"},
{17, nullptr, "IsTouchScreenManaged"},
{20, nullptr, "DeactivateMouse"},
{21, nullptr, "SetMouseAutoPilotState"},
{22, nullptr, "UnsetMouseAutoPilotState"},
{25, nullptr, "SetDebugMouseAutoPilotState"},
{26, nullptr, "UnsetDebugMouseAutoPilotState"},
{30, nullptr, "DeactivateKeyboard"},
{31, nullptr, "SetKeyboardAutoPilotState"},
{32, nullptr, "UnsetKeyboardAutoPilotState"},
{50, nullptr, "DeactivateXpad"},
{51, nullptr, "SetXpadAutoPilotState"},
{52, nullptr, "UnsetXpadAutoPilotState"},
{53, nullptr, "DeactivateJoyXpad"},
{60, nullptr, "ClearNpadSystemCommonPolicy"},
{61, nullptr, "DeactivateNpad"},
{62, nullptr, "ForceDisconnectNpad"},
{91, nullptr, "DeactivateGesture"},
{110, nullptr, "DeactivateHomeButton"},
{111, nullptr, "SetHomeButtonAutoPilotState"},
{112, nullptr, "UnsetHomeButtonAutoPilotState"},
{120, nullptr, "DeactivateSleepButton"},
{121, nullptr, "SetSleepButtonAutoPilotState"},
{122, nullptr, "UnsetSleepButtonAutoPilotState"},
{123, nullptr, "DeactivateInputDetector"},
{130, nullptr, "DeactivateCaptureButton"},
{131, nullptr, "SetCaptureButtonAutoPilotState"},
{132, nullptr, "UnsetCaptureButtonAutoPilotState"},
{133, nullptr, "SetShiftAccelerometerCalibrationValue"},
{134, nullptr, "GetShiftAccelerometerCalibrationValue"},
{135, nullptr, "SetShiftGyroscopeCalibrationValue"},
{136, nullptr, "GetShiftGyroscopeCalibrationValue"},
{140, nullptr, "DeactivateConsoleSixAxisSensor"},
{141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"},
{142, nullptr, "DeactivateSevenSixAxisSensor"},
{143, nullptr, "GetConsoleSixAxisSensorCountStates"},
{144, nullptr, "GetAccelerometerFsr"},
{145, nullptr, "SetAccelerometerFsr"},
{146, nullptr, "GetAccelerometerOdr"},
{147, nullptr, "SetAccelerometerOdr"},
{148, nullptr, "GetGyroscopeFsr"},
{149, nullptr, "SetGyroscopeFsr"},
{150, nullptr, "GetGyroscopeOdr"},
{151, nullptr, "SetGyroscopeOdr"},
{152, nullptr, "GetWhoAmI"},
{201, nullptr, "ActivateFirmwareUpdate"},
{202, nullptr, "DeactivateFirmwareUpdate"},
{203, nullptr, "StartFirmwareUpdate"},
{204, nullptr, "GetFirmwareUpdateStage"},
{205, nullptr, "GetFirmwareVersion"},
{206, nullptr, "GetDestinationFirmwareVersion"},
{207, nullptr, "DiscardFirmwareInfoCacheForRevert"},
{208, nullptr, "StartFirmwareUpdateForRevert"},
{209, nullptr, "GetAvailableFirmwareVersionForRevert"},
{210, nullptr, "IsFirmwareUpdatingDevice"},
{211, nullptr, "StartFirmwareUpdateIndividual"},
{215, nullptr, "SetUsbFirmwareForceUpdateEnabled"},
{216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"},
{221, nullptr, "UpdateControllerColor"},
{222, nullptr, "ConnectUsbPadsAsync"},
{223, nullptr, "DisconnectUsbPadsAsync"},
{224, nullptr, "UpdateDesignInfo"},
{225, nullptr, "GetUniquePadDriverState"},
{226, nullptr, "GetSixAxisSensorDriverStates"},
{227, nullptr, "GetRxPacketHistory"},
{228, nullptr, "AcquireOperationEventHandle"},
{229, nullptr, "ReadSerialFlash"},
{230, nullptr, "WriteSerialFlash"},
{231, nullptr, "GetOperationResult"},
{232, nullptr, "EnableShipmentMode"},
{233, nullptr, "ClearPairingInfo"},
{234, nullptr, "GetUniquePadDeviceTypeSetInternal"},
{235, nullptr, "EnableAnalogStickPower"},
{236, nullptr, "RequestKuinaUartClockCal"},
{237, nullptr, "GetKuinaUartClockCal"},
{238, nullptr, "SetKuinaUartClockTrim"},
{239, nullptr, "KuinaLoopbackTest"},
{240, nullptr, "RequestBatteryVoltage"},
{241, nullptr, "GetBatteryVoltage"},
{242, nullptr, "GetUniquePadPowerInfo"},
{243, nullptr, "RebootUniquePad"},
{244, nullptr, "RequestKuinaFirmwareVersion"},
{245, nullptr, "GetKuinaFirmwareVersion"},
{246, nullptr, "GetVidPid"},
{247, nullptr, "GetAnalogStickCalibrationValue"},
{248, nullptr, "GetUniquePadIdsFull"},
{249, nullptr, "ConnectUniquePad"},
{250, nullptr, "IsVirtual"},
{251, nullptr, "GetAnalogStickModuleParam"},
{301, nullptr, "GetAbstractedPadHandles"},
{302, nullptr, "GetAbstractedPadState"},
{303, nullptr, "GetAbstractedPadsState"},
{321, nullptr, "SetAutoPilotVirtualPadState"},
{322, nullptr, "UnsetAutoPilotVirtualPadState"},
{323, nullptr, "UnsetAllAutoPilotVirtualPadState"},
{324, nullptr, "AttachHdlsWorkBuffer"},
{325, nullptr, "ReleaseHdlsWorkBuffer"},
{326, nullptr, "DumpHdlsNpadAssignmentState"},
{327, nullptr, "DumpHdlsStates"},
{328, nullptr, "ApplyHdlsNpadAssignmentState"},
{329, nullptr, "ApplyHdlsStateList"},
{330, nullptr, "AttachHdlsVirtualDevice"},
{331, nullptr, "DetachHdlsVirtualDevice"},
{332, nullptr, "SetHdlsState"},
{350, nullptr, "AddRegisteredDevice"},
{400, nullptr, "DisableExternalMcuOnNxDevice"},
{401, nullptr, "DisableRailDeviceFiltering"},
{402, nullptr, "EnableWiredPairing"},
{403, nullptr, "EnableShipmentModeAutoClear"},
{404, nullptr, "SetRailEnabled"},
{500, nullptr, "SetFactoryInt"},
{501, nullptr, "IsFactoryBootEnabled"},
{550, nullptr, "SetAnalogStickModelDataTemporarily"},
{551, nullptr, "GetAnalogStickModelData"},
{552, nullptr, "ResetAnalogStickModelData"},
{600, nullptr, "ConvertPadState"},
{650, nullptr, "AddButtonPlayData"},
{651, nullptr, "StartButtonPlayData"},
{652, nullptr, "StopButtonPlayData"},
{2000, nullptr, "DeactivateDigitizer"},
{2001, nullptr, "SetDigitizerAutoPilotState"},
{2002, nullptr, "UnsetDigitizerAutoPilotState"},
{2002, nullptr, "ReloadFirmwareDebugSettings"},
};
// clang-format on
RegisterHandlers(functions);
}
IHidDebugServer::~IHidDebugServer() = default;
std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() {
resource_manager->Initialize();
return resource_manager;
}
} // namespace Service::HID

View File

@@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::HID {
class ResourceManager;
class IHidDebugServer final : public ServiceFramework<IHidDebugServer> {
public:
explicit IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource);
~IHidDebugServer() override;
private:
std::shared_ptr<ResourceManager> GetResourceManager();
std::shared_ptr<ResourceManager> resource_manager;
};
} // namespace Service::HID

View File

@@ -0,0 +1,99 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/hid/hid_firmware_settings.h"
namespace Service::HID {
HidFirmwareSettings::HidFirmwareSettings() {
LoadSettings(true);
}
void HidFirmwareSettings::Reload() {
LoadSettings(true);
}
void HidFirmwareSettings::LoadSettings(bool reload_config) {
if (is_initalized && !reload_config) {
return;
}
// TODO: Use nn::settings::fwdbg::GetSettingsItemValue to load config values
is_debug_pad_enabled = true;
is_device_managed = true;
is_touch_i2c_managed = is_device_managed;
is_future_devices_emulated = false;
is_mcu_hardware_error_emulated = false;
is_rail_enabled = true;
is_firmware_update_failure_emulated = false;
is_firmware_update_failure = {};
is_ble_disabled = false;
is_dscale_disabled = false;
is_handheld_forced = true;
features_per_id_disabled = {};
is_touch_firmware_auto_update_disabled = false;
is_initalized = true;
}
bool HidFirmwareSettings::IsDebugPadEnabled() {
LoadSettings(false);
return is_debug_pad_enabled;
}
bool HidFirmwareSettings::IsDeviceManaged() {
LoadSettings(false);
return is_device_managed;
}
bool HidFirmwareSettings::IsEmulateFutureDevice() {
LoadSettings(false);
return is_future_devices_emulated;
}
bool HidFirmwareSettings::IsTouchI2cManaged() {
LoadSettings(false);
return is_touch_i2c_managed;
}
bool HidFirmwareSettings::IsHandheldForced() {
LoadSettings(false);
return is_handheld_forced;
}
bool HidFirmwareSettings::IsRailEnabled() {
LoadSettings(false);
return is_rail_enabled;
}
bool HidFirmwareSettings::IsHardwareErrorEmulated() {
LoadSettings(false);
return is_mcu_hardware_error_emulated;
}
bool HidFirmwareSettings::IsBleDisabled() {
LoadSettings(false);
return is_ble_disabled;
}
bool HidFirmwareSettings::IsDscaleDisabled() {
LoadSettings(false);
return is_dscale_disabled;
}
bool HidFirmwareSettings::IsTouchAutoUpdateDisabled() {
LoadSettings(false);
return is_touch_firmware_auto_update_disabled;
}
HidFirmwareSettings::FirmwareSetting HidFirmwareSettings::GetFirmwareUpdateFailure() {
LoadSettings(false);
return is_firmware_update_failure;
}
HidFirmwareSettings::FeaturesPerId HidFirmwareSettings::FeaturesDisabledPerId() {
LoadSettings(false);
return features_per_id_disabled;
}
} // namespace Service::HID

View File

@@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_types.h"
namespace Service::HID {
/// Loads firmware config from nn::settings::fwdbg
class HidFirmwareSettings {
public:
using FirmwareSetting = std::array<u8, 4>;
using FeaturesPerId = std::array<bool, 0xA8>;
HidFirmwareSettings();
void Reload();
void LoadSettings(bool reload_config);
bool IsDebugPadEnabled();
bool IsDeviceManaged();
bool IsEmulateFutureDevice();
bool IsTouchI2cManaged();
bool IsHandheldForced();
bool IsRailEnabled();
bool IsHardwareErrorEmulated();
bool IsBleDisabled();
bool IsDscaleDisabled();
bool IsTouchAutoUpdateDisabled();
FirmwareSetting GetFirmwareUpdateFailure();
FeaturesPerId FeaturesDisabledPerId();
private:
bool is_initalized{};
// Debug settings
bool is_debug_pad_enabled{};
bool is_device_managed{};
bool is_touch_i2c_managed{};
bool is_future_devices_emulated{};
bool is_mcu_hardware_error_emulated{};
bool is_rail_enabled{};
bool is_firmware_update_failure_emulated{};
bool is_ble_disabled{};
bool is_dscale_disabled{};
bool is_handheld_forced{};
bool is_touch_firmware_auto_update_disabled{};
FirmwareSetting is_firmware_update_failure{};
FeaturesPerId features_per_id_disabled{};
};
} // namespace Service::HID

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,149 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::HID {
class ResourceManager;
class HidFirmwareSettings;
class IHidServer final : public ServiceFramework<IHidServer> {
public:
explicit IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
std::shared_ptr<HidFirmwareSettings> settings);
~IHidServer() override;
std::shared_ptr<ResourceManager> GetResourceManager();
private:
void CreateAppletResource(HLERequestContext& ctx);
void ActivateDebugPad(HLERequestContext& ctx);
void ActivateTouchScreen(HLERequestContext& ctx);
void ActivateMouse(HLERequestContext& ctx);
void ActivateKeyboard(HLERequestContext& ctx);
void SendKeyboardLockKeyEvent(HLERequestContext& ctx);
void AcquireXpadIdEventHandle(HLERequestContext& ctx);
void ReleaseXpadIdEventHandle(HLERequestContext& ctx);
void ActivateXpad(HLERequestContext& ctx);
void GetXpadIds(HLERequestContext& ctx);
void ActivateJoyXpad(HLERequestContext& ctx);
void GetJoyXpadLifoHandle(HLERequestContext& ctx);
void GetJoyXpadIds(HLERequestContext& ctx);
void ActivateSixAxisSensor(HLERequestContext& ctx);
void DeactivateSixAxisSensor(HLERequestContext& ctx);
void GetSixAxisSensorLifoHandle(HLERequestContext& ctx);
void ActivateJoySixAxisSensor(HLERequestContext& ctx);
void DeactivateJoySixAxisSensor(HLERequestContext& ctx);
void GetJoySixAxisSensorLifoHandle(HLERequestContext& ctx);
void StartSixAxisSensor(HLERequestContext& ctx);
void StopSixAxisSensor(HLERequestContext& ctx);
void IsSixAxisSensorFusionEnabled(HLERequestContext& ctx);
void EnableSixAxisSensorFusion(HLERequestContext& ctx);
void SetSixAxisSensorFusionParameters(HLERequestContext& ctx);
void GetSixAxisSensorFusionParameters(HLERequestContext& ctx);
void ResetSixAxisSensorFusionParameters(HLERequestContext& ctx);
void SetGyroscopeZeroDriftMode(HLERequestContext& ctx);
void GetGyroscopeZeroDriftMode(HLERequestContext& ctx);
void ResetGyroscopeZeroDriftMode(HLERequestContext& ctx);
void IsSixAxisSensorAtRest(HLERequestContext& ctx);
void IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx);
void EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx);
void IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx);
void LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx);
void GetSixAxisSensorIcInformation(HLERequestContext& ctx);
void ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx);
void ActivateGesture(HLERequestContext& ctx);
void SetSupportedNpadStyleSet(HLERequestContext& ctx);
void GetSupportedNpadStyleSet(HLERequestContext& ctx);
void SetSupportedNpadIdType(HLERequestContext& ctx);
void ActivateNpad(HLERequestContext& ctx);
void DeactivateNpad(HLERequestContext& ctx);
void AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx);
void DisconnectNpad(HLERequestContext& ctx);
void GetPlayerLedPattern(HLERequestContext& ctx);
void ActivateNpadWithRevision(HLERequestContext& ctx);
void SetNpadJoyHoldType(HLERequestContext& ctx);
void GetNpadJoyHoldType(HLERequestContext& ctx);
void SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx);
void SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx);
void SetNpadJoyAssignmentModeDual(HLERequestContext& ctx);
void MergeSingleJoyAsDualJoy(HLERequestContext& ctx);
void StartLrAssignmentMode(HLERequestContext& ctx);
void StopLrAssignmentMode(HLERequestContext& ctx);
void SetNpadHandheldActivationMode(HLERequestContext& ctx);
void GetNpadHandheldActivationMode(HLERequestContext& ctx);
void SwapNpadAssignment(HLERequestContext& ctx);
void IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx);
void EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx);
void SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx);
void SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx);
void SetNpadCaptureButtonAssignment(HLERequestContext& ctx);
void ClearNpadCaptureButtonAssignment(HLERequestContext& ctx);
void GetVibrationDeviceInfo(HLERequestContext& ctx);
void SendVibrationValue(HLERequestContext& ctx);
void GetActualVibrationValue(HLERequestContext& ctx);
void CreateActiveVibrationDeviceList(HLERequestContext& ctx);
void PermitVibration(HLERequestContext& ctx);
void IsVibrationPermitted(HLERequestContext& ctx);
void SendVibrationValues(HLERequestContext& ctx);
void SendVibrationGcErmCommand(HLERequestContext& ctx);
void GetActualVibrationGcErmCommand(HLERequestContext& ctx);
void BeginPermitVibrationSession(HLERequestContext& ctx);
void EndPermitVibrationSession(HLERequestContext& ctx);
void IsVibrationDeviceMounted(HLERequestContext& ctx);
void ActivateConsoleSixAxisSensor(HLERequestContext& ctx);
void StartConsoleSixAxisSensor(HLERequestContext& ctx);
void StopConsoleSixAxisSensor(HLERequestContext& ctx);
void ActivateSevenSixAxisSensor(HLERequestContext& ctx);
void StartSevenSixAxisSensor(HLERequestContext& ctx);
void StopSevenSixAxisSensor(HLERequestContext& ctx);
void InitializeSevenSixAxisSensor(HLERequestContext& ctx);
void FinalizeSevenSixAxisSensor(HLERequestContext& ctx);
void ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx);
void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
void GetPalmaConnectionHandle(HLERequestContext& ctx);
void InitializePalma(HLERequestContext& ctx);
void AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx);
void GetPalmaOperationInfo(HLERequestContext& ctx);
void PlayPalmaActivity(HLERequestContext& ctx);
void SetPalmaFrModeType(HLERequestContext& ctx);
void ReadPalmaStep(HLERequestContext& ctx);
void EnablePalmaStep(HLERequestContext& ctx);
void ResetPalmaStep(HLERequestContext& ctx);
void ReadPalmaApplicationSection(HLERequestContext& ctx);
void WritePalmaApplicationSection(HLERequestContext& ctx);
void ReadPalmaUniqueCode(HLERequestContext& ctx);
void SetPalmaUniqueCodeInvalid(HLERequestContext& ctx);
void WritePalmaActivityEntry(HLERequestContext& ctx);
void WritePalmaRgbLedPatternEntry(HLERequestContext& ctx);
void WritePalmaWaveEntry(HLERequestContext& ctx);
void SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
void GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
void SuspendPalmaFeature(HLERequestContext& ctx);
void GetPalmaOperationResult(HLERequestContext& ctx);
void ReadPalmaPlayLog(HLERequestContext& ctx);
void ResetPalmaPlayLog(HLERequestContext& ctx);
void SetIsPalmaAllConnectable(HLERequestContext& ctx);
void SetIsPalmaPairedConnectable(HLERequestContext& ctx);
void PairPalma(HLERequestContext& ctx);
void SetPalmaBoostMode(HLERequestContext& ctx);
void CancelWritePalmaWaveEntry(HLERequestContext& ctx);
void EnablePalmaBoostMode(HLERequestContext& ctx);
void GetPalmaBluetoothAddress(HLERequestContext& ctx);
void SetDisallowedPalmaConnection(HLERequestContext& ctx);
void SetNpadCommunicationMode(HLERequestContext& ctx);
void GetNpadCommunicationMode(HLERequestContext& ctx);
void SetTouchScreenConfiguration(HLERequestContext& ctx);
void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx);
std::shared_ptr<ResourceManager> resource_manager;
std::shared_ptr<HidFirmwareSettings> firmware_settings;
};
} // namespace Service::HID

View File

@@ -0,0 +1,551 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/controllers/touchscreen.h"
#include "core/hle/service/hid/errors.h"
#include "core/hle/service/hid/hid_system_server.h"
#include "core/hle/service/hid/resource_manager.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::HID {
IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource)
: ServiceFramework{system_, "hid:sys"}, service_context{system_, service_name},
resource_manager{resource} {
// clang-format off
static const FunctionInfo functions[] = {
{31, nullptr, "SendKeyboardLockKeyEvent"},
{101, nullptr, "AcquireHomeButtonEventHandle"},
{111, nullptr, "ActivateHomeButton"},
{121, nullptr, "AcquireSleepButtonEventHandle"},
{131, nullptr, "ActivateSleepButton"},
{141, nullptr, "AcquireCaptureButtonEventHandle"},
{151, nullptr, "ActivateCaptureButton"},
{161, nullptr, "GetPlatformConfig"},
{210, nullptr, "AcquireNfcDeviceUpdateEventHandle"},
{211, nullptr, "GetNpadsWithNfc"},
{212, nullptr, "AcquireNfcActivateEventHandle"},
{213, nullptr, "ActivateNfc"},
{214, nullptr, "GetXcdHandleForNpadWithNfc"},
{215, nullptr, "IsNfcActivated"},
{230, nullptr, "AcquireIrSensorEventHandle"},
{231, nullptr, "ActivateIrSensor"},
{232, nullptr, "GetIrSensorState"},
{233, nullptr, "GetXcdHandleForNpadWithIrSensor"},
{301, nullptr, "ActivateNpadSystem"},
{303, &IHidSystemServer::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"},
{304, &IHidSystemServer::EnableAssigningSingleOnSlSrPress, "EnableAssigningSingleOnSlSrPress"},
{305, &IHidSystemServer::DisableAssigningSingleOnSlSrPress, "DisableAssigningSingleOnSlSrPress"},
{306, &IHidSystemServer::GetLastActiveNpad, "GetLastActiveNpad"},
{307, nullptr, "GetNpadSystemExtStyle"},
{308, &IHidSystemServer::ApplyNpadSystemCommonPolicyFull, "ApplyNpadSystemCommonPolicyFull"},
{309, &IHidSystemServer::GetNpadFullKeyGripColor, "GetNpadFullKeyGripColor"},
{310, &IHidSystemServer::GetMaskedSupportedNpadStyleSet, "GetMaskedSupportedNpadStyleSet"},
{311, nullptr, "SetNpadPlayerLedBlinkingDevice"},
{312, &IHidSystemServer::SetSupportedNpadStyleSetAll, "SetSupportedNpadStyleSetAll"},
{313, nullptr, "GetNpadCaptureButtonAssignment"},
{314, nullptr, "GetAppletFooterUiType"},
{315, &IHidSystemServer::GetAppletDetailedUiType, "GetAppletDetailedUiType"},
{316, &IHidSystemServer::GetNpadInterfaceType, "GetNpadInterfaceType"},
{317, &IHidSystemServer::GetNpadLeftRightInterfaceType, "GetNpadLeftRightInterfaceType"},
{318, &IHidSystemServer::HasBattery, "HasBattery"},
{319, &IHidSystemServer::HasLeftRightBattery, "HasLeftRightBattery"},
{321, &IHidSystemServer::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"},
{322, &IHidSystemServer::GetIrSensorState, "GetIrSensorState"},
{323, nullptr, "GetXcdHandleForNpadWithIrSensor"},
{324, nullptr, "GetUniquePadButtonSet"},
{325, nullptr, "GetUniquePadColor"},
{326, nullptr, "GetUniquePadAppletDetailedUiType"},
{327, nullptr, "GetAbstractedPadIdDataFromNpad"},
{328, nullptr, "AttachAbstractedPadToNpad"},
{329, nullptr, "DetachAbstractedPadAll"},
{330, nullptr, "CheckAbstractedPadConnection"},
{500, nullptr, "SetAppletResourceUserId"},
{501, nullptr, "RegisterAppletResourceUserId"},
{502, nullptr, "UnregisterAppletResourceUserId"},
{503, nullptr, "EnableAppletToGetInput"},
{504, nullptr, "SetAruidValidForVibration"},
{505, nullptr, "EnableAppletToGetSixAxisSensor"},
{506, nullptr, "EnableAppletToGetPadInput"},
{507, nullptr, "EnableAppletToGetTouchScreen"},
{510, nullptr, "SetVibrationMasterVolume"},
{511, nullptr, "GetVibrationMasterVolume"},
{512, nullptr, "BeginPermitVibrationSession"},
{513, nullptr, "EndPermitVibrationSession"},
{514, nullptr, "Unknown514"},
{520, nullptr, "EnableHandheldHids"},
{521, nullptr, "DisableHandheldHids"},
{522, nullptr, "SetJoyConRailEnabled"},
{523, nullptr, "IsJoyConRailEnabled"},
{524, nullptr, "IsHandheldHidsEnabled"},
{525, nullptr, "IsJoyConAttachedOnAllRail"},
{540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"},
{541, nullptr, "GetPlayReportControllerUsages"},
{542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"},
{543, nullptr, "GetRegisteredDevicesOld"},
{544, &IHidSystemServer::AcquireConnectionTriggerTimeoutEvent, "AcquireConnectionTriggerTimeoutEvent"},
{545, nullptr, "SendConnectionTrigger"},
{546, &IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport, "AcquireDeviceRegisteredEventForControllerSupport"},
{547, nullptr, "GetAllowedBluetoothLinksCount"},
{548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"},
{549, nullptr, "GetConnectableRegisteredDevices"},
{700, nullptr, "ActivateUniquePad"},
{702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"},
{703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"},
{751, &IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"},
{800, nullptr, "ListSixAxisSensorHandles"},
{801, nullptr, "IsSixAxisSensorUserCalibrationSupported"},
{802, nullptr, "ResetSixAxisSensorCalibrationValues"},
{803, nullptr, "StartSixAxisSensorUserCalibration"},
{804, nullptr, "CancelSixAxisSensorUserCalibration"},
{805, nullptr, "GetUniquePadBluetoothAddress"},
{806, nullptr, "DisconnectUniquePad"},
{807, nullptr, "GetUniquePadType"},
{808, nullptr, "GetUniquePadInterface"},
{809, nullptr, "GetUniquePadSerialNumber"},
{810, nullptr, "GetUniquePadControllerNumber"},
{811, nullptr, "GetSixAxisSensorUserCalibrationStage"},
{812, nullptr, "GetConsoleUniqueSixAxisSensorHandle"},
{821, nullptr, "StartAnalogStickManualCalibration"},
{822, nullptr, "RetryCurrentAnalogStickManualCalibrationStage"},
{823, nullptr, "CancelAnalogStickManualCalibration"},
{824, nullptr, "ResetAnalogStickManualCalibration"},
{825, nullptr, "GetAnalogStickState"},
{826, nullptr, "GetAnalogStickManualCalibrationStage"},
{827, nullptr, "IsAnalogStickButtonPressed"},
{828, nullptr, "IsAnalogStickInReleasePosition"},
{829, nullptr, "IsAnalogStickInCircumference"},
{830, nullptr, "SetNotificationLedPattern"},
{831, nullptr, "SetNotificationLedPatternWithTimeout"},
{832, nullptr, "PrepareHidsForNotificationWake"},
{850, &IHidSystemServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
{851, nullptr, "EnableUsbFullKeyController"},
{852, nullptr, "IsUsbConnected"},
{870, &IHidSystemServer::IsHandheldButtonPressedOnConsoleMode, "IsHandheldButtonPressedOnConsoleMode"},
{900, nullptr, "ActivateInputDetector"},
{901, nullptr, "NotifyInputDetector"},
{1000, &IHidSystemServer::InitializeFirmwareUpdate, "InitializeFirmwareUpdate"},
{1001, nullptr, "GetFirmwareVersion"},
{1002, nullptr, "GetAvailableFirmwareVersion"},
{1003, nullptr, "IsFirmwareUpdateAvailable"},
{1004, nullptr, "CheckFirmwareUpdateRequired"},
{1005, nullptr, "StartFirmwareUpdate"},
{1006, nullptr, "AbortFirmwareUpdate"},
{1007, nullptr, "GetFirmwareUpdateState"},
{1008, nullptr, "ActivateAudioControl"},
{1009, nullptr, "AcquireAudioControlEventHandle"},
{1010, nullptr, "GetAudioControlStates"},
{1011, nullptr, "DeactivateAudioControl"},
{1050, nullptr, "IsSixAxisSensorAccurateUserCalibrationSupported"},
{1051, nullptr, "StartSixAxisSensorAccurateUserCalibration"},
{1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"},
{1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"},
{1100, nullptr, "GetHidbusSystemServiceObject"},
{1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"},
{1130, nullptr, "InitializeUsbFirmwareUpdate"},
{1131, nullptr, "FinalizeUsbFirmwareUpdate"},
{1132, nullptr, "CheckUsbFirmwareUpdateRequired"},
{1133, nullptr, "StartUsbFirmwareUpdate"},
{1134, nullptr, "GetUsbFirmwareUpdateState"},
{1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"},
{1150, nullptr, "SetTouchScreenMagnification"},
{1151, nullptr, "GetTouchScreenFirmwareVersion"},
{1152, nullptr, "SetTouchScreenDefaultConfiguration"},
{1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
{1154, nullptr, "IsFirmwareAvailableForNotification"},
{1155, nullptr, "SetForceHandheldStyleVibration"},
{1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"},
{1157, nullptr, "CancelConnectionTrigger"},
{1200, nullptr, "IsButtonConfigSupported"},
{1201, nullptr, "IsButtonConfigEmbeddedSupported"},
{1202, nullptr, "DeleteButtonConfig"},
{1203, nullptr, "DeleteButtonConfigEmbedded"},
{1204, nullptr, "SetButtonConfigEnabled"},
{1205, nullptr, "SetButtonConfigEmbeddedEnabled"},
{1206, nullptr, "IsButtonConfigEnabled"},
{1207, nullptr, "IsButtonConfigEmbeddedEnabled"},
{1208, nullptr, "SetButtonConfigEmbedded"},
{1209, nullptr, "SetButtonConfigFull"},
{1210, nullptr, "SetButtonConfigLeft"},
{1211, nullptr, "SetButtonConfigRight"},
{1212, nullptr, "GetButtonConfigEmbedded"},
{1213, nullptr, "GetButtonConfigFull"},
{1214, nullptr, "GetButtonConfigLeft"},
{1215, nullptr, "GetButtonConfigRight"},
{1250, nullptr, "IsCustomButtonConfigSupported"},
{1251, nullptr, "IsDefaultButtonConfigEmbedded"},
{1252, nullptr, "IsDefaultButtonConfigFull"},
{1253, nullptr, "IsDefaultButtonConfigLeft"},
{1254, nullptr, "IsDefaultButtonConfigRight"},
{1255, nullptr, "IsButtonConfigStorageEmbeddedEmpty"},
{1256, nullptr, "IsButtonConfigStorageFullEmpty"},
{1257, nullptr, "IsButtonConfigStorageLeftEmpty"},
{1258, nullptr, "IsButtonConfigStorageRightEmpty"},
{1259, nullptr, "GetButtonConfigStorageEmbeddedDeprecated"},
{1260, nullptr, "GetButtonConfigStorageFullDeprecated"},
{1261, nullptr, "GetButtonConfigStorageLeftDeprecated"},
{1262, nullptr, "GetButtonConfigStorageRightDeprecated"},
{1263, nullptr, "SetButtonConfigStorageEmbeddedDeprecated"},
{1264, nullptr, "SetButtonConfigStorageFullDeprecated"},
{1265, nullptr, "SetButtonConfigStorageLeftDeprecated"},
{1266, nullptr, "SetButtonConfigStorageRightDeprecated"},
{1267, nullptr, "DeleteButtonConfigStorageEmbedded"},
{1268, nullptr, "DeleteButtonConfigStorageFull"},
{1269, nullptr, "DeleteButtonConfigStorageLeft"},
{1270, nullptr, "DeleteButtonConfigStorageRight"},
{1271, nullptr, "IsUsingCustomButtonConfig"},
{1272, nullptr, "IsAnyCustomButtonConfigEnabled"},
{1273, nullptr, "SetAllCustomButtonConfigEnabled"},
{1274, nullptr, "SetDefaultButtonConfig"},
{1275, nullptr, "SetAllDefaultButtonConfig"},
{1276, nullptr, "SetHidButtonConfigEmbedded"},
{1277, nullptr, "SetHidButtonConfigFull"},
{1278, nullptr, "SetHidButtonConfigLeft"},
{1279, nullptr, "SetHidButtonConfigRight"},
{1280, nullptr, "GetHidButtonConfigEmbedded"},
{1281, nullptr, "GetHidButtonConfigFull"},
{1282, nullptr, "GetHidButtonConfigLeft"},
{1283, nullptr, "GetHidButtonConfigRight"},
{1284, nullptr, "GetButtonConfigStorageEmbedded"},
{1285, nullptr, "GetButtonConfigStorageFull"},
{1286, nullptr, "GetButtonConfigStorageLeft"},
{1287, nullptr, "GetButtonConfigStorageRight"},
{1288, nullptr, "SetButtonConfigStorageEmbedded"},
{1289, nullptr, "SetButtonConfigStorageFull"},
{1290, nullptr, "DeleteButtonConfigStorageRight"},
{1291, nullptr, "DeleteButtonConfigStorageRight"},
};
// clang-format on
RegisterHandlers(functions);
joy_detach_event = service_context.CreateEvent("IHidSystemServer::JoyDetachEvent");
acquire_device_registered_event =
service_context.CreateEvent("IHidSystemServer::AcquireDeviceRegisteredEvent");
acquire_connection_trigger_timeout_event =
service_context.CreateEvent("IHidSystemServer::AcquireConnectionTriggerTimeoutEvent");
unique_pad_connection_event =
service_context.CreateEvent("IHidSystemServer::AcquireUniquePadConnectionEventHandle");
}
IHidSystemServer::~IHidSystemServer() {
service_context.CloseEvent(joy_detach_event);
service_context.CloseEvent(acquire_device_registered_event);
service_context.CloseEvent(acquire_connection_trigger_timeout_event);
service_context.CloseEvent(unique_pad_connection_event);
};
void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "called");
GetResourceManager()
->GetController<Controller_NPad>(HidController::NPad)
.ApplyNpadSystemCommonPolicy();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IHidSystemServer::EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IHidSystemServer::DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IHidSystemServer::GetLastActiveNpad(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "(STUBBED) called"); // Spams a lot when controller applet is running
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(system.HIDCore().GetLastActiveController());
}
void IHidSystemServer::ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "called");
GetResourceManager()
->GetController<Controller_NPad>(HidController::NPad)
.ApplyNpadSystemCommonPolicy();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IHidSystemServer::GetNpadFullKeyGripColor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
npad_id_type); // Spams a lot when controller applet is running
Core::HID::NpadColor left_color{};
Core::HID::NpadColor right_color{};
// TODO: Get colors from Npad
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.PushRaw(left_color);
rb.PushRaw(right_color);
}
void IHidSystemServer::GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
LOG_INFO(Service_HID, "(STUBBED) called");
Core::HID::NpadStyleSet supported_styleset =
GetResourceManager()
->GetController<Controller_NPad>(HidController::NPad)
.GetSupportedStyleSet()
.raw;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(supported_styleset);
}
void IHidSystemServer::SetSupportedNpadStyleSetAll(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
LOG_INFO(Service_HID, "(STUBBED) called");
Core::HID::NpadStyleSet supported_styleset =
GetResourceManager()
->GetController<Controller_NPad>(HidController::NPad)
.GetSupportedStyleSet()
.raw;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(supported_styleset);
}
void IHidSystemServer::GetAppletDetailedUiType(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
LOG_DEBUG(Service_HID, "called, npad_id_type={}",
npad_id_type); // Spams a lot when controller applet is running
const Service::HID::Controller_NPad::AppletDetailedUiType detailed_ui_type =
GetResourceManager()
->GetController<Controller_NPad>(HidController::NPad)
.GetAppletDetailedUiType(npad_id_type);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushRaw(detailed_ui_type);
}
void IHidSystemServer::GetNpadInterfaceType(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
npad_id_type); // Spams a lot when controller applet is running
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth);
}
void IHidSystemServer::GetNpadLeftRightInterfaceType(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
npad_id_type); // Spams a lot when controller applet is running
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth);
rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth);
}
void IHidSystemServer::HasBattery(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
npad_id_type); // Spams a lot when controller applet is running
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(false);
}
void IHidSystemServer::HasLeftRightBattery(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
npad_id_type); // Spams a lot when controller applet is running
struct LeftRightBattery {
bool left;
bool right;
};
LeftRightBattery left_right_battery{
.left = false,
.right = false,
};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushRaw(left_right_battery);
}
void IHidSystemServer::GetUniquePadsFromNpad(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
npad_id_type); // Spams a lot when controller applet is running
const std::vector<Core::HID::UniquePadId> unique_pads{};
if (!unique_pads.empty()) {
ctx.WriteBuffer(unique_pads);
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<u32>(unique_pads.size()));
}
void IHidSystemServer::GetIrSensorState(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
LOG_WARNING(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IHidSystemServer::AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx) {
LOG_INFO(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
}
void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx) {
LOG_INFO(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
}
void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");
struct RegisterData {
std::array<u8, 0x68> data;
};
static_assert(sizeof(RegisterData) == 0x68, "RegisterData is an invalid size");
std::vector<RegisterData> registered_devices{};
if (!registered_devices.empty()) {
ctx.WriteBuffer(registered_devices);
}
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u64>(registered_devices.size());
}
void IHidSystemServer::AcquireUniquePadConnectionEventHandle(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.PushCopyObjects(unique_pad_connection_event->GetReadableEvent());
rb.Push(ResultSuccess);
}
void IHidSystemServer::GetUniquePadIds(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u64>(0);
}
void IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) {
LOG_INFO(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(joy_detach_event->GetReadableEvent());
}
void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
const bool is_enabled = false;
LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(is_enabled);
}
void IHidSystemServer::IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx) {
const bool button_pressed = false;
LOG_DEBUG(Service_HID, "(STUBBED) called, is_enabled={}",
button_pressed); // Spams a lot when controller applet is open
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(button_pressed);
}
void IHidSystemServer::InitializeFirmwareUpdate(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");
Core::HID::TouchScreenConfigurationForNx touchscreen_config{
.mode = Core::HID::TouchScreenModeForNx::Finger,
};
if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
}
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(touchscreen_config);
}
std::shared_ptr<ResourceManager> IHidSystemServer::GetResourceManager() {
resource_manager->Initialize();
return resource_manager;
}
} // namespace Service::HID

View File

@@ -0,0 +1,63 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Kernel {
class KEvent;
}
namespace Service::HID {
class ResourceManager;
class IHidSystemServer final : public ServiceFramework<IHidSystemServer> {
public:
explicit IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource);
~IHidSystemServer() override;
private:
void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx);
void EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx);
void DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx);
void GetLastActiveNpad(HLERequestContext& ctx);
void ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx);
void GetNpadFullKeyGripColor(HLERequestContext& ctx);
void GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx);
void SetSupportedNpadStyleSetAll(HLERequestContext& ctx);
void GetAppletDetailedUiType(HLERequestContext& ctx);
void GetNpadInterfaceType(HLERequestContext& ctx);
void GetNpadLeftRightInterfaceType(HLERequestContext& ctx);
void HasBattery(HLERequestContext& ctx);
void HasLeftRightBattery(HLERequestContext& ctx);
void GetUniquePadsFromNpad(HLERequestContext& ctx);
void GetIrSensorState(HLERequestContext& ctx);
void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx);
void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx);
void GetRegisteredDevices(HLERequestContext& ctx);
void AcquireUniquePadConnectionEventHandle(HLERequestContext& ctx);
void GetUniquePadIds(HLERequestContext& ctx);
void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx);
void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
void IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx);
void InitializeFirmwareUpdate(HLERequestContext& ctx);
void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx);
void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
std::shared_ptr<ResourceManager> GetResourceManager();
Kernel::KEvent* acquire_connection_trigger_timeout_event;
Kernel::KEvent* acquire_device_registered_event;
Kernel::KEvent* joy_detach_event;
Kernel::KEvent* unique_pad_connection_event;
KernelHelpers::ServiceContext service_context;
std::shared_ptr<ResourceManager> resource_manager;
};
} // namespace Service::HID

View File

@@ -3,15 +3,12 @@
#pragma once
#include "core/core.h"
#include "core/hid/hid_types.h"
#include "core/hid/irs_types.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Core::HID {
class EmulatedController;
} // namespace Core::HID

View File

@@ -0,0 +1,184 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "common/logging/log.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hid/hid_core.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/service/hid/resource_manager.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/hid/controllers/console_sixaxis.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/controllers/debug_pad.h"
#include "core/hle/service/hid/controllers/gesture.h"
#include "core/hle/service/hid/controllers/keyboard.h"
#include "core/hle/service/hid/controllers/mouse.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/controllers/palma.h"
#include "core/hle/service/hid/controllers/stubbed.h"
#include "core/hle/service/hid/controllers/touchscreen.h"
#include "core/hle/service/hid/controllers/xpad.h"
namespace Service::HID {
// Updating period for each HID device.
// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
// Correct npad_update_ns is 4ms this is overclocked to lower input lag
constexpr auto npad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz)
constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz)
constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
ResourceManager::ResourceManager(Core::System& system_)
: system{system_}, service_context{system_, "hid"} {}
ResourceManager::~ResourceManager() = default;
void ResourceManager::Initialize() {
if (is_initialized) {
return;
}
u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer();
MakeController<Controller_DebugPad>(HidController::DebugPad, shared_memory);
MakeController<Controller_Touchscreen>(HidController::Touchscreen, shared_memory);
MakeController<Controller_Mouse>(HidController::Mouse, shared_memory);
MakeController<Controller_Keyboard>(HidController::Keyboard, shared_memory);
MakeController<Controller_XPad>(HidController::XPad, shared_memory);
MakeController<Controller_Stubbed>(HidController::HomeButton, shared_memory);
MakeController<Controller_Stubbed>(HidController::SleepButton, shared_memory);
MakeController<Controller_Stubbed>(HidController::CaptureButton, shared_memory);
MakeController<Controller_Stubbed>(HidController::InputDetector, shared_memory);
MakeController<Controller_Stubbed>(HidController::UniquePad, shared_memory);
MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad, shared_memory);
MakeController<Controller_Gesture>(HidController::Gesture, shared_memory);
MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor, shared_memory);
MakeController<Controller_Stubbed>(HidController::DebugMouse, shared_memory);
MakeControllerWithServiceContext<Controller_Palma>(HidController::Palma, shared_memory);
// Homebrew doesn't try to activate some controllers, so we activate them by default
GetController<Controller_NPad>(HidController::NPad).Activate();
GetController<Controller_Touchscreen>(HidController::Touchscreen).Activate();
GetController<Controller_Stubbed>(HidController::HomeButton).SetCommonHeaderOffset(0x4C00);
GetController<Controller_Stubbed>(HidController::SleepButton).SetCommonHeaderOffset(0x4E00);
GetController<Controller_Stubbed>(HidController::CaptureButton).SetCommonHeaderOffset(0x5000);
GetController<Controller_Stubbed>(HidController::InputDetector).SetCommonHeaderOffset(0x5200);
GetController<Controller_Stubbed>(HidController::UniquePad).SetCommonHeaderOffset(0x5A00);
GetController<Controller_Stubbed>(HidController::DebugMouse).SetCommonHeaderOffset(0x3DC00);
system.HIDCore().ReloadInputDevices();
is_initialized = true;
}
void ResourceManager::UpdateControllers(std::uintptr_t user_data,
std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();
for (const auto& controller : controllers) {
// Keyboard has it's own update event
if (controller == controllers[static_cast<size_t>(HidController::Keyboard)]) {
continue;
}
// Mouse has it's own update event
if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) {
continue;
}
// Npad has it's own update event
if (controller == controllers[static_cast<size_t>(HidController::NPad)]) {
continue;
}
controller->OnUpdate(core_timing);
}
}
void ResourceManager::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();
controllers[static_cast<size_t>(HidController::NPad)]->OnUpdate(core_timing);
}
void ResourceManager::UpdateMouseKeyboard(std::uintptr_t user_data,
std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();
controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing);
controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing);
}
void ResourceManager::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();
controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing);
}
IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource)
: ServiceFramework{system_, "IAppletResource"} {
static const FunctionInfo functions[] = {
{0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
};
RegisterHandlers(functions);
resource->Initialize();
// Register update callbacks
npad_update_event = Core::Timing::CreateEvent(
"HID::UpdatePadCallback",
[this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
-> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService();
resource->UpdateNpad(user_data, ns_late);
return std::nullopt;
});
default_update_event = Core::Timing::CreateEvent(
"HID::UpdateDefaultCallback",
[this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
-> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService();
resource->UpdateControllers(user_data, ns_late);
return std::nullopt;
});
mouse_keyboard_update_event = Core::Timing::CreateEvent(
"HID::UpdateMouseKeyboardCallback",
[this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
-> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService();
resource->UpdateMouseKeyboard(user_data, ns_late);
return std::nullopt;
});
motion_update_event = Core::Timing::CreateEvent(
"HID::UpdateMotionCallback",
[this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
-> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService();
resource->UpdateMotion(user_data, ns_late);
return std::nullopt;
});
system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event);
system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns,
default_update_event);
system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns,
mouse_keyboard_update_event);
system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns,
motion_update_event);
}
IAppletResource::~IAppletResource() {
system.CoreTiming().UnscheduleEvent(npad_update_event, 0);
system.CoreTiming().UnscheduleEvent(default_update_event, 0);
system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
}
void IAppletResource::GetSharedMemoryHandle(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(&system.Kernel().GetHidSharedMem());
}
} // namespace Service::HID

View File

@@ -0,0 +1,104 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <chrono>
#include "core/core.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
namespace Core::Timing {
struct EventType;
}
namespace Core::HID {
class HIDCore;
}
namespace Service::HID {
enum class HidController : std::size_t {
DebugPad,
Touchscreen,
Mouse,
Keyboard,
XPad,
HomeButton,
SleepButton,
CaptureButton,
InputDetector,
UniquePad,
NPad,
Gesture,
ConsoleSixAxisSensor,
DebugMouse,
Palma,
MaxControllers,
};
class ResourceManager {
public:
explicit ResourceManager(Core::System& system_);
~ResourceManager();
template <typename T>
T& GetController(HidController controller) {
return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
}
template <typename T>
const T& GetController(HidController controller) const {
return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
}
void Initialize();
void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
private:
template <typename T>
void MakeController(HidController controller, u8* shared_memory) {
if constexpr (std::is_constructible_v<T, Core::System&, u8*>) {
controllers[static_cast<std::size_t>(controller)] =
std::make_unique<T>(system, shared_memory);
} else {
controllers[static_cast<std::size_t>(controller)] =
std::make_unique<T>(system.HIDCore(), shared_memory);
}
}
template <typename T>
void MakeControllerWithServiceContext(HidController controller, u8* shared_memory) {
controllers[static_cast<std::size_t>(controller)] =
std::make_unique<T>(system.HIDCore(), shared_memory, service_context);
}
bool is_initialized{false};
std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
controllers{};
Core::System& system;
KernelHelpers::ServiceContext service_context;
};
class IAppletResource final : public ServiceFramework<IAppletResource> {
public:
explicit IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource);
~IAppletResource() override;
private:
void GetSharedMemoryHandle(HLERequestContext& ctx);
std::shared_ptr<Core::Timing::EventType> npad_update_event;
std::shared_ptr<Core::Timing::EventType> default_update_event;
std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
std::shared_ptr<Core::Timing::EventType> motion_update_event;
};
} // namespace Service::HID

View File

@@ -115,12 +115,20 @@ public:
{400, nullptr, "InitializeSystem"},
{401, nullptr, "FinalizeSystem"},
{402, nullptr, "SetOperationMode"},
{403, nullptr, "InitializeSystem2"},
{403, &ISystemLocalCommunicationService::InitializeSystem2, "InitializeSystem2"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void InitializeSystem2(HLERequestContext& ctx) {
LOG_WARNING(Service_LDN, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
};
class IUserLocalCommunicationService final

View File

@@ -15,7 +15,7 @@
namespace Service::android {
struct GraphicBuffer;
class GraphicBuffer;
class BufferItem final {
public:

View File

@@ -5,7 +5,6 @@
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp
#include "common/logging/log.h"
#include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvnflinger/buffer_item.h"
#include "core/hle/service/nvnflinger/buffer_queue_consumer.h"
#include "core/hle/service/nvnflinger/buffer_queue_core.h"
@@ -14,9 +13,8 @@
namespace Service::android {
BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_,
Service::Nvidia::NvCore::NvMap& nvmap_)
: core{std::move(core_)}, slots{core->slots}, nvmap(nvmap_) {}
BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_)
: core{std::move(core_)}, slots{core->slots} {}
BufferQueueConsumer::~BufferQueueConsumer() = default;
@@ -136,8 +134,6 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
slots[slot].buffer_state = BufferState::Free;
nvmap.FreeHandle(slots[slot].graphic_buffer->BufferId(), true);
listener = core->connected_producer_listener;
LOG_DEBUG(Service_Nvnflinger, "releasing slot {}", slot);
@@ -175,6 +171,25 @@ Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_
return Status::NoError;
}
Status BufferQueueConsumer::Disconnect() {
LOG_DEBUG(Service_Nvnflinger, "called");
std::scoped_lock lock{core->mutex};
if (core->consumer_listener == nullptr) {
LOG_ERROR(Service_Nvnflinger, "no consumer is connected");
return Status::BadValue;
}
core->is_abandoned = true;
core->consumer_listener = nullptr;
core->queue.clear();
core->FreeAllBuffersLocked();
core->SignalDequeueCondition();
return Status::NoError;
}
Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) {
if (out_slot_mask == nullptr) {
LOG_ERROR(Service_Nvnflinger, "out_slot_mask may not be nullptr");

View File

@@ -13,10 +13,6 @@
#include "core/hle/service/nvnflinger/buffer_queue_defs.h"
#include "core/hle/service/nvnflinger/status.h"
namespace Service::Nvidia::NvCore {
class NvMap;
} // namespace Service::Nvidia::NvCore
namespace Service::android {
class BufferItem;
@@ -25,19 +21,18 @@ class IConsumerListener;
class BufferQueueConsumer final {
public:
explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_,
Service::Nvidia::NvCore::NvMap& nvmap_);
explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_);
~BufferQueueConsumer();
Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present);
Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence);
Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app);
Status Disconnect();
Status GetReleasedBuffers(u64* out_slot_mask);
private:
std::shared_ptr<BufferQueueCore> core;
BufferQueueDefs::SlotsType& slots;
Service::Nvidia::NvCore::NvMap& nvmap;
};
} // namespace Service::android

View File

@@ -14,24 +14,12 @@ BufferQueueCore::BufferQueueCore() = default;
BufferQueueCore::~BufferQueueCore() = default;
void BufferQueueCore::NotifyShutdown() {
std::scoped_lock lock{mutex};
is_shutting_down = true;
SignalDequeueCondition();
}
void BufferQueueCore::SignalDequeueCondition() {
dequeue_possible.store(true);
dequeue_condition.notify_all();
}
bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) {
if (is_shutting_down) {
return false;
}
dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); });
dequeue_possible.store(false);

View File

@@ -34,8 +34,6 @@ public:
BufferQueueCore();
~BufferQueueCore();
void NotifyShutdown();
private:
void SignalDequeueCondition();
bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk);
@@ -74,7 +72,6 @@ private:
u32 transform_hint{};
bool is_allocating{};
mutable std::condition_variable_any is_allocating_condition;
bool is_shutting_down{};
};
} // namespace Service::android

View File

@@ -13,7 +13,6 @@
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/hle_ipc.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvnflinger/buffer_queue_core.h"
#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
#include "core/hle/service/nvnflinger/consumer_listener.h"
@@ -533,8 +532,6 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
item.is_droppable = core->dequeue_buffer_cannot_block || async;
item.swap_interval = swap_interval;
nvmap.DuplicateHandle(item.graphic_buffer->BufferId(), true);
sticky_transform = sticky_transform_;
if (core->queue.empty()) {
@@ -744,19 +741,13 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
return Status::NoError;
}
// HACK: We are not Android. Remove handle for items in queue, and clear queue.
// Allows synchronous destruction of nvmap handles.
for (auto& item : core->queue) {
nvmap.FreeHandle(item.graphic_buffer->BufferId(), true);
}
core->queue.clear();
switch (api) {
case NativeWindowApi::Egl:
case NativeWindowApi::Cpu:
case NativeWindowApi::Media:
case NativeWindowApi::Camera:
if (core->connected_api == api) {
core->queue.clear();
core->FreeAllBuffersLocked();
core->connected_producer_listener = nullptr;
core->connected_api = NativeWindowApi::NoConnectedApi;
@@ -785,7 +776,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
}
Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
const std::shared_ptr<GraphicBuffer>& buffer) {
const std::shared_ptr<NvGraphicBuffer>& buffer) {
LOG_DEBUG(Service_Nvnflinger, "slot {}", slot);
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
@@ -796,7 +787,7 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
slots[slot] = {};
slots[slot].fence = Fence::NoFence();
slots[slot].graphic_buffer = buffer;
slots[slot].graphic_buffer = std::make_shared<GraphicBuffer>(nvmap, buffer);
slots[slot].frame_number = 0;
// Most games preallocate a buffer and pass a valid buffer here. However, it is possible for
@@ -839,7 +830,7 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u
}
case TransactionId::SetPreallocatedBuffer: {
const auto slot = parcel_in.Read<s32>();
const auto buffer = parcel_in.ReadObject<GraphicBuffer>();
const auto buffer = parcel_in.ReadObject<NvGraphicBuffer>();
status = SetPreallocatedBuffer(slot, buffer);
break;
@@ -867,7 +858,7 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u
status = RequestBuffer(slot, &buf);
parcel_out.WriteFlattenedObject(buf);
parcel_out.WriteFlattenedObject<NvGraphicBuffer>(buf.get());
break;
}
case TransactionId::QueueBuffer: {

View File

@@ -38,6 +38,7 @@ namespace Service::android {
class BufferQueueCore;
class IProducerListener;
struct NvGraphicBuffer;
class BufferQueueProducer final : public IBinder {
public:
@@ -65,7 +66,7 @@ public:
bool producer_controlled_by_app, QueueBufferOutput* output);
Status Disconnect(NativeWindowApi api);
Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<GraphicBuffer>& buffer);
Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<NvGraphicBuffer>& buffer);
private:
BufferQueueProducer(const BufferQueueProducer&) = delete;

View File

@@ -13,7 +13,7 @@
namespace Service::android {
struct GraphicBuffer;
class GraphicBuffer;
enum class BufferState : u32 {
Free = 0,

View File

@@ -27,6 +27,26 @@ void ConsumerBase::Connect(bool controlled_by_app) {
consumer->Connect(shared_from_this(), controlled_by_app);
}
void ConsumerBase::Abandon() {
LOG_DEBUG(Service_Nvnflinger, "called");
std::scoped_lock lock{mutex};
if (!is_abandoned) {
this->AbandonLocked();
is_abandoned = true;
}
}
void ConsumerBase::AbandonLocked() {
for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
this->FreeBufferLocked(i);
}
// disconnect from the BufferQueue
consumer->Disconnect();
consumer = nullptr;
}
void ConsumerBase::FreeBufferLocked(s32 slot_index) {
LOG_DEBUG(Service_Nvnflinger, "slot_index={}", slot_index);

View File

@@ -24,6 +24,7 @@ class BufferQueueConsumer;
class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> {
public:
void Connect(bool controlled_by_app);
void Abandon();
protected:
explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_);
@@ -34,6 +35,7 @@ protected:
void OnBuffersReleased() override;
void OnSidebandStreamChanged() override;
void AbandonLocked();
void FreeBufferLocked(s32 slot_index);
Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when);
Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer>& graphic_buffer);

View File

@@ -166,7 +166,7 @@ constexpr SharedMemoryPoolLayout SharedBufferPoolLayout = [] {
}();
void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) {
auto buffer = std::make_shared<android::GraphicBuffer>();
auto buffer = std::make_shared<android::NvGraphicBuffer>();
buffer->width = SharedBufferWidth;
buffer->height = SharedBufferHeight;
buffer->stride = SharedBufferBlockLinearStride;

View File

@@ -47,7 +47,10 @@ void Nvnflinger::SplitVSync(std::stop_token stop_token) {
vsync_signal.Wait();
const auto lock_guard = Lock();
Compose();
if (!is_abandoned) {
Compose();
}
}
}
@@ -98,7 +101,6 @@ Nvnflinger::~Nvnflinger() {
}
ShutdownLayers();
vsync_thread = {};
if (nvdrv) {
nvdrv->Close(disp_fd);
@@ -106,12 +108,20 @@ Nvnflinger::~Nvnflinger() {
}
void Nvnflinger::ShutdownLayers() {
const auto lock_guard = Lock();
for (auto& display : displays) {
for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) {
display.GetLayer(layer).Core().NotifyShutdown();
// Abandon consumers.
{
const auto lock_guard = Lock();
for (auto& display : displays) {
for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) {
display.GetLayer(layer).GetConsumer().Abandon();
}
}
is_abandoned = true;
}
// Join the vsync thread, if it exists.
vsync_thread = {};
}
void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {

View File

@@ -140,6 +140,8 @@ private:
s32 swap_interval = 1;
bool is_abandoned = false;
/// Event that handles screen composition.
std::shared_ptr<Core::Timing::EventType> multi_composition_event;
std::shared_ptr<Core::Timing::EventType> single_composition_event;

View File

@@ -19,7 +19,7 @@ enum class Status : s32 {
Busy = -16,
NoInit = -19,
BadValue = -22,
InvalidOperation = -37,
InvalidOperation = -38,
BufferNeedsReallocation = 1,
ReleaseAllBuffers = 2,
};

View File

@@ -0,0 +1,34 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
namespace Service::android {
static NvGraphicBuffer GetBuffer(std::shared_ptr<NvGraphicBuffer>& buffer) {
if (buffer) {
return *buffer;
} else {
return {};
}
}
GraphicBuffer::GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
: NvGraphicBuffer(width_, height_, format_, usage_), m_nvmap(nullptr) {}
GraphicBuffer::GraphicBuffer(Service::Nvidia::NvCore::NvMap& nvmap,
std::shared_ptr<NvGraphicBuffer> buffer)
: NvGraphicBuffer(GetBuffer(buffer)), m_nvmap(std::addressof(nvmap)) {
if (this->BufferId() > 0) {
m_nvmap->DuplicateHandle(this->BufferId(), true);
}
}
GraphicBuffer::~GraphicBuffer() {
if (m_nvmap != nullptr && this->BufferId() > 0) {
m_nvmap->FreeHandle(this->BufferId(), true);
}
}
} // namespace Service::android

View File

@@ -6,16 +6,22 @@
#pragma once
#include <memory>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/service/nvnflinger/pixel_format.h"
namespace Service::Nvidia::NvCore {
class NvMap;
} // namespace Service::Nvidia::NvCore
namespace Service::android {
struct GraphicBuffer final {
constexpr GraphicBuffer() = default;
struct NvGraphicBuffer {
constexpr NvGraphicBuffer() = default;
constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
constexpr NvGraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
: width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_},
usage{static_cast<s32>(usage_)} {}
@@ -93,6 +99,17 @@ struct GraphicBuffer final {
u32 offset{};
INSERT_PADDING_WORDS(60);
};
static_assert(sizeof(GraphicBuffer) == 0x16C, "GraphicBuffer has wrong size");
static_assert(sizeof(NvGraphicBuffer) == 0x16C, "NvGraphicBuffer has wrong size");
class GraphicBuffer final : public NvGraphicBuffer {
public:
explicit GraphicBuffer(u32 width, u32 height, PixelFormat format, u32 usage);
explicit GraphicBuffer(Service::Nvidia::NvCore::NvMap& nvmap,
std::shared_ptr<NvGraphicBuffer> buffer);
~GraphicBuffer();
private:
Service::Nvidia::NvCore::NvMap* m_nvmap{};
};
} // namespace Service::android

View File

@@ -431,8 +431,7 @@ void SET_SYS::GetAutoUpdateEnableFlag(HLERequestContext& ctx) {
void SET_SYS::GetBatteryPercentageFlag(HLERequestContext& ctx) {
u8 battery_percentage_flag{1};
LOG_WARNING(Service_SET, "(STUBBED) called, battery_percentage_flag={}",
battery_percentage_flag);
LOG_DEBUG(Service_SET, "(STUBBED) called, battery_percentage_flag={}", battery_percentage_flag);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
@@ -492,6 +491,29 @@ void SET_SYS::GetChineseTraditionalInputMethod(HLERequestContext& ctx) {
rb.PushEnum(ChineseTraditionalInputMethod::Unknown0);
}
void SET_SYS::GetHomeMenuScheme(HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "(STUBBED) called");
const HomeMenuScheme default_color = {
.main = 0xFF323232,
.back = 0xFF323232,
.sub = 0xFFFFFFFF,
.bezel = 0xFFFFFFFF,
.extra = 0xFF000000,
};
IPC::ResponseBuilder rb{ctx, 7};
rb.Push(ResultSuccess);
rb.PushRaw(default_color);
}
void SET_SYS::GetHomeMenuSchemeModel(HLERequestContext& ctx) {
LOG_WARNING(Service_SET, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(0);
}
void SET_SYS::GetFieldTestingFlag(HLERequestContext& ctx) {
LOG_WARNING(Service_SET, "(STUBBED) called");
@@ -674,7 +696,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
{171, nullptr, "SetChineseTraditionalInputMethod"},
{172, nullptr, "GetPtmCycleCountReliability"},
{173, nullptr, "SetPtmCycleCountReliability"},
{174, nullptr, "GetHomeMenuScheme"},
{174, &SET_SYS::GetHomeMenuScheme, "GetHomeMenuScheme"},
{175, nullptr, "GetThemeSettings"},
{176, nullptr, "SetThemeSettings"},
{177, nullptr, "GetThemeKey"},
@@ -685,7 +707,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
{182, nullptr, "SetT"},
{183, nullptr, "GetPlatformRegion"},
{184, nullptr, "SetPlatformRegion"},
{185, nullptr, "GetHomeMenuSchemeModel"},
{185, &SET_SYS::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"},
{186, nullptr, "GetMemoryUsageRateFlag"},
{187, nullptr, "GetTouchScreenMode"},
{188, nullptr, "SetTouchScreenMode"},

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