Compare commits

..

2 Commits

Author SHA1 Message Date
xbzk edc38b8230 Update src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/UShortSetting.kt
removed acidental copy paste
2026-08-29 21:27:59 +02:00
xbzk ffecb4af7b [debug] debug knobs adjustments 2026-08-29 07:43:28 -03:00
17 changed files with 276 additions and 143 deletions
+27 -11
View File
@@ -43,6 +43,7 @@ This guide will walk you through adding a new boolean toggle setting to Eden's c
Firstly add your desired toggle: Firstly add your desired toggle:
Example: `src/common/setting.h` Example: `src/common/setting.h`
```cpp ```cpp
SwitchableSetting<bool> your_setting_name{linkage, false, "your_setting_name", Category::RendererExtensions}; SwitchableSetting<bool> your_setting_name{linkage, false, "your_setting_name", Category::RendererExtensions};
``` ```
@@ -67,6 +68,7 @@ Common Categories:
Add the toggle to the Qt UI, where you wish for it to appear and place it there. Add the toggle to the Qt UI, where you wish for it to appear and place it there.
Example: `src/qt_common/config/shared_translation.cpp` Example: `src/qt_common/config/shared_translation.cpp`
```cpp ```cpp
INSERT(Settings, INSERT(Settings,
your_setting_name, your_setting_name,
@@ -91,6 +93,7 @@ INSERT(Settings,
Add where it should be in the settings. Add where it should be in the settings.
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt` Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt`
```kts ```kts
RENDERER_YOUR_SETTING_NAME("your_setting_name"), RENDERER_YOUR_SETTING_NAME("your_setting_name"),
``` ```
@@ -106,6 +109,7 @@ RENDERER_YOUR_SETTING_NAME("your_setting_name"),
Add the toggle to the Kotlin (Android) UI Add the toggle to the Kotlin (Android) UI
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt` Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt`
```kts ```kts
put( put(
SwitchSetting( SwitchSetting(
@@ -123,6 +127,7 @@ put(
Add your setting within the right category. Add your setting within the right category.
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt` Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt`
```kts ```kts
add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key) add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)
``` ```
@@ -137,6 +142,7 @@ add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)
Add your setting and description in the appropriate place. Add your setting and description in the appropriate place.
Example: `src/android/app/src/main/res/values/strings.xml` Example: `src/android/app/src/main/res/values/strings.xml`
```xml ```xml
<string name="your_setting_name">Your Setting Display Name</string> <string name="your_setting_name">Your Setting Display Name</string>
<string name="your_setting_name_description">Detailed description of what this setting does. Explain any caveats, requirements, or warnings here.</string> <string name="your_setting_name_description">Detailed description of what this setting does. Explain any caveats, requirements, or warnings here.</string>
@@ -150,6 +156,7 @@ Now the UI part is done find a place in the code for the toggle,
And use it to your heart's desire! And use it to your heart's desire!
Example: Example:
```cpp ```cpp
const bool your_value = Settings::values.your_setting_name.GetValue(); const bool your_value = Settings::values.your_setting_name.GetValue();
@@ -196,25 +203,31 @@ Common advantages recap:
#### Accessing Debug Knobs (dev side) #### Accessing Debug Knobs (dev side)
Use the `Settings::getDebugKnobAt(u8 i)` function to check if a specific bit is set: Use the `Settings::GetDebugKnobAt(u8 i)` function to check if a specific bit is set:
```cpp ```cpp
//cpp side //cpp side
#include "common/settings.h" #include "common/settings.h"
//To use it as a general purpose uint var:
unsigned int debug_knobs = Settings::values.debug_knobs.GetValue();
// Check if bit 0 is set // Check if bit 0 is set
bool feature_enabled = Settings::getDebugKnobAt(0); bool feature_enabled = Settings::GetDebugKnobAt(0);
// Check if bit 15 is set // Check if bit 15 is set
bool another_feature = Settings::getDebugKnobAt(15); bool another_feature = Settings::GetDebugKnobAt(15);
``` ```
```kts ```kts
//kotlin side //kotlin side
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
//To use it as a general purpose uint var
val debug_knobs: Int = UShortSetting.DEBUG_KNOBS.getInt()
// Check if bit x is set // Check if bit x is set
bool feature_enabled = Settings.getDebugKnobAt(x); //x as integer from 0 to 15 bool feature_enabled = Settings.GetDebugKnobAt(x); //x as integer from 0 to 15
``` ```
The function returns `true` if the specified bit (0-15) is set in the `debug_knobs` value, `false` otherwise. The function returns `true` if the specified bit (0-15) is set in the `debug_knobs` value, `false` otherwise.
@@ -247,6 +260,7 @@ There are two main confusions when talking about knobs:
Sometimes when an user reports: knobs 1 and 2 gets better performance, dev may get confuse whether he means the knobs 1 and 2 literally, or the 1st and 2nd knobs (knobs 0 and 1). Sometimes when an user reports: knobs 1 and 2 gets better performance, dev may get confuse whether he means the knobs 1 and 2 literally, or the 1st and 2nd knobs (knobs 0 and 1).
Debug knobs are **zero-based**, which means: Debug knobs are **zero-based**, which means:
* The first knob is the knob(0) (or knob0 henceforth), and the last one is the 15 (knob15, likewise) * The first knob is the knob(0) (or knob0 henceforth), and the last one is the 15 (knob15, likewise)
* You can talk: "knob0 is enabled/disabled", "In this video i was using only knobs 0 and 2", etc. * You can talk: "knob0 is enabled/disabled", "In this video i was using only knobs 0 and 2", etc.
@@ -259,6 +273,7 @@ Whenever you're instructing tests or reporting results, be precise about whether
ALWAYS use the word in PLURAL (knobs), without mentioning which one, to refer to the setting, aka multiple knobs at once: ALWAYS use the word in PLURAL (knobs), without mentioning which one, to refer to the setting, aka multiple knobs at once:
Examples: Examples:
- **knobs=0**: no knobs enabled - **knobs=0**: no knobs enabled
- **knobs=1**: knob0 enabled, others disabled - **knobs=1**: knob0 enabled, others disabled
- **knobs=2**: knob1 enabled, others disabled - **knobs=2**: knob1 enabled, others disabled
@@ -270,6 +285,7 @@ Examples:
Use the word in SINGULAR (knob), or in plural but referring which ones, when meaning multiple knobs at once: Use the word in SINGULAR (knob), or in plural but referring which ones, when meaning multiple knobs at once:
Examples: Examples:
- **knob0**: knob 0 enabled, others disabled - **knob0**: knob 0 enabled, others disabled
- **knob1**: knob 1 enabled, others disabled - **knob1**: knob 1 enabled, others disabled
- **knobs 0 and 1**: knobs 0 and 1 enabled, others disabled - **knobs 0 and 1**: knobs 0 and 1 enabled, others disabled
@@ -282,12 +298,12 @@ Examples:
```cpp ```cpp
void SomeFunction() { void SomeFunction() {
if (Settings::getDebugKnobAt(0)) { if (Settings::GetDebugKnobAt(0)) {
LOG_DEBUG(Common, "Debug feature 0 is enabled"); LOG_DEBUG(Common, "Debug feature 0 is enabled");
// Additional debug code here // Additional debug code here
} }
if (Settings::getDebugKnobAt(1)) { if (Settings::GetDebugKnobAt(1)) {
LOG_DEBUG(Common, "Debug feature 1 is enabled"); LOG_DEBUG(Common, "Debug feature 1 is enabled");
// Different debug behavior // Different debug behavior
} }
@@ -299,7 +315,7 @@ void SomeFunction() {
```cpp ```cpp
bool UseOptimizedPath() { bool UseOptimizedPath() {
// Skip optimization if debug bit 2 is set for testing // Skip optimization if debug bit 2 is set for testing
return !Settings::getDebugKnobAt(2); return !Settings::GetDebugKnobAt(2);
} }
``` ```
@@ -308,13 +324,13 @@ bool UseOptimizedPath() {
```cpp ```cpp
void ExperimentalFeature() { void ExperimentalFeature() {
static constexpr u8 EXPERIMENTAL_FEATURE_BIT = 3; static constexpr u8 EXPERIMENTAL_FEATURE_BIT = 3;
if (!Settings::getDebugKnobAt(EXPERIMENTAL_FEATURE_BIT)) { if (!Settings::GetDebugKnobAt(EXPERIMENTAL_FEATURE_BIT)) {
// Fallback to stable implementation // Fallback to stable implementation
StableImplementation(); StableImplementation();
return; return;
} }
// Experimental implementation // Experimental implementation
ExperimentalImplementation(); ExperimentalImplementation();
} }
@@ -220,7 +220,7 @@ object NativeLibrary {
external fun refreshThreadPolicies() external fun refreshThreadPolicies()
external fun getDebugKnobAt(index: Int): Boolean external fun GetDebugKnobAt(index: Int): Boolean
/** /**
* Set the current speed limit to the configured turbo speed. * Set the current speed limit to the configured turbo speed.
@@ -35,8 +35,8 @@ object Settings {
fun getPlayerString(player: Int): String = fun getPlayerString(player: Int): String =
YuzuApplication.appContext.getString(R.string.preferences_player, player) YuzuApplication.appContext.getString(R.string.preferences_player, player)
fun getDebugKnobAt(index: Int): Boolean { fun GetDebugKnobAt(index: Int): Boolean {
return org.yuzu.yuzu_emu.NativeLibrary.getDebugKnobAt(index) return org.yuzu.yuzu_emu.NativeLibrary.GetDebugKnobAt(index)
} }
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch" const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
@@ -11,8 +11,7 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
enum class ShortSetting(override val key: String) : AbstractShortSetting { enum class ShortSetting(override val key: String) : AbstractShortSetting {
RENDERER_SPEED_LIMIT("speed_limit"), RENDERER_SPEED_LIMIT("speed_limit"),
RENDERER_TURBO_SPEED_LIMIT("turbo_speed_limit"), RENDERER_TURBO_SPEED_LIMIT("turbo_speed_limit"),
RENDERER_SLOW_SPEED_LIMIT("slow_speed_limit"), RENDERER_SLOW_SPEED_LIMIT("slow_speed_limit")
DEBUG_KNOBS("debug_knobs")
; ;
override fun getShort(needsGlobal: Boolean): Short = NativeConfig.getShort(key, needsGlobal) override fun getShort(needsGlobal: Boolean): Short = NativeConfig.getShort(key, needsGlobal)
@@ -29,4 +28,4 @@ enum class ShortSetting(override val key: String) : AbstractShortSetting {
override fun getValueAsString(needsGlobal: Boolean): String = getShort(needsGlobal).toString() override fun getValueAsString(needsGlobal: Boolean): String = getShort(needsGlobal).toString()
override fun reset() = NativeConfig.setShort(key, defaultValue) override fun reset() = NativeConfig.setShort(key, defaultValue)
} }
@@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.features.settings.model
import org.yuzu.yuzu_emu.utils.NativeConfig
enum class UShortSetting(override val key: String) : AbstractIntSetting {
DEBUG_KNOBS("debug_knobs")
;
override fun getInt(needsGlobal: Boolean): Int =
NativeConfig.getUnsignedShort(key, needsGlobal)
override fun setInt(value: Int) {
if (NativeConfig.isPerGameConfigLoaded()) {
global = false
}
NativeConfig.setUnsignedShort(key, value)
}
override val defaultValue: Int by lazy { NativeConfig.getDefaultToString(key).toInt() }
override fun getValueAsString(needsGlobal: Boolean): String = getInt(needsGlobal).toString()
override fun reset() = NativeConfig.setUnsignedShort(key, defaultValue)
}
@@ -20,6 +20,7 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.LongSetting import org.yuzu.yuzu_emu.features.settings.model.LongSetting
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
import org.yuzu.yuzu_emu.features.settings.model.StringSetting import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.features.settings.model.UShortSetting
import org.yuzu.yuzu_emu.network.NetDataValidators import org.yuzu.yuzu_emu.network.NetDataValidators
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
import org.yuzu.yuzu_emu.utils.NativeConfig import org.yuzu.yuzu_emu.utils.NativeConfig
@@ -1034,7 +1035,7 @@ abstract class SettingsItem(
) )
put( put(
SpinBoxSetting( SpinBoxSetting(
ShortSetting.DEBUG_KNOBS, UShortSetting.DEBUG_KNOBS,
titleId = R.string.debug_knobs, titleId = R.string.debug_knobs,
descriptionId = R.string.debug_knobs_description, descriptionId = R.string.debug_knobs_description,
valueHint = R.string.debug_knobs_hint, valueHint = R.string.debug_knobs_hint,
@@ -25,6 +25,7 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.Settings.MenuTag import org.yuzu.yuzu_emu.features.settings.model.Settings.MenuTag
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
import org.yuzu.yuzu_emu.features.settings.model.StringSetting import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.features.settings.model.UShortSetting
import org.yuzu.yuzu_emu.features.settings.model.view.* import org.yuzu.yuzu_emu.features.settings.model.view.*
import org.yuzu.yuzu_emu.utils.InputHandler import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
@@ -1326,7 +1327,7 @@ class SettingsFragmentPresenter(
add(HeaderSetting(R.string.general)) add(HeaderSetting(R.string.general))
add(ShortSetting.DEBUG_KNOBS.key) add(UShortSetting.DEBUG_KNOBS.key)
add(StringSetting.PROGRAM_ARGS.key) add(StringSetting.PROGRAM_ARGS.key)
if (!NativeConfig.isPerGameConfigLoaded()) { if (!NativeConfig.isPerGameConfigLoaded()) {
@@ -80,6 +80,12 @@ object NativeConfig {
@Synchronized @Synchronized
external fun setShort(key: String, value: Short) external fun setShort(key: String, value: Short)
@Synchronized
external fun getUnsignedShort(key: String, needsGlobal: Boolean): Int
@Synchronized
external fun setUnsignedShort(key: String, value: Int)
@Synchronized @Synchronized
external fun getInt(key: String, needsGlobal: Boolean): Int external fun getInt(key: String, needsGlobal: Boolean): Int
+2 -2
View File
@@ -1300,8 +1300,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_refreshThreadPolicies(JNIEnv* env, jo
Common::RefreshThreadPolicies(); Common::RefreshThreadPolicies();
} }
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_getDebugKnobAt(JNIEnv* env, jobject jobj, jint index) { jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_GetDebugKnobAt(JNIEnv* env, jobject jobj, jint index) {
return static_cast<jboolean>(Settings::getDebugKnobAt(static_cast<u8>(index))); return static_cast<jboolean>(Settings::GetDebugKnobAt(static_cast<u8>(index)));
} }
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setTurboSpeedLimit(JNIEnv *env, jobject jobj, jboolean enabled) { void Java_org_yuzu_yuzu_1emu_NativeLibrary_setTurboSpeedLimit(JNIEnv *env, jobject jobj, jboolean enabled) {
@@ -130,6 +130,25 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setShort(JNIEnv* env, jobject ob
setting->SetValue(value); setting->SetValue(value);
} }
jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getUnsignedShort(JNIEnv* env, jobject obj,
jstring jkey,
jboolean needGlobal) {
auto setting = getSetting<u16>(env, jkey);
if (setting == nullptr) {
return -1;
}
return static_cast<jint>(setting->GetValue(static_cast<bool>(needGlobal)));
}
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setUnsignedShort(JNIEnv* env, jobject obj,
jstring jkey, jint value) {
auto setting = getSetting<u16>(env, jkey);
if (setting == nullptr) {
return;
}
setting->SetValue(static_cast<u16>(value));
}
jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getInt(JNIEnv* env, jobject obj, jstring jkey, jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getInt(JNIEnv* env, jobject obj, jstring jkey,
jboolean needGlobal) { jboolean needGlobal) {
auto setting = getSetting<int>(env, jkey); auto setting = getSetting<int>(env, jkey);
+1 -1
View File
@@ -146,7 +146,7 @@ void LogSettings() {
#undef LOG_PATH #undef LOG_PATH
} }
bool getDebugKnobAt(u8 i) { bool GetDebugKnobAt(u8 i) {
return (values.debug_knobs.GetValue() & (1 << (i & 0xF))) != 0; return (values.debug_knobs.GetValue() & (1 << (i & 0xF))) != 0;
} }
+2 -2
View File
@@ -904,7 +904,7 @@ struct Values {
0, 0,
65535, 65535,
"debug_knobs", "debug_knobs",
Category::Debugging, Category::System,
Specialization::Countable, Specialization::Countable,
true, true,
true}; true};
@@ -947,7 +947,7 @@ constexpr u32 MAX_FRAME_GEN_MULTIPLIER = 4;
[[nodiscard]] size_t FrameGenMaxGenerations(); [[nodiscard]] size_t FrameGenMaxGenerations();
bool getDebugKnobAt(u8 i); bool GetDebugKnobAt(u8 i);
void UpdateGPUAccuracy(); void UpdateGPUAccuracy();
bool IsGPULevelHigh(); bool IsGPULevelHigh();
+106 -61
View File
@@ -25,33 +25,42 @@ NvMap::Handle::Handle(u64 size_, Id id_)
flags.raw = 0; flags.raw = 0;
} }
NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress, NvCore::SessionId pSessionId) { NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress,
NvCore::SessionId pSessionId) {
std::scoped_lock lock(mutex);
// Handles cannot be allocated twice // Handles cannot be allocated twice
if (allocated) { if (allocated) {
return NvResult::AccessDenied; return NvResult::AccessDenied;
} }
flags = pFlags; flags = pFlags;
kind = pKind; kind = pKind;
align = pAlign < YUZU_PAGESIZE ? YUZU_PAGESIZE : pAlign; align = pAlign < YUZU_PAGESIZE ? YUZU_PAGESIZE : pAlign;
session_id = pSessionId; session_id = pSessionId;
// This flag is only applicable for handles with an address passed // This flag is only applicable for handles with an address passed
if (pAddress) { if (pAddress) {
flags.keep_uncached_after_free.Assign(0); flags.keep_uncached_after_free.Assign(0);
} else { } else {
LOG_CRITICAL(Service_NVDRV, "Mapping nvmap handles without a CPU side address is unimplemented!"); LOG_CRITICAL(Service_NVDRV,
"Mapping nvmap handles without a CPU side address is unimplemented!");
} }
size = Common::AlignUp(size, YUZU_PAGESIZE); size = Common::AlignUp(size, YUZU_PAGESIZE);
aligned_size = Common::AlignUp(size, align); aligned_size = Common::AlignUp(size, align);
address = pAddress; address = pAddress;
allocated = true; allocated = true;
return NvResult::Success; return NvResult::Success;
} }
NvResult NvMap::Handle::Duplicate(bool internal_session) { NvResult NvMap::Handle::Duplicate(bool internal_session) {
std::scoped_lock lock(mutex);
// Unallocated handles cannot be duplicated as duplication requires memory accounting (in HOS) // Unallocated handles cannot be duplicated as duplication requires memory accounting (in HOS)
if (!allocated) [[unlikely]] { if (!allocated) [[unlikely]] {
return NvResult::BadValue; return NvResult::BadValue;
} }
// If we internally use FromId the duplication tracking of handles won't work accurately due to // If we internally use FromId the duplication tracking of handles won't work accurately due to
// us not implementing per-process handle refs. // us not implementing per-process handle refs.
if (internal_session) { if (internal_session) {
@@ -59,14 +68,16 @@ NvResult NvMap::Handle::Duplicate(bool internal_session) {
} else { } else {
dupes++; dupes++;
} }
return NvResult::Success; return NvResult::Success;
} }
NvMap::NvMap(Container& core_, Tegra::Host1x::Host1x& host1x_) : host1x{host1x_}, core{core_} {} NvMap::NvMap(Container& core_, Tegra::Host1x::Host1x& host1x_) : host1x{host1x_}, core{core_} {}
void NvMap::AddHandle(Handle&& handle_description) { void NvMap::AddHandle(std::shared_ptr<Handle> handle_description) {
std::scoped_lock l(handles_lock); std::scoped_lock lock(handles_lock);
handles.insert_or_assign(handle_description.id, std::move(handle_description));
handles.emplace(handle_description->id, std::move(handle_description));
} }
void NvMap::UnmapHandle(Handle& handle_description) { void NvMap::UnmapHandle(Handle& handle_description) {
@@ -105,48 +116,57 @@ void NvMap::UnmapHandle(Handle& handle_description) {
bool NvMap::TryRemoveHandle(const Handle& handle_description) { bool NvMap::TryRemoveHandle(const Handle& handle_description) {
// No dupes left, we can remove from handle map // No dupes left, we can remove from handle map
if (handle_description.dupes == 0 && handle_description.internal_dupes == 0) { if (handle_description.dupes == 0 && handle_description.internal_dupes == 0) {
std::scoped_lock l(handles_lock); std::scoped_lock lock(handles_lock);
auto it = handles.find(handle_description.id);
auto it{handles.find(handle_description.id)};
if (it != handles.end()) { if (it != handles.end()) {
handles.erase(it); handles.erase(it);
} }
return true; return true;
} else { } else {
return false; return false;
} }
} }
NvResult NvMap::CreateHandle(u64 size, Handle::Id& out_handle) { NvResult NvMap::CreateHandle(u64 size, std::shared_ptr<NvMap::Handle>& result_out) {
if (!Common::AlignUp(size, YUZU_PAGESIZE)) { if (!size) [[unlikely]] {
return NvResult::BadValue; return NvResult::BadValue;
} }
u32 id = next_handle_id.fetch_add(HandleIdIncrement, std::memory_order_relaxed);
AddHandle(Handle(size, id)); u32 id{next_handle_id.fetch_add(HandleIdIncrement, std::memory_order_relaxed)};
out_handle = id; auto handle_description{std::make_shared<Handle>(size, id)};
AddHandle(handle_description);
result_out = handle_description;
return NvResult::Success; return NvResult::Success;
} }
std::optional<std::reference_wrapper<NvMap::Handle>> NvMap::GetHandle(Handle::Id handle) { std::shared_ptr<NvMap::Handle> NvMap::GetHandle(Handle::Id handle) {
if (auto const it = handles.find(handle); it != handles.end()) std::scoped_lock lock(handles_lock);
return {it->second}; try {
return std::nullopt; return handles.at(handle);
} catch (std::out_of_range&) {
return nullptr;
}
} }
DAddr NvMap::GetHandleAddress(Handle::Id handle) { DAddr NvMap::GetHandleAddress(Handle::Id handle) {
if (auto const it = handles.find(handle); it != handles.end()) std::scoped_lock lock(handles_lock);
return it->second.d_address; try {
return 0; return handles.at(handle)->d_address;
} catch (std::out_of_range&) {
return 0;
}
} }
DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) { DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) {
std::scoped_lock lock(handles_lock); auto handle_description{GetHandle(handle)};
auto o = GetHandle(handle); if (!handle_description) [[unlikely]] {
if (!o) [[unlikely]] {
return 0; return 0;
} }
auto handle_description = &o->get(); std::scoped_lock lock(handle_description->mutex);
const auto map_low_area = [&] { const auto map_low_area = [&] {
if (handle_description->pin_virt_address == 0) { if (handle_description->pin_virt_address == 0) {
u32 address = host1x.Allocator().Allocate(u32(handle_description->aligned_size)); u32 address = host1x.Allocator().Allocate(u32(handle_description->aligned_size));
@@ -160,15 +180,17 @@ DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) {
{ {
// Lock now to prevent our queue entry from being removed for allocation in-between the // Lock now to prevent our queue entry from being removed for allocation in-between the
// following check and erase // following check and erase
std::scoped_lock ql(unmap_queue_lock); std::scoped_lock queueLock(unmap_queue_lock);
if (handle_description->unmap_queue_entry) { if (handle_description->unmap_queue_entry) {
unmap_queue.erase(*handle_description->unmap_queue_entry); unmap_queue.erase(*handle_description->unmap_queue_entry);
handle_description->unmap_queue_entry.reset(); handle_description->unmap_queue_entry.reset();
if (low_area_pin) { if (low_area_pin) {
map_low_area(); map_low_area();
handle_description->pins++; handle_description->pins++;
return DAddr(handle_description->pin_virt_address); return static_cast<DAddr>(handle_description->pin_virt_address);
} }
handle_description->pins++; handle_description->pins++;
return handle_description->d_address; return handle_description->d_address;
} }
@@ -189,11 +211,12 @@ DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) {
while ((address = smmu.Allocate(aligned_up)) == 0) { while ((address = smmu.Allocate(aligned_up)) == 0) {
// Free handles until the allocation succeeds // Free handles until the allocation succeeds
std::scoped_lock queueLock(unmap_queue_lock); std::scoped_lock queueLock(unmap_queue_lock);
if (auto free_handle = handles.find(unmap_queue.front()); free_handle != handles.end()) { if (auto freeHandleDesc{unmap_queue.front()}) {
// Handles in the unmap queue are guaranteed not to be pinned so don't bother // Handles in the unmap queue are guaranteed not to be pinned so don't bother
// checking if they are before unmapping // checking if they are before unmapping
std::scoped_lock freeLock(freeHandleDesc->mutex);
if (handle_description->d_address) if (handle_description->d_address)
UnmapHandle(free_handle->second); UnmapHandle(*freeHandleDesc);
} else { } else {
LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!"); LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!");
} }
@@ -211,44 +234,51 @@ DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) {
handle_description->pins++; handle_description->pins++;
if (low_area_pin) { if (low_area_pin) {
return DAddr(handle_description->pin_virt_address); return static_cast<DAddr>(handle_description->pin_virt_address);
} }
return handle_description->d_address; return handle_description->d_address;
} }
void NvMap::UnpinHandle(Handle::Id handle) { void NvMap::UnpinHandle(Handle::Id handle) {
std::scoped_lock lock(handles_lock); auto handle_description{GetHandle(handle)};
if (auto o = GetHandle(handle); o) { if (!handle_description) {
auto handle_description = &o->get(); return;
if (--handle_description->pins < 0) { }
LOG_WARNING(Service_NVDRV, "Pin count imbalance detected!");
} else if (!handle_description->pins) { std::scoped_lock lock(handle_description->mutex);
std::scoped_lock ql(unmap_queue_lock); if (--handle_description->pins < 0) {
// Add to the unmap queue allowing this handle's memory to be freed if needed LOG_WARNING(Service_NVDRV, "Pin count imbalance detected!");
unmap_queue.push_back(handle); } else if (!handle_description->pins) {
handle_description->unmap_queue_entry = std::prev(unmap_queue.end()); std::scoped_lock queueLock(unmap_queue_lock);
}
// Add to the unmap queue allowing this handle's memory to be freed if needed
unmap_queue.push_back(handle_description);
handle_description->unmap_queue_entry = std::prev(unmap_queue.end());
} }
} }
void NvMap::DuplicateHandle(Handle::Id handle, bool internal_session) { void NvMap::DuplicateHandle(Handle::Id handle, bool internal_session) {
std::scoped_lock lock(handles_lock); auto handle_description{GetHandle(handle)};
auto o = GetHandle(handle); if (!handle_description) {
if (!o) {
LOG_CRITICAL(Service_NVDRV, "Unregistered handle!"); LOG_CRITICAL(Service_NVDRV, "Unregistered handle!");
return; return;
} }
auto result = o->get().Duplicate(internal_session);
auto result = handle_description->Duplicate(internal_session);
if (result != NvResult::Success) { if (result != NvResult::Success) {
LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!"); LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!");
} }
} }
std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool internal_session) { std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool internal_session) {
// We use a weak ptr here so we can tell when the handle has been freed and report that back to guest std::weak_ptr<Handle> hWeak{GetHandle(handle)};
std::scoped_lock lock(handles_lock); FreeInfo freeInfo;
if (auto o = GetHandle(handle); o) {
auto handle_description = &o->get(); // We use a weak ptr here so we can tell when the handle has been freed and report that back to
// guest
if (auto handle_description = hWeak.lock()) {
std::scoped_lock lock(handle_description->mutex);
if (internal_session) { if (internal_session) {
if (--handle_description->internal_dupes < 0) if (--handle_description->internal_dupes < 0)
LOG_WARNING(Service_NVDRV, "Internal duplicate count imbalance detected!"); LOG_WARNING(Service_NVDRV, "Internal duplicate count imbalance detected!");
@@ -258,25 +288,25 @@ std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool interna
} else if (handle_description->dupes == 0) { } else if (handle_description->dupes == 0) {
// Force unmap the handle // Force unmap the handle
if (handle_description->d_address) { if (handle_description->d_address) {
std::scoped_lock ql(unmap_queue_lock); std::scoped_lock queueLock(unmap_queue_lock);
UnmapHandle(*handle_description); UnmapHandle(*handle_description);
} }
handle_description->pins = 0; handle_description->pins = 0;
} }
} }
// Try to remove the shared ptr to the handle from the map, if nothing else is using the // Try to remove the shared ptr to the handle from the map, if nothing else is using the
// handle then it will now be freed when `handle_description` goes out of scope // handle then it will now be freed when `handle_description` goes out of scope
if (TryRemoveHandle(*handle_description)) { if (TryRemoveHandle(*handle_description)) {
LOG_DEBUG(Service_NVDRV, "Removed nvmap handle: {}", handle); LOG_DEBUG(Service_NVDRV, "Removed nvmap handle: {}", handle);
} else { } else {
LOG_DEBUG(Service_NVDRV, "Tried to free nvmap handle: {} but didn't as it still has duplicates", handle); LOG_DEBUG(Service_NVDRV,
"Tried to free nvmap handle: {} but didn't as it still has duplicates",
handle);
} }
// // If the handle hasn't been freed from memory, mark that
// if (!hWeak.expired()) { freeInfo = {
// LOG_DEBUG(Service_NVDRV, "nvmap handle: {} wasn't freed as it is still in use", handle);
// freeInfo.can_unlock = false;
// }
return FreeInfo{
.address = handle_description->address, .address = handle_description->address,
.size = handle_description->size, .size = handle_description->size,
.was_uncached = handle_description->flags.map_uncached.Value() != 0, .was_uncached = handle_description->flags.map_uncached.Value() != 0,
@@ -285,15 +315,30 @@ std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool interna
} else { } else {
return std::nullopt; return std::nullopt;
} }
// If the handle hasn't been freed from memory, mark that
if (!hWeak.expired()) {
LOG_DEBUG(Service_NVDRV, "nvmap handle: {} wasn't freed as it is still in use", handle);
freeInfo.can_unlock = false;
}
return freeInfo;
} }
void NvMap::UnmapAllHandles(NvCore::SessionId session_id) { void NvMap::UnmapAllHandles(NvCore::SessionId session_id) {
std::scoped_lock lk{handles_lock}; auto handles_copy = [&] {
for (auto it = handles.begin(); it != handles.end(); ++it) { std::scoped_lock lk{handles_lock};
if (it->second.session_id.id != session_id.id || it->second.dupes <= 0) { return handles;
continue; }();
for (auto& [id, handle] : handles_copy) {
{
std::scoped_lock lk{handle->mutex};
if (handle->session_id.id != session_id.id || handle->dupes <= 0) {
continue;
}
} }
FreeHandle(it->first, false); FreeHandle(id, false);
} }
} }
+44 -30
View File
@@ -12,11 +12,6 @@
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <optional> #include <optional>
#if BOOST_VERSION >= 109000
#include <boost/unordered/unordered_node_map.hpp>
#else
#include <unordered_map>
#endif
#include <ankerl/unordered_dense.h> #include <ankerl/unordered_dense.h>
#include <assert.h> #include <assert.h>
@@ -36,36 +31,54 @@ class Host1x;
namespace Service::Nvidia::NvCore { namespace Service::Nvidia::NvCore {
class Container; class Container;
/// @brief The nvmap core class holds the global state for nvmap and provides methods to manage handles /**
* @brief The nvmap core class holds the global state for nvmap and provides methods to manage
* handles
*/
class NvMap { class NvMap {
public: public:
/// @brief A handle to a contiguous block of memory in an application's address space /**
* @brief A handle to a contiguous block of memory in an application's address space
*/
struct Handle { struct Handle {
using Id = u32; std::mutex mutex;
std::optional<typename std::list<Handle::Id>::iterator> unmap_queue_entry{};
u64 align{}; //!< The alignment to use when pinning the handle onto the SMMU u64 align{}; //!< The alignment to use when pinning the handle onto the SMMU
u64 size; //!< Page-aligned size of the memory the handle refers to u64 size; //!< Page-aligned size of the memory the handle refers to
u64 aligned_size; //!< `align`-aligned size of the memory the handle refers to u64 aligned_size; //!< `align`-aligned size of the memory the handle refers to
u64 orig_size; //!< Original unaligned size of the memory this handle refers to u64 orig_size; //!< Original unaligned size of the memory this handle refers to
DAddr d_address{}; //!< The memory location in the device's AS that this handle corresponds to, this can also be in the nvdrv tmem
VAddr address{}; //!< The memory location in the guest's AS that this handle corresponds to, this can also be in the nvdrv tmem
s64 pins{};
s32 dupes{1}; //!< How many guest references there are to this handle s32 dupes{1}; //!< How many guest references there are to this handle
s32 internal_dupes{0}; //!< How many emulator-internal references there are to this handle s32 internal_dupes{0}; //!< How many emulator-internal references there are to this handle
using Id = u32;
Id id; //!< A globally unique identifier for this handle Id id; //!< A globally unique identifier for this handle
s64 pins{};
u32 pin_virt_address{}; u32 pin_virt_address{};
std::optional<typename std::list<std::shared_ptr<Handle>>::iterator> unmap_queue_entry{};
union Flags { union Flags {
u32 raw; u32 raw;
BitField<0, 1, u32> map_uncached; //!< If the handle should be mapped as uncached BitField<0, 1, u32> map_uncached; //!< If the handle should be mapped as uncached
BitField<2, 1, u32> keep_uncached_after_free; //!< Only applicable when the handle was allocated with a fixed address BitField<2, 1, u32> keep_uncached_after_free; //!< Only applicable when the handle was
BitField<4, 1, u32> _unk0_; //!< Passed to IOVMM for pins //!< allocated with a fixed address
BitField<4, 1, u32> _unk0_; //!< Passed to IOVMM for pins
} flags{}; } flags{};
static_assert(sizeof(Flags) == sizeof(u32)); static_assert(sizeof(Flags) == sizeof(u32));
NvCore::SessionId session_id{};
VAddr address{}; //!< The memory location in the guest's AS that this handle corresponds to,
//!< this can also be in the nvdrv tmem
bool is_shared_mem_mapped{}; //!< If this nvmap has been mapped with the MapSharedMem IPC
//!< call
u8 kind{}; //!< Used for memory compression u8 kind{}; //!< Used for memory compression
bool allocated : 1 = false; //!< If the handle has been allocated with `Alloc` bool allocated{}; //!< If the handle has been allocated with `Alloc`
bool in_heap : 1 = false; bool in_heap{};
bool is_shared_mem_mapped : 1 = false; //!< If this nvmap has been mapped with the MapSharedMem IPC < call NvCore::SessionId session_id{};
DAddr d_address{}; //!< The memory location in the device's AS that this handle corresponds
//!< to, this can also be in the nvdrv tmem
Handle(u64 size, Id id); Handle(u64 size, Id id);
@@ -110,9 +123,9 @@ public:
/** /**
* @brief Creates an unallocated handle of the given size * @brief Creates an unallocated handle of the given size
*/ */
[[nodiscard]] NvResult CreateHandle(u64 size, Handle::Id& out_handle); [[nodiscard]] NvResult CreateHandle(u64 size, std::shared_ptr<NvMap::Handle>& result_out);
std::optional<std::reference_wrapper<Handle>> GetHandle(Handle::Id handle); std::shared_ptr<Handle> GetHandle(Handle::Id handle);
DAddr GetHandleAddress(Handle::Id handle); DAddr GetHandleAddress(Handle::Id handle);
@@ -144,21 +157,20 @@ public:
void UnmapAllHandles(NvCore::SessionId session_id); void UnmapAllHandles(NvCore::SessionId session_id);
std::list<Handle::Id> unmap_queue{}; private:
/// Main owning map of handles std::list<std::shared_ptr<Handle>> unmap_queue{};
#if BOOST_VERSION >= 109000
boost::unordered_node_map<Handle::Id, Handle> handles{};
#else
std::unordered_map<Handle::Id, Handle> handles{};
#endif
std::mutex unmap_queue_lock{}; //!< Protects access to `unmap_queue` std::mutex unmap_queue_lock{}; //!< Protects access to `unmap_queue`
ankerl::unordered_dense::map<Handle::Id, std::shared_ptr<Handle>>
handles{}; //!< Main owning map of handles
std::mutex handles_lock; //!< Protects access to `handles` std::mutex handles_lock; //!< Protects access to `handles`
static constexpr u32 HandleIdIncrement{4}; //!< Each new handle ID is an increment of 4 from the previous
static constexpr u32 HandleIdIncrement{
4}; //!< Each new handle ID is an increment of 4 from the previous
std::atomic<u32> next_handle_id{HandleIdIncrement}; std::atomic<u32> next_handle_id{HandleIdIncrement};
Tegra::Host1x::Host1x& host1x; Tegra::Host1x::Host1x& host1x;
Container& core;
void AddHandle(Handle&& handle); void AddHandle(std::shared_ptr<Handle> handle);
/** /**
* @brief Unmaps and frees the SMMU memory region a handle is mapped to * @brief Unmaps and frees the SMMU memory region a handle is mapped to
@@ -172,5 +184,7 @@ public:
* @return If the handle was removed from the map * @return If the handle was removed from the map
*/ */
bool TryRemoveHandle(const Handle& handle_description); bool TryRemoveHandle(const Handle& handle_description);
Container& core;
}; };
} // namespace Service::Nvidia::NvCore } // namespace Service::Nvidia::NvCore
@@ -328,11 +328,10 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
} }
} }
auto o = nvmap.GetHandle(params.handle); auto handle{nvmap.GetHandle(params.handle)};
if (!o) { if (!handle) {
return NvResult::BadValue; return NvResult::BadValue;
} }
auto handle = &o->get();
DAddr device_address = DAddr(nvmap.PinHandle(params.handle, false) + params.buffer_offset); DAddr device_address = DAddr(nvmap.PinHandle(params.handle, false) + params.buffer_offset);
u64 size{params.mapping_size ? params.mapping_size : handle->orig_size}; u64 size{params.mapping_size ? params.mapping_size : handle->orig_size};
@@ -103,13 +103,17 @@ NvResult nvhost_nvdec_common::Submit(IoctlSubmit& params, std::span<u8> data, De
for (std::size_t i = 0; i < syncpt_increments.size(); i++) { for (std::size_t i = 0; i < syncpt_increments.size(); i++) {
const SyncptIncr& syncpt_incr = syncpt_increments[i]; const SyncptIncr& syncpt_incr = syncpt_increments[i];
fence_thresholds[i] = syncpoint_manager.IncrementSyncpointMaxExt(syncpt_incr.id, syncpt_incr.increments); fence_thresholds[i] =
syncpoint_manager.IncrementSyncpointMaxExt(syncpt_incr.id, syncpt_incr.increments);
} }
for (const auto& cmd_buffer : command_buffers) { for (const auto& cmd_buffer : command_buffers) {
const auto object = nvmap.GetHandle(cmd_buffer.memory_id); const auto object = nvmap.GetHandle(cmd_buffer.memory_id);
ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;); ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;);
Core::Memory::CpuGuestMemory<Tegra::ChCommandHeader, Core::Memory::GuestMemoryFlags::SafeRead> cmdlist(session->process->GetMemory(), object->get().address + cmd_buffer.offset, cmd_buffer.word_count); Core::Memory::CpuGuestMemory<Tegra::ChCommandHeader,
Core::Memory::GuestMemoryFlags::SafeRead>
cmdlist(session->process->GetMemory(), object->address + cmd_buffer.offset,
cmd_buffer.word_count);
host1x.PushEntries(fd, std::move(cmdlist)); host1x.PushEntries(fd, std::move(cmdlist));
} }
+25 -23
View File
@@ -83,14 +83,17 @@ void nvmap::OnClose(DeviceFD fd) {
NvResult nvmap::IocCreate(IocCreateParams& params) { NvResult nvmap::IocCreate(IocCreateParams& params) {
LOG_DEBUG(Service_NVDRV, "called, size={:#08x}", params.size); LOG_DEBUG(Service_NVDRV, "called, size={:#08x}", params.size);
NvCore::NvMap::Handle handle_description(0, 0); std::shared_ptr<NvCore::NvMap::Handle> handle_description{};
// Orig size is the unaligned size, set the handle to that auto result =
auto result = file.CreateHandle(params.size, params.handle); file.CreateHandle(Common::AlignUp(params.size, YUZU_PAGESIZE), handle_description);
if (result != NvResult::Success) { if (result != NvResult::Success) {
LOG_CRITICAL(Service_NVDRV, "Failed to create Object"); LOG_CRITICAL(Service_NVDRV, "Failed to create Object");
return result; return result;
} }
LOG_DEBUG(Service_NVDRV, "handle: {}, size: {:#x}", params.handle, params.size); handle_description->orig_size = params.size; // Orig size is the unaligned size
params.handle = handle_description->id;
LOG_DEBUG(Service_NVDRV, "handle: {}, size: {:#x}", handle_description->id, params.size);
return NvResult::Success; return NvResult::Success;
} }
@@ -112,27 +115,30 @@ NvResult nvmap::IocAlloc(IocAllocParams& params, DeviceFD fd) {
params.align = YUZU_PAGESIZE; params.align = YUZU_PAGESIZE;
} }
std::scoped_lock lock(file.handles_lock); auto handle_description{file.GetHandle(params.handle)};
auto o = file.GetHandle(params.handle); if (!handle_description) {
if (!o) {
LOG_CRITICAL(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); LOG_CRITICAL(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
return NvResult::BadValue; return NvResult::BadValue;
} }
auto handle_description = &o->get();
if (handle_description->allocated) { if (handle_description->allocated) {
LOG_CRITICAL(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle); LOG_CRITICAL(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle);
return NvResult::InsufficientMemory; return NvResult::InsufficientMemory;
} }
const auto result = handle_description->Alloc(params.flags, params.align, params.kind, params.address, sessions[fd]); const auto result = handle_description->Alloc(params.flags, params.align, params.kind,
params.address, sessions[fd]);
if (result != NvResult::Success) { if (result != NvResult::Success) {
LOG_CRITICAL(Service_NVDRV, "Object failed to allocate, handle={:08X}", params.handle); LOG_CRITICAL(Service_NVDRV, "Object failed to allocate, handle={:08X}", params.handle);
return result; return result;
} }
bool is_out_io{}; bool is_out_io{};
auto process = container.GetSession(sessions[fd])->process; auto process = container.GetSession(sessions[fd])->process;
ASSERT(process->GetPageTable().LockForMapDeviceAddressSpace(&is_out_io, handle_description->address, handle_description->size, Kernel::KMemoryPermission::None, true, false).IsSuccess()); ASSERT(process->GetPageTable()
.LockForMapDeviceAddressSpace(&is_out_io, handle_description->address,
handle_description->size,
Kernel::KMemoryPermission::None, true, false)
.IsSuccess());
return result; return result;
} }
@@ -145,13 +151,13 @@ NvResult nvmap::IocGetId(IocGetIdParams& params) {
return NvResult::BadValue; return NvResult::BadValue;
} }
std::scoped_lock lock(file.handles_lock); auto handle_description{file.GetHandle(params.handle)};
auto o = file.GetHandle(params.handle); if (!handle_description) {
if (!o) {
LOG_CRITICAL(Service_NVDRV, "Error!"); LOG_CRITICAL(Service_NVDRV, "Error!");
return NvResult::AccessDenied; // This will always return EPERM irrespective of if the handle exists or not return NvResult::AccessDenied; // This will always return EPERM irrespective of if the
// handle exists or not
} }
auto handle_description = &o->get();
params.id = handle_description->id; params.id = handle_description->id;
return NvResult::Success; return NvResult::Success;
} }
@@ -168,14 +174,12 @@ NvResult nvmap::IocFromId(IocFromIdParams& params) {
return NvResult::BadValue; return NvResult::BadValue;
} }
std::scoped_lock lock(file.handles_lock); auto handle_description{file.GetHandle(params.id)};
auto o = file.GetHandle(params.id); if (!handle_description) {
if (!o) {
LOG_CRITICAL(Service_NVDRV, "Unregistered handle!"); LOG_CRITICAL(Service_NVDRV, "Unregistered handle!");
return NvResult::BadValue; return NvResult::BadValue;
} }
auto handle_description = &o->get();
auto result = handle_description->Duplicate(false); auto result = handle_description->Duplicate(false);
if (result != NvResult::Success) { if (result != NvResult::Success) {
LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!"); LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!");
@@ -195,14 +199,12 @@ NvResult nvmap::IocParam(IocParamParams& params) {
return NvResult::BadValue; return NvResult::BadValue;
} }
std::scoped_lock lock(file.handles_lock); auto handle_description{file.GetHandle(params.handle)};
auto o = file.GetHandle(params.handle); if (!handle_description) {
if (!o) {
LOG_CRITICAL(Service_NVDRV, "Not registered handle!"); LOG_CRITICAL(Service_NVDRV, "Not registered handle!");
return NvResult::BadValue; return NvResult::BadValue;
} }
auto handle_description = &o->get();
switch (params.param) { switch (params.param) {
case HandleParameterType::Size: case HandleParameterType::Size:
params.result = static_cast<u32_le>(handle_description->orig_size); params.result = static_cast<u32_le>(handle_description->orig_size);