diff options
Diffstat (limited to 'src/core')
28 files changed, 785 insertions, 363 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 81eaf0942..ffbda7925 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -13,6 +13,8 @@ add_library(core STATIC arm/dynarmic/arm_exclusive_monitor.h arm/exclusive_monitor.cpp arm/exclusive_monitor.h + arm/symbols.cpp + arm/symbols.h constants.cpp constants.h core.cpp diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 0951e1976..08bf1201d 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp @@ -8,134 +8,13 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/arm/arm_interface.h" +#include "core/arm/symbols.h" #include "core/core.h" +#include "core/hle/kernel/k_process.h" #include "core/loader/loader.h" #include "core/memory.h" namespace Core { -namespace { - -constexpr u64 ELF_DYNAMIC_TAG_NULL = 0; -constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5; -constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6; -constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11; - -enum class ELFSymbolType : u8 { - None = 0, - Object = 1, - Function = 2, - Section = 3, - File = 4, - Common = 5, - TLS = 6, -}; - -enum class ELFSymbolBinding : u8 { - Local = 0, - Global = 1, - Weak = 2, -}; - -enum class ELFSymbolVisibility : u8 { - Default = 0, - Internal = 1, - Hidden = 2, - Protected = 3, -}; - -struct ELFSymbol { - u32 name_index; - union { - u8 info; - - BitField<0, 4, ELFSymbolType> type; - BitField<4, 4, ELFSymbolBinding> binding; - }; - ELFSymbolVisibility visibility; - u16 sh_index; - u64 value; - u64 size; -}; -static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size."); - -using Symbols = std::vector<std::pair<ELFSymbol, std::string>>; - -Symbols GetSymbols(VAddr text_offset, Core::Memory::Memory& memory) { - const auto mod_offset = text_offset + memory.Read32(text_offset + 4); - - if (mod_offset < text_offset || (mod_offset & 0b11) != 0 || - memory.Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) { - return {}; - } - - const auto dynamic_offset = memory.Read32(mod_offset + 0x4) + mod_offset; - - VAddr string_table_offset{}; - VAddr symbol_table_offset{}; - u64 symbol_entry_size{}; - - VAddr dynamic_index = dynamic_offset; - while (true) { - const u64 tag = memory.Read64(dynamic_index); - const u64 value = memory.Read64(dynamic_index + 0x8); - dynamic_index += 0x10; - - if (tag == ELF_DYNAMIC_TAG_NULL) { - break; - } - - if (tag == ELF_DYNAMIC_TAG_STRTAB) { - string_table_offset = value; - } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) { - symbol_table_offset = value; - } else if (tag == ELF_DYNAMIC_TAG_SYMENT) { - symbol_entry_size = value; - } - } - - if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) { - return {}; - } - - const auto string_table_address = text_offset + string_table_offset; - const auto symbol_table_address = text_offset + symbol_table_offset; - - Symbols out; - - VAddr symbol_index = symbol_table_address; - while (symbol_index < string_table_address) { - ELFSymbol symbol{}; - memory.ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol)); - - VAddr string_offset = string_table_address + symbol.name_index; - std::string name; - for (u8 c = memory.Read8(string_offset); c != 0; c = memory.Read8(++string_offset)) { - name += static_cast<char>(c); - } - - symbol_index += symbol_entry_size; - out.push_back({symbol, name}); - } - - return out; -} - -std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr func_address) { - const auto iter = - std::find_if(symbols.begin(), symbols.end(), [func_address](const auto& pair) { - const auto& symbol = pair.first; - const auto end_address = symbol.value + symbol.size; - return func_address >= symbol.value && func_address < end_address; - }); - - if (iter == symbols.end()) { - return std::nullopt; - } - - return iter->second; -} - -} // Anonymous namespace constexpr u64 SEGMENT_BASE = 0x7100000000ull; @@ -169,9 +48,11 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex return {}; } - std::map<std::string, Symbols> symbols; + std::map<std::string, Symbols::Symbols> symbols; for (const auto& module : modules) { - symbols.insert_or_assign(module.second, GetSymbols(module.first, memory)); + symbols.insert_or_assign(module.second, + Symbols::GetSymbols(module.first, system.Memory(), + system.CurrentProcess()->Is64BitProcess())); } for (auto& entry : out) { @@ -193,7 +74,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex const auto symbol_set = symbols.find(entry.module); if (symbol_set != symbols.end()) { - const auto symbol = GetSymbolName(symbol_set->second, entry.offset); + const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset); if (symbol.has_value()) { // TODO(DarkLordZach): Add demangling of symbol names. entry.name = *symbol; @@ -225,9 +106,11 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { return {}; } - std::map<std::string, Symbols> symbols; + std::map<std::string, Symbols::Symbols> symbols; for (const auto& module : modules) { - symbols.insert_or_assign(module.second, GetSymbols(module.first, memory)); + symbols.insert_or_assign(module.second, + Symbols::GetSymbols(module.first, system.Memory(), + system.CurrentProcess()->Is64BitProcess())); } for (auto& entry : out) { @@ -249,7 +132,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { const auto symbol_set = symbols.find(entry.module); if (symbol_set != symbols.end()) { - const auto symbol = GetSymbolName(symbol_set->second, entry.offset); + const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset); if (symbol.has_value()) { // TODO(DarkLordZach): Add demangling of symbol names. entry.name = *symbol; diff --git a/src/core/arm/symbols.cpp b/src/core/arm/symbols.cpp new file mode 100644 index 000000000..26c44f0c7 --- /dev/null +++ b/src/core/arm/symbols.cpp @@ -0,0 +1,190 @@ +// Copyright 2022 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/bit_field.h" +#include "common/common_funcs.h" +#include "core/arm/symbols.h" +#include "core/core.h" +#include "core/memory.h" + +namespace Core { +namespace { + +constexpr u64 ELF_DYNAMIC_TAG_NULL = 0; +constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5; +constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6; +constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11; + +enum class ELFSymbolType : u8 { + None = 0, + Object = 1, + Function = 2, + Section = 3, + File = 4, + Common = 5, + TLS = 6, +}; + +enum class ELFSymbolBinding : u8 { + Local = 0, + Global = 1, + Weak = 2, +}; + +enum class ELFSymbolVisibility : u8 { + Default = 0, + Internal = 1, + Hidden = 2, + Protected = 3, +}; + +struct ELF64Symbol { + u32 name_index; + union { + u8 info; + + BitField<0, 4, ELFSymbolType> type; + BitField<4, 4, ELFSymbolBinding> binding; + }; + ELFSymbolVisibility visibility; + u16 sh_index; + u64 value; + u64 size; +}; +static_assert(sizeof(ELF64Symbol) == 0x18, "ELF64Symbol has incorrect size."); + +struct ELF32Symbol { + u32 name_index; + u32 value; + u32 size; + union { + u8 info; + + BitField<0, 4, ELFSymbolType> type; + BitField<4, 4, ELFSymbolBinding> binding; + }; + ELFSymbolVisibility visibility; + u16 sh_index; +}; +static_assert(sizeof(ELF32Symbol) == 0x10, "ELF32Symbol has incorrect size."); + +} // Anonymous namespace + +namespace Symbols { + +template <typename Word, typename ELFSymbol, typename ByteReader> +static Symbols GetSymbols(ByteReader ReadBytes) { + const auto Read8{[&](u64 index) { + u8 ret; + ReadBytes(&ret, index, sizeof(u8)); + return ret; + }}; + + const auto Read32{[&](u64 index) { + u32 ret; + ReadBytes(&ret, index, sizeof(u32)); + return ret; + }}; + + const auto ReadWord{[&](u64 index) { + Word ret; + ReadBytes(&ret, index, sizeof(Word)); + return ret; + }}; + + const u32 mod_offset = Read32(4); + + if (Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) { + return {}; + } + + VAddr string_table_offset{}; + VAddr symbol_table_offset{}; + u64 symbol_entry_size{}; + + const auto dynamic_offset = Read32(mod_offset + 0x4) + mod_offset; + + VAddr dynamic_index = dynamic_offset; + while (true) { + const Word tag = ReadWord(dynamic_index); + const Word value = ReadWord(dynamic_index + sizeof(Word)); + dynamic_index += 2 * sizeof(Word); + + if (tag == ELF_DYNAMIC_TAG_NULL) { + break; + } + + if (tag == ELF_DYNAMIC_TAG_STRTAB) { + string_table_offset = value; + } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) { + symbol_table_offset = value; + } else if (tag == ELF_DYNAMIC_TAG_SYMENT) { + symbol_entry_size = value; + } + } + + if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) { + return {}; + } + + Symbols out; + + VAddr symbol_index = symbol_table_offset; + while (symbol_index < string_table_offset) { + ELFSymbol symbol{}; + ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol)); + + VAddr string_offset = string_table_offset + symbol.name_index; + std::string name; + for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) { + name += static_cast<char>(c); + } + + symbol_index += symbol_entry_size; + out[name] = std::make_pair(symbol.value, symbol.size); + } + + return out; +} + +Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) { + const auto ReadBytes{ + [&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }}; + + if (is_64) { + return GetSymbols<u64, ELF64Symbol>(ReadBytes); + } else { + return GetSymbols<u32, ELF32Symbol>(ReadBytes); + } +} + +Symbols GetSymbols(std::span<const u8> data, bool is_64) { + const auto ReadBytes{[&](void* ptr, size_t offset, size_t size) { + std::memcpy(ptr, data.data() + offset, size); + }}; + + if (is_64) { + return GetSymbols<u64, ELF64Symbol>(ReadBytes); + } else { + return GetSymbols<u32, ELF32Symbol>(ReadBytes); + } +} + +std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr) { + const auto iter = std::find_if(symbols.cbegin(), symbols.cend(), [addr](const auto& pair) { + const auto& [name, sym_info] = pair; + const auto& [start_address, size] = sym_info; + const auto end_address = start_address + size; + return addr >= start_address && addr < end_address; + }); + + if (iter == symbols.cend()) { + return std::nullopt; + } + + return iter->first; +} + +} // namespace Symbols +} // namespace Core diff --git a/src/core/arm/symbols.h b/src/core/arm/symbols.h new file mode 100644 index 000000000..99e6a9f8e --- /dev/null +++ b/src/core/arm/symbols.h @@ -0,0 +1,27 @@ +// Copyright 2022 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <map> +#include <optional> +#include <span> +#include <string> +#include <utility> + +#include "common/common_types.h" + +namespace Core::Memory { +class Memory; +} // namespace Core::Memory + +namespace Core::Symbols { + +using Symbols = std::map<std::string, std::pair<VAddr, std::size_t>, std::less<>>; + +Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64 = true); +Symbols GetSymbols(std::span<const u8> data, bool is_64 = true); +std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr); + +} // namespace Core::Symbols diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index c4e185757..73e724f3d 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -148,29 +148,33 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { // LayeredExeFS const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); + const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); + + std::vector<VirtualDir> patch_dirs = {sdmc_load_dir}; if (load_dir != nullptr && load_dir->GetSize() > 0) { - auto patch_dirs = load_dir->GetSubdirectories(); - std::sort( - patch_dirs.begin(), patch_dirs.end(), - [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); - - std::vector<VirtualDir> layers; - layers.reserve(patch_dirs.size() + 1); - for (const auto& subdir : patch_dirs) { - if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end()) - continue; + const auto load_patch_dirs = load_dir->GetSubdirectories(); + patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end()); + } - auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs"); - if (exefs_dir != nullptr) - layers.push_back(std::move(exefs_dir)); - } - layers.push_back(exefs); + std::sort(patch_dirs.begin(), patch_dirs.end(), + [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); - auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers)); - if (layered != nullptr) { - LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully"); - exefs = std::move(layered); - } + std::vector<VirtualDir> layers; + layers.reserve(patch_dirs.size() + 1); + for (const auto& subdir : patch_dirs) { + if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end()) + continue; + + auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs"); + if (exefs_dir != nullptr) + layers.push_back(std::move(exefs_dir)); + } + layers.push_back(exefs); + + auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers)); + if (layered != nullptr) { + LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully"); + exefs = std::move(layered); } if (Settings::values.dump_exefs) { @@ -536,11 +540,20 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u // SDMC mod directory (RomFS LayeredFS) const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); - if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0 && - IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs"))) { - const auto mod_disabled = - std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end(); - out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", "LayeredFS"); + if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0) { + std::string types; + if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs"))) { + AppendCommaIfNotEmpty(types, "LayeredExeFS"); + } + if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs"))) { + AppendCommaIfNotEmpty(types, "LayeredFS"); + } + + if (!types.empty()) { + const auto mod_disabled = + std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end(); + out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", types); + } } // DLC diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp index eef0ff493..de565048b 100644 --- a/src/core/hid/emulated_console.cpp +++ b/src/core/hid/emulated_console.cpp @@ -132,7 +132,7 @@ void EmulatedConsole::SetMotionParam(Common::ParamPackage param) { } void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) { - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; auto& raw_status = console.motion_values.raw_status; auto& emulated = console.motion_values.emulated; @@ -151,6 +151,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) { emulated.UpdateOrientation(raw_status.delta_timestamp); if (is_configuring) { + lock.unlock(); TriggerOnChange(ConsoleTriggerType::Motion); return; } @@ -166,6 +167,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) { // Find what is this value motion.verticalization_error = 0.0f; + lock.unlock(); TriggerOnChange(ConsoleTriggerType::Motion); } @@ -173,11 +175,12 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st if (index >= console.touch_values.size()) { return; } - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; console.touch_values[index] = TransformToTouch(callback); if (is_configuring) { + lock.unlock(); TriggerOnChange(ConsoleTriggerType::Touch); return; } @@ -189,26 +192,32 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st .pressed = console.touch_values[index].pressed.value, }; + lock.unlock(); TriggerOnChange(ConsoleTriggerType::Touch); } ConsoleMotionValues EmulatedConsole::GetMotionValues() const { + std::scoped_lock lock{mutex}; return console.motion_values; } TouchValues EmulatedConsole::GetTouchValues() const { + std::scoped_lock lock{mutex}; return console.touch_values; } ConsoleMotion EmulatedConsole::GetMotion() const { + std::scoped_lock lock{mutex}; return console.motion_state; } TouchFingerState EmulatedConsole::GetTouch() const { + std::scoped_lock lock{mutex}; return console.touch_state; } void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) { + std::scoped_lock lock{callback_mutex}; for (const auto& poller_pair : callback_list) { const ConsoleUpdateCallback& poller = poller_pair.second; if (poller.on_change) { @@ -218,13 +227,13 @@ void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) { } int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{callback_mutex}; callback_list.insert_or_assign(last_callback_key, update_callback); return last_callback_key++; } void EmulatedConsole::DeleteCallback(int key) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{callback_mutex}; const auto& iterator = callback_list.find(key); if (iterator == callback_list.end()) { LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h index 5eb170823..53677bdc5 100644 --- a/src/core/hid/emulated_console.h +++ b/src/core/hid/emulated_console.h @@ -183,6 +183,7 @@ private: TouchDevices touch_devices; mutable std::mutex mutex; + mutable std::mutex callback_mutex; std::unordered_map<int, ConsoleUpdateCallback> callback_list; int last_callback_key = 0; diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 7e05666d6..c3f21066c 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -353,14 +353,17 @@ void EmulatedController::DisableConfiguration() { } void EmulatedController::EnableSystemButtons() { + std::scoped_lock lock{mutex}; system_buttons_enabled = true; } void EmulatedController::DisableSystemButtons() { + std::scoped_lock lock{mutex}; system_buttons_enabled = false; } void EmulatedController::ResetSystemButtons() { + std::scoped_lock lock{mutex}; controller.home_button_state.home.Assign(false); controller.capture_button_state.capture.Assign(false); } @@ -494,139 +497,141 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback if (index >= controller.button_values.size()) { return; } - { - std::lock_guard lock{mutex}; - bool value_changed = false; - const auto new_status = TransformToButton(callback); - auto& current_status = controller.button_values[index]; + std::unique_lock lock{mutex}; + bool value_changed = false; + const auto new_status = TransformToButton(callback); + auto& current_status = controller.button_values[index]; - // Only read button values that have the same uuid or are pressed once - if (current_status.uuid != uuid) { - if (!new_status.value) { - return; - } + // Only read button values that have the same uuid or are pressed once + if (current_status.uuid != uuid) { + if (!new_status.value) { + return; } + } - current_status.toggle = new_status.toggle; - current_status.uuid = uuid; - - // Update button status with current - if (!current_status.toggle) { - current_status.locked = false; - if (current_status.value != new_status.value) { - current_status.value = new_status.value; - value_changed = true; - } - } else { - // Toggle button and lock status - if (new_status.value && !current_status.locked) { - current_status.locked = true; - current_status.value = !current_status.value; - value_changed = true; - } + current_status.toggle = new_status.toggle; + current_status.uuid = uuid; - // Unlock button ready for next press - if (!new_status.value && current_status.locked) { - current_status.locked = false; - } + // Update button status with current + if (!current_status.toggle) { + current_status.locked = false; + if (current_status.value != new_status.value) { + current_status.value = new_status.value; + value_changed = true; } - - if (!value_changed) { - return; + } else { + // Toggle button and lock status + if (new_status.value && !current_status.locked) { + current_status.locked = true; + current_status.value = !current_status.value; + value_changed = true; } - if (is_configuring) { - controller.npad_button_state.raw = NpadButton::None; - controller.debug_pad_button_state.raw = 0; - TriggerOnChange(ControllerTriggerType::Button, false); - return; + // Unlock button ready for next press + if (!new_status.value && current_status.locked) { + current_status.locked = false; } + } - switch (index) { - case Settings::NativeButton::A: - controller.npad_button_state.a.Assign(current_status.value); - controller.debug_pad_button_state.a.Assign(current_status.value); - break; - case Settings::NativeButton::B: - controller.npad_button_state.b.Assign(current_status.value); - controller.debug_pad_button_state.b.Assign(current_status.value); - break; - case Settings::NativeButton::X: - controller.npad_button_state.x.Assign(current_status.value); - controller.debug_pad_button_state.x.Assign(current_status.value); - break; - case Settings::NativeButton::Y: - controller.npad_button_state.y.Assign(current_status.value); - controller.debug_pad_button_state.y.Assign(current_status.value); - break; - case Settings::NativeButton::LStick: - controller.npad_button_state.stick_l.Assign(current_status.value); - break; - case Settings::NativeButton::RStick: - controller.npad_button_state.stick_r.Assign(current_status.value); - break; - case Settings::NativeButton::L: - controller.npad_button_state.l.Assign(current_status.value); - controller.debug_pad_button_state.l.Assign(current_status.value); - break; - case Settings::NativeButton::R: - controller.npad_button_state.r.Assign(current_status.value); - controller.debug_pad_button_state.r.Assign(current_status.value); - break; - case Settings::NativeButton::ZL: - controller.npad_button_state.zl.Assign(current_status.value); - controller.debug_pad_button_state.zl.Assign(current_status.value); - break; - case Settings::NativeButton::ZR: - controller.npad_button_state.zr.Assign(current_status.value); - controller.debug_pad_button_state.zr.Assign(current_status.value); - break; - case Settings::NativeButton::Plus: - controller.npad_button_state.plus.Assign(current_status.value); - controller.debug_pad_button_state.plus.Assign(current_status.value); - break; - case Settings::NativeButton::Minus: - controller.npad_button_state.minus.Assign(current_status.value); - controller.debug_pad_button_state.minus.Assign(current_status.value); - break; - case Settings::NativeButton::DLeft: - controller.npad_button_state.left.Assign(current_status.value); - controller.debug_pad_button_state.d_left.Assign(current_status.value); - break; - case Settings::NativeButton::DUp: - controller.npad_button_state.up.Assign(current_status.value); - controller.debug_pad_button_state.d_up.Assign(current_status.value); - break; - case Settings::NativeButton::DRight: - controller.npad_button_state.right.Assign(current_status.value); - controller.debug_pad_button_state.d_right.Assign(current_status.value); - break; - case Settings::NativeButton::DDown: - controller.npad_button_state.down.Assign(current_status.value); - controller.debug_pad_button_state.d_down.Assign(current_status.value); - break; - case Settings::NativeButton::SL: - controller.npad_button_state.left_sl.Assign(current_status.value); - controller.npad_button_state.right_sl.Assign(current_status.value); - break; - case Settings::NativeButton::SR: - controller.npad_button_state.left_sr.Assign(current_status.value); - controller.npad_button_state.right_sr.Assign(current_status.value); - break; - case Settings::NativeButton::Home: - if (!system_buttons_enabled) { - break; - } - controller.home_button_state.home.Assign(current_status.value); + if (!value_changed) { + return; + } + + if (is_configuring) { + controller.npad_button_state.raw = NpadButton::None; + controller.debug_pad_button_state.raw = 0; + lock.unlock(); + TriggerOnChange(ControllerTriggerType::Button, false); + return; + } + + switch (index) { + case Settings::NativeButton::A: + controller.npad_button_state.a.Assign(current_status.value); + controller.debug_pad_button_state.a.Assign(current_status.value); + break; + case Settings::NativeButton::B: + controller.npad_button_state.b.Assign(current_status.value); + controller.debug_pad_button_state.b.Assign(current_status.value); + break; + case Settings::NativeButton::X: + controller.npad_button_state.x.Assign(current_status.value); + controller.debug_pad_button_state.x.Assign(current_status.value); + break; + case Settings::NativeButton::Y: + controller.npad_button_state.y.Assign(current_status.value); + controller.debug_pad_button_state.y.Assign(current_status.value); + break; + case Settings::NativeButton::LStick: + controller.npad_button_state.stick_l.Assign(current_status.value); + break; + case Settings::NativeButton::RStick: + controller.npad_button_state.stick_r.Assign(current_status.value); + break; + case Settings::NativeButton::L: + controller.npad_button_state.l.Assign(current_status.value); + controller.debug_pad_button_state.l.Assign(current_status.value); + break; + case Settings::NativeButton::R: + controller.npad_button_state.r.Assign(current_status.value); + controller.debug_pad_button_state.r.Assign(current_status.value); + break; + case Settings::NativeButton::ZL: + controller.npad_button_state.zl.Assign(current_status.value); + controller.debug_pad_button_state.zl.Assign(current_status.value); + break; + case Settings::NativeButton::ZR: + controller.npad_button_state.zr.Assign(current_status.value); + controller.debug_pad_button_state.zr.Assign(current_status.value); + break; + case Settings::NativeButton::Plus: + controller.npad_button_state.plus.Assign(current_status.value); + controller.debug_pad_button_state.plus.Assign(current_status.value); + break; + case Settings::NativeButton::Minus: + controller.npad_button_state.minus.Assign(current_status.value); + controller.debug_pad_button_state.minus.Assign(current_status.value); + break; + case Settings::NativeButton::DLeft: + controller.npad_button_state.left.Assign(current_status.value); + controller.debug_pad_button_state.d_left.Assign(current_status.value); + break; + case Settings::NativeButton::DUp: + controller.npad_button_state.up.Assign(current_status.value); + controller.debug_pad_button_state.d_up.Assign(current_status.value); + break; + case Settings::NativeButton::DRight: + controller.npad_button_state.right.Assign(current_status.value); + controller.debug_pad_button_state.d_right.Assign(current_status.value); + break; + case Settings::NativeButton::DDown: + controller.npad_button_state.down.Assign(current_status.value); + controller.debug_pad_button_state.d_down.Assign(current_status.value); + break; + case Settings::NativeButton::SL: + controller.npad_button_state.left_sl.Assign(current_status.value); + controller.npad_button_state.right_sl.Assign(current_status.value); + break; + case Settings::NativeButton::SR: + controller.npad_button_state.left_sr.Assign(current_status.value); + controller.npad_button_state.right_sr.Assign(current_status.value); + break; + case Settings::NativeButton::Home: + if (!system_buttons_enabled) { break; - case Settings::NativeButton::Screenshot: - if (!system_buttons_enabled) { - break; - } - controller.capture_button_state.capture.Assign(current_status.value); + } + controller.home_button_state.home.Assign(current_status.value); + break; + case Settings::NativeButton::Screenshot: + if (!system_buttons_enabled) { break; } + controller.capture_button_state.capture.Assign(current_status.value); + break; } + + lock.unlock(); + if (!is_connected) { if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) { Connect(); @@ -643,7 +648,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, if (index >= controller.stick_values.size()) { return; } - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; const auto stick_value = TransformToStick(callback); // Only read stick values that have the same uuid or are over the threshold to avoid flapping @@ -659,6 +664,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, if (is_configuring) { controller.analog_stick_state.left = {}; controller.analog_stick_state.right = {}; + lock.unlock(); TriggerOnChange(ControllerTriggerType::Stick, false); return; } @@ -685,6 +691,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, break; } + lock.unlock(); TriggerOnChange(ControllerTriggerType::Stick, true); } @@ -693,7 +700,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac if (index >= controller.trigger_values.size()) { return; } - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; const auto trigger_value = TransformToTrigger(callback); // Only read trigger values that have the same uuid or are pressed once @@ -709,6 +716,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac if (is_configuring) { controller.gc_trigger_state.left = 0; controller.gc_trigger_state.right = 0; + lock.unlock(); TriggerOnChange(ControllerTriggerType::Trigger, false); return; } @@ -727,6 +735,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac break; } + lock.unlock(); TriggerOnChange(ControllerTriggerType::Trigger, true); } @@ -735,7 +744,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback if (index >= controller.motion_values.size()) { return; } - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; auto& raw_status = controller.motion_values[index].raw_status; auto& emulated = controller.motion_values[index].emulated; @@ -756,6 +765,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback force_update_motion = raw_status.force_update; if (is_configuring) { + lock.unlock(); TriggerOnChange(ControllerTriggerType::Motion, false); return; } @@ -767,6 +777,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback motion.orientation = emulated.GetOrientation(); motion.is_at_rest = !emulated.IsMoving(motion_sensitivity); + lock.unlock(); TriggerOnChange(ControllerTriggerType::Motion, true); } @@ -775,10 +786,11 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac if (index >= controller.battery_values.size()) { return; } - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; controller.battery_values[index] = TransformToBattery(callback); if (is_configuring) { + lock.unlock(); TriggerOnChange(ControllerTriggerType::Battery, false); return; } @@ -835,6 +847,8 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac }; break; } + + lock.unlock(); TriggerOnChange(ControllerTriggerType::Battery, true); } @@ -932,6 +946,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) } bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const { + std::scoped_lock lock{mutex}; const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; switch (type) { case NpadStyleIndex::ProController: @@ -947,6 +962,7 @@ bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const { } bool EmulatedController::IsControllerSupported(bool use_temporary_value) const { + std::scoped_lock lock{mutex}; const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; switch (type) { case NpadStyleIndex::ProController: @@ -982,40 +998,44 @@ void EmulatedController::Connect(bool use_temporary_value) { LOG_ERROR(Service_HID, "Controller type {} is not supported", type); return; } - { - std::lock_guard lock{mutex}; - if (is_configuring) { - tmp_is_connected = true; - TriggerOnChange(ControllerTriggerType::Connected, false); - return; - } - if (is_connected) { - return; - } - is_connected = true; + std::unique_lock lock{mutex}; + if (is_configuring) { + tmp_is_connected = true; + lock.unlock(); + TriggerOnChange(ControllerTriggerType::Connected, false); + return; + } + + if (is_connected) { + return; } + is_connected = true; + + lock.unlock(); TriggerOnChange(ControllerTriggerType::Connected, true); } void EmulatedController::Disconnect() { - { - std::lock_guard lock{mutex}; - if (is_configuring) { - tmp_is_connected = false; - TriggerOnChange(ControllerTriggerType::Disconnected, false); - return; - } + std::unique_lock lock{mutex}; + if (is_configuring) { + tmp_is_connected = false; + lock.unlock(); + TriggerOnChange(ControllerTriggerType::Disconnected, false); + return; + } - if (!is_connected) { - return; - } - is_connected = false; + if (!is_connected) { + return; } + is_connected = false; + + lock.unlock(); TriggerOnChange(ControllerTriggerType::Disconnected, true); } bool EmulatedController::IsConnected(bool get_temporary_value) const { + std::scoped_lock lock{mutex}; if (get_temporary_value && is_configuring) { return tmp_is_connected; } @@ -1029,10 +1049,12 @@ bool EmulatedController::IsVibrationEnabled() const { } NpadIdType EmulatedController::GetNpadIdType() const { + std::scoped_lock lock{mutex}; return npad_id_type; } NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const { + std::scoped_lock lock{mutex}; if (get_temporary_value && is_configuring) { return tmp_npad_type; } @@ -1040,27 +1062,28 @@ NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) c } void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { - { - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; - if (is_configuring) { - if (tmp_npad_type == npad_type_) { - return; - } - tmp_npad_type = npad_type_; - TriggerOnChange(ControllerTriggerType::Type, false); + if (is_configuring) { + if (tmp_npad_type == npad_type_) { return; } + tmp_npad_type = npad_type_; + lock.unlock(); + TriggerOnChange(ControllerTriggerType::Type, false); + return; + } - if (npad_type == npad_type_) { - return; - } - if (is_connected) { - LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", - NpadIdTypeToIndex(npad_id_type)); - } - npad_type = npad_type_; + if (npad_type == npad_type_) { + return; + } + if (is_connected) { + LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", + NpadIdTypeToIndex(npad_id_type)); } + npad_type = npad_type_; + + lock.unlock(); TriggerOnChange(ControllerTriggerType::Type, true); } @@ -1088,30 +1111,37 @@ LedPattern EmulatedController::GetLedPattern() const { } ButtonValues EmulatedController::GetButtonsValues() const { + std::scoped_lock lock{mutex}; return controller.button_values; } SticksValues EmulatedController::GetSticksValues() const { + std::scoped_lock lock{mutex}; return controller.stick_values; } TriggerValues EmulatedController::GetTriggersValues() const { + std::scoped_lock lock{mutex}; return controller.trigger_values; } ControllerMotionValues EmulatedController::GetMotionValues() const { + std::scoped_lock lock{mutex}; return controller.motion_values; } ColorValues EmulatedController::GetColorsValues() const { + std::scoped_lock lock{mutex}; return controller.color_values; } BatteryValues EmulatedController::GetBatteryValues() const { + std::scoped_lock lock{mutex}; return controller.battery_values; } HomeButtonState EmulatedController::GetHomeButtons() const { + std::scoped_lock lock{mutex}; if (is_configuring) { return {}; } @@ -1119,6 +1149,7 @@ HomeButtonState EmulatedController::GetHomeButtons() const { } CaptureButtonState EmulatedController::GetCaptureButtons() const { + std::scoped_lock lock{mutex}; if (is_configuring) { return {}; } @@ -1126,6 +1157,7 @@ CaptureButtonState EmulatedController::GetCaptureButtons() const { } NpadButtonState EmulatedController::GetNpadButtons() const { + std::scoped_lock lock{mutex}; if (is_configuring) { return {}; } @@ -1133,6 +1165,7 @@ NpadButtonState EmulatedController::GetNpadButtons() const { } DebugPadButton EmulatedController::GetDebugPadButtons() const { + std::scoped_lock lock{mutex}; if (is_configuring) { return {}; } @@ -1140,20 +1173,27 @@ DebugPadButton EmulatedController::GetDebugPadButtons() const { } AnalogSticks EmulatedController::GetSticks() const { + std::unique_lock lock{mutex}; + if (is_configuring) { return {}; } + // Some drivers like stick from buttons need constant refreshing for (auto& device : stick_devices) { if (!device) { continue; } + lock.unlock(); device->SoftUpdate(); + lock.lock(); } + return controller.analog_stick_state; } NpadGcTriggerState EmulatedController::GetTriggers() const { + std::scoped_lock lock{mutex}; if (is_configuring) { return {}; } @@ -1161,26 +1201,35 @@ NpadGcTriggerState EmulatedController::GetTriggers() const { } MotionState EmulatedController::GetMotions() const { + std::unique_lock lock{mutex}; + + // Some drivers like mouse motion need constant refreshing if (force_update_motion) { for (auto& device : motion_devices) { if (!device) { continue; } + lock.unlock(); device->ForceUpdate(); + lock.lock(); } } + return controller.motion_state; } ControllerColors EmulatedController::GetColors() const { + std::scoped_lock lock{mutex}; return controller.colors_state; } BatteryLevelState EmulatedController::GetBattery() const { + std::scoped_lock lock{mutex}; return controller.battery_state; } void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) { + std::scoped_lock lock{callback_mutex}; for (const auto& poller_pair : callback_list) { const ControllerUpdateCallback& poller = poller_pair.second; if (!is_npad_service_update && poller.is_npad_service) { @@ -1193,13 +1242,13 @@ void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npa } int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{callback_mutex}; callback_list.insert_or_assign(last_callback_key, std::move(update_callback)); return last_callback_key++; } void EmulatedController::DeleteCallback(int key) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{callback_mutex}; const auto& iterator = callback_list.find(key); if (iterator == callback_list.end()) { LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index aa52f9572..1e224685d 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -400,7 +400,7 @@ private: */ void TriggerOnChange(ControllerTriggerType type, bool is_service_update); - NpadIdType npad_id_type; + const NpadIdType npad_id_type; NpadStyleIndex npad_type{NpadStyleIndex::None}; NpadStyleTag supported_style_tag{NpadStyleSet::All}; bool is_connected{false}; @@ -434,6 +434,7 @@ private: StickDevices tas_stick_devices; mutable std::mutex mutex; + mutable std::mutex callback_mutex; std::unordered_map<int, ControllerUpdateCallback> callback_list; int last_callback_key = 0; diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp index 708480f2d..cc0dcd931 100644 --- a/src/core/hid/emulated_devices.cpp +++ b/src/core/hid/emulated_devices.cpp @@ -169,7 +169,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal if (index >= device_status.keyboard_values.size()) { return; } - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; bool value_changed = false; const auto new_status = TransformToButton(callback); auto& current_status = device_status.keyboard_values[index]; @@ -201,6 +201,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal } if (is_configuring) { + lock.unlock(); TriggerOnChange(DeviceTriggerType::Keyboard); return; } @@ -208,6 +209,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal // Index should be converted from NativeKeyboard to KeyboardKeyIndex UpdateKey(index, current_status.value); + lock.unlock(); TriggerOnChange(DeviceTriggerType::Keyboard); } @@ -227,7 +229,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c if (index >= device_status.keyboard_moddifier_values.size()) { return; } - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; bool value_changed = false; const auto new_status = TransformToButton(callback); auto& current_status = device_status.keyboard_moddifier_values[index]; @@ -259,6 +261,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c } if (is_configuring) { + lock.unlock(); TriggerOnChange(DeviceTriggerType::KeyboardModdifier); return; } @@ -289,6 +292,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c break; } + lock.unlock(); TriggerOnChange(DeviceTriggerType::KeyboardModdifier); } @@ -297,7 +301,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba if (index >= device_status.mouse_button_values.size()) { return; } - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; bool value_changed = false; const auto new_status = TransformToButton(callback); auto& current_status = device_status.mouse_button_values[index]; @@ -329,6 +333,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba } if (is_configuring) { + lock.unlock(); TriggerOnChange(DeviceTriggerType::Mouse); return; } @@ -351,6 +356,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba break; } + lock.unlock(); TriggerOnChange(DeviceTriggerType::Mouse); } @@ -359,13 +365,14 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba if (index >= device_status.mouse_analog_values.size()) { return; } - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; const auto analog_value = TransformToAnalog(callback); device_status.mouse_analog_values[index] = analog_value; if (is_configuring) { device_status.mouse_position_state = {}; + lock.unlock(); TriggerOnChange(DeviceTriggerType::Mouse); return; } @@ -379,17 +386,19 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba break; } + lock.unlock(); TriggerOnChange(DeviceTriggerType::Mouse); } void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) { - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; const auto touch_value = TransformToTouch(callback); device_status.mouse_stick_value = touch_value; if (is_configuring) { device_status.mouse_position_state = {}; + lock.unlock(); TriggerOnChange(DeviceTriggerType::Mouse); return; } @@ -397,42 +406,52 @@ void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callbac device_status.mouse_position_state.x = touch_value.x.value; device_status.mouse_position_state.y = touch_value.y.value; + lock.unlock(); TriggerOnChange(DeviceTriggerType::Mouse); } KeyboardValues EmulatedDevices::GetKeyboardValues() const { + std::scoped_lock lock{mutex}; return device_status.keyboard_values; } KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const { + std::scoped_lock lock{mutex}; return device_status.keyboard_moddifier_values; } MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const { + std::scoped_lock lock{mutex}; return device_status.mouse_button_values; } KeyboardKey EmulatedDevices::GetKeyboard() const { + std::scoped_lock lock{mutex}; return device_status.keyboard_state; } KeyboardModifier EmulatedDevices::GetKeyboardModifier() const { + std::scoped_lock lock{mutex}; return device_status.keyboard_moddifier_state; } MouseButton EmulatedDevices::GetMouseButtons() const { + std::scoped_lock lock{mutex}; return device_status.mouse_button_state; } MousePosition EmulatedDevices::GetMousePosition() const { + std::scoped_lock lock{mutex}; return device_status.mouse_position_state; } AnalogStickState EmulatedDevices::GetMouseWheel() const { + std::scoped_lock lock{mutex}; return device_status.mouse_wheel_state; } void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) { + std::scoped_lock lock{callback_mutex}; for (const auto& poller_pair : callback_list) { const InterfaceUpdateCallback& poller = poller_pair.second; if (poller.on_change) { @@ -442,13 +461,13 @@ void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) { } int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{callback_mutex}; callback_list.insert_or_assign(last_callback_key, std::move(update_callback)); return last_callback_key++; } void EmulatedDevices::DeleteCallback(int key) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{callback_mutex}; const auto& iterator = callback_list.find(key); if (iterator == callback_list.end()) { LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h index 790d3b411..73e9f0293 100644 --- a/src/core/hid/emulated_devices.h +++ b/src/core/hid/emulated_devices.h @@ -200,6 +200,7 @@ private: MouseStickDevice mouse_stick_device; mutable std::mutex mutex; + mutable std::mutex callback_mutex; std::unordered_map<int, InterfaceUpdateCallback> callback_list; int last_callback_key = 0; diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp index 8027bec00..7765e7848 100644 --- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp +++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp @@ -148,9 +148,9 @@ u64 GenerateUniformRange(u64 min, u64 max, F f) { } // Anonymous namespace u64 KSystemControl::GenerateRandomU64() { - static std::random_device device; - static std::mt19937 gen(device()); - static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); + std::random_device device; + std::mt19937 gen(device()); + std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); return distribution(gen); } diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h index c097373a8..423e8d8f5 100644 --- a/src/core/hle/kernel/k_auto_object.h +++ b/src/core/hle/kernel/k_auto_object.h @@ -161,7 +161,7 @@ public: do { ASSERT(cur_ref_count > 0); } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1, - std::memory_order_relaxed)); + std::memory_order_acq_rel)); // If ref count hits zero, destroy the object. if (cur_ref_count - 1 == 0) { diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 599013cf6..47ea3c89c 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp @@ -346,7 +346,8 @@ ResultCode KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std:: return ResultSuccess; } -ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) { +ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size, + ICacheInvalidationStrategy icache_invalidation_strategy) { // Validate the mapping request. R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), ResultInvalidMemoryRegion); @@ -396,7 +397,11 @@ ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std bool reprotected_pages = false; SCOPE_EXIT({ if (reprotected_pages && any_code_pages) { - system.InvalidateCpuInstructionCacheRange(dst_address, size); + if (icache_invalidation_strategy == ICacheInvalidationStrategy::InvalidateRange) { + system.InvalidateCpuInstructionCacheRange(dst_address, size); + } else { + system.InvalidateCpuInstructionCaches(); + } } }); @@ -563,6 +568,8 @@ ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None, KMemoryAttribute::None); + system.InvalidateCpuInstructionCaches(); + return ResultSuccess; } diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index bfabdf38c..dd6022975 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h @@ -26,6 +26,8 @@ class KMemoryBlockManager; class KPageTable final { public: + enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll }; + YUZU_NON_COPYABLE(KPageTable); YUZU_NON_MOVEABLE(KPageTable); @@ -38,7 +40,8 @@ public: ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state, KMemoryPermission perm); ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size); - ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size); + ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size, + ICacheInvalidationStrategy icache_invalidation_strategy); ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, VAddr src_addr); ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index 48b17fc74..9f171e3da 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -422,7 +422,7 @@ private: bool is_64bit_process = true; /// Total running time for the process in ticks. - u64 total_process_running_time_ticks = 0; + std::atomic<u64> total_process_running_time_ticks = 0; /// Per-process handle table for storing created object handles in. KHandleTable handle_table; diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h index 93c47f1b1..016e0a818 100644 --- a/src/core/hle/kernel/k_scheduler_lock.h +++ b/src/core/hle/kernel/k_scheduler_lock.h @@ -4,6 +4,7 @@ #pragma once +#include <atomic> #include "common/assert.h" #include "core/hle/kernel/k_spin_lock.h" #include "core/hle/kernel/k_thread.h" @@ -75,7 +76,7 @@ private: KernelCore& kernel; KAlignedSpinLock spin_lock{}; s32 lock_count{}; - KThread* owner_thread{}; + std::atomic<KThread*> owner_thread{}; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 94c8faf68..d3bb1c871 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -723,7 +723,7 @@ void KThread::UpdateState() { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); // Set our suspend flags in state. - const auto old_state = thread_state; + const ThreadState old_state = thread_state; const auto new_state = static_cast<ThreadState>(this->GetSuspendFlags()) | (old_state & ThreadState::Mask); thread_state = new_state; @@ -738,7 +738,7 @@ void KThread::Continue() { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); // Clear our suspend flags in state. - const auto old_state = thread_state; + const ThreadState old_state = thread_state; thread_state = old_state & ThreadState::Mask; // Note the state change in scheduler. diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index f46db7298..d0fd85130 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -5,6 +5,7 @@ #pragma once #include <array> +#include <atomic> #include <span> #include <string> #include <utility> @@ -751,7 +752,7 @@ private: KAffinityMask original_physical_affinity_mask{}; s32 original_physical_ideal_core_id{}; s32 num_core_migration_disables{}; - ThreadState thread_state{}; + std::atomic<ThreadState> thread_state{}; std::atomic<bool> termination_requested{}; bool wait_cancelled{}; bool cancellable{}; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index cc6ee0eae..d840d44e6 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -85,7 +85,7 @@ struct KernelCore::Impl { void InitializeCores() { for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { - cores[core_id].Initialize(current_process->Is64BitProcess()); + cores[core_id].Initialize((*current_process).Is64BitProcess()); system.Memory().SetCurrentPageTable(*current_process, core_id); } } @@ -168,11 +168,11 @@ struct KernelCore::Impl { // Shutdown all processes. if (current_process) { - current_process->Finalize(); + (*current_process).Finalize(); // current_process->Close(); // TODO: The current process should be destroyed based on accurate ref counting after // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak. - current_process->Destroy(); + (*current_process).Destroy(); current_process = nullptr; } @@ -711,7 +711,7 @@ struct KernelCore::Impl { // Lists all processes that exist in the current session. std::vector<KProcess*> process_list; - KProcess* current_process{}; + std::atomic<KProcess*> current_process{}; std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; Kernel::TimeManager time_manager; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 976d63234..0c86435b5 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1713,7 +1713,8 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha return ResultInvalidMemoryRegion; } - return page_table.UnmapCodeMemory(dst_address, src_address, size); + return page_table.UnmapCodeMemory(dst_address, src_address, size, + KPageTable::ICacheInvalidationStrategy::InvalidateAll); } /// Exits the current process diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 2477c5612..cf727c167 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -389,8 +389,12 @@ public: if (bss_size) { auto block_guard = detail::ScopeExit([&] { - page_table.UnmapCodeMemory(addr + nro_size, bss_addr, bss_size); - page_table.UnmapCodeMemory(addr, nro_addr, nro_size); + page_table.UnmapCodeMemory( + addr + nro_size, bss_addr, bss_size, + Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange); + page_table.UnmapCodeMemory( + addr, nro_addr, nro_size, + Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange); }); const ResultCode result{ @@ -570,17 +574,21 @@ public: auto& page_table{system.CurrentProcess()->PageTable()}; if (info.bss_size != 0) { - CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size + - info.ro_size + info.data_size, - info.bss_address, info.bss_size)); + CASCADE_CODE(page_table.UnmapCodeMemory( + info.nro_address + info.text_size + info.ro_size + info.data_size, info.bss_address, + info.bss_size, Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); } - CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size, - info.src_addr + info.text_size + info.ro_size, - info.data_size)); - CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size, - info.src_addr + info.text_size, info.ro_size)); - CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address, info.src_addr, info.text_size)); + CASCADE_CODE(page_table.UnmapCodeMemory( + info.nro_address + info.text_size + info.ro_size, + info.src_addr + info.text_size + info.ro_size, info.data_size, + Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); + CASCADE_CODE(page_table.UnmapCodeMemory( + info.nro_address + info.text_size, info.src_addr + info.text_size, info.ro_size, + Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); + CASCADE_CODE(page_table.UnmapCodeMemory( + info.nro_address, info.src_addr, info.text_size, + Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); return ResultSuccess; } diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index d6702e4e1..d25b050e2 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -689,6 +689,9 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con case OptName::REUSEADDR: ASSERT(value == 0 || value == 1); return Translate(socket->SetReuseAddr(value != 0)); + case OptName::KEEPALIVE: + ASSERT(value == 0 || value == 1); + return Translate(socket->SetKeepAlive(value != 0)); case OptName::BROADCAST: ASSERT(value == 0 || value == 1); return Translate(socket->SetBroadcast(value != 0)); diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp index fb6142c49..a193fb578 100644 --- a/src/core/hle/service/sockets/sfdnsres.cpp +++ b/src/core/hle/service/sockets/sfdnsres.cpp @@ -2,8 +2,28 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <string_view> +#include <utility> +#include <vector> + +#include "common/string_util.h" +#include "common/swap.h" +#include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/sockets/sfdnsres.h" +#include "core/memory.h" + +#ifdef _WIN32 +#include <ws2tcpip.h> +#elif YUZU_UNIX +#include <arpa/inet.h> +#include <netdb.h> +#include <netinet/in.h> +#include <sys/socket.h> +#ifndef EAI_NODATA +#define EAI_NODATA EAI_NONAME +#endif +#endif namespace Service::Sockets { @@ -21,7 +41,7 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres" {9, nullptr, "CancelRequest"}, {10, nullptr, "GetHostByNameRequestWithOptions"}, {11, nullptr, "GetHostByAddrRequestWithOptions"}, - {12, nullptr, "GetAddrInfoRequestWithOptions"}, + {12, &SFDNSRES::GetAddrInfoRequestWithOptions, "GetAddrInfoRequestWithOptions"}, {13, nullptr, "GetNameInfoRequestWithOptions"}, {14, nullptr, "ResolverSetOptionRequest"}, {15, nullptr, "ResolverGetOptionRequest"}, @@ -31,7 +51,142 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres" SFDNSRES::~SFDNSRES() = default; -void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) { +enum class NetDbError : s32 { + Internal = -1, + Success = 0, + HostNotFound = 1, + TryAgain = 2, + NoRecovery = 3, + NoData = 4, +}; + +static NetDbError AddrInfoErrorToNetDbError(s32 result) { + // Best effort guess to map errors + switch (result) { + case 0: + return NetDbError::Success; + case EAI_AGAIN: + return NetDbError::TryAgain; + case EAI_NODATA: + return NetDbError::NoData; + default: + return NetDbError::HostNotFound; + } +} + +static std::vector<u8> SerializeAddrInfo(const addrinfo* addrinfo, s32 result_code, + std::string_view host) { + // Adapted from + // https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190 + std::vector<u8> data; + + auto* current = addrinfo; + while (current != nullptr) { + struct SerializedResponseHeader { + u32 magic; + s32 flags; + s32 family; + s32 socket_type; + s32 protocol; + u32 address_length; + }; + static_assert(sizeof(SerializedResponseHeader) == 0x18, + "Response header size must be 0x18 bytes"); + + constexpr auto header_size = sizeof(SerializedResponseHeader); + const auto addr_size = + current->ai_addr && current->ai_addrlen > 0 ? current->ai_addrlen : 4; + const auto canonname_size = current->ai_canonname ? strlen(current->ai_canonname) + 1 : 1; + + const auto last_size = data.size(); + data.resize(last_size + header_size + addr_size + canonname_size); + + // Header in network byte order + SerializedResponseHeader header{}; + + constexpr auto HEADER_MAGIC = 0xBEEFCAFE; + header.magic = htonl(HEADER_MAGIC); + header.family = htonl(current->ai_family); + header.flags = htonl(current->ai_flags); + header.socket_type = htonl(current->ai_socktype); + header.protocol = htonl(current->ai_protocol); + header.address_length = current->ai_addr ? htonl((u32)current->ai_addrlen) : 0; + + auto* header_ptr = data.data() + last_size; + std::memcpy(header_ptr, &header, header_size); + + if (header.address_length == 0) { + std::memset(header_ptr + header_size, 0, 4); + } else { + switch (current->ai_family) { + case AF_INET: { + struct SockAddrIn { + s16 sin_family; + u16 sin_port; + u32 sin_addr; + u8 sin_zero[8]; + }; + + SockAddrIn serialized_addr{}; + const auto addr = *reinterpret_cast<sockaddr_in*>(current->ai_addr); + serialized_addr.sin_port = htons(addr.sin_port); + serialized_addr.sin_family = htons(addr.sin_family); + serialized_addr.sin_addr = htonl(addr.sin_addr.s_addr); + std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn)); + + char addr_string_buf[64]{}; + inet_ntop(AF_INET, &addr.sin_addr, addr_string_buf, std::size(addr_string_buf)); + LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host, addr_string_buf); + break; + } + case AF_INET6: { + struct SockAddrIn6 { + s16 sin6_family; + u16 sin6_port; + u32 sin6_flowinfo; + u8 sin6_addr[16]; + u32 sin6_scope_id; + }; + + SockAddrIn6 serialized_addr{}; + const auto addr = *reinterpret_cast<sockaddr_in6*>(current->ai_addr); + serialized_addr.sin6_family = htons(addr.sin6_family); + serialized_addr.sin6_port = htons(addr.sin6_port); + serialized_addr.sin6_flowinfo = htonl(addr.sin6_flowinfo); + serialized_addr.sin6_scope_id = htonl(addr.sin6_scope_id); + std::memcpy(serialized_addr.sin6_addr, &addr.sin6_addr, + sizeof(SockAddrIn6::sin6_addr)); + std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn6)); + + char addr_string_buf[64]{}; + inet_ntop(AF_INET6, &addr.sin6_addr, addr_string_buf, std::size(addr_string_buf)); + LOG_INFO(Service, "Resolved host '{}' to IPv6 address {}", host, addr_string_buf); + break; + } + default: + std::memcpy(header_ptr + header_size, current->ai_addr, addr_size); + break; + } + } + if (current->ai_canonname) { + std::memcpy(header_ptr + addr_size, current->ai_canonname, canonname_size); + } else { + *(header_ptr + header_size + addr_size) = 0; + } + + current = current->ai_next; + } + + // 4-byte sentinel value + data.push_back(0); + data.push_back(0); + data.push_back(0); + data.push_back(0); + + return data; +} + +static std::pair<u32, s32> GetAddrInfoRequestImpl(Kernel::HLERequestContext& ctx) { struct Parameters { u8 use_nsd_resolve; u32 unknown; @@ -42,11 +197,51 @@ void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) { const auto parameters = rp.PopRaw<Parameters>(); LOG_WARNING(Service, - "(STUBBED) called. use_nsd_resolve={}, unknown=0x{:08X}, process_id=0x{:016X}", + "called with ignored parameters: use_nsd_resolve={}, unknown={}, process_id={}", parameters.use_nsd_resolve, parameters.unknown, parameters.process_id); - IPC::ResponseBuilder rb{ctx, 2}; + const auto host_buffer = ctx.ReadBuffer(0); + const std::string host = Common::StringFromBuffer(host_buffer); + + const auto service_buffer = ctx.ReadBuffer(1); + const std::string service = Common::StringFromBuffer(service_buffer); + + addrinfo* addrinfo; + // Pass null for hints. Serialized hints are also passed in a buffer, but are ignored for now + s32 result_code = getaddrinfo(host.c_str(), service.c_str(), nullptr, &addrinfo); + + u32 data_size = 0; + if (result_code == 0 && addrinfo != nullptr) { + const std::vector<u8>& data = SerializeAddrInfo(addrinfo, result_code, host); + data_size = static_cast<u32>(data.size()); + freeaddrinfo(addrinfo); + + ctx.WriteBuffer(data, 0); + } + + return std::make_pair(data_size, result_code); +} + +void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) { + auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode + rb.Push(result_code); // errno + rb.Push(data_size); // serialized size +} + +void SFDNSRES::GetAddrInfoRequestWithOptions(Kernel::HLERequestContext& ctx) { + // Additional options are ignored + auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx); + + IPC::ResponseBuilder rb{ctx, 5}; rb.Push(ResultSuccess); + rb.Push(data_size); // serialized size + rb.Push(result_code); // errno + rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode + rb.Push(0); } -} // namespace Service::Sockets +} // namespace Service::Sockets
\ No newline at end of file diff --git a/src/core/hle/service/sockets/sfdnsres.h b/src/core/hle/service/sockets/sfdnsres.h index 5d3b4dc2d..f0c57377d 100644 --- a/src/core/hle/service/sockets/sfdnsres.h +++ b/src/core/hle/service/sockets/sfdnsres.h @@ -19,6 +19,7 @@ public: private: void GetAddrInfoRequest(Kernel::HLERequestContext& ctx); + void GetAddrInfoRequestWithOptions(Kernel::HLERequestContext& ctx); }; } // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h index 02dbbae40..d69c86431 100644 --- a/src/core/hle/service/sockets/sockets.h +++ b/src/core/hle/service/sockets/sockets.h @@ -46,6 +46,7 @@ enum class Protocol : u32 { enum class OptName : u32 { REUSEADDR = 0x4, + KEEPALIVE = 0x8, BROADCAST = 0x20, LINGER = 0x80, SNDBUF = 0x1001, diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp index a3e0664b9..0784a165d 100644 --- a/src/core/network/network.cpp +++ b/src/core/network/network.cpp @@ -600,6 +600,10 @@ Errno Socket::SetReuseAddr(bool enable) { return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0); } +Errno Socket::SetKeepAlive(bool enable) { + return SetSockOpt<u32>(fd, SO_KEEPALIVE, enable ? 1 : 0); +} + Errno Socket::SetBroadcast(bool enable) { return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0); } diff --git a/src/core/network/sockets.h b/src/core/network/sockets.h index 5e39e7c54..caaefce7c 100644 --- a/src/core/network/sockets.h +++ b/src/core/network/sockets.h @@ -67,6 +67,8 @@ public: Errno SetReuseAddr(bool enable); + Errno SetKeepAlive(bool enable); + Errno SetBroadcast(bool enable); Errno SetSndBuf(u32 value); |