Compare commits

..

14 Commits

Author SHA1 Message Date
f2753d07ad Android 212 2024-01-26 02:00:26 +00:00
ea0480e651 Merge yuzu-emu#12796 2024-01-26 02:00:26 +00:00
6b17e4b325 Merge yuzu-emu#12769 2024-01-26 02:00:26 +00:00
0e4282bb89 Merge yuzu-emu#12749 2024-01-26 02:00:26 +00:00
e04368ad7c Merge pull request #12759 from liamwhite/mp-misc
core: miscellaneous fixes
2024-01-25 16:21:38 -05:00
3e2d3548f2 Merge pull request #12777 from t895/firmware-warning
android: Add key warning
2024-01-25 16:21:29 -05:00
eb9036d75b Merge pull request #12783 from liamwhite/cmif-generation
service: add template serializer for method calls
2024-01-25 15:40:09 -05:00
01a2d978eb service: add template serializer for method calls 2024-01-25 14:35:51 -05:00
b8be8dff69 android: Add key check 2024-01-25 12:58:19 -05:00
bc317a9807 android: Add option to make MessageDialogFragments non-dismissible
Additionally fixes an issue where its viewmodel could hold onto a stale positive action
2024-01-25 12:53:49 -05:00
97ca160b08 frontend_common: Consistently use references
Was swapping between references and pointers for no reason. Just unify them here since each of these utility functions will need their parameters to be alive.
2024-01-25 12:53:49 -05:00
1a3fc3724a frontend_common: Remove key rederivation and keep key check 2024-01-25 12:53:48 -05:00
a120f8ff4d nvservices: close map handles on session close 2024-01-22 21:18:52 -05:00
96833cd809 kernel: target invalidate to given process 2024-01-22 21:18:52 -05:00
34 changed files with 973 additions and 375 deletions

View File

@ -1,9 +1,8 @@
| Pull Request | Commit | Title | Author | Merged? | | Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----| |----|----|----|----|----|
| [12749](https://github.com/yuzu-emu/yuzu-android//pull/12749) | [`e3171486d`](https://github.com/yuzu-emu/yuzu-android//pull/12749/files) | general: workarounds for SMMU syncing issues | [liamwhite](https://github.com/liamwhite/) | Yes | | [12749](https://github.com/yuzu-emu/yuzu-android//pull/12749) | [`e3171486d`](https://github.com/yuzu-emu/yuzu-android//pull/12749/files) | general: workarounds for SMMU syncing issues | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12759](https://github.com/yuzu-emu/yuzu-android//pull/12759) | [`a120f8ff4`](https://github.com/yuzu-emu/yuzu-android//pull/12759/files) | core: miscellaneous fixes | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12769](https://github.com/yuzu-emu/yuzu-android//pull/12769) | [`ad4622da2`](https://github.com/yuzu-emu/yuzu-android//pull/12769/files) | core: hid: Reduce controller requests | [german77](https://github.com/german77/) | Yes | | [12769](https://github.com/yuzu-emu/yuzu-android//pull/12769) | [`ad4622da2`](https://github.com/yuzu-emu/yuzu-android//pull/12769/files) | core: hid: Reduce controller requests | [german77](https://github.com/german77/) | Yes |
| [12777](https://github.com/yuzu-emu/yuzu-android//pull/12777) | [`b8be8dff6`](https://github.com/yuzu-emu/yuzu-android//pull/12777/files) | android: Add key warning | [t895](https://github.com/t895/) | Yes | | [12796](https://github.com/yuzu-emu/yuzu-android//pull/12796) | [`677c2c2cd`](https://github.com/yuzu-emu/yuzu-android//pull/12796/files) | android: Controller focus optimizations | [t895](https://github.com/t895/) | Yes |
End of merge log. You can find the original README.md below the break. End of merge log. You can find the original README.md below the break.

View File

@ -193,6 +193,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
return super.dispatchKeyEvent(event) return super.dispatchKeyEvent(event)
} }
if (emulationViewModel.drawerOpen.value) {
return super.dispatchKeyEvent(event)
}
return InputHandler.dispatchKeyEvent(event) return InputHandler.dispatchKeyEvent(event)
} }
@ -203,6 +207,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
return super.dispatchGenericMotionEvent(event) return super.dispatchGenericMotionEvent(event)
} }
if (emulationViewModel.drawerOpen.value) {
return super.dispatchGenericMotionEvent(event)
}
// Don't attempt to do anything if we are disconnecting a device. // Don't attempt to do anything if we are disconnecting a device.
if (event.actionMasked == MotionEvent.ACTION_CANCEL) { if (event.actionMasked == MotionEvent.ACTION_CANCEL) {
return true return true

View File

@ -77,7 +77,7 @@ class AboutFragment : Fragment() {
} }
binding.textVersionName.text = BuildConfig.VERSION_NAME binding.textVersionName.text = BuildConfig.VERSION_NAME
binding.textVersionName.setOnClickListener { binding.buttonVersionName.setOnClickListener {
val clipBoard = val clipBoard =
requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText(getString(R.string.build), BuildConfig.GIT_HASH) val clip = ClipData.newPlainText(getString(R.string.build), BuildConfig.GIT_HASH)

View File

@ -38,6 +38,7 @@ import androidx.window.layout.WindowLayoutInfo
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider import com.google.android.material.slider.Slider
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.HomeNavigationDirections
@ -184,10 +185,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
override fun onDrawerOpened(drawerView: View) { override fun onDrawerOpened(drawerView: View) {
binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
binding.inGameMenu.requestFocus()
emulationViewModel.setDrawerOpen(true)
} }
override fun onDrawerClosed(drawerView: View) { override fun onDrawerClosed(drawerView: View) {
binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt()) binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt())
emulationViewModel.setDrawerOpen(false)
} }
override fun onDrawerStateChanged(newState: Int) { override fun onDrawerStateChanged(newState: Int) {
@ -239,6 +243,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
requireContext().theme requireContext().theme
) )
} }
binding.inGameMenu.requestFocus()
true true
} }
@ -247,6 +252,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
null, null,
Settings.MenuTag.SECTION_ROOT Settings.MenuTag.SECTION_ROOT
) )
binding.inGameMenu.requestFocus()
binding.root.findNavController().navigate(action) binding.root.findNavController().navigate(action)
true true
} }
@ -256,6 +262,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
args.game, args.game,
Settings.MenuTag.SECTION_ROOT Settings.MenuTag.SECTION_ROOT
) )
binding.inGameMenu.requestFocus()
binding.root.findNavController().navigate(action) binding.root.findNavController().navigate(action)
true true
} }
@ -287,6 +294,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
) )
} }
} }
binding.inGameMenu.requestFocus()
NativeConfig.saveGlobalConfig() NativeConfig.saveGlobalConfig()
true true
} }
@ -295,7 +303,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
emulationState.stop() emulationState.stop()
emulationViewModel.setIsEmulationStopping(true) emulationViewModel.setIsEmulationStopping(true)
binding.drawerLayout.close() binding.drawerLayout.close()
binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) binding.inGameMenu.requestFocus()
true true
} }
@ -312,12 +320,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
if (!NativeLibrary.isRunning()) { if (!NativeLibrary.isRunning()) {
return return
} }
emulationViewModel.setDrawerOpen(!binding.drawerLayout.isOpen)
if (binding.drawerLayout.isOpen) {
binding.drawerLayout.close()
} else {
binding.drawerLayout.open()
}
} }
} }
) )
@ -408,6 +411,18 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
} }
} }
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
emulationViewModel.drawerOpen.collect {
if (it) {
binding.drawerLayout.open()
binding.inGameMenu.requestFocus()
} else {
binding.drawerLayout.close()
}
}
}
}
} }
} }

View File

@ -6,6 +6,7 @@ package org.yuzu.yuzu_emu.model
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
class EmulationViewModel : ViewModel() { class EmulationViewModel : ViewModel() {
val emulationStarted: StateFlow<Boolean> get() = _emulationStarted val emulationStarted: StateFlow<Boolean> get() = _emulationStarted
@ -23,6 +24,9 @@ class EmulationViewModel : ViewModel() {
val shaderMessage: StateFlow<String> get() = _shaderMessage val shaderMessage: StateFlow<String> get() = _shaderMessage
private val _shaderMessage = MutableStateFlow("") private val _shaderMessage = MutableStateFlow("")
private val _drawerOpen = MutableStateFlow(false)
val drawerOpen = _drawerOpen.asStateFlow()
fun setEmulationStarted(started: Boolean) { fun setEmulationStarted(started: Boolean) {
_emulationStarted.value = started _emulationStarted.value = started
} }
@ -49,6 +53,10 @@ class EmulationViewModel : ViewModel() {
setTotalShaders(max) setTotalShaders(max)
} }
fun setDrawerOpen(value: Boolean) {
_drawerOpen.value = value
}
fun clear() { fun clear() {
setEmulationStarted(false) setEmulationStarted(false)
setIsEmulationStopping(false) setIsEmulationStopping(false)

View File

@ -11,12 +11,14 @@
android:id="@+id/appbar_about" android:id="@+id/appbar_about"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true"
android:touchscreenBlocksFocus="false">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_about" android:id="@+id/toolbar_about"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
android:touchscreenBlocksFocus="false"
app:navigationIcon="@drawable/ic_back" app:navigationIcon="@drawable/ic_back"
app:title="@string/about" /> app:title="@string/about" />
@ -28,6 +30,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:fadeScrollbars="false" android:fadeScrollbars="false"
android:scrollbars="vertical" android:scrollbars="vertical"
android:defaultFocusHighlightEnabled="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior"> app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout <LinearLayout

View File

@ -0,0 +1,155 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinator_about"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:touchscreenBlocksFocus="false">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_info"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:touchscreenBlocksFocus="false"
app:navigationIcon="@drawable/ic_back" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:id="@+id/scroll_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:defaultFocusHighlightEnabled="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:id="@+id/content_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingHorizontal="16dp"
android:baselineAligned="false">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_weight="3"
android:gravity="top|center_horizontal"
android:paddingHorizontal="16dp">
<com.google.android.material.button.MaterialButton
android:id="@+id/button_copy"
style="@style/Widget.Material3.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/copy_details" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button_verify_integrity"
style="@style/Widget.Material3.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/verify_integrity" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_weight="1">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/path"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/path_field"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:editable="false"
android:importantForAutofill="no"
android:inputType="none"
android:minHeight="48dp"
android:textAlignment="viewStart"
tools:text="1.0.0" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/program_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/program_id_field"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:editable="false"
android:importantForAutofill="no"
android:inputType="none"
android:minHeight="48dp"
android:textAlignment="viewStart"
tools:text="1.0.0" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/developer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/developer_field"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:editable="false"
android:importantForAutofill="no"
android:inputType="none"
android:minHeight="48dp"
android:textAlignment="viewStart"
tools:text="1.0.0" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/version"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/version_field"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:editable="false"
android:importantForAutofill="no"
android:inputType="none"
android:minHeight="48dp"
android:textAlignment="viewStart"
tools:text="1.0.0" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -14,6 +14,7 @@
android:clipToPadding="false" android:clipToPadding="false"
android:fadeScrollbars="false" android:fadeScrollbars="false"
android:scrollbars="vertical" android:scrollbars="vertical"
android:defaultFocusHighlightEnabled="false"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/icon_layout" app:layout_constraintStart_toEndOf="@+id/icon_layout"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">

View File

@ -23,6 +23,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:focusable="false"
android:clickable="false" android:clickable="false"
android:checked="false" /> android:checked="false" />

View File

@ -6,16 +6,14 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp" android:layout_marginHorizontal="16dp"
android:layout_marginVertical="12dp" android:layout_marginVertical="12dp">
android:focusable="true">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:padding="16dp" android:padding="16dp"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical">
android:animateLayoutChanges="true">
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/path" android:id="@+id/path"

View File

@ -11,12 +11,14 @@
android:id="@+id/appbar_about" android:id="@+id/appbar_about"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true"
android:touchscreenBlocksFocus="false">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_about" android:id="@+id/toolbar_about"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
android:touchscreenBlocksFocus="false"
app:title="@string/about" app:title="@string/about"
app:navigationIcon="@drawable/ic_back" /> app:navigationIcon="@drawable/ic_back" />
@ -28,6 +30,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:scrollbars="vertical" android:scrollbars="vertical"
android:fadeScrollbars="false" android:fadeScrollbars="false"
android:defaultFocusHighlightEnabled="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior"> app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout <LinearLayout

View File

@ -11,6 +11,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
android:touchscreenBlocksFocus="false"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
@ -19,6 +20,7 @@
android:id="@+id/toolbar_addons" android:id="@+id/toolbar_addons"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
android:touchscreenBlocksFocus="false"
app:navigationIcon="@drawable/ic_back" /> app:navigationIcon="@drawable/ic_back" />
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
@ -28,6 +30,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:clipToPadding="false" android:clipToPadding="false"
android:defaultFocusHighlightEnabled="false"
android:nextFocusDown="@id/button_install"
app:layout_behavior="@string/appbar_scrolling_view_behavior" app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"

View File

@ -10,12 +10,14 @@
android:id="@+id/appbar_applets" android:id="@+id/appbar_applets"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true"
android:touchscreenBlocksFocus="false">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_applets" android:id="@+id/toolbar_applets"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
android:touchscreenBlocksFocus="false"
app:navigationIcon="@drawable/ic_back" app:navigationIcon="@drawable/ic_back"
app:title="@string/applets" /> app:title="@string/applets" />

View File

@ -15,12 +15,14 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
android:touchscreenBlocksFocus="false"
app:liftOnScrollTargetViewId="@id/list_drivers"> app:liftOnScrollTargetViewId="@id/list_drivers">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_drivers" android:id="@+id/toolbar_drivers"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
android:touchscreenBlocksFocus="false"
app:navigationIcon="@drawable/ic_back" app:navigationIcon="@drawable/ic_back"
app:title="@string/gpu_driver_manager" /> app:title="@string/gpu_driver_manager" />

View File

@ -11,12 +11,14 @@
android:id="@+id/appbar_ea" android:id="@+id/appbar_ea"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true"
android:touchscreenBlocksFocus="false">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_about" android:id="@+id/toolbar_about"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
android:touchscreenBlocksFocus="false"
app:navigationIcon="@drawable/ic_back" app:navigationIcon="@drawable/ic_back"
app:title="@string/early_access" /> app:title="@string/early_access" />
@ -30,6 +32,7 @@
android:paddingBottom="20dp" android:paddingBottom="20dp"
android:scrollbars="vertical" android:scrollbars="vertical"
android:fadeScrollbars="false" android:fadeScrollbars="false"
android:defaultFocusHighlightEnabled="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior"> app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout <LinearLayout

View File

@ -5,6 +5,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:keepScreenOn="true" android:keepScreenOn="true"
android:defaultFocusHighlightEnabled="false"
tools:context="org.yuzu.yuzu_emu.fragments.EmulationFragment" tools:context="org.yuzu.yuzu_emu.fragments.EmulationFragment"
tools:openDrawer="start"> tools:openDrawer="start">
@ -24,7 +25,8 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="center" android:layout_gravity="center"
android:focusable="false" android:focusable="false"
android:focusableInTouchMode="false" /> android:focusableInTouchMode="false"
android:defaultFocusHighlightEnabled="false" />
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
android:id="@+id/loading_indicator" android:id="@+id/loading_indicator"
@ -32,7 +34,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:focusable="false" android:defaultFocusHighlightEnabled="false"
android:clickable="false"> android:clickable="false">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
@ -118,6 +120,7 @@
android:layout_gravity="center" android:layout_gravity="center"
android:focusable="true" android:focusable="true"
android:focusableInTouchMode="true" android:focusableInTouchMode="true"
android:defaultFocusHighlightEnabled="false"
android:visibility="invisible" /> android:visibility="invisible" />
<Button <Button
@ -160,6 +163,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="start" android:layout_gravity="start"
android:focusedByDefault="true"
app:headerLayout="@layout/header_in_game" app:headerLayout="@layout/header_in_game"
app:menu="@menu/menu_in_game" app:menu="@menu/menu_in_game"
tools:visibility="gone" /> tools:visibility="gone" />

View File

@ -15,12 +15,14 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
android:touchscreenBlocksFocus="false"
app:liftOnScrollTargetViewId="@id/list_folders"> app:liftOnScrollTargetViewId="@id/list_folders">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_folders" android:id="@+id/toolbar_folders"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
android:touchscreenBlocksFocus="false"
app:navigationIcon="@drawable/ic_back" app:navigationIcon="@drawable/ic_back"
app:title="@string/game_folders" /> app:title="@string/game_folders" />
@ -31,6 +33,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false" android:clipToPadding="false"
android:defaultFocusHighlightEnabled="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior" /> app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -11,12 +11,14 @@
android:id="@+id/appbar_info" android:id="@+id/appbar_info"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:touchscreenBlocksFocus="false"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_info" android:id="@+id/toolbar_info"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
android:touchscreenBlocksFocus="false"
app:navigationIcon="@drawable/ic_back" /> app:navigationIcon="@drawable/ic_back" />
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
@ -25,6 +27,7 @@
android:id="@+id/scroll_info" android:id="@+id/scroll_info"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:defaultFocusHighlightEnabled="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior"> app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout <LinearLayout

View File

@ -12,7 +12,8 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:scrollbars="vertical" android:scrollbars="vertical"
android:fadeScrollbars="false" android:fadeScrollbars="false"
android:clipToPadding="false"> android:clipToPadding="false"
android:defaultFocusHighlightEnabled="false">
<LinearLayout <LinearLayout
android:id="@+id/layout_all" android:id="@+id/layout_all"
@ -86,7 +87,7 @@
android:id="@+id/list_properties" android:id="@+id/list_properties"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:listitem="@layout/card_simple_outlined" /> android:defaultFocusHighlightEnabled="false" />
</LinearLayout> </LinearLayout>

View File

@ -27,6 +27,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clipToPadding="false" android:clipToPadding="false"
android:defaultFocusHighlightEnabled="false"
tools:listitem="@layout/card_game" /> tools:listitem="@layout/card_game" />
</RelativeLayout> </RelativeLayout>

View File

@ -7,7 +7,8 @@
android:background="?attr/colorSurface" android:background="?attr/colorSurface"
android:scrollbars="vertical" android:scrollbars="vertical"
android:fadeScrollbars="false" android:fadeScrollbars="false"
android:clipToPadding="false"> android:clipToPadding="false"
android:defaultFocusHighlightEnabled="false">
<androidx.appcompat.widget.LinearLayoutCompat <androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/linear_layout_settings" android:id="@+id/linear_layout_settings"

View File

@ -10,12 +10,14 @@
android:id="@+id/appbar_installables" android:id="@+id/appbar_installables"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true"
android:touchscreenBlocksFocus="false">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_installables" android:id="@+id/toolbar_installables"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
android:touchscreenBlocksFocus="false"
app:title="@string/manage_yuzu_data" app:title="@string/manage_yuzu_data"
app:navigationIcon="@drawable/ic_back" /> app:navigationIcon="@drawable/ic_back" />

View File

@ -10,12 +10,14 @@
android:id="@+id/appbar_licenses" android:id="@+id/appbar_licenses"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true"
android:touchscreenBlocksFocus="false">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_licenses" android:id="@+id/toolbar_licenses"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
android:touchscreenBlocksFocus="false"
app:title="@string/licenses" app:title="@string/licenses"
app:navigationIcon="@drawable/ic_back" /> app:navigationIcon="@drawable/ic_back" />

View File

@ -11,6 +11,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
android:touchscreenBlocksFocus="false"
app:elevation="0dp"> app:elevation="0dp">
<com.google.android.material.appbar.CollapsingToolbarLayout <com.google.android.material.appbar.CollapsingToolbarLayout
@ -24,6 +25,7 @@
android:id="@+id/toolbar_settings" android:id="@+id/toolbar_settings"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
android:touchscreenBlocksFocus="false"
app:layout_collapseMode="pin" app:layout_collapseMode="pin"
app:navigationIcon="@drawable/ic_back" /> app:navigationIcon="@drawable/ic_back" />

View File

@ -6,7 +6,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:focusable="true" android:focusable="false"
android:paddingHorizontal="20dp" android:paddingHorizontal="20dp"
android:paddingVertical="16dp"> android:paddingVertical="16dp">

View File

@ -12,4 +12,5 @@
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textColor="?attr/colorPrimary" android:textColor="?attr/colorPrimary"
android:textStyle="bold" android:textStyle="bold"
android:focusable="false"
tools:text="CPU Settings" /> tools:text="CPU Settings" />

View File

@ -472,6 +472,8 @@ add_library(core STATIC
hle/service/caps/caps_types.h hle/service/caps/caps_types.h
hle/service/caps/caps_u.cpp hle/service/caps/caps_u.cpp
hle/service/caps/caps_u.h hle/service/caps/caps_u.h
hle/service/cmif_serialization.h
hle/service/cmif_types.h
hle/service/erpt/erpt.cpp hle/service/erpt/erpt.cpp
hle/service/erpt/erpt.h hle/service/erpt/erpt.h
hle/service/es/es.cpp hle/service/es/es.cpp

View File

@ -0,0 +1,337 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/div_ceil.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/service.h"
namespace Service {
// clang-format off
struct RequestLayout {
u32 copy_handle_count;
u32 move_handle_count;
u32 cmif_raw_data_size;
u32 domain_interface_count;
};
template <ArgumentType Type1, ArgumentType Type2, typename MethodArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t ArgIndex = 0>
constexpr u32 GetArgumentRawDataSize() {
if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) {
return static_cast<u32>(DataOffset);
} else {
using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
if constexpr (ArgumentTraits<ArgType>::Type == Type1 || ArgumentTraits<ArgType>::Type == Type2) {
constexpr size_t ArgAlign = alignof(ArgType);
constexpr size_t ArgSize = sizeof(ArgType);
static_assert(PrevAlign <= ArgAlign, "Input argument is not ordered by alignment");
constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
constexpr size_t ArgEnd = ArgOffset + ArgSize;
return GetArgumentRawDataSize<Type1, Type2, MethodArguments, ArgAlign, ArgEnd, ArgIndex + 1>();
} else {
return GetArgumentRawDataSize<Type1, Type2, MethodArguments, PrevAlign, DataOffset, ArgIndex + 1>();
}
}
}
template <ArgumentType DataType, typename MethodArguments, size_t ArgCount = 0, size_t ArgIndex = 0>
constexpr u32 GetArgumentTypeCount() {
if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) {
return static_cast<u32>(ArgCount);
} else {
using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
if constexpr (ArgumentTraits<ArgType>::Type == DataType) {
return GetArgumentTypeCount<DataType, MethodArguments, ArgCount + 1, ArgIndex + 1>();
} else {
return GetArgumentTypeCount<DataType, MethodArguments, ArgCount, ArgIndex + 1>();
}
}
}
template <typename MethodArguments>
constexpr RequestLayout GetNonDomainReplyInLayout() {
return RequestLayout{
.copy_handle_count = GetArgumentTypeCount<ArgumentType::InCopyHandle, MethodArguments>(),
.move_handle_count = 0,
.cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::InData, ArgumentType::InProcessId, MethodArguments>(),
.domain_interface_count = 0,
};
}
template <typename MethodArguments>
constexpr RequestLayout GetDomainReplyInLayout() {
return RequestLayout{
.copy_handle_count = GetArgumentTypeCount<ArgumentType::InCopyHandle, MethodArguments>(),
.move_handle_count = 0,
.cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::InData, ArgumentType::InProcessId, MethodArguments>(),
.domain_interface_count = GetArgumentTypeCount<ArgumentType::InInterface, MethodArguments>(),
};
}
template <typename MethodArguments>
constexpr RequestLayout GetNonDomainReplyOutLayout() {
return RequestLayout{
.copy_handle_count = GetArgumentTypeCount<ArgumentType::OutCopyHandle, MethodArguments>(),
.move_handle_count = GetArgumentTypeCount<ArgumentType::OutMoveHandle, MethodArguments>() + GetArgumentTypeCount<ArgumentType::OutInterface, MethodArguments>(),
.cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::OutData, ArgumentType::OutData, MethodArguments>(),
.domain_interface_count = 0,
};
}
template <typename MethodArguments>
constexpr RequestLayout GetDomainReplyOutLayout() {
return RequestLayout{
.copy_handle_count = GetArgumentTypeCount<ArgumentType::OutCopyHandle, MethodArguments>(),
.move_handle_count = GetArgumentTypeCount<ArgumentType::OutMoveHandle, MethodArguments>(),
.cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::OutData, ArgumentType::OutData, MethodArguments>(),
.domain_interface_count = GetArgumentTypeCount<ArgumentType::OutInterface, MethodArguments>(),
};
}
template <bool Domain, typename MethodArguments>
constexpr RequestLayout GetReplyInLayout() {
return Domain ? GetDomainReplyInLayout<MethodArguments>() : GetNonDomainReplyInLayout<MethodArguments>();
}
template <bool Domain, typename MethodArguments>
constexpr RequestLayout GetReplyOutLayout() {
return Domain ? GetDomainReplyOutLayout<MethodArguments>() : GetNonDomainReplyOutLayout<MethodArguments>();
}
using OutTemporaryBuffers = std::array<Common::ScratchBuffer<u8>, 3>;
template <bool Domain, typename MethodArguments, typename CallArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t HandleIndex = 0, size_t InBufferIndex = 0, size_t OutBufferIndex = 0, bool RawDataFinished = false, size_t ArgIndex = 0>
void ReadInArgument(CallArguments& args, const u8* raw_data, HLERequestContext& ctx, OutTemporaryBuffers& temp) {
if constexpr (ArgIndex >= std::tuple_size_v<CallArguments>) {
return;
} else {
using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InData || ArgumentTraits<ArgType>::Type == ArgumentType::InProcessId) {
constexpr size_t ArgAlign = alignof(ArgType);
constexpr size_t ArgSize = sizeof(ArgType);
static_assert(PrevAlign <= ArgAlign, "Input argument is not ordered by alignment");
static_assert(!RawDataFinished, "All input interface arguments must appear after raw data");
constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
constexpr size_t ArgEnd = ArgOffset + ArgSize;
if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InProcessId) {
// TODO: abort parsing if PID is not provided?
// TODO: validate against raw data value?
std::get<ArgIndex>(args).pid = ctx.GetPID();
} else {
std::memcpy(&std::get<ArgIndex>(args), raw_data + ArgOffset, ArgSize);
}
return ReadInArgument<Domain, MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, false, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InInterface) {
constexpr size_t ArgAlign = alignof(u32);
constexpr size_t ArgSize = sizeof(u32);
constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
constexpr size_t ArgEnd = ArgOffset + ArgSize;
static_assert(Domain);
ASSERT(ctx.GetDomainMessageHeader().input_object_count > 0);
u32 value{};
std::memcpy(&value, raw_data + ArgOffset, ArgSize);
std::get<ArgIndex>(args) = ctx.GetDomainHandler<ArgType::Type>(value - 1);
return ReadInArgument<Domain, MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, true, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InCopyHandle) {
std::get<ArgIndex>(args) = std::move(ctx.GetObjectFromHandle<typename ArgType::Type>(ctx.GetCopyHandle(HandleIndex)));
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex + 1, InBufferIndex, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InLargeData) {
constexpr size_t BufferSize = sizeof(ArgType);
// Clear the existing data.
std::memset(&std::get<ArgIndex>(args), 0, BufferSize);
std::span<const u8> buffer{};
ASSERT(ctx.CanReadBuffer(InBufferIndex));
if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
buffer = ctx.ReadBuffer(InBufferIndex);
} else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
buffer = ctx.ReadBufferA(InBufferIndex);
} else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
buffer = ctx.ReadBufferX(InBufferIndex);
}
std::memcpy(&std::get<ArgIndex>(args), buffer.data(), std::min(BufferSize, buffer.size()));
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex + 1, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InBuffer) {
using ElementType = typename ArgType::Type;
std::span<const u8> buffer{};
if (ctx.CanReadBuffer(InBufferIndex)) {
if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
buffer = ctx.ReadBuffer(InBufferIndex);
} else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
buffer = ctx.ReadBufferA(InBufferIndex);
} else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
buffer = ctx.ReadBufferX(InBufferIndex);
}
}
ElementType* ptr = (ElementType*) buffer.data();
size_t size = buffer.size() / sizeof(ElementType);
std::get<ArgIndex>(args) = std::span(ptr, size);
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex + 1, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutLargeData) {
constexpr size_t BufferSize = sizeof(ArgType);
// Clear the existing data.
std::memset(&std::get<ArgIndex>(args), 0, BufferSize);
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutBuffer) {
using ElementType = typename ArgType::Type;
// Set up scratch buffer.
auto& buffer = temp[OutBufferIndex];
if (ctx.CanWriteBuffer(OutBufferIndex)) {
buffer.resize_destructive(ctx.GetWriteBufferSize(OutBufferIndex));
} else {
buffer.resize_destructive(0);
}
ElementType* ptr = (ElementType*) buffer.data();
size_t size = buffer.size() / sizeof(ElementType);
std::get<ArgIndex>(args) = std::span(ptr, size);
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else {
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
}
}
}
template <bool Domain, typename MethodArguments, typename CallArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t OutBufferIndex = 0, bool RawDataFinished = false, size_t ArgIndex = 0>
void WriteOutArgument(CallArguments& args, u8* raw_data, HLERequestContext& ctx, OutTemporaryBuffers& temp) {
if constexpr (ArgIndex >= std::tuple_size_v<CallArguments>) {
return;
} else {
using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutData) {
constexpr size_t ArgAlign = alignof(ArgType);
constexpr size_t ArgSize = sizeof(ArgType);
static_assert(PrevAlign <= ArgAlign, "Output argument is not ordered by alignment");
static_assert(!RawDataFinished, "All output interface arguments must appear after raw data");
constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
constexpr size_t ArgEnd = ArgOffset + ArgSize;
std::memcpy(raw_data + ArgOffset, &std::get<ArgIndex>(args), ArgSize);
return WriteOutArgument<Domain, MethodArguments, CallArguments, ArgAlign, ArgEnd, OutBufferIndex, false, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutInterface) {
if constexpr (Domain) {
ctx.AddDomainObject(std::get<ArgIndex>(args));
} else {
ctx.AddMoveInterface(std::get<ArgIndex>(args));
}
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, true, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutCopyHandle) {
ctx.AddCopyObject(std::get<ArgIndex>(args).GetPointerUnsafe());
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutMoveHandle) {
ctx.AddMoveObject(std::get<ArgIndex>(args).GetPointerUnsafe());
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutLargeData) {
constexpr size_t BufferSize = sizeof(ArgType);
ASSERT(ctx.CanWriteBuffer(OutBufferIndex));
if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
ctx.WriteBuffer(std::get<ArgIndex>(args), OutBufferIndex);
} else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
ctx.WriteBufferB(&std::get<ArgIndex>(args), BufferSize, OutBufferIndex);
} else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
ctx.WriteBufferC(&std::get<ArgIndex>(args), BufferSize, OutBufferIndex);
}
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutBuffer) {
auto& buffer = temp[OutBufferIndex];
const size_t size = buffer.size();
if (ctx.CanWriteBuffer(OutBufferIndex)) {
if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
ctx.WriteBuffer(buffer.data(), size, OutBufferIndex);
} else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
ctx.WriteBufferB(buffer.data(), size, OutBufferIndex);
} else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
ctx.WriteBufferC(buffer.data(), size, OutBufferIndex);
}
}
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>( args, raw_data, ctx, temp);
} else {
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
}
}
}
template <bool Domain, typename T, typename... A>
void CmifReplyWrapImpl(HLERequestContext& ctx, T& t, Result (T::*f)(A...)) {
// Verify domain state.
if constexpr (Domain) {
ASSERT_MSG(ctx.GetManager()->IsDomain(), "Domain reply used on non-domain session");
} else {
ASSERT_MSG(!ctx.GetManager()->IsDomain(), "Non-domain reply used on domain session");
}
using MethodArguments = std::tuple<std::remove_reference_t<A>...>;
OutTemporaryBuffers buffers{};
auto call_arguments = std::tuple<typename RemoveOut<A>::Type...>();
// Read inputs.
const size_t offset_plus_command_id = ctx.GetDataPayloadOffset() + 2;
ReadInArgument<Domain, MethodArguments>(call_arguments, reinterpret_cast<u8*>(ctx.CommandBuffer() + offset_plus_command_id), ctx, buffers);
// Call.
const auto Callable = [&]<typename... CallArgs>(CallArgs&... args) {
return (t.*f)(args...);
};
const Result res = std::apply(Callable, call_arguments);
// Write result.
constexpr RequestLayout layout = GetReplyOutLayout<Domain, MethodArguments>();
IPC::ResponseBuilder rb{ctx, 2 + Common::DivCeil(layout.cmif_raw_data_size, sizeof(u32)), layout.copy_handle_count, layout.move_handle_count + layout.domain_interface_count};
rb.Push(res);
// Write out arguments.
WriteOutArgument<Domain, MethodArguments>(call_arguments, reinterpret_cast<u8*>(ctx.CommandBuffer() + rb.GetCurrentOffset()), ctx, buffers);
}
// clang-format on
template <typename Self>
template <bool Domain, auto F>
inline void ServiceFramework<Self>::CmifReplyWrap(HLERequestContext& ctx) {
return CmifReplyWrapImpl<Domain>(ctx, *static_cast<Self*>(this), F);
}
} // namespace Service

View File

@ -0,0 +1,234 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/service/hle_ipc.h"
namespace Service {
// clang-format off
template <typename T>
class Out {
public:
/* implicit */ Out(T& t) : raw(&t) {}
~Out() = default;
T* Get() const {
return raw;
}
T& operator*() {
return *raw;
}
private:
T* raw;
};
template <typename T>
using SharedPointer = std::shared_ptr<T>;
struct ClientProcessId {
explicit operator bool() const {
return pid != 0;
}
const u64& operator*() const {
return pid;
}
u64 pid;
};
using ClientAppletResourceUserId = ClientProcessId;
template <typename T>
class InCopyHandle : public Kernel::KScopedAutoObject<T> {
public:
using Type = T;
template <typename... Args>
/* implicit */ InCopyHandle(Args&&... args) : Kernel::KScopedAutoObject<T>(std::forward<Args...>(args)...) {}
~InCopyHandle() = default;
InCopyHandle& operator=(InCopyHandle&& rhs) {
Kernel::KScopedAutoObject<T>::operator=(std::move(rhs));
return *this;
}
};
template <typename T>
class OutCopyHandle : public Kernel::KScopedAutoObject<T> {
public:
using Type = T;
template <typename... Args>
/* implicit */ OutCopyHandle(Args&&... args) : Kernel::KScopedAutoObject<T>(std::forward<Args...>(args)...) {}
~OutCopyHandle() = default;
OutCopyHandle& operator=(OutCopyHandle&& rhs) {
Kernel::KScopedAutoObject<T>::operator=(std::move(rhs));
return *this;
}
};
template <typename T>
class OutMoveHandle : public Kernel::KScopedAutoObject<T> {
public:
using Type = T;
template <typename... Args>
/* implicit */ OutMoveHandle(Args&&... args) : Kernel::KScopedAutoObject<T>(std::forward<Args...>(args)...) {}
~OutMoveHandle() = default;
OutMoveHandle& operator=(OutMoveHandle&& rhs) {
Kernel::KScopedAutoObject<T>::operator=(std::move(rhs));
return *this;
}
};
enum BufferAttr : int {
BufferAttr_In = (1U << 0),
BufferAttr_Out = (1U << 1),
BufferAttr_HipcMapAlias = (1U << 2),
BufferAttr_HipcPointer = (1U << 3),
BufferAttr_FixedSize = (1U << 4),
BufferAttr_HipcAutoSelect = (1U << 5),
BufferAttr_HipcMapTransferAllowsNonSecure = (1U << 6),
BufferAttr_HipcMapTransferAllowsNonDevice = (1U << 7),
};
template <typename T, int A>
struct Buffer : public std::span<T> {
static_assert(std::is_trivial_v<T>, "Buffer type must be trivial");
static_assert((A & BufferAttr_FixedSize) == 0, "Buffer attr must not contain FixedSize");
static_assert(((A & BufferAttr_In) == 0) ^ ((A & BufferAttr_Out) == 0), "Buffer attr must be In or Out");
static constexpr BufferAttr Attr = static_cast<BufferAttr>(A);
using Type = T;
Buffer& operator=(const std::span<T>& rhs) {
std::span<T>::operator=(rhs);
return *this;
}
T& operator*() const {
return *this->data();
}
explicit operator bool() const {
return this->size() > 0;
}
};
template <BufferAttr A>
using InBuffer = Buffer<const u8, BufferAttr_In | A>;
template <typename T, BufferAttr A>
using InArray = Buffer<T, BufferAttr_In | A>;
template <BufferAttr A>
using OutBuffer = Buffer<u8, BufferAttr_Out | A>;
template <typename T, BufferAttr A>
using OutArray = Buffer<T, BufferAttr_Out | A>;
template <typename T, int A>
struct LargeData : public T {
static_assert(std::is_trivial_v<T>, "LargeData type must be trivial");
static_assert((A & BufferAttr_FixedSize) != 0, "LargeData attr must contain FixedSize");
static_assert(((A & BufferAttr_In) == 0) ^ ((A & BufferAttr_Out) == 0), "LargeData attr must be In or Out");
static constexpr BufferAttr Attr = static_cast<BufferAttr>(A);
using Type = T;
};
template <typename T, BufferAttr A>
using InLargeData = LargeData<T, BufferAttr_FixedSize | BufferAttr_In | A>;
template <typename T, BufferAttr A>
using OutLargeData = LargeData<T, BufferAttr_FixedSize | BufferAttr_Out | A>;
template <typename T>
struct RemoveOut {
using Type = std::remove_reference_t<T>;
};
template <typename T>
struct RemoveOut<Out<T>> {
using Type = T;
};
enum class ArgumentType {
InProcessId,
InData,
InInterface,
InCopyHandle,
OutData,
OutInterface,
OutCopyHandle,
OutMoveHandle,
InBuffer,
InLargeData,
OutBuffer,
OutLargeData,
};
template <typename T>
struct ArgumentTraits;
template <>
struct ArgumentTraits<ClientProcessId> {
static constexpr ArgumentType Type = ArgumentType::InProcessId;
};
template <typename T>
struct ArgumentTraits<SharedPointer<T>> {
static constexpr ArgumentType Type = ArgumentType::InInterface;
};
template <typename T>
struct ArgumentTraits<InCopyHandle<T>> {
static constexpr ArgumentType Type = ArgumentType::InCopyHandle;
};
template <typename T>
struct ArgumentTraits<Out<SharedPointer<T>>> {
static constexpr ArgumentType Type = ArgumentType::OutInterface;
};
template <typename T>
struct ArgumentTraits<Out<T>> {
static constexpr ArgumentType Type = ArgumentType::OutData;
};
template <typename T>
struct ArgumentTraits<OutCopyHandle<T>> {
static constexpr ArgumentType Type = ArgumentType::OutCopyHandle;
};
template <typename T>
struct ArgumentTraits<OutMoveHandle<T>> {
static constexpr ArgumentType Type = ArgumentType::OutMoveHandle;
};
template <typename T, int A>
struct ArgumentTraits<Buffer<T, A>> {
static constexpr ArgumentType Type = (A & BufferAttr_In) == 0 ? ArgumentType::OutBuffer : ArgumentType::InBuffer;
};
template <typename T, int A>
struct ArgumentTraits<LargeData<T, A>> {
static constexpr ArgumentType Type = (A & BufferAttr_In) == 0 ? ArgumentType::OutLargeData : ArgumentType::InLargeData;
};
template <typename T>
struct ArgumentTraits {
static constexpr ArgumentType Type = ArgumentType::InData;
};
// clang-format on
} // namespace Service

View File

@ -501,6 +501,22 @@ bool HLERequestContext::CanWriteBuffer(std::size_t buffer_index) const {
} }
} }
void HLERequestContext::AddMoveInterface(SessionRequestHandlerPtr s) {
ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve(
Kernel::LimitableResource::SessionCountMax, 1));
auto* session = Kernel::KSession::Create(kernel);
session->Initialize(nullptr, 0);
Kernel::KSession::Register(kernel, session);
auto& server = manager.lock()->GetServerManager();
auto next_manager = std::make_shared<Service::SessionRequestManager>(kernel, server);
next_manager->SetSessionHandler(std::move(s));
server.RegisterSession(&session->GetServerSession(), next_manager);
AddMoveObject(&session->GetClientSession());
}
std::string HLERequestContext::Description() const { std::string HLERequestContext::Description() const {
if (!command_header) { if (!command_header) {
return "No command header available"; return "No command header available";

View File

@ -339,6 +339,8 @@ public:
outgoing_move_objects.emplace_back(object); outgoing_move_objects.emplace_back(object);
} }
void AddMoveInterface(SessionRequestHandlerPtr s);
void AddCopyObject(Kernel::KAutoObject* object) { void AddCopyObject(Kernel::KAutoObject* object) {
outgoing_copy_objects.emplace_back(object); outgoing_copy_objects.emplace_back(object);
} }

View File

@ -6,12 +6,12 @@
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/result.h" #include "core/hle/result.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/jit/jit.h" #include "core/hle/service/jit/jit.h"
#include "core/hle/service/jit/jit_code_memory.h" #include "core/hle/service/jit/jit_code_memory.h"
#include "core/hle/service/jit/jit_context.h" #include "core/hle/service/jit/jit_context.h"
#include "core/hle/service/server_manager.h" #include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
#include "core/memory.h" #include "core/memory.h"
namespace Service::JIT { namespace Service::JIT {
@ -21,6 +21,9 @@ struct CodeRange {
u64 size; u64 size;
}; };
using Struct32 = std::array<u64, 4>;
static_assert(sizeof(Struct32) == 32, "Struct32 has wrong size");
class IJitEnvironment final : public ServiceFramework<IJitEnvironment> { class IJitEnvironment final : public ServiceFramework<IJitEnvironment> {
public: public:
explicit IJitEnvironment(Core::System& system_, explicit IJitEnvironment(Core::System& system_,
@ -29,12 +32,13 @@ public:
: ServiceFramework{system_, "IJitEnvironment"}, process{std::move(process_)}, : ServiceFramework{system_, "IJitEnvironment"}, process{std::move(process_)},
user_rx{std::move(user_rx_)}, user_ro{std::move(user_ro_)}, user_rx{std::move(user_rx_)}, user_ro{std::move(user_ro_)},
context{system_.ApplicationMemory()} { context{system_.ApplicationMemory()} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &IJitEnvironment::GenerateCode, "GenerateCode"}, {0, C<&IJitEnvironment::GenerateCode>, "GenerateCode"},
{1, &IJitEnvironment::Control, "Control"}, {1, C<&IJitEnvironment::Control>, "Control"},
{1000, &IJitEnvironment::LoadPlugin, "LoadPlugin"}, {1000, C<&IJitEnvironment::LoadPlugin>, "LoadPlugin"},
{1001, &IJitEnvironment::GetCodeAddress, "GetCodeAddress"}, {1001, C<&IJitEnvironment::GetCodeAddress>, "GetCodeAddress"},
}; };
// clang-format on // clang-format on
@ -50,28 +54,10 @@ public:
configuration.sys_ro_memory = configuration.user_ro_memory; configuration.sys_ro_memory = configuration.user_ro_memory;
} }
void GenerateCode(HLERequestContext& ctx) { Result GenerateCode(Out<s32> out_return_value, Out<CodeRange> out_range0,
LOG_DEBUG(Service_JIT, "called"); Out<CodeRange> out_range1, OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
u32 data_size, u64 command, CodeRange range0, CodeRange range1,
struct InputParameters { Struct32 data, InBuffer<BufferAttr_HipcMapAlias> buffer) {
u32 data_size;
u64 command;
std::array<CodeRange, 2> ranges;
Struct32 data;
};
struct OutputParameters {
s32 return_value;
std::array<CodeRange, 2> ranges;
};
IPC::RequestParser rp{ctx};
const auto parameters{rp.PopRaw<InputParameters>()};
// Optional input/output buffers
const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span<const u8>()};
std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
// Function call prototype: // Function call prototype:
// void GenerateCode(s32* ret, CodeRange* c0_out, CodeRange* c1_out, JITConfiguration* cfg, // void GenerateCode(s32* ret, CodeRange* c0_out, CodeRange* c1_out, JITConfiguration* cfg,
// u64 cmd, u8* input_buf, size_t input_size, CodeRange* c0_in, // u64 cmd, u8* input_buf, size_t input_size, CodeRange* c0_in,
@ -83,66 +69,36 @@ public:
// other arguments are used to transfer state between the game and the plugin. // other arguments are used to transfer state between the game and the plugin.
const VAddr ret_ptr{context.AddHeap(0u)}; const VAddr ret_ptr{context.AddHeap(0u)};
const VAddr c0_in_ptr{context.AddHeap(parameters.ranges[0])}; const VAddr c0_in_ptr{context.AddHeap(range0)};
const VAddr c1_in_ptr{context.AddHeap(parameters.ranges[1])}; const VAddr c1_in_ptr{context.AddHeap(range1)};
const VAddr c0_out_ptr{context.AddHeap(ClearSize(parameters.ranges[0]))}; const VAddr c0_out_ptr{context.AddHeap(ClearSize(range0))};
const VAddr c1_out_ptr{context.AddHeap(ClearSize(parameters.ranges[1]))}; const VAddr c1_out_ptr{context.AddHeap(ClearSize(range1))};
const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())}; const VAddr input_ptr{context.AddHeap(buffer.data(), buffer.size())};
const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())}; const VAddr output_ptr{context.AddHeap(out_buffer.data(), out_buffer.size())};
const VAddr data_ptr{context.AddHeap(parameters.data)}; const VAddr data_ptr{context.AddHeap(data)};
const VAddr configuration_ptr{context.AddHeap(configuration)}; const VAddr configuration_ptr{context.AddHeap(configuration)};
// The callback does not directly return a value, it only writes to the output pointer // The callback does not directly return a value, it only writes to the output pointer
context.CallFunction(callbacks.GenerateCode, ret_ptr, c0_out_ptr, c1_out_ptr, context.CallFunction(callbacks.GenerateCode, ret_ptr, c0_out_ptr, c1_out_ptr,
configuration_ptr, parameters.command, input_ptr, input_buffer.size(), configuration_ptr, command, input_ptr, buffer.size(), c0_in_ptr,
c0_in_ptr, c1_in_ptr, data_ptr, parameters.data_size, output_ptr, c1_in_ptr, data_ptr, data_size, output_ptr, out_buffer.size());
output_buffer.size());
const s32 return_value{context.GetHeap<s32>(ret_ptr)}; *out_return_value = context.GetHeap<s32>(ret_ptr);
*out_range0 = context.GetHeap<CodeRange>(c0_out_ptr);
*out_range1 = context.GetHeap<CodeRange>(c1_out_ptr);
context.GetHeap(output_ptr, out_buffer.data(), out_buffer.size());
if (return_value == 0) { if (*out_return_value != 0) {
// The callback has written to the output executable code range,
// requiring an instruction cache invalidation
Core::InvalidateInstructionCacheRange(process.GetPointerUnsafe(),
configuration.user_rx_memory.offset,
configuration.user_rx_memory.size);
// Write back to the IPC output buffer, if provided
if (ctx.CanWriteBuffer()) {
context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());
ctx.WriteBuffer(output_buffer.data(), output_buffer.size());
}
const OutputParameters out{
.return_value = return_value,
.ranges =
{
context.GetHeap<CodeRange>(c0_out_ptr),
context.GetHeap<CodeRange>(c1_out_ptr),
},
};
IPC::ResponseBuilder rb{ctx, 8};
rb.Push(ResultSuccess);
rb.PushRaw(out);
} else {
LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed"); LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed");
IPC::ResponseBuilder rb{ctx, 2}; R_THROW(ResultUnknown);
rb.Push(ResultUnknown);
} }
};
void Control(HLERequestContext& ctx) { R_SUCCEED();
LOG_DEBUG(Service_JIT, "called"); }
IPC::RequestParser rp{ctx};
const auto command{rp.PopRaw<u64>()};
// Optional input/output buffers
const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span<const u8>()};
std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
Result Control(Out<s32> out_return_value, InBuffer<BufferAttr_HipcMapAlias> in_data,
OutBuffer<BufferAttr_HipcMapAlias> out_data, u64 command) {
// Function call prototype: // Function call prototype:
// u64 Control(s32* ret, JITConfiguration* cfg, u64 cmd, u8* input_buf, size_t input_size, // u64 Control(s32* ret, JITConfiguration* cfg, u64 cmd, u8* input_buf, size_t input_size,
// u8* output_buf, size_t output_size); // u8* output_buf, size_t output_size);
@ -152,53 +108,30 @@ public:
const VAddr ret_ptr{context.AddHeap(0u)}; const VAddr ret_ptr{context.AddHeap(0u)};
const VAddr configuration_ptr{context.AddHeap(configuration)}; const VAddr configuration_ptr{context.AddHeap(configuration)};
const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())}; const VAddr input_ptr{context.AddHeap(in_data.data(), in_data.size())};
const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())}; const VAddr output_ptr{context.AddHeap(out_data.data(), out_data.size())};
const u64 wrapper_value{context.CallFunction(callbacks.Control, ret_ptr, configuration_ptr, const u64 wrapper_value{context.CallFunction(callbacks.Control, ret_ptr, configuration_ptr,
command, input_ptr, input_buffer.size(), command, input_ptr, in_data.size(), output_ptr,
output_ptr, output_buffer.size())}; out_data.size())};
const s32 return_value{context.GetHeap<s32>(ret_ptr)}; *out_return_value = context.GetHeap<s32>(ret_ptr);
context.GetHeap(output_ptr, out_data.data(), out_data.size());
if (wrapper_value == 0 && return_value == 0) { if (wrapper_value == 0 && *out_return_value == 0) {
// Write back to the IPC output buffer, if provided R_SUCCEED();
if (ctx.CanWriteBuffer()) {
context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());
ctx.WriteBuffer(output_buffer.data(), output_buffer.size());
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(return_value);
} else {
LOG_WARNING(Service_JIT, "plugin Control callback failed");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
} }
LOG_WARNING(Service_JIT, "plugin Control callback failed");
R_THROW(ResultUnknown);
} }
void LoadPlugin(HLERequestContext& ctx) { Result LoadPlugin(u64 tmem_size, InCopyHandle<Kernel::KTransferMemory>& tmem,
LOG_DEBUG(Service_JIT, "called"); InBuffer<BufferAttr_HipcMapAlias> nrr,
InBuffer<BufferAttr_HipcMapAlias> nro) {
IPC::RequestParser rp{ctx};
const auto tmem_size{rp.PopRaw<u64>()};
const auto tmem_handle{ctx.GetCopyHandle(0)};
const auto nro_plugin{ctx.ReadBuffer(1)};
if (tmem_size == 0) {
LOG_ERROR(Service_JIT, "attempted to load plugin with empty transfer memory");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
auto tmem{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(tmem_handle)};
if (tmem.IsNull()) { if (tmem.IsNull()) {
LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle"); LOG_ERROR(Service_JIT, "Invalid transfer memory handle!");
IPC::ResponseBuilder rb{ctx, 2}; R_THROW(ResultUnknown);
rb.Push(ResultUnknown);
return;
} }
// Set up the configuration with the required TransferMemory address // Set up the configuration with the required TransferMemory address
@ -206,7 +139,7 @@ public:
configuration.transfer_memory.size = tmem_size; configuration.transfer_memory.size = tmem_size;
// Gather up all the callbacks from the loaded plugin // Gather up all the callbacks from the loaded plugin
auto symbols{Core::Symbols::GetSymbols(nro_plugin, true)}; auto symbols{Core::Symbols::GetSymbols(nro, true)};
const auto GetSymbol{[&](const std::string& name) { return symbols[name].first; }}; const auto GetSymbol{[&](const std::string& name) { return symbols[name].first; }};
callbacks.rtld_fini = GetSymbol("_fini"); callbacks.rtld_fini = GetSymbol("_fini");
@ -223,16 +156,12 @@ public:
if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 || if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 ||
callbacks.OnPrepared == 0) { callbacks.OnPrepared == 0) {
LOG_ERROR(Service_JIT, "plugin does not implement all necessary functionality"); LOG_ERROR(Service_JIT, "plugin does not implement all necessary functionality");
IPC::ResponseBuilder rb{ctx, 2}; R_THROW(ResultUnknown);
rb.Push(ResultUnknown);
return;
} }
if (!context.LoadNRO(nro_plugin)) { if (!context.LoadNRO(nro)) {
LOG_ERROR(Service_JIT, "failed to load plugin"); LOG_ERROR(Service_JIT, "failed to load plugin");
IPC::ResponseBuilder rb{ctx, 2}; R_THROW(ResultUnknown);
rb.Push(ResultUnknown);
return;
} }
context.MapProcessMemory(configuration.sys_ro_memory.offset, context.MapProcessMemory(configuration.sys_ro_memory.offset,
@ -252,9 +181,7 @@ public:
const auto version{context.CallFunction(callbacks.GetVersion)}; const auto version{context.CallFunction(callbacks.GetVersion)};
if (version != 1) { if (version != 1) {
LOG_ERROR(Service_JIT, "unknown plugin version {}", version); LOG_ERROR(Service_JIT, "unknown plugin version {}", version);
IPC::ResponseBuilder rb{ctx, 2}; R_THROW(ResultUnknown);
rb.Push(ResultUnknown);
return;
} }
// Function prototype: // Function prototype:
@ -280,22 +207,19 @@ public:
const auto configuration_ptr{context.AddHeap(configuration)}; const auto configuration_ptr{context.AddHeap(configuration)};
context.CallFunction(callbacks.OnPrepared, configuration_ptr); context.CallFunction(callbacks.OnPrepared, configuration_ptr);
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(ResultSuccess);
} }
void GetCodeAddress(HLERequestContext& ctx) { Result GetCodeAddress(Out<u64> rx_offset, Out<u64> ro_offset) {
LOG_DEBUG(Service_JIT, "called"); LOG_DEBUG(Service_JIT, "called");
IPC::ResponseBuilder rb{ctx, 6}; *rx_offset = configuration.user_rx_memory.offset;
rb.Push(ResultSuccess); *ro_offset = configuration.user_ro_memory.offset;
rb.Push(configuration.user_rx_memory.offset);
rb.Push(configuration.user_ro_memory.offset); R_SUCCEED();
} }
private: private:
using Struct32 = std::array<u8, 32>;
struct GuestCallbacks { struct GuestCallbacks {
VAddr rtld_fini; VAddr rtld_fini;
VAddr rtld_init; VAddr rtld_init;
@ -335,7 +259,7 @@ public:
explicit JITU(Core::System& system_) : ServiceFramework{system_, "jit:u"} { explicit JITU(Core::System& system_) : ServiceFramework{system_, "jit:u"} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &JITU::CreateJitEnvironment, "CreateJitEnvironment"}, {0, C<&JITU::CreateJitEnvironment>, "CreateJitEnvironment"},
}; };
// clang-format on // clang-format on
@ -343,76 +267,33 @@ public:
} }
private: private:
void CreateJitEnvironment(HLERequestContext& ctx) { Result CreateJitEnvironment(Out<SharedPointer<IJitEnvironment>> out_jit_environment,
LOG_DEBUG(Service_JIT, "called"); u64 rx_size, u64 ro_size, InCopyHandle<Kernel::KProcess>& process,
InCopyHandle<Kernel::KCodeMemory>& rx_mem,
struct Parameters { InCopyHandle<Kernel::KCodeMemory>& ro_mem) {
u64 rx_size;
u64 ro_size;
};
IPC::RequestParser rp{ctx};
const auto parameters{rp.PopRaw<Parameters>()};
const auto process_handle{ctx.GetCopyHandle(0)};
const auto rx_mem_handle{ctx.GetCopyHandle(1)};
const auto ro_mem_handle{ctx.GetCopyHandle(2)};
if (parameters.rx_size == 0 || parameters.ro_size == 0) {
LOG_ERROR(Service_JIT, "attempted to init with empty code regions");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle)};
if (process.IsNull()) { if (process.IsNull()) {
LOG_ERROR(Service_JIT, "process is null for handle=0x{:08X}", process_handle); LOG_ERROR(Service_JIT, "process is null");
IPC::ResponseBuilder rb{ctx, 2}; R_THROW(ResultUnknown);
rb.Push(ResultUnknown);
return;
} }
auto rx_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(rx_mem_handle)};
if (rx_mem.IsNull()) { if (rx_mem.IsNull()) {
LOG_ERROR(Service_JIT, "rx_mem is null for handle=0x{:08X}", rx_mem_handle); LOG_ERROR(Service_JIT, "rx_mem is null");
IPC::ResponseBuilder rb{ctx, 2}; R_THROW(ResultUnknown);
rb.Push(ResultUnknown);
return;
} }
if (rx_mem.IsNull()) {
auto ro_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(ro_mem_handle)}; LOG_ERROR(Service_JIT, "ro_mem is null");
if (ro_mem.IsNull()) { R_THROW(ResultUnknown);
LOG_ERROR(Service_JIT, "ro_mem is null for handle=0x{:08X}", ro_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
} }
CodeMemory rx, ro; CodeMemory rx, ro;
Result res;
res = rx.Initialize(*process, *rx_mem, parameters.rx_size, R_TRY(rx.Initialize(*process, *rx_mem, rx_size, Kernel::Svc::MemoryPermission::ReadExecute,
Kernel::Svc::MemoryPermission::ReadExecute, generate_random); generate_random));
if (R_FAILED(res)) { R_TRY(ro.Initialize(*process, *ro_mem, ro_size, Kernel::Svc::MemoryPermission::Read,
LOG_ERROR(Service_JIT, "rx_mem could not be mapped for handle=0x{:08X}", rx_mem_handle); generate_random));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
return;
}
res = ro.Initialize(*process, *ro_mem, parameters.ro_size, *out_jit_environment = std::make_shared<IJitEnvironment>(system, std::move(process),
Kernel::Svc::MemoryPermission::Read, generate_random); std::move(rx), std::move(ro));
if (R_FAILED(res)) { R_SUCCEED();
LOG_ERROR(Service_JIT, "ro_mem could not be mapped for handle=0x{:08X}", ro_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IJitEnvironment>(system, std::move(process), std::move(rx),
std::move(ro));
} }
private: private:

View File

@ -6,13 +6,13 @@
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ro/ro.h" #include "core/hle/service/ro/ro.h"
#include "core/hle/service/ro/ro_nro_utils.h" #include "core/hle/service/ro/ro_nro_utils.h"
#include "core/hle/service/ro/ro_results.h" #include "core/hle/service/ro/ro_results.h"
#include "core/hle/service/ro/ro_types.h" #include "core/hle/service/ro/ro_types.h"
#include "core/hle/service/server_manager.h" #include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
namespace Service::RO { namespace Service::RO {
@ -500,46 +500,65 @@ private:
} }
}; };
class RoInterface { class RoInterface : public ServiceFramework<RoInterface> {
public: public:
explicit RoInterface(std::shared_ptr<RoContext> ro, NrrKind nrr_kind) explicit RoInterface(Core::System& system_, const char* name_, std::shared_ptr<RoContext> ro,
: m_ro(ro), m_context_id(InvalidContextId), m_nrr_kind(nrr_kind) {} NrrKind nrr_kind)
: ServiceFramework{system_, name_}, m_ro(ro), m_context_id(InvalidContextId),
m_nrr_kind(nrr_kind) {
// clang-format off
static const FunctionInfo functions[] = {
{0, C<&RoInterface::MapManualLoadModuleMemory>, "MapManualLoadModuleMemory"},
{1, C<&RoInterface::UnmapManualLoadModuleMemory>, "UnmapManualLoadModuleMemory"},
{2, C<&RoInterface::RegisterModuleInfo>, "RegisterModuleInfo"},
{3, C<&RoInterface::UnregisterModuleInfo>, "UnregisterModuleInfo"},
{4, C<&RoInterface::RegisterProcessHandle>, "RegisterProcessHandle"},
{10, C<&RoInterface::RegisterProcessModuleInfo>, "RegisterProcessModuleInfo"},
};
// clang-format on
RegisterHandlers(functions);
}
~RoInterface() { ~RoInterface() {
m_ro->UnregisterProcess(m_context_id); m_ro->UnregisterProcess(m_context_id);
} }
Result MapManualLoadModuleMemory(u64* out_load_address, u64 client_pid, u64 nro_address, Result MapManualLoadModuleMemory(Out<u64> out_load_address, ClientProcessId client_pid,
u64 nro_size, u64 bss_address, u64 bss_size) { u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) {
R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
R_RETURN(m_ro->MapManualLoadModuleMemory(out_load_address, m_context_id, nro_address, R_RETURN(m_ro->MapManualLoadModuleMemory(out_load_address.Get(), m_context_id, nro_address,
nro_size, bss_address, bss_size)); nro_size, bss_address, bss_size));
} }
Result UnmapManualLoadModuleMemory(u64 client_pid, u64 nro_address) { Result UnmapManualLoadModuleMemory(ClientProcessId client_pid, u64 nro_address) {
R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
R_RETURN(m_ro->UnmapManualLoadModuleMemory(m_context_id, nro_address)); R_RETURN(m_ro->UnmapManualLoadModuleMemory(m_context_id, nro_address));
} }
Result RegisterModuleInfo(u64 client_pid, u64 nrr_address, u64 nrr_size) { Result RegisterModuleInfo(ClientProcessId client_pid, u64 nrr_address, u64 nrr_size) {
R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
R_RETURN( R_RETURN(
m_ro->RegisterModuleInfo(m_context_id, nrr_address, nrr_size, NrrKind::User, true)); m_ro->RegisterModuleInfo(m_context_id, nrr_address, nrr_size, NrrKind::User, true));
} }
Result UnregisterModuleInfo(u64 client_pid, u64 nrr_address) { Result UnregisterModuleInfo(ClientProcessId client_pid, u64 nrr_address) {
R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
R_RETURN(m_ro->UnregisterModuleInfo(m_context_id, nrr_address)); R_RETURN(m_ro->UnregisterModuleInfo(m_context_id, nrr_address));
} }
Result RegisterProcessHandle(u64 client_pid, Kernel::KProcess* process) { Result RegisterProcessHandle(ClientProcessId client_pid,
InCopyHandle<Kernel::KProcess>& process) {
// Register the process. // Register the process.
R_RETURN(m_ro->RegisterProcess(std::addressof(m_context_id), process, client_pid)); R_RETURN(m_ro->RegisterProcess(std::addressof(m_context_id), process.GetPointerUnsafe(),
*client_pid));
} }
Result RegisterProcessModuleInfo(u64 client_pid, u64 nrr_address, u64 nrr_size, Result RegisterProcessModuleInfo(ClientProcessId client_pid, u64 nrr_address, u64 nrr_size,
Kernel::KProcess* process) { InCopyHandle<Kernel::KProcess>& process) {
// Validate the process. // Validate the process.
R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
// Register the module. // Register the module.
R_RETURN(m_ro->RegisterModuleInfo(m_context_id, nrr_address, nrr_size, m_nrr_kind, R_RETURN(m_ro->RegisterModuleInfo(m_context_id, nrr_address, nrr_size, m_nrr_kind,
@ -552,137 +571,6 @@ private:
NrrKind m_nrr_kind{}; NrrKind m_nrr_kind{};
}; };
class IRoInterface : public ServiceFramework<IRoInterface> {
public:
explicit IRoInterface(Core::System& system_, const char* name_, std::shared_ptr<RoContext> ro,
NrrKind nrr_kind)
: ServiceFramework{system_, name_}, interface {
ro, nrr_kind
} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IRoInterface::MapManualLoadModuleMemory, "MapManualLoadModuleMemory"},
{1, &IRoInterface::UnmapManualLoadModuleMemory, "UnmapManualLoadModuleMemory"},
{2, &IRoInterface::RegisterModuleInfo, "RegisterModuleInfo"},
{3, &IRoInterface::UnregisterModuleInfo, "UnregisterModuleInfo"},
{4, &IRoInterface::RegisterProcessHandle, "RegisterProcessHandle"},
{10, &IRoInterface::RegisterProcessModuleInfo, "RegisterProcessModuleInfo"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void MapManualLoadModuleMemory(HLERequestContext& ctx) {
LOG_DEBUG(Service_LDR, "(called)");
struct InputParameters {
u64 client_pid;
u64 nro_address;
u64 nro_size;
u64 bss_address;
u64 bss_size;
};
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<InputParameters>();
u64 load_address = 0;
auto result = interface.MapManualLoadModuleMemory(&load_address, ctx.GetPID(),
params.nro_address, params.nro_size,
params.bss_address, params.bss_size);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(result);
rb.Push(load_address);
}
void UnmapManualLoadModuleMemory(HLERequestContext& ctx) {
LOG_DEBUG(Service_LDR, "(called)");
struct InputParameters {
u64 client_pid;
u64 nro_address;
};
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<InputParameters>();
auto result = interface.UnmapManualLoadModuleMemory(ctx.GetPID(), params.nro_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void RegisterModuleInfo(HLERequestContext& ctx) {
LOG_DEBUG(Service_LDR, "(called)");
struct InputParameters {
u64 client_pid;
u64 nrr_address;
u64 nrr_size;
};
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<InputParameters>();
auto result =
interface.RegisterModuleInfo(ctx.GetPID(), params.nrr_address, params.nrr_size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void UnregisterModuleInfo(HLERequestContext& ctx) {
LOG_DEBUG(Service_LDR, "(called)");
struct InputParameters {
u64 client_pid;
u64 nrr_address;
};
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<InputParameters>();
auto result = interface.UnregisterModuleInfo(ctx.GetPID(), params.nrr_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void RegisterProcessHandle(HLERequestContext& ctx) {
LOG_DEBUG(Service_LDR, "(called)");
auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0));
auto client_pid = ctx.GetPID();
auto result = interface.RegisterProcessHandle(client_pid, process.GetPointerUnsafe());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void RegisterProcessModuleInfo(HLERequestContext& ctx) {
LOG_DEBUG(Service_LDR, "(called)");
struct InputParameters {
u64 client_pid;
u64 nrr_address;
u64 nrr_size;
};
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<InputParameters>();
auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0));
auto client_pid = ctx.GetPID();
auto result = interface.RegisterProcessModuleInfo(
client_pid, params.nrr_address, params.nrr_size, process.GetPointerUnsafe());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
RoInterface interface;
};
} // namespace } // namespace
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
@ -691,11 +579,11 @@ void LoopProcess(Core::System& system) {
auto ro = std::make_shared<RoContext>(); auto ro = std::make_shared<RoContext>();
const auto RoInterfaceFactoryForUser = [&, ro] { const auto RoInterfaceFactoryForUser = [&, ro] {
return std::make_shared<IRoInterface>(system, "ldr:ro", ro, NrrKind::User); return std::make_shared<RoInterface>(system, "ldr:ro", ro, NrrKind::User);
}; };
const auto RoInterfaceFactoryForJitPlugin = [&, ro] { const auto RoInterfaceFactoryForJitPlugin = [&, ro] {
return std::make_shared<IRoInterface>(system, "ro:1", ro, NrrKind::JitPlugin); return std::make_shared<RoInterface>(system, "ro:1", ro, NrrKind::JitPlugin);
}; };
server_manager->RegisterNamedService("ldr:ro", std::move(RoInterfaceFactoryForUser)); server_manager->RegisterNamedService("ldr:ro", std::move(RoInterfaceFactoryForUser));

View File

@ -206,6 +206,22 @@ protected:
RegisterHandlersBaseTipc(functions, n); RegisterHandlersBaseTipc(functions, n);
} }
protected:
template <bool Domain, auto F>
void CmifReplyWrap(HLERequestContext& ctx);
/**
* Wraps the template pointer-to-member function for use in a domain session.
*/
template <auto F>
static constexpr HandlerFnP<Self> D = &Self::template CmifReplyWrap<true, F>;
/**
* Wraps the template pointer-to-member function for use in a non-domain session.
*/
template <auto F>
static constexpr HandlerFnP<Self> C = &Self::template CmifReplyWrap<false, F>;
private: private:
/** /**
* This function is used to allow invocation of pointers to handlers stored in the base class * This function is used to allow invocation of pointers to handlers stored in the base class