diff options
Diffstat (limited to '')
793 files changed, 31706 insertions, 13427 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 6e8d11919..95302c419 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,18 +1,15 @@ +# SPDX-FileCopyrightText: 2018 yuzu Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + add_library(core STATIC arm/arm_interface.h arm/arm_interface.cpp - arm/cpu_interrupt_handler.cpp - arm/cpu_interrupt_handler.h - arm/dynarmic/arm_dynarmic_32.cpp - arm/dynarmic/arm_dynarmic_32.h - arm/dynarmic/arm_dynarmic_64.cpp - arm/dynarmic/arm_dynarmic_64.h - arm/dynarmic/arm_dynarmic_cp15.cpp - arm/dynarmic/arm_dynarmic_cp15.h arm/dynarmic/arm_exclusive_monitor.cpp 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 @@ -34,6 +31,13 @@ add_library(core STATIC crypto/ctr_encryption_layer.h crypto/xts_encryption_layer.cpp crypto/xts_encryption_layer.h + debugger/debugger_interface.h + debugger/debugger.cpp + debugger/debugger.h + debugger/gdbstub_arch.cpp + debugger/gdbstub_arch.h + debugger/gdbstub.cpp + debugger/gdbstub.h device_memory.cpp device_memory.h file_sys/bis_factory.cpp @@ -122,6 +126,8 @@ add_library(core STATIC frontend/applets/error.h frontend/applets/general_frontend.cpp frontend/applets/general_frontend.h + frontend/applets/mii_edit.cpp + frontend/applets/mii_edit.h frontend/applets/profile_select.cpp frontend/applets/profile_select.h frontend/applets/software_keyboard.cpp @@ -132,8 +138,6 @@ add_library(core STATIC frontend/emu_window.h frontend/framebuffer_layout.cpp frontend/framebuffer_layout.h - hardware_interrupt_manager.cpp - hardware_interrupt_manager.h hid/emulated_console.cpp hid/emulated_console.h hid/emulated_controller.cpp @@ -147,11 +151,13 @@ add_library(core STATIC hid/input_converter.h hid/input_interpreter.cpp hid/input_interpreter.h + hid/irs_types.h hid/motion_input.cpp hid/motion_input.h hle/api_version.h hle/ipc.h hle/ipc_helpers.h + hle/kernel/board/nintendo/nx/k_memory_layout.h hle/kernel/board/nintendo/nx/k_system_control.cpp hle/kernel/board/nintendo/nx/k_system_control.h hle/kernel/board/nintendo/nx/secure_monitor.h @@ -164,6 +170,7 @@ add_library(core STATIC hle/kernel/hle_ipc.h hle/kernel/init/init_slab_setup.cpp hle/kernel/init/init_slab_setup.h + hle/kernel/initial_process.h hle/kernel/k_address_arbiter.cpp hle/kernel/k_address_arbiter.h hle/kernel/k_address_space_info.cpp @@ -205,9 +212,11 @@ add_library(core STATIC hle/kernel/k_memory_region.h hle/kernel/k_memory_region_type.h hle/kernel/k_page_bitmap.h + hle/kernel/k_page_buffer.cpp + hle/kernel/k_page_buffer.h hle/kernel/k_page_heap.cpp hle/kernel/k_page_heap.h - hle/kernel/k_page_linked_list.h + hle/kernel/k_page_group.h hle/kernel/k_page_table.cpp hle/kernel/k_page_table.h hle/kernel/k_port.cpp @@ -242,6 +251,8 @@ add_library(core STATIC hle/kernel/k_system_control.h hle/kernel/k_thread.cpp hle/kernel/k_thread.h + hle/kernel/k_thread_local_page.cpp + hle/kernel/k_thread_local_page.h hle/kernel/k_thread_queue.cpp hle/kernel/k_thread_queue.h hle/kernel/k_trace.h @@ -298,6 +309,9 @@ add_library(core STATIC hle/service/am/applets/applet_error.h hle/service/am/applets/applet_general_backend.cpp hle/service/am/applets/applet_general_backend.h + hle/service/am/applets/applet_mii_edit.cpp + hle/service/am/applets/applet_mii_edit.h + hle/service/am/applets/applet_mii_edit_types.h hle/service/am/applets/applet_profile_select.cpp hle/service/am/applets/applet_profile_select.h hle/service/am/applets/applet_software_keyboard.cpp @@ -421,8 +435,11 @@ add_library(core STATIC hle/service/grc/grc.h hle/service/hid/hid.cpp hle/service/hid/hid.h + hle/service/hid/hidbus.cpp + hle/service/hid/hidbus.h hle/service/hid/irs.cpp hle/service/hid/irs.h + hle/service/hid/irs_ring_lifo.h hle/service/hid/ring_lifo.h hle/service/hid/xcd.cpp hle/service/hid/xcd.h @@ -441,17 +458,48 @@ add_library(core STATIC hle/service/hid/controllers/mouse.h hle/service/hid/controllers/npad.cpp hle/service/hid/controllers/npad.h + hle/service/hid/controllers/palma.cpp + hle/service/hid/controllers/palma.h hle/service/hid/controllers/stubbed.cpp hle/service/hid/controllers/stubbed.h hle/service/hid/controllers/touchscreen.cpp hle/service/hid/controllers/touchscreen.h hle/service/hid/controllers/xpad.cpp hle/service/hid/controllers/xpad.h + hle/service/hid/hidbus/hidbus_base.cpp + hle/service/hid/hidbus/hidbus_base.h + hle/service/hid/hidbus/ringcon.cpp + hle/service/hid/hidbus/ringcon.h + hle/service/hid/hidbus/starlink.cpp + hle/service/hid/hidbus/starlink.h + hle/service/hid/hidbus/stubbed.cpp + hle/service/hid/hidbus/stubbed.h + hle/service/hid/irsensor/clustering_processor.cpp + hle/service/hid/irsensor/clustering_processor.h + hle/service/hid/irsensor/image_transfer_processor.cpp + hle/service/hid/irsensor/image_transfer_processor.h + hle/service/hid/irsensor/ir_led_processor.cpp + hle/service/hid/irsensor/ir_led_processor.h + hle/service/hid/irsensor/moment_processor.cpp + hle/service/hid/irsensor/moment_processor.h + hle/service/hid/irsensor/pointing_processor.cpp + hle/service/hid/irsensor/pointing_processor.h + hle/service/hid/irsensor/processor_base.cpp + hle/service/hid/irsensor/processor_base.h + hle/service/hid/irsensor/tera_plugin_processor.cpp + hle/service/hid/irsensor/tera_plugin_processor.h + hle/service/jit/jit_context.cpp + hle/service/jit/jit_context.h + hle/service/jit/jit.cpp + hle/service/jit/jit.h hle/service/lbl/lbl.cpp hle/service/lbl/lbl.h - hle/service/ldn/errors.h + hle/service/ldn/lan_discovery.cpp + hle/service/ldn/lan_discovery.h + hle/service/ldn/ldn_results.h hle/service/ldn/ldn.cpp hle/service/ldn/ldn.h + hle/service/ldn/ldn_types.h hle/service/ldr/ldr.cpp hle/service/ldr/ldr.h hle/service/lm/lm.cpp @@ -467,12 +515,20 @@ add_library(core STATIC hle/service/mii/types.h hle/service/mm/mm_u.cpp hle/service/mm/mm_u.h + hle/service/mnpp/mnpp_app.cpp + hle/service/mnpp/mnpp_app.h hle/service/ncm/ncm.cpp hle/service/ncm/ncm.h hle/service/nfc/nfc.cpp hle/service/nfc/nfc.h + hle/service/nfp/amiibo_crypto.cpp + hle/service/nfp/amiibo_crypto.h hle/service/nfp/nfp.cpp hle/service/nfp/nfp.h + hle/service/nfp/nfp_device.cpp + hle/service/nfp/nfp_device.h + hle/service/nfp/nfp_result.h + hle/service/nfp/nfp_types.h hle/service/nfp/nfp_user.cpp hle/service/nfp/nfp_user.h hle/service/ngct/ngct.cpp @@ -484,14 +540,20 @@ add_library(core STATIC hle/service/npns/npns.cpp hle/service/npns/npns.h hle/service/ns/errors.h + hle/service/ns/iplatform_service_manager.cpp + hle/service/ns/iplatform_service_manager.h hle/service/ns/language.cpp hle/service/ns/language.h hle/service/ns/ns.cpp hle/service/ns/ns.h hle/service/ns/pdm_qry.cpp hle/service/ns/pdm_qry.h - hle/service/ns/pl_u.cpp - hle/service/ns/pl_u.h + hle/service/nvdrv/core/container.cpp + hle/service/nvdrv/core/container.h + hle/service/nvdrv/core/nvmap.cpp + hle/service/nvdrv/core/nvmap.h + hle/service/nvdrv/core/syncpoint_manager.cpp + hle/service/nvdrv/core/syncpoint_manager.h hle/service/nvdrv/devices/nvdevice.h hle/service/nvdrv/devices/nvdisp_disp0.cpp hle/service/nvdrv/devices/nvdisp_disp0.h @@ -520,12 +582,35 @@ add_library(core STATIC hle/service/nvdrv/nvdrv_interface.h hle/service/nvdrv/nvmemp.cpp hle/service/nvdrv/nvmemp.h - hle/service/nvdrv/syncpoint_manager.cpp - hle/service/nvdrv/syncpoint_manager.h - hle/service/nvflinger/buffer_queue.cpp - hle/service/nvflinger/buffer_queue.h + hle/service/nvflinger/binder.h + hle/service/nvflinger/buffer_item.h + hle/service/nvflinger/buffer_item_consumer.cpp + hle/service/nvflinger/buffer_item_consumer.h + hle/service/nvflinger/buffer_queue_consumer.cpp + hle/service/nvflinger/buffer_queue_consumer.h + hle/service/nvflinger/buffer_queue_core.cpp + hle/service/nvflinger/buffer_queue_core.h + hle/service/nvflinger/buffer_queue_defs.h + hle/service/nvflinger/buffer_queue_producer.cpp + hle/service/nvflinger/buffer_queue_producer.h + hle/service/nvflinger/buffer_slot.h + hle/service/nvflinger/buffer_transform_flags.h + hle/service/nvflinger/consumer_base.cpp + hle/service/nvflinger/consumer_base.h + hle/service/nvflinger/consumer_listener.h + hle/service/nvflinger/graphic_buffer_producer.cpp + hle/service/nvflinger/graphic_buffer_producer.h + hle/service/nvflinger/hos_binder_driver_server.cpp + hle/service/nvflinger/hos_binder_driver_server.h hle/service/nvflinger/nvflinger.cpp hle/service/nvflinger/nvflinger.h + hle/service/nvflinger/parcel.h + hle/service/nvflinger/pixel_format.h + hle/service/nvflinger/producer_listener.h + hle/service/nvflinger/status.h + hle/service/nvflinger/ui/fence.h + hle/service/nvflinger/ui/graphic_buffer.h + hle/service/nvflinger/window.h hle/service/olsc/olsc.cpp hle/service/olsc/olsc.h hle/service/pcie/pcie.cpp @@ -544,6 +629,10 @@ add_library(core STATIC hle/service/psc/psc.h hle/service/ptm/psm.cpp hle/service/ptm/psm.h + hle/service/ptm/ptm.cpp + hle/service/ptm/ptm.h + hle/service/ptm/ts.cpp + hle/service/ptm/ts.h hle/service/kernel_helpers.cpp hle/service/kernel_helpers.h hle/service/service.cpp @@ -634,10 +723,15 @@ add_library(core STATIC hle/service/vi/vi_u.h hle/service/wlan/wlan.cpp hle/service/wlan/wlan.h + internal_network/network.cpp + internal_network/network.h + internal_network/network_interface.cpp + internal_network/network_interface.h + internal_network/sockets.h + internal_network/socket_proxy.cpp + internal_network/socket_proxy.h loader/deconstructed_rom_directory.cpp loader/deconstructed_rom_directory.h - loader/elf.cpp - loader/elf.h loader/kip.cpp loader/kip.h loader/loader.cpp @@ -661,11 +755,6 @@ add_library(core STATIC memory/dmnt_cheat_vm.h memory.cpp memory.h - network/network.cpp - network/network.h - network/network_interface.cpp - network/network_interface.h - network/sockets.h perf_stats.cpp perf_stats.h reporter.cpp @@ -682,16 +771,11 @@ if (MSVC) /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data - /we4456 # Declaration of 'identifier' hides previous local declaration - /we4457 # Declaration of 'identifier' hides function parameter - /we4458 # Declaration of 'identifier' hides class member - /we4459 # Declaration of 'identifier' hides global declaration ) else() target_compile_options(core PRIVATE -Werror=conversion -Werror=ignored-qualifiers - -Werror=shadow $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess> $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> @@ -705,8 +789,11 @@ endif() create_target_directory_groups(core) -target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) -target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus) +target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core) +target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus) +if (MINGW) + target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) +endif() if (ENABLE_WEB_SERVICE) target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 0951e1976..953d96439 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp @@ -1,6 +1,9 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef _MSC_VER +#include <cxxabi.h> +#endif #include <map> #include <optional> @@ -8,170 +11,43 @@ #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/debugger/debugger.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/svc.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; - } +#include "core/arm/dynarmic/arm_dynarmic_32.h" +#include "core/arm/dynarmic/arm_dynarmic_64.h" - return iter->second; -} - -} // Anonymous namespace +namespace Core { constexpr u64 SEGMENT_BASE = 0x7100000000ull; std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext( - System& system, const ThreadContext64& ctx) { - std::vector<BacktraceEntry> out; - auto& memory = system.Memory(); - - auto fp = ctx.cpu_registers[29]; - auto lr = ctx.cpu_registers[30]; - while (true) { - out.push_back({ - .module = "", - .address = 0, - .original_address = lr, - .offset = 0, - .name = {}, - }); - - if (fp == 0) { - break; - } + Core::System& system, const ARM_Interface::ThreadContext32& ctx) { + return ARM_Dynarmic_32::GetBacktraceFromContext(system, ctx); +} - lr = memory.Read64(fp + 8) - 4; - fp = memory.Read64(fp); - } +std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext( + Core::System& system, const ARM_Interface::ThreadContext64& ctx) { + return ARM_Dynarmic_64::GetBacktraceFromContext(system, ctx); +} +void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out) { std::map<VAddr, std::string> modules; auto& loader{system.GetAppLoader()}; if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) { - return {}; + 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) { @@ -188,91 +64,137 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex entry.offset = entry.original_address - base; entry.address = SEGMENT_BASE + entry.offset; - if (entry.module.empty()) + if (entry.module.empty()) { entry.module = "unknown"; + } 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()) { +#ifdef _MSC_VER // TODO(DarkLordZach): Add demangling of symbol names. entry.name = *symbol; +#else + int status{-1}; + char* demangled{abi::__cxa_demangle(symbol->c_str(), nullptr, nullptr, &status)}; + if (status == 0 && demangled != nullptr) { + entry.name = demangled; + std::free(demangled); + } else { + entry.name = *symbol; + } +#endif } } } +} + +void ARM_Interface::LogBacktrace() const { + const VAddr sp = GetSP(); + const VAddr pc = GetPC(); + LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc); + LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address", + "Offset", "Symbol"); + LOG_ERROR(Core_ARM, ""); - return out; + const auto backtrace = GetBacktrace(); + for (const auto& entry : backtrace) { + LOG_ERROR(Core_ARM, "{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address, + entry.original_address, entry.offset, entry.name); + } } -std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { - std::vector<BacktraceEntry> out; - auto& memory = system.Memory(); +void ARM_Interface::Run() { + using Kernel::StepState; + using Kernel::SuspendType; - auto fp = GetReg(29); - auto lr = GetReg(30); while (true) { - out.push_back({"", 0, lr, 0, ""}); - if (!fp) { + Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())}; + Dynarmic::HaltReason hr{}; + + // Notify the debugger and go to sleep if a step was performed + // and this thread has been scheduled again. + if (current_thread->GetStepState() == StepState::StepPerformed) { + system.GetDebugger().NotifyThreadStopped(current_thread); + current_thread->RequestSuspend(SuspendType::Debug); break; } - lr = memory.Read64(fp + 8) - 4; - fp = memory.Read64(fp); - } - std::map<VAddr, std::string> modules; - auto& loader{system.GetAppLoader()}; - if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) { - return {}; - } - - std::map<std::string, Symbols> symbols; - for (const auto& module : modules) { - symbols.insert_or_assign(module.second, GetSymbols(module.first, memory)); - } + // Otherwise, run the thread. + system.EnterDynarmicProfile(); + if (current_thread->GetStepState() == StepState::StepPending) { + hr = StepJit(); - for (auto& entry : out) { - VAddr base = 0; - for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) { - const auto& module{*iter}; - if (entry.original_address >= module.first) { - entry.module = module.second; - base = module.first; - break; + if (Has(hr, step_thread)) { + current_thread->SetStepState(StepState::StepPerformed); } + } else { + hr = RunJit(); + } + system.ExitDynarmicProfile(); + + // Notify the debugger and go to sleep if a breakpoint was hit, + // or if the thread is unable to continue for any reason. + if (Has(hr, breakpoint) || Has(hr, no_execute)) { + RewindBreakpointInstruction(); + if (system.DebuggerEnabled()) { + system.GetDebugger().NotifyThreadStopped(current_thread); + } + current_thread->RequestSuspend(Kernel::SuspendType::Debug); + break; } - entry.offset = entry.original_address - base; - entry.address = SEGMENT_BASE + entry.offset; - - if (entry.module.empty()) - entry.module = "unknown"; - - const auto symbol_set = symbols.find(entry.module); - if (symbol_set != symbols.end()) { - const auto symbol = GetSymbolName(symbol_set->second, entry.offset); - if (symbol.has_value()) { - // TODO(DarkLordZach): Add demangling of symbol names. - entry.name = *symbol; + // Notify the debugger and go to sleep if a watchpoint was hit. + if (Has(hr, watchpoint)) { + if (system.DebuggerEnabled()) { + system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint()); } + current_thread->RequestSuspend(SuspendType::Debug); + break; + } + + // Handle syscalls and scheduling (this may change the current thread/core) + if (Has(hr, svc_call)) { + Kernel::Svc::Call(system, GetSvcNumber()); + break; + } + if (Has(hr, break_loop) || !uses_wall_clock) { + break; } } +} - return out; +void ARM_Interface::LoadWatchpointArray(const WatchpointArray& wp) { + watchpoints = ℘ } -void ARM_Interface::LogBacktrace() const { - const VAddr sp = GetReg(13); - const VAddr pc = GetPC(); - LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc); - LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address", - "Offset", "Symbol"); - LOG_ERROR(Core_ARM, ""); +const Kernel::DebugWatchpoint* ARM_Interface::MatchingWatchpoint( + VAddr addr, u64 size, Kernel::DebugWatchpointType access_type) const { + if (!watchpoints) { + return nullptr; + } - const auto backtrace = GetBacktrace(); - for (const auto& entry : backtrace) { - LOG_ERROR(Core_ARM, "{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address, - entry.original_address, entry.offset, entry.name); + const VAddr start_address{addr}; + const VAddr end_address{addr + size}; + + for (size_t i = 0; i < Core::Hardware::NUM_WATCHPOINTS; i++) { + const auto& watch{(*watchpoints)[i]}; + + if (end_address <= watch.start_address) { + continue; + } + if (start_address >= watch.end_address) { + continue; + } + if ((access_type & watch.type) == Kernel::DebugWatchpointType::None) { + continue; + } + + return &watch; } + + return nullptr; } } // namespace Core diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index c60322442..7d62d030e 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -1,11 +1,14 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2014 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include <array> +#include <span> #include <vector> + +#include <dynarmic/interface/halt_reason.h> + #include "common/common_funcs.h" #include "common/common_types.h" #include "core/hardware_properties.h" @@ -16,13 +19,15 @@ struct PageTable; namespace Kernel { enum class VMAPermission : u8; -} +enum class DebugWatchpointType : u8; +struct DebugWatchpoint; +} // namespace Kernel namespace Core { class System; class CPUInterruptHandler; -using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>; +using WatchpointArray = std::array<Kernel::DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>; /// Generic ARMv8 CPU interface class ARM_Interface { @@ -30,10 +35,8 @@ public: YUZU_NON_COPYABLE(ARM_Interface); YUZU_NON_MOVEABLE(ARM_Interface); - explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers_, - bool uses_wall_clock_) - : system{system_}, interrupt_handlers{interrupt_handlers_}, uses_wall_clock{ - uses_wall_clock_} {} + explicit ARM_Interface(System& system_, bool uses_wall_clock_) + : system{system_}, uses_wall_clock{uses_wall_clock_} {} virtual ~ARM_Interface() = default; struct ThreadContext32 { @@ -64,10 +67,7 @@ public: static_assert(sizeof(ThreadContext64) == 0x320); /// Runs the CPU until an event happens - virtual void Run() = 0; - - /// Step CPU by one instruction - virtual void Step() = 0; + void Run(); /// Clear all instruction cache virtual void ClearInstructionCache() = 0; @@ -101,6 +101,12 @@ public: virtual u64 GetPC() const = 0; /** + * Get the current Stack Pointer + * @return Returns current SP + */ + virtual u64 GetSP() const = 0; + + /** * Get an ARM register * @param index Register index * @return Returns the value in the register @@ -164,12 +170,16 @@ public: virtual void SaveContext(ThreadContext64& ctx) = 0; virtual void LoadContext(const ThreadContext32& ctx) = 0; virtual void LoadContext(const ThreadContext64& ctx) = 0; + void LoadWatchpointArray(const WatchpointArray& wp); /// Clears the exclusive monitor's state. virtual void ClearExclusiveState() = 0; - /// Prepare core for thread reschedule (if needed to correctly handle state) - virtual void PrepareReschedule() = 0; + /// Signal an interrupt and ask the core to halt as soon as possible. + virtual void SignalInterrupt() = 0; + + /// Clear a previous interrupt. + virtual void ClearInterrupt() = 0; struct BacktraceEntry { std::string module; @@ -180,23 +190,36 @@ public: }; static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system, + const ThreadContext32& ctx); + static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system, const ThreadContext64& ctx); - std::vector<BacktraceEntry> GetBacktrace() const; + virtual std::vector<BacktraceEntry> GetBacktrace() const = 0; - /// fp (= r29) points to the last frame record. - /// Note that this is the frame record for the *previous* frame, not the current one. - /// Note we need to subtract 4 from our last read to get the proper address - /// Frame records are two words long: - /// fp+0 : pointer to previous frame record - /// fp+8 : value of lr for frame void LogBacktrace() const; + static constexpr Dynarmic::HaltReason step_thread = Dynarmic::HaltReason::Step; + static constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2; + static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3; + static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4; + static constexpr Dynarmic::HaltReason watchpoint = Dynarmic::HaltReason::MemoryAbort; + static constexpr Dynarmic::HaltReason no_execute = Dynarmic::HaltReason::UserDefined6; + protected: /// System context that this ARM interface is running under. System& system; - CPUInterrupts& interrupt_handlers; + const WatchpointArray* watchpoints; bool uses_wall_clock; + + static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out); + const Kernel::DebugWatchpoint* MatchingWatchpoint( + VAddr addr, u64 size, Kernel::DebugWatchpointType access_type) const; + + virtual Dynarmic::HaltReason RunJit() = 0; + virtual Dynarmic::HaltReason StepJit() = 0; + virtual u32 GetSvcNumber() const = 0; + virtual const Kernel::DebugWatchpoint* HaltedWatchpoint() const = 0; + virtual void RewindBreakpointInstruction() = 0; }; } // namespace Core diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp deleted file mode 100644 index 9c8898700..000000000 --- a/src/core/arm/cpu_interrupt_handler.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/thread.h" -#include "core/arm/cpu_interrupt_handler.h" - -namespace Core { - -CPUInterruptHandler::CPUInterruptHandler() : interrupt_event{std::make_unique<Common::Event>()} {} - -CPUInterruptHandler::~CPUInterruptHandler() = default; - -void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) { - if (is_interrupted_) { - interrupt_event->Set(); - } - is_interrupted = is_interrupted_; -} - -void CPUInterruptHandler::AwaitInterrupt() { - interrupt_event->Wait(); -} - -} // namespace Core diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h deleted file mode 100644 index c20c280f1..000000000 --- a/src/core/arm/cpu_interrupt_handler.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <atomic> -#include <memory> - -namespace Common { -class Event; -} - -namespace Core { - -class CPUInterruptHandler { -public: - CPUInterruptHandler(); - ~CPUInterruptHandler(); - - CPUInterruptHandler(const CPUInterruptHandler&) = delete; - CPUInterruptHandler& operator=(const CPUInterruptHandler&) = delete; - - CPUInterruptHandler(CPUInterruptHandler&&) = delete; - CPUInterruptHandler& operator=(CPUInterruptHandler&&) = delete; - - bool IsInterrupted() const { - return is_interrupted; - } - - void SetInterrupt(bool is_interrupted); - - void AwaitInterrupt(); - -private: - std::unique_ptr<Common::Event> interrupt_event; - std::atomic_bool is_interrupted{false}; -}; - -} // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index b0d89c539..d1e70f19d 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cinttypes> #include <memory> @@ -12,12 +11,13 @@ #include "common/logging/log.h" #include "common/page_table.h" #include "common/settings.h" -#include "core/arm/cpu_interrupt_handler.h" #include "core/arm/dynarmic/arm_dynarmic_32.h" #include "core/arm/dynarmic/arm_dynarmic_cp15.h" #include "core/arm/dynarmic/arm_exclusive_monitor.h" #include "core/core.h" #include "core/core_timing.h" +#include "core/debugger/debugger.h" +#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/svc.h" #include "core/memory.h" @@ -28,69 +28,106 @@ using namespace Common::Literals; class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { public: explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_) - : parent{parent_}, memory(parent.system.Memory()) {} + : parent{parent_}, + memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {} u8 MemoryRead8(u32 vaddr) override { + CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read); return memory.Read8(vaddr); } u16 MemoryRead16(u32 vaddr) override { + CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read); return memory.Read16(vaddr); } u32 MemoryRead32(u32 vaddr) override { + CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read); return memory.Read32(vaddr); } u64 MemoryRead64(u32 vaddr) override { + CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read); return memory.Read64(vaddr); } + std::optional<u32> MemoryReadCode(u32 vaddr) override { + if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) { + return std::nullopt; + } + return memory.Read32(vaddr); + } void MemoryWrite8(u32 vaddr, u8 value) override { - memory.Write8(vaddr, value); + if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { + memory.Write8(vaddr, value); + } } void MemoryWrite16(u32 vaddr, u16 value) override { - memory.Write16(vaddr, value); + if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) { + memory.Write16(vaddr, value); + } } void MemoryWrite32(u32 vaddr, u32 value) override { - memory.Write32(vaddr, value); + if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) { + memory.Write32(vaddr, value); + } } void MemoryWrite64(u32 vaddr, u64 value) override { - memory.Write64(vaddr, value); + if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) { + memory.Write64(vaddr, value); + } } bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override { - return memory.WriteExclusive8(vaddr, value, expected); + return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) && + memory.WriteExclusive8(vaddr, value, expected); } bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override { - return memory.WriteExclusive16(vaddr, value, expected); + return CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write) && + memory.WriteExclusive16(vaddr, value, expected); } bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override { - return memory.WriteExclusive32(vaddr, value, expected); + return CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write) && + memory.WriteExclusive32(vaddr, value, expected); } bool MemoryWriteExclusive64(u32 vaddr, u64 value, u64 expected) override { - return memory.WriteExclusive64(vaddr, value, expected); + return CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write) && + memory.WriteExclusive64(vaddr, value, expected); } void InterpreterFallback(u32 pc, std::size_t num_instructions) override { - UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc, - MemoryReadCode(pc)); + parent.LogBacktrace(); + LOG_ERROR(Core_ARM, + "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, + num_instructions, memory.Read32(pc)); } void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { - LOG_CRITICAL(Core_ARM, - "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", - exception, pc, MemoryReadCode(pc), parent.IsInThumbMode()); - UNIMPLEMENTED(); + switch (exception) { + case Dynarmic::A32::Exception::NoExecuteFault: + LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#08x}", pc); + ReturnException(pc, ARM_Interface::no_execute); + return; + default: + if (debugger_enabled) { + ReturnException(pc, ARM_Interface::breakpoint); + return; + } + + parent.LogBacktrace(); + LOG_CRITICAL(Core_ARM, + "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", + exception, pc, memory.Read32(pc), parent.IsInThumbMode()); + } } void CallSVC(u32 swi) override { - parent.svc_called = true; parent.svc_swi = swi; - parent.jit->HaltExecution(); + parent.jit.load()->HaltExecution(ARM_Interface::svc_call); } void AddTicks(u64 ticks) override { if (parent.uses_wall_clock) { return; } + // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a // rough approximation of the amount of executed ticks in the system, it may be thrown off // if not all cores are doing a similar amount of work. Instead of doing this, we should @@ -107,18 +144,45 @@ public: u64 GetTicksRemaining() override { if (parent.uses_wall_clock) { - if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { + if (!IsInterrupted()) { return minimum_run_cycles; } return 0U; } + return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); } + bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) { + if (!debugger_enabled) { + return true; + } + + const auto match{parent.MatchingWatchpoint(addr, size, type)}; + if (match) { + parent.halted_watchpoint = match; + parent.jit.load()->HaltExecution(ARM_Interface::watchpoint); + return false; + } + + return true; + } + + void ReturnException(u32 pc, Dynarmic::HaltReason hr) { + parent.SaveContext(parent.breakpoint_context); + parent.breakpoint_context.cpu_registers[15] = pc; + parent.jit.load()->HaltExecution(hr); + } + + bool IsInterrupted() { + return parent.system.Kernel().PhysicalCore(parent.core_index).IsInterrupted(); + } + ARM_Dynarmic_32& parent; Core::Memory::Memory& memory; std::size_t num_interpreted_instructions{}; - static constexpr u64 minimum_run_cycles = 1000U; + bool debugger_enabled{}; + static constexpr u64 minimum_run_cycles = 10000U; }; std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* page_table) const { @@ -126,17 +190,21 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* config.callbacks = cb.get(); config.coprocessors[15] = cp15; config.define_unpredictable_behaviour = true; - static constexpr std::size_t PAGE_BITS = 12; - static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - PAGE_BITS); + static constexpr std::size_t YUZU_PAGEBITS = 12; + static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - YUZU_PAGEBITS); if (page_table) { config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>( page_table->pointers.data()); + config.absolute_offset_page_table = true; + config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS; + config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; + config.only_detect_misalignment_via_page_table_on_page_boundary = true; + config.fastmem_pointer = page_table->fastmem_arena; + + config.fastmem_exclusive_access = config.fastmem_pointer != nullptr; + config.recompile_on_exclusive_fastmem_failure = true; } - config.absolute_offset_page_table = true; - config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS; - config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; - config.only_detect_misalignment_via_page_table_on_page_boundary = true; // Multi-process state config.processor_id = core_index; @@ -144,10 +212,21 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* // Timing config.wall_clock_cntpct = uses_wall_clock; + config.enable_cycle_counting = true; // Code cache size config.code_cache_size = 512_MiB; - config.far_code_offset = 400_MiB; + + // Allow memory fault handling to work + if (system.DebuggerEnabled()) { + config.check_halt_on_memory_access = true; + } + + // null_jit + if (!page_table) { + // Don't waste too much memory on null_jit + config.code_cache_size = 8_MiB; + } // Safe optimizations if (Settings::values.cpu_debug_mode) { @@ -177,80 +256,101 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* } if (!Settings::values.cpuopt_fastmem) { config.fastmem_pointer = nullptr; + config.fastmem_exclusive_access = false; } - } - - // Unsafe optimizations - if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { - config.unsafe_optimizations = true; - if (Settings::values.cpuopt_unsafe_unfuse_fma) { - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; + if (!Settings::values.cpuopt_fastmem_exclusives) { + config.fastmem_exclusive_access = false; } - if (Settings::values.cpuopt_unsafe_reduce_fp_error) { - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; + if (!Settings::values.cpuopt_recompile_exclusives) { + config.recompile_on_exclusive_fastmem_failure = false; } - if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) { - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; + } else { + // Unsafe optimizations + if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { + config.unsafe_optimizations = true; + if (Settings::values.cpuopt_unsafe_unfuse_fma) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; + } + if (Settings::values.cpuopt_unsafe_reduce_fp_error) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; + } + if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; + } + if (Settings::values.cpuopt_unsafe_inaccurate_nan) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; + } + if (Settings::values.cpuopt_unsafe_ignore_global_monitor) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; + } } - if (Settings::values.cpuopt_unsafe_inaccurate_nan) { + + // Curated optimizations + if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { + config.unsafe_optimizations = true; + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; } - } - // Curated optimizations - if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { - config.unsafe_optimizations = true; - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; + // Paranoia mode for debugging optimizations + if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) { + config.unsafe_optimizations = false; + config.optimizations = Dynarmic::no_optimizations; + } } return std::make_unique<Dynarmic::A32::Jit>(config); } -void ARM_Dynarmic_32::Run() { - while (true) { - jit->Run(); - if (!svc_called) { - break; - } - svc_called = false; - Kernel::Svc::Call(system, svc_swi); - if (shutdown) { - break; - } - } +Dynarmic::HaltReason ARM_Dynarmic_32::RunJit() { + return jit.load()->Run(); +} + +Dynarmic::HaltReason ARM_Dynarmic_32::StepJit() { + return jit.load()->Step(); +} + +u32 ARM_Dynarmic_32::GetSvcNumber() const { + return svc_swi; +} + +const Kernel::DebugWatchpoint* ARM_Dynarmic_32::HaltedWatchpoint() const { + return halted_watchpoint; } -void ARM_Dynarmic_32::Step() { - jit->Step(); +void ARM_Dynarmic_32::RewindBreakpointInstruction() { + LoadContext(breakpoint_context); } -ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, - bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, - std::size_t core_index_) - : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_}, - cb(std::make_unique<DynarmicCallbacks32>(*this)), +ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, bool uses_wall_clock_, + ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_) + : ARM_Interface{system_, uses_wall_clock_}, cb(std::make_unique<DynarmicCallbacks32>(*this)), cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_}, exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, - jit(MakeJit(nullptr)) {} + null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {} ARM_Dynarmic_32::~ARM_Dynarmic_32() = default; void ARM_Dynarmic_32::SetPC(u64 pc) { - jit->Regs()[15] = static_cast<u32>(pc); + jit.load()->Regs()[15] = static_cast<u32>(pc); } u64 ARM_Dynarmic_32::GetPC() const { - return jit->Regs()[15]; + return jit.load()->Regs()[15]; +} + +u64 ARM_Dynarmic_32::GetSP() const { + return jit.load()->Regs()[13]; } u64 ARM_Dynarmic_32::GetReg(int index) const { - return jit->Regs()[index]; + return jit.load()->Regs()[index]; } void ARM_Dynarmic_32::SetReg(int index, u64 value) { - jit->Regs()[index] = static_cast<u32>(value); + jit.load()->Regs()[index] = static_cast<u32>(value); } u128 ARM_Dynarmic_32::GetVectorReg(int index) const { @@ -260,11 +360,11 @@ u128 ARM_Dynarmic_32::GetVectorReg(int index) const { void ARM_Dynarmic_32::SetVectorReg(int index, u128 value) {} u32 ARM_Dynarmic_32::GetPSTATE() const { - return jit->Cpsr(); + return jit.load()->Cpsr(); } void ARM_Dynarmic_32::SetPSTATE(u32 cpsr) { - jit->SetCpsr(cpsr); + jit.load()->SetCpsr(cpsr); } u64 ARM_Dynarmic_32::GetTlsAddress() const { @@ -285,7 +385,7 @@ void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) { void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { Dynarmic::A32::Context context; - jit->SaveContext(context); + jit.load()->SaveContext(context); ctx.cpu_registers = context.Regs(); ctx.extension_registers = context.ExtRegs(); ctx.cpsr = context.Cpsr(); @@ -298,24 +398,27 @@ void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) { context.ExtRegs() = ctx.extension_registers; context.SetCpsr(ctx.cpsr); context.SetFpscr(ctx.fpscr); - jit->LoadContext(context); + jit.load()->LoadContext(context); } -void ARM_Dynarmic_32::PrepareReschedule() { - jit->HaltExecution(); - shutdown = true; +void ARM_Dynarmic_32::SignalInterrupt() { + jit.load()->HaltExecution(break_loop); +} + +void ARM_Dynarmic_32::ClearInterrupt() { + jit.load()->ClearHalt(break_loop); } void ARM_Dynarmic_32::ClearInstructionCache() { - jit->ClearCache(); + jit.load()->ClearCache(); } void ARM_Dynarmic_32::InvalidateCacheRange(VAddr addr, std::size_t size) { - jit->InvalidateCacheRange(static_cast<u32>(addr), size); + jit.load()->InvalidateCacheRange(static_cast<u32>(addr), size); } void ARM_Dynarmic_32::ClearExclusiveState() { - jit->ClearExclusiveState(); + jit.load()->ClearExclusiveState(); } void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table, @@ -326,13 +429,49 @@ void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table, auto key = std::make_pair(&page_table, new_address_space_size_in_bits); auto iter = jit_cache.find(key); if (iter != jit_cache.end()) { - jit = iter->second; + jit.store(iter->second.get()); LoadContext(ctx); return; } - jit = MakeJit(&page_table); + std::shared_ptr new_jit = MakeJit(&page_table); + jit.store(new_jit.get()); LoadContext(ctx); - jit_cache.emplace(key, jit); + jit_cache.emplace(key, std::move(new_jit)); +} + +std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::System& system, + u64 fp, u64 lr, u64 pc) { + std::vector<BacktraceEntry> out; + auto& memory = system.Memory(); + + out.push_back({"", 0, pc, 0, ""}); + + // fp (= r11) points to the last frame record. + // Frame records are two words long: + // fp+0 : pointer to previous frame record + // fp+4 : value of lr for frame + while (true) { + out.push_back({"", 0, lr, 0, ""}); + if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) { + break; + } + lr = memory.Read32(fp + 4); + fp = memory.Read32(fp); + } + + SymbolicateBacktrace(system, out); + + return out; +} + +std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktraceFromContext( + System& system, const ThreadContext32& ctx) { + const auto& reg = ctx.cpu_registers; + return GetBacktrace(system, reg[11], reg[14], reg[15]); +} + +std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace() const { + return GetBacktrace(system, GetReg(11), GetReg(14), GetReg(15)); } } // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index 5d47b600d..d24ba2289 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -1,9 +1,9 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include <atomic> #include <memory> #include <unordered_map> @@ -28,20 +28,19 @@ class System; class ARM_Dynarmic_32 final : public ARM_Interface { public: - ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, bool uses_wall_clock_, - ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_); + ARM_Dynarmic_32(System& system_, bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, + std::size_t core_index_); ~ARM_Dynarmic_32() override; void SetPC(u64 pc) override; u64 GetPC() const override; + u64 GetSP() const override; u64 GetReg(int index) const override; void SetReg(int index, u64 value) override; u128 GetVectorReg(int index) const override; void SetVectorReg(int index, u128 value) override; u32 GetPSTATE() const override; void SetPSTATE(u32 pstate) override; - void Run() override; - void Step() override; VAddr GetTlsAddress() const override; void SetTlsAddress(VAddr address) override; void SetTPIDR_EL0(u64 value) override; @@ -56,7 +55,8 @@ public: void LoadContext(const ThreadContext32& ctx) override; void LoadContext(const ThreadContext64& ctx) override {} - void PrepareReschedule() override; + void SignalInterrupt() override; + void ClearInterrupt() override; void ClearExclusiveState() override; void ClearInstructionCache() override; @@ -64,9 +64,23 @@ public: void PageTableChanged(Common::PageTable& new_page_table, std::size_t new_address_space_size_in_bits) override; + static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system, + const ThreadContext32& ctx); + + std::vector<BacktraceEntry> GetBacktrace() const override; + +protected: + Dynarmic::HaltReason RunJit() override; + Dynarmic::HaltReason StepJit() override; + u32 GetSvcNumber() const override; + const Kernel::DebugWatchpoint* HaltedWatchpoint() const override; + void RewindBreakpointInstruction() override; + private: std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const; + static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr, u64 pc); + using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; using JitCacheType = std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A32::Jit>, Common::PairHash>; @@ -79,13 +93,18 @@ private: std::shared_ptr<DynarmicCP15> cp15; std::size_t core_index; DynarmicExclusiveMonitor& exclusive_monitor; - std::shared_ptr<Dynarmic::A32::Jit> jit; + + std::shared_ptr<Dynarmic::A32::Jit> null_jit; + + // A raw pointer here is fine; we never delete Jit instances. + std::atomic<Dynarmic::A32::Jit*> jit; // SVC callback u32 svc_swi{}; - bool svc_called{}; - bool shutdown{}; + // Watchpoint info + const Kernel::DebugWatchpoint* halted_watchpoint; + ThreadContext32 breakpoint_context; }; } // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 56836bd05..1d46f6d40 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cinttypes> #include <memory> @@ -11,11 +10,11 @@ #include "common/logging/log.h" #include "common/page_table.h" #include "common/settings.h" -#include "core/arm/cpu_interrupt_handler.h" #include "core/arm/dynarmic/arm_dynarmic_64.h" #include "core/arm/dynarmic/arm_exclusive_monitor.h" #include "core/core.h" #include "core/core_timing.h" +#include "core/debugger/debugger.h" #include "core/hardware_properties.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/svc.h" @@ -29,61 +28,89 @@ using namespace Common::Literals; class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { public: explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_) - : parent{parent_}, memory(parent.system.Memory()) {} + : parent{parent_}, + memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {} u8 MemoryRead8(u64 vaddr) override { + CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read); return memory.Read8(vaddr); } u16 MemoryRead16(u64 vaddr) override { + CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read); return memory.Read16(vaddr); } u32 MemoryRead32(u64 vaddr) override { + CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read); return memory.Read32(vaddr); } u64 MemoryRead64(u64 vaddr) override { + CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read); return memory.Read64(vaddr); } Vector MemoryRead128(u64 vaddr) override { + CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read); return {memory.Read64(vaddr), memory.Read64(vaddr + 8)}; } + std::optional<u32> MemoryReadCode(u64 vaddr) override { + if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) { + return std::nullopt; + } + return memory.Read32(vaddr); + } void MemoryWrite8(u64 vaddr, u8 value) override { - memory.Write8(vaddr, value); + if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { + memory.Write8(vaddr, value); + } } void MemoryWrite16(u64 vaddr, u16 value) override { - memory.Write16(vaddr, value); + if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) { + memory.Write16(vaddr, value); + } } void MemoryWrite32(u64 vaddr, u32 value) override { - memory.Write32(vaddr, value); + if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) { + memory.Write32(vaddr, value); + } } void MemoryWrite64(u64 vaddr, u64 value) override { - memory.Write64(vaddr, value); + if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) { + memory.Write64(vaddr, value); + } } void MemoryWrite128(u64 vaddr, Vector value) override { - memory.Write64(vaddr, value[0]); - memory.Write64(vaddr + 8, value[1]); + if (CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Write)) { + memory.Write64(vaddr, value[0]); + memory.Write64(vaddr + 8, value[1]); + } } bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override { - return memory.WriteExclusive8(vaddr, value, expected); + return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) && + memory.WriteExclusive8(vaddr, value, expected); } bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override { - return memory.WriteExclusive16(vaddr, value, expected); + return CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write) && + memory.WriteExclusive16(vaddr, value, expected); } bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, std::uint32_t expected) override { - return memory.WriteExclusive32(vaddr, value, expected); + return CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write) && + memory.WriteExclusive32(vaddr, value, expected); } bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, std::uint64_t expected) override { - return memory.WriteExclusive64(vaddr, value, expected); + return CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write) && + memory.WriteExclusive64(vaddr, value, expected); } bool MemoryWriteExclusive128(u64 vaddr, Vector value, Vector expected) override { - return memory.WriteExclusive128(vaddr, value, expected); + return CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Write) && + memory.WriteExclusive128(vaddr, value, expected); } void InterpreterFallback(u64 pc, std::size_t num_instructions) override { + parent.LogBacktrace(); LOG_ERROR(Core_ARM, "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, - num_instructions, MemoryReadCode(pc)); + num_instructions, memory.Read32(pc)); } void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op, @@ -93,17 +120,19 @@ public: static constexpr u64 ICACHE_LINE_SIZE = 64; const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1); - parent.InvalidateCacheRange(cache_line_start, ICACHE_LINE_SIZE); + parent.system.InvalidateCpuInstructionCacheRange(cache_line_start, ICACHE_LINE_SIZE); break; } case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU: - parent.ClearInstructionCache(); + parent.system.InvalidateCpuInstructionCaches(); break; case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable: default: LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op); break; } + + parent.jit.load()->HaltExecution(Dynarmic::HaltReason::CacheInvalidation); } void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override { @@ -114,17 +143,25 @@ public: case Dynarmic::A64::Exception::SendEventLocal: case Dynarmic::A64::Exception::Yield: return; - case Dynarmic::A64::Exception::Breakpoint: + case Dynarmic::A64::Exception::NoExecuteFault: + LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#016x}", pc); + ReturnException(pc, ARM_Interface::no_execute); + return; default: - ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", - static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); + if (debugger_enabled) { + ReturnException(pc, ARM_Interface::breakpoint); + return; + } + + parent.LogBacktrace(); + LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", + static_cast<std::size_t>(exception), pc, memory.Read32(pc)); } } void CallSVC(u32 swi) override { - parent.svc_called = true; parent.svc_swi = swi; - parent.jit->HaltExecution(); + parent.jit.load()->HaltExecution(ARM_Interface::svc_call); } void AddTicks(u64 ticks) override { @@ -146,11 +183,12 @@ public: u64 GetTicksRemaining() override { if (parent.uses_wall_clock) { - if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { + if (!IsInterrupted()) { return minimum_run_cycles; } return 0U; } + return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); } @@ -158,11 +196,37 @@ public: return parent.system.CoreTiming().GetClockTicks(); } + bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) { + if (!debugger_enabled) { + return true; + } + + const auto match{parent.MatchingWatchpoint(addr, size, type)}; + if (match) { + parent.halted_watchpoint = match; + parent.jit.load()->HaltExecution(ARM_Interface::watchpoint); + return false; + } + + return true; + } + + void ReturnException(u64 pc, Dynarmic::HaltReason hr) { + parent.SaveContext(parent.breakpoint_context); + parent.breakpoint_context.pc = pc; + parent.jit.load()->HaltExecution(hr); + } + + bool IsInterrupted() { + return parent.system.Kernel().PhysicalCore(parent.core_index).IsInterrupted(); + } + ARM_Dynarmic_64& parent; Core::Memory::Memory& memory; u64 tpidrro_el0 = 0; u64 tpidr_el0 = 0; - static constexpr u64 minimum_run_cycles = 1000U; + bool debugger_enabled{}; + static constexpr u64 minimum_run_cycles = 10000U; }; std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* page_table, @@ -185,6 +249,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* config.fastmem_pointer = page_table->fastmem_arena; config.fastmem_address_space_bits = address_space_bits; config.silently_mirror_fastmem = false; + + config.fastmem_exclusive_access = config.fastmem_pointer != nullptr; + config.recompile_on_exclusive_fastmem_failure = true; } // Multi-process state @@ -203,10 +270,21 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* // Timing config.wall_clock_cntpct = uses_wall_clock; + config.enable_cycle_counting = true; // Code cache size config.code_cache_size = 512_MiB; - config.far_code_offset = 400_MiB; + + // Allow memory fault handling to work + if (system.DebuggerEnabled()) { + config.check_halt_on_memory_access = true; + } + + // null_jit + if (!page_table) { + // Don't waste too much memory on null_jit + config.code_cache_size = 8_MiB; + } // Safe optimizations if (Settings::values.cpu_debug_mode) { @@ -236,95 +314,117 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* } if (!Settings::values.cpuopt_fastmem) { config.fastmem_pointer = nullptr; + config.fastmem_exclusive_access = false; } - } - - // Unsafe optimizations - if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { - config.unsafe_optimizations = true; - if (Settings::values.cpuopt_unsafe_unfuse_fma) { - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; + if (!Settings::values.cpuopt_fastmem_exclusives) { + config.fastmem_exclusive_access = false; } - if (Settings::values.cpuopt_unsafe_reduce_fp_error) { - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; + if (!Settings::values.cpuopt_recompile_exclusives) { + config.recompile_on_exclusive_fastmem_failure = false; } - if (Settings::values.cpuopt_unsafe_inaccurate_nan) { - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; + } else { + // Unsafe optimizations + if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { + config.unsafe_optimizations = true; + if (Settings::values.cpuopt_unsafe_unfuse_fma) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; + } + if (Settings::values.cpuopt_unsafe_reduce_fp_error) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; + } + if (Settings::values.cpuopt_unsafe_inaccurate_nan) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; + } + if (Settings::values.cpuopt_unsafe_fastmem_check) { + config.fastmem_address_space_bits = 64; + } + if (Settings::values.cpuopt_unsafe_ignore_global_monitor) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; + } } - if (Settings::values.cpuopt_unsafe_fastmem_check) { + + // Curated optimizations + if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { + config.unsafe_optimizations = true; + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; config.fastmem_address_space_bits = 64; + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; } - } - // Curated optimizations - if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { - config.unsafe_optimizations = true; - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; - config.fastmem_address_space_bits = 64; + // Paranoia mode for debugging optimizations + if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) { + config.unsafe_optimizations = false; + config.optimizations = Dynarmic::no_optimizations; + } } return std::make_shared<Dynarmic::A64::Jit>(config); } -void ARM_Dynarmic_64::Run() { - while (true) { - jit->Run(); - if (!svc_called) { - break; - } - svc_called = false; - Kernel::Svc::Call(system, svc_swi); - if (shutdown) { - break; - } - } +Dynarmic::HaltReason ARM_Dynarmic_64::RunJit() { + return jit.load()->Run(); +} + +Dynarmic::HaltReason ARM_Dynarmic_64::StepJit() { + return jit.load()->Step(); +} + +u32 ARM_Dynarmic_64::GetSvcNumber() const { + return svc_swi; +} + +const Kernel::DebugWatchpoint* ARM_Dynarmic_64::HaltedWatchpoint() const { + return halted_watchpoint; } -void ARM_Dynarmic_64::Step() { - jit->Step(); +void ARM_Dynarmic_64::RewindBreakpointInstruction() { + LoadContext(breakpoint_context); } -ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, - bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, - std::size_t core_index_) - : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_}, +ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, bool uses_wall_clock_, + ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_) + : ARM_Interface{system_, uses_wall_clock_}, cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_}, exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, - jit(MakeJit(nullptr, 48)) {} + null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {} ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; void ARM_Dynarmic_64::SetPC(u64 pc) { - jit->SetPC(pc); + jit.load()->SetPC(pc); } u64 ARM_Dynarmic_64::GetPC() const { - return jit->GetPC(); + return jit.load()->GetPC(); +} + +u64 ARM_Dynarmic_64::GetSP() const { + return jit.load()->GetSP(); } u64 ARM_Dynarmic_64::GetReg(int index) const { - return jit->GetRegister(index); + return jit.load()->GetRegister(index); } void ARM_Dynarmic_64::SetReg(int index, u64 value) { - jit->SetRegister(index, value); + jit.load()->SetRegister(index, value); } u128 ARM_Dynarmic_64::GetVectorReg(int index) const { - return jit->GetVector(index); + return jit.load()->GetVector(index); } void ARM_Dynarmic_64::SetVectorReg(int index, u128 value) { - jit->SetVector(index, value); + jit.load()->SetVector(index, value); } u32 ARM_Dynarmic_64::GetPSTATE() const { - return jit->GetPstate(); + return jit.load()->GetPstate(); } void ARM_Dynarmic_64::SetPSTATE(u32 pstate) { - jit->SetPstate(pstate); + jit.load()->SetPstate(pstate); } u64 ARM_Dynarmic_64::GetTlsAddress() const { @@ -344,42 +444,47 @@ void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) { } void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { - ctx.cpu_registers = jit->GetRegisters(); - ctx.sp = jit->GetSP(); - ctx.pc = jit->GetPC(); - ctx.pstate = jit->GetPstate(); - ctx.vector_registers = jit->GetVectors(); - ctx.fpcr = jit->GetFpcr(); - ctx.fpsr = jit->GetFpsr(); + Dynarmic::A64::Jit* j = jit.load(); + ctx.cpu_registers = j->GetRegisters(); + ctx.sp = j->GetSP(); + ctx.pc = j->GetPC(); + ctx.pstate = j->GetPstate(); + ctx.vector_registers = j->GetVectors(); + ctx.fpcr = j->GetFpcr(); + ctx.fpsr = j->GetFpsr(); ctx.tpidr = cb->tpidr_el0; } void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) { - jit->SetRegisters(ctx.cpu_registers); - jit->SetSP(ctx.sp); - jit->SetPC(ctx.pc); - jit->SetPstate(ctx.pstate); - jit->SetVectors(ctx.vector_registers); - jit->SetFpcr(ctx.fpcr); - jit->SetFpsr(ctx.fpsr); + Dynarmic::A64::Jit* j = jit.load(); + j->SetRegisters(ctx.cpu_registers); + j->SetSP(ctx.sp); + j->SetPC(ctx.pc); + j->SetPstate(ctx.pstate); + j->SetVectors(ctx.vector_registers); + j->SetFpcr(ctx.fpcr); + j->SetFpsr(ctx.fpsr); SetTPIDR_EL0(ctx.tpidr); } -void ARM_Dynarmic_64::PrepareReschedule() { - jit->HaltExecution(); - shutdown = true; +void ARM_Dynarmic_64::SignalInterrupt() { + jit.load()->HaltExecution(break_loop); +} + +void ARM_Dynarmic_64::ClearInterrupt() { + jit.load()->ClearHalt(break_loop); } void ARM_Dynarmic_64::ClearInstructionCache() { - jit->ClearCache(); + jit.load()->ClearCache(); } void ARM_Dynarmic_64::InvalidateCacheRange(VAddr addr, std::size_t size) { - jit->InvalidateCacheRange(addr, size); + jit.load()->InvalidateCacheRange(addr, size); } void ARM_Dynarmic_64::ClearExclusiveState() { - jit->ClearExclusiveState(); + jit.load()->ClearExclusiveState(); } void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table, @@ -390,13 +495,49 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table, auto key = std::make_pair(&page_table, new_address_space_size_in_bits); auto iter = jit_cache.find(key); if (iter != jit_cache.end()) { - jit = iter->second; + jit.store(iter->second.get()); LoadContext(ctx); return; } - jit = MakeJit(&page_table, new_address_space_size_in_bits); + std::shared_ptr new_jit = MakeJit(&page_table, new_address_space_size_in_bits); + jit.store(new_jit.get()); LoadContext(ctx); - jit_cache.emplace(key, jit); + jit_cache.emplace(key, std::move(new_jit)); +} + +std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::System& system, + u64 fp, u64 lr, u64 pc) { + std::vector<BacktraceEntry> out; + auto& memory = system.Memory(); + + out.push_back({"", 0, pc, 0, ""}); + + // fp (= x29) points to the previous frame record. + // Frame records are two words long: + // fp+0 : pointer to previous frame record + // fp+8 : value of lr for frame + while (true) { + out.push_back({"", 0, lr, 0, ""}); + if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) { + break; + } + lr = memory.Read64(fp + 8); + fp = memory.Read64(fp); + } + + SymbolicateBacktrace(system, out); + + return out; +} + +std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktraceFromContext( + System& system, const ThreadContext64& ctx) { + const auto& reg = ctx.cpu_registers; + return GetBacktrace(system, reg[29], reg[30], ctx.pc); +} + +std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace() const { + return GetBacktrace(system, GetReg(29), GetReg(30), GetPC()); } } // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index 0c4e46c64..ed1a5eb96 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -1,9 +1,9 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include <atomic> #include <memory> #include <unordered_map> @@ -20,26 +20,24 @@ class Memory; namespace Core { class DynarmicCallbacks64; -class CPUInterruptHandler; class DynarmicExclusiveMonitor; class System; class ARM_Dynarmic_64 final : public ARM_Interface { public: - ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, bool uses_wall_clock_, - ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_); + ARM_Dynarmic_64(System& system_, bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, + std::size_t core_index_); ~ARM_Dynarmic_64() override; void SetPC(u64 pc) override; u64 GetPC() const override; + u64 GetSP() const override; u64 GetReg(int index) const override; void SetReg(int index, u64 value) override; u128 GetVectorReg(int index) const override; void SetVectorReg(int index, u128 value) override; u32 GetPSTATE() const override; void SetPSTATE(u32 pstate) override; - void Run() override; - void Step() override; VAddr GetTlsAddress() const override; void SetTlsAddress(VAddr address) override; void SetTPIDR_EL0(u64 value) override; @@ -50,7 +48,8 @@ public: void LoadContext(const ThreadContext32& ctx) override {} void LoadContext(const ThreadContext64& ctx) override; - void PrepareReschedule() override; + void SignalInterrupt() override; + void ClearInterrupt() override; void ClearExclusiveState() override; void ClearInstructionCache() override; @@ -58,10 +57,24 @@ public: void PageTableChanged(Common::PageTable& new_page_table, std::size_t new_address_space_size_in_bits) override; + static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system, + const ThreadContext64& ctx); + + std::vector<BacktraceEntry> GetBacktrace() const override; + +protected: + Dynarmic::HaltReason RunJit() override; + Dynarmic::HaltReason StepJit() override; + u32 GetSvcNumber() const override; + const Kernel::DebugWatchpoint* HaltedWatchpoint() const override; + void RewindBreakpointInstruction() override; + private: std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table, std::size_t address_space_bits) const; + static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr, u64 pc); + using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; using JitCacheType = std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A64::Jit>, Common::PairHash>; @@ -73,13 +86,17 @@ private: std::size_t core_index; DynarmicExclusiveMonitor& exclusive_monitor; - std::shared_ptr<Dynarmic::A64::Jit> jit; + std::shared_ptr<Dynarmic::A64::Jit> null_jit; + + // A raw pointer here is fine; we never delete Jit instances. + std::atomic<Dynarmic::A64::Jit*> jit; // SVC callback u32 svc_swi{}; - bool svc_called{}; - bool shutdown{}; + // Breakpoint info + const Kernel::DebugWatchpoint* halted_watchpoint; + ThreadContext64 breakpoint_context; }; } // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp index a043e6735..200efe4db 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp @@ -1,6 +1,5 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <fmt/format.h> #include "common/logging/log.h" @@ -9,6 +8,10 @@ #include "core/core.h" #include "core/core_timing.h" +#ifdef _MSC_VER +#include <intrin.h> +#endif + using Callback = Dynarmic::A32::Coprocessor::Callback; using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord; using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords; @@ -20,7 +23,7 @@ struct fmt::formatter<Dynarmic::A32::CoprocReg> { } template <typename FormatContext> auto format(const Dynarmic::A32::CoprocReg& reg, FormatContext& ctx) { - return format_to(ctx.out(), "cp{}", static_cast<size_t>(reg)); + return fmt::format_to(ctx.out(), "cp{}", static_cast<size_t>(reg)); } }; @@ -48,12 +51,31 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1 switch (opc2) { case 4: // CP15_DATA_SYNC_BARRIER - // This is a dummy write, we ignore the value written here. - return &dummy_value; + return Callback{ + [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t { +#ifdef _MSC_VER + _mm_mfence(); + _mm_lfence(); +#else + asm volatile("mfence\n\tlfence\n\t" : : : "memory"); +#endif + return 0; + }, + std::nullopt, + }; case 5: // CP15_DATA_MEMORY_BARRIER - // This is a dummy write, we ignore the value written here. - return &dummy_value; + return Callback{ + [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t { +#ifdef _MSC_VER + _mm_mfence(); +#else + asm volatile("mfence\n\t" : : : "memory"); +#endif + return 0; + }, + std::nullopt, + }; } } diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.h b/src/core/arm/dynarmic/arm_dynarmic_cp15.h index f271b2070..d90b3e568 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_cp15.h +++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.h @@ -1,6 +1,5 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -36,6 +35,8 @@ public: ARM_Dynarmic_32& parent; u32 uprw = 0; u32 uro = 0; + + friend class ARM_Dynarmic_32; }; } // namespace Core diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.cpp b/src/core/arm/dynarmic/arm_exclusive_monitor.cpp index 397d054a8..fa0c48b25 100644 --- a/src/core/arm/dynarmic/arm_exclusive_monitor.cpp +++ b/src/core/arm/dynarmic/arm_exclusive_monitor.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/arm/dynarmic/arm_exclusive_monitor.h" #include "core/memory.h" @@ -37,8 +36,8 @@ u128 DynarmicExclusiveMonitor::ExclusiveRead128(std::size_t core_index, VAddr ad }); } -void DynarmicExclusiveMonitor::ClearExclusive() { - monitor.Clear(); +void DynarmicExclusiveMonitor::ClearExclusive(std::size_t core_index) { + monitor.ClearProcessor(core_index); } bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) { diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.h b/src/core/arm/dynarmic/arm_exclusive_monitor.h index 265c4ecef..57e6dd0d0 100644 --- a/src/core/arm/dynarmic/arm_exclusive_monitor.h +++ b/src/core/arm/dynarmic/arm_exclusive_monitor.h @@ -1,11 +1,8 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include <unordered_map> - #include <dynarmic/interface/exclusive_monitor.h> #include "common/common_types.h" @@ -29,7 +26,7 @@ public: u32 ExclusiveRead32(std::size_t core_index, VAddr addr) override; u64 ExclusiveRead64(std::size_t core_index, VAddr addr) override; u128 ExclusiveRead128(std::size_t core_index, VAddr addr) override; - void ClearExclusive() override; + void ClearExclusive(std::size_t core_index) override; bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override; bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override; diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp index d8cba369d..2db0b035d 100644 --- a/src/core/arm/exclusive_monitor.cpp +++ b/src/core/arm/exclusive_monitor.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #ifdef ARCHITECTURE_x86_64 #include "core/arm/dynarmic/arm_exclusive_monitor.h" diff --git a/src/core/arm/exclusive_monitor.h b/src/core/arm/exclusive_monitor.h index 62f6e6023..15d3c96c1 100644 --- a/src/core/arm/exclusive_monitor.h +++ b/src/core/arm/exclusive_monitor.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -23,7 +22,7 @@ public: virtual u32 ExclusiveRead32(std::size_t core_index, VAddr addr) = 0; virtual u64 ExclusiveRead64(std::size_t core_index, VAddr addr) = 0; virtual u128 ExclusiveRead128(std::size_t core_index, VAddr addr) = 0; - virtual void ClearExclusive() = 0; + virtual void ClearExclusive(std::size_t core_index) = 0; virtual bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) = 0; virtual bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) = 0; diff --git a/src/core/arm/symbols.cpp b/src/core/arm/symbols.cpp new file mode 100644 index 000000000..0259c7ea2 --- /dev/null +++ b/src/core/arm/symbols.cpp @@ -0,0 +1,130 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/bit_field.h" +#include "common/common_funcs.h" +#include "common/elf.h" +#include "core/arm/symbols.h" +#include "core/core.h" +#include "core/memory.h" + +using namespace Common::ELF; + +namespace Core { +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 == ElfDtNull) { + break; + } + + if (tag == ElfDtStrtab) { + string_table_offset = value; + } else if (tag == ElfDtSymtab) { + symbol_table_offset = value; + } else if (tag == ElfDtSyment) { + 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.st_name; + 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.st_value, symbol.st_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, Elf64_Sym>(ReadBytes); + } else { + return GetSymbols<u32, Elf32_Sym>(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, Elf64_Sym>(ReadBytes); + } else { + return GetSymbols<u32, Elf32_Sym>(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..42631b09a --- /dev/null +++ b/src/core/arm/symbols.h @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#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/constants.cpp b/src/core/constants.cpp index dccb3e03c..4430173ef 100644 --- a/src/core/constants.cpp +++ b/src/core/constants.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/constants.h" diff --git a/src/core/constants.h b/src/core/constants.h index 81c5cb279..f916ce0b6 100644 --- a/src/core/constants.h +++ b/src/core/constants.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/core.cpp b/src/core/core.cpp index 3f9a7f44b..1deeee154 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -1,6 +1,5 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2014 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> #include <atomic> @@ -8,6 +7,7 @@ #include <memory> #include <utility> +#include "audio_core/audio_core.h" #include "common/fs/fs.h" #include "common/logging/log.h" #include "common/microprofile.h" @@ -17,6 +17,7 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/cpu_manager.h" +#include "core/debugger/debugger.h" #include "core/device_memory.h" #include "core/file_sys/bis_factory.h" #include "core/file_sys/mode.h" @@ -26,9 +27,10 @@ #include "core/file_sys/savedata_factory.h" #include "core/file_sys/vfs_concat.h" #include "core/file_sys/vfs_real.h" -#include "core/hardware_interrupt_manager.h" #include "core/hid/hid_core.h" +#include "core/hle/kernel/k_memory_manager.h" #include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/physical_core.h" @@ -36,18 +38,19 @@ #include "core/hle/service/apm/apm_controller.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/glue/glue_manager.h" -#include "core/hle/service/hid/hid.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" #include "core/hle/service/time/time_manager.h" +#include "core/internal_network/network.h" #include "core/loader/loader.h" #include "core/memory.h" #include "core/memory/cheat_engine.h" -#include "core/network/network.h" #include "core/perf_stats.h" #include "core/reporter.h" #include "core/telemetry_session.h" #include "core/tools/freezer.h" +#include "network/network.h" +#include "video_core/host1x/host1x.h" #include "video_core/renderer_base.h" #include "video_core/video_core.h" @@ -127,7 +130,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, struct System::Impl { explicit Impl(System& system) - : kernel{system}, fs_controller{system}, memory{system}, hid_core{}, + : kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{}, cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {} SystemResultStatus Run() { @@ -136,7 +139,6 @@ struct System::Impl { kernel.Suspend(false); core_timing.SyncPause(false); - cpu_manager.Pause(false); is_paused = false; return status; @@ -148,28 +150,34 @@ struct System::Impl { core_timing.SyncPause(true); kernel.Suspend(true); - cpu_manager.Pause(true); is_paused = true; return status; } - std::unique_lock<std::mutex> StallCPU() { + bool IsPaused() const { + std::unique_lock lk(suspend_guard); + return is_paused; + } + + std::unique_lock<std::mutex> StallProcesses() { std::unique_lock<std::mutex> lk(suspend_guard); kernel.Suspend(true); core_timing.SyncPause(true); - cpu_manager.Pause(true); return lk; } - void UnstallCPU() { + void UnstallProcesses() { if (!is_paused) { core_timing.SyncPause(false); kernel.Suspend(false); - cpu_manager.Pause(false); } } + void InitializeDebugger(System& system, u16 port) { + debugger = std::make_unique<Debugger>(system, port); + } + SystemResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { LOG_DEBUG(Core, "initialized OK"); @@ -207,14 +215,16 @@ struct System::Impl { telemetry_session = std::make_unique<Core::TelemetrySession>(); + host1x_core = std::make_unique<Tegra::Host1x::Host1x>(system); gpu_core = VideoCore::CreateGPU(emu_window, system); if (!gpu_core) { return SystemResultStatus::ErrorVideoCore; } + audio_core = std::make_unique<AudioCore::AudioCore>(system); + service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); services = std::make_unique<Service::Services>(service_manager, system); - interrupt_manager = std::make_unique<Hardware::InterruptManager>(system); // Initialize time manager, which must happen after kernel is created time_manager.Initialize(); @@ -252,9 +262,16 @@ struct System::Impl { } telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider); + + // Create a resource limit for the process. + const auto physical_memory_size = + kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application); + auto* resource_limit = Kernel::CreateResourceLimitForProcess(system, physical_memory_size); + + // Create the process. auto main_process = Kernel::KProcess::Create(system.Kernel()); ASSERT(Kernel::KProcess::Initialize(main_process, system, "main", - Kernel::KProcess::ProcessType::Userland) + Kernel::KProcess::ProcessType::Userland, resource_limit) .IsSuccess()); const auto [load_result, load_parameters] = app_loader->Load(*main_process, system); if (load_result != Loader::ResultStatus::Success) { @@ -281,7 +298,7 @@ struct System::Impl { if (Settings::values.gamecard_current_game) { fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, filepath)); } else if (!Settings::values.gamecard_path.GetValue().empty()) { - const auto gamecard_path = Settings::values.gamecard_path.GetValue(); + const auto& gamecard_path = Settings::values.gamecard_path.GetValue(); fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, gamecard_path)); } } @@ -294,11 +311,33 @@ struct System::Impl { GetAndResetPerfStats(); perf_stats->BeginSystemFrame(); + std::string name = "Unknown Game"; + if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) { + LOG_ERROR(Core, "Failed to read title for ROM (Error {})", load_result); + } + + std::string title_version; + const FileSys::PatchManager pm(program_id, system.GetFileSystemController(), + system.GetContentProvider()); + const auto metadata = pm.GetControlMetadata(); + if (metadata.first != nullptr) { + title_version = metadata.first->GetVersionString(); + } + if (auto room_member = room_network.GetRoomMember().lock()) { + Network::GameInfo game_info; + game_info.name = name; + game_info.id = program_id; + game_info.version = title_version; + room_member->SendGameInfo(game_info); + } + status = SystemResultStatus::Success; return status; } void Shutdown() { + SetShuttingDown(true); + // Log last frame performance stats if game was loded if (perf_stats) { const auto perf_results = GetAndResetPerfStats(); @@ -317,25 +356,45 @@ struct System::Impl { is_powered_on = false; exit_lock = false; - gpu_core->NotifyShutdown(); + if (gpu_core != nullptr) { + gpu_core->NotifyShutdown(); + } + kernel.ShutdownCores(); + cpu_manager.Shutdown(); + debugger.reset(); + kernel.CloseServices(); services.reset(); service_manager.reset(); cheat_engine.reset(); telemetry_session.reset(); - cpu_manager.Shutdown(); time_manager.Shutdown(); core_timing.Shutdown(); app_loader.reset(); + audio_core.reset(); gpu_core.reset(); + host1x_core.reset(); perf_stats.reset(); kernel.Shutdown(); memory.Reset(); applet_manager.ClearAll(); + if (auto room_member = room_network.GetRoomMember().lock()) { + Network::GameInfo game_info{}; + room_member->SendGameInfo(game_info); + } + LOG_DEBUG(Core, "Shutdown OK"); } + bool IsShuttingDown() const { + return is_shutting_down; + } + + void SetShuttingDown(bool shutting_down) { + is_shutting_down = shutting_down; + } + Loader::ResultStatus GetGameName(std::string& out) const { if (app_loader == nullptr) return Loader::ResultStatus::ErrorNotInitialized; @@ -378,8 +437,9 @@ struct System::Impl { return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs()); } - std::mutex suspend_guard; + mutable std::mutex suspend_guard; bool is_paused{}; + std::atomic<bool> is_shutting_down{}; Timing::CoreTiming core_timing; Kernel::KernelCore kernel; @@ -391,10 +451,13 @@ struct System::Impl { /// AppLoader used to load the current executing application std::unique_ptr<Loader::AppLoader> app_loader; std::unique_ptr<Tegra::GPU> gpu_core; - std::unique_ptr<Hardware::InterruptManager> interrupt_manager; + std::unique_ptr<Tegra::Host1x::Host1x> host1x_core; std::unique_ptr<Core::DeviceMemory> device_memory; + std::unique_ptr<AudioCore::AudioCore> audio_core; Core::Memory::Memory memory; Core::HID::HIDCore hid_core; + Network::RoomNetwork room_network; + CpuManager cpu_manager; std::atomic_bool is_powered_on{}; bool exit_lock = false; @@ -426,6 +489,9 @@ struct System::Impl { /// Network instance Network::NetworkInstance network_instance; + /// Debugger + std::unique_ptr<Core::Debugger> debugger; + SystemResultStatus status = SystemResultStatus::Success; std::string status_details = ""; @@ -462,8 +528,8 @@ SystemResultStatus System::Pause() { return impl->Pause(); } -SystemResultStatus System::SingleStep() { - return SystemResultStatus::Success; +bool System::IsPaused() const { + return impl->IsPaused(); } void System::InvalidateCpuInstructionCaches() { @@ -478,12 +544,30 @@ void System::Shutdown() { impl->Shutdown(); } -std::unique_lock<std::mutex> System::StallCPU() { - return impl->StallCPU(); +bool System::IsShuttingDown() const { + return impl->IsShuttingDown(); } -void System::UnstallCPU() { - impl->UnstallCPU(); +void System::SetShuttingDown(bool shutting_down) { + impl->SetShuttingDown(shutting_down); +} + +void System::DetachDebugger() { + if (impl->debugger) { + impl->debugger->NotifyShutdown(); + } +} + +std::unique_lock<std::mutex> System::StallProcesses() { + return impl->StallProcesses(); +} + +void System::UnstallProcesses() { + impl->UnstallProcesses(); +} + +void System::InitializeDebugger() { + impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue()); } SystemResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath, @@ -495,10 +579,6 @@ bool System::IsPoweredOn() const { return impl->is_powered_on.load(std::memory_order::relaxed); } -void System::PrepareReschedule() { - // Deprecated, does nothing, kept for backward compatibility. -} - void System::PrepareReschedule(const u32 core_index) { impl->kernel.PrepareReschedule(core_index); } @@ -589,12 +669,12 @@ const Tegra::GPU& System::GPU() const { return *impl->gpu_core; } -Core::Hardware::InterruptManager& System::InterruptManager() { - return *impl->interrupt_manager; +Tegra::Host1x::Host1x& System::Host1x() { + return *impl->host1x_core; } -const Core::Hardware::InterruptManager& System::InterruptManager() const { - return *impl->interrupt_manager; +const Tegra::Host1x::Host1x& System::Host1x() const { + return *impl->host1x_core; } VideoCore::RendererBase& System::Renderer() { @@ -621,6 +701,14 @@ const HID::HIDCore& System::HIDCore() const { return impl->hid_core; } +AudioCore::AudioCore& System::AudioCore() { + return *impl->audio_core; +} + +const AudioCore::AudioCore& System::AudioCore() const { + return *impl->audio_core; +} + Timing::CoreTiming& System::CoreTiming() { return impl->core_timing; } @@ -803,6 +891,26 @@ bool System::IsMulticore() const { return impl->is_multicore; } +bool System::DebuggerEnabled() const { + return Settings::values.use_gdbstub.GetValue(); +} + +Core::Debugger& System::GetDebugger() { + return *impl->debugger; +} + +const Core::Debugger& System::GetDebugger() const { + return *impl->debugger; +} + +Network::RoomNetwork& System::GetRoomNetwork() { + return impl->room_network; +} + +const Network::RoomNetwork& System::GetRoomNetwork() const { + return impl->room_network; +} + void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) { impl->execute_program_callback = std::move(callback); } diff --git a/src/core/core.h b/src/core/core.h index 52ff90359..7843cc8ad 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -1,6 +1,5 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2014 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -75,28 +74,36 @@ class TimeManager; namespace Tegra { class DebugContext; class GPU; +namespace Host1x { +class Host1x; +} // namespace Host1x } // namespace Tegra namespace VideoCore { class RendererBase; } // namespace VideoCore +namespace AudioCore { +class AudioCore; +} // namespace AudioCore + namespace Core::Timing { class CoreTiming; } -namespace Core::Hardware { -class InterruptManager; -} - namespace Core::HID { class HIDCore; } +namespace Network { +class RoomNetwork; +} + namespace Core { class ARM_Interface; class CpuManager; +class Debugger; class DeviceMemory; class ExclusiveMonitor; class SpeedLimiter; @@ -147,11 +154,8 @@ public: */ [[nodiscard]] SystemResultStatus Pause(); - /** - * Step the CPU one instruction - * @return Result status, indicating whether or not the operation succeeded. - */ - [[nodiscard]] SystemResultStatus SingleStep(); + /// Check if the core is currently paused. + [[nodiscard]] bool IsPaused() const; /** * Invalidate the CPU instruction caches @@ -165,8 +169,22 @@ public: /// Shutdown the emulated system. void Shutdown(); - std::unique_lock<std::mutex> StallCPU(); - void UnstallCPU(); + /// Check if the core is shutting down. + [[nodiscard]] bool IsShuttingDown() const; + + /// Set the shutting down state. + void SetShuttingDown(bool shutting_down); + + /// Forcibly detach the debugger if it is running. + void DetachDebugger(); + + std::unique_lock<std::mutex> StallProcesses(); + void UnstallProcesses(); + + /** + * Initialize the debugger. + */ + void InitializeDebugger(); /** * Load an executable application. @@ -194,9 +212,6 @@ public: [[nodiscard]] const Core::TelemetrySession& TelemetrySession() const; /// Prepare the core emulation for a reschedule - void PrepareReschedule(); - - /// Prepare the core emulation for a reschedule void PrepareReschedule(u32 core_index); /// Gets and resets core performance statistics @@ -244,12 +259,24 @@ public: /// Gets an immutable reference to the GPU interface. [[nodiscard]] const Tegra::GPU& GPU() const; + /// Gets a mutable reference to the Host1x interface + [[nodiscard]] Tegra::Host1x::Host1x& Host1x(); + + /// Gets an immutable reference to the Host1x interface. + [[nodiscard]] const Tegra::Host1x::Host1x& Host1x() const; + /// Gets a mutable reference to the renderer. [[nodiscard]] VideoCore::RendererBase& Renderer(); /// Gets an immutable reference to the renderer. [[nodiscard]] const VideoCore::RendererBase& Renderer() const; + /// Gets a mutable reference to the audio interface + [[nodiscard]] AudioCore::AudioCore& AudioCore(); + + /// Gets an immutable reference to the audio interface. + [[nodiscard]] const AudioCore::AudioCore& AudioCore() const; + /// Gets the global scheduler [[nodiscard]] Kernel::GlobalSchedulerContext& GlobalSchedulerContext(); @@ -274,12 +301,6 @@ public: /// Provides a constant reference to the core timing instance. [[nodiscard]] const Timing::CoreTiming& CoreTiming() const; - /// Provides a reference to the interrupt manager instance. - [[nodiscard]] Core::Hardware::InterruptManager& InterruptManager(); - - /// Provides a constant reference to the interrupt manager instance. - [[nodiscard]] const Core::Hardware::InterruptManager& InterruptManager() const; - /// Provides a reference to the kernel instance. [[nodiscard]] Kernel::KernelCore& Kernel(); @@ -357,6 +378,15 @@ public: [[nodiscard]] Service::Time::TimeManager& GetTimeManager(); [[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const; + [[nodiscard]] Core::Debugger& GetDebugger(); + [[nodiscard]] const Core::Debugger& GetDebugger() const; + + /// Gets a mutable reference to the Room Network. + [[nodiscard]] Network::RoomNetwork& GetRoomNetwork(); + + /// Gets an immutable reference to the Room Network. + [[nodiscard]] const Network::RoomNetwork& GetRoomNetwork() const; + void SetExitLock(bool locked); [[nodiscard]] bool GetExitLock() const; @@ -378,6 +408,9 @@ public: /// Tells if system is running on multicore. [[nodiscard]] bool IsMulticore() const; + /// Tells if the system debugger is enabled. + [[nodiscard]] bool DebuggerEnabled() const; + /// Type used for the frontend to designate a callback for System to re-launch the application /// using a specified program index. using ExecuteProgramCallback = std::function<void(std::size_t)>; diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index c2f0f609f..6c0fcb7b5 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <mutex> @@ -21,10 +20,11 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac } struct CoreTiming::Event { - u64 time; + s64 time; u64 fifo_order; std::uintptr_t user_data; std::weak_ptr<EventType> type; + s64 reschedule_time; // Sort by time, unless the times are the same, in which case sort by // the order added to the queue @@ -43,10 +43,10 @@ CoreTiming::CoreTiming() CoreTiming::~CoreTiming() = default; void CoreTiming::ThreadEntry(CoreTiming& instance) { - constexpr char name[] = "yuzu:HostTiming"; + constexpr char name[] = "HostTiming"; MicroProfileOnThreadCreate(name); Common::SetCurrentThreadName(name); - Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh); + Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical); instance.on_thread_init(); instance.ThreadLoop(); MicroProfileOnThreadExit(); @@ -57,7 +57,8 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) { event_fifo_id = 0; shutting_down = false; ticks = 0; - const auto empty_timed_callback = [](std::uintptr_t, std::chrono::nanoseconds) {}; + const auto empty_timed_callback = [](std::uintptr_t, u64, std::chrono::nanoseconds) + -> std::optional<std::chrono::nanoseconds> { return std::nullopt; }; ev_lost = CreateEvent("_lost_event", empty_timed_callback); if (is_multicore) { timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this)); @@ -80,12 +81,17 @@ void CoreTiming::Shutdown() { void CoreTiming::Pause(bool is_paused) { paused = is_paused; pause_event.Set(); + + if (!is_paused) { + pause_end_time = GetGlobalTimeNs().count(); + } } void CoreTiming::SyncPause(bool is_paused) { if (is_paused == paused && paused_set == paused) { return; } + Pause(is_paused); if (timer_thread) { if (!is_paused) { @@ -95,6 +101,10 @@ void CoreTiming::SyncPause(bool is_paused) { while (paused_set != is_paused) ; } + + if (!is_paused) { + pause_end_time = GetGlobalTimeNs().count(); + } } bool CoreTiming::IsRunning() const { @@ -107,15 +117,33 @@ bool CoreTiming::HasPendingEvents() const { void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future, const std::shared_ptr<EventType>& event_type, - std::uintptr_t user_data) { + std::uintptr_t user_data, bool absolute_time) { { std::scoped_lock scope{basic_lock}; - const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + ns_into_future).count()); + const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future}; + + event_queue.emplace_back( + Event{next_time.count(), event_fifo_id++, user_data, event_type, 0}); + std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + } - event_queue.emplace_back(Event{timeout, event_fifo_id++, user_data, event_type}); + event.Set(); +} + +void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time, + std::chrono::nanoseconds resched_time, + const std::shared_ptr<EventType>& event_type, + std::uintptr_t user_data, bool absolute_time) { + { + std::scoped_lock scope{basic_lock}; + const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time}; + + event_queue.emplace_back( + Event{next_time.count(), event_fifo_id++, user_data, event_type, resched_time.count()}); std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } + event.Set(); } @@ -194,20 +222,39 @@ std::optional<s64> CoreTiming::Advance() { Event evt = std::move(event_queue.front()); std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); event_queue.pop_back(); - basic_lock.unlock(); if (const auto event_type{evt.type.lock()}) { - event_type->callback( - evt.user_data, std::chrono::nanoseconds{static_cast<s64>(global_timer - evt.time)}); + basic_lock.unlock(); + + const auto new_schedule_time{event_type->callback( + evt.user_data, evt.time, + std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})}; + + basic_lock.lock(); + + if (evt.reschedule_time != 0) { + const auto next_schedule_time{new_schedule_time.has_value() + ? new_schedule_time.value().count() + : evt.reschedule_time}; + + // If this event was scheduled into a pause, its time now is going to be way behind. + // Re-set this event to continue from the end of the pause. + auto next_time{evt.time + next_schedule_time}; + if (evt.time < pause_end_time) { + next_time = pause_end_time + next_schedule_time; + } + + event_queue.emplace_back( + Event{next_time, event_fifo_id++, evt.user_data, evt.type, next_schedule_time}); + std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + } } - basic_lock.lock(); global_timer = GetGlobalTimeNs().count(); } if (!event_queue.empty()) { - const s64 next_time = event_queue.front().time - global_timer; - return next_time; + return event_queue.front().time; } else { return std::nullopt; } @@ -220,16 +267,35 @@ void CoreTiming::ThreadLoop() { paused_set = false; const auto next_time = Advance(); if (next_time) { - if (*next_time > 0) { - std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time); - event.WaitFor(next_time_ns); + // There are more events left in the queue, wait until the next event. + const auto wait_time = *next_time - GetGlobalTimeNs().count(); + if (wait_time > 0) { + // Assume a timer resolution of 1ms. + static constexpr s64 TimerResolutionNS = 1000000; + + // Sleep in discrete intervals of the timer resolution, and spin the rest. + const auto sleep_time = wait_time - (wait_time % TimerResolutionNS); + if (sleep_time > 0) { + event.WaitFor(std::chrono::nanoseconds(sleep_time)); + } + + while (!paused && !event.IsSet() && GetGlobalTimeNs().count() < *next_time) { + // Yield to reduce thread starvation. + std::this_thread::yield(); + } + + if (event.IsSet()) { + event.Reset(); + } } } else { + // Queue is empty, wait until another event is scheduled and signals us to continue. wait_set = true; event.Wait(); } wait_set = false; } + paused_set = true; clock->Pause(true); pause_event.Wait(); diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 888828fd0..3259397b2 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,21 +7,21 @@ #include <chrono> #include <functional> #include <memory> +#include <mutex> #include <optional> #include <string> #include <thread> #include <vector> #include "common/common_types.h" -#include "common/spin_lock.h" #include "common/thread.h" #include "common/wall_clock.h" namespace Core::Timing { /// A callback that may be scheduled for a particular core timing event. -using TimedCallback = - std::function<void(std::uintptr_t user_data, std::chrono::nanoseconds ns_late)>; +using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>( + std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)>; /// Contains the characteristics of a particular event. struct EventType { @@ -94,7 +93,15 @@ public: /// Schedules an event in core timing void ScheduleEvent(std::chrono::nanoseconds ns_into_future, - const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0); + const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0, + bool absolute_time = false); + + /// Schedules an event which will automatically re-schedule itself with the given time, until + /// unscheduled + void ScheduleLoopingEvent(std::chrono::nanoseconds start_time, + std::chrono::nanoseconds resched_time, + const std::shared_ptr<EventType>& event_type, + std::uintptr_t user_data = 0, bool absolute_time = false); void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data); @@ -137,7 +144,7 @@ private: std::unique_ptr<Common::WallClock> clock; - u64 global_timer = 0; + s64 global_timer = 0; // The queue is a min-heap using std::make_heap/push_heap/pop_heap. // We don't use std::priority_queue because we need to be able to serialize, unserialize and @@ -149,8 +156,8 @@ private: std::shared_ptr<EventType> ev_lost; Common::Event event{}; Common::Event pause_event{}; - Common::SpinLock basic_lock{}; - Common::SpinLock advance_lock{}; + std::mutex basic_lock; + std::mutex advance_lock; std::unique_ptr<std::thread> timer_thread; std::atomic<bool> paused{}; std::atomic<bool> paused_set{}; @@ -160,6 +167,7 @@ private: std::function<void()> on_thread_init{}; bool is_multicore{}; + s64 pause_end_time{}; /// Cycle timing u64 ticks{}; diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h index 14c36a485..fe5aaefc7 100644 --- a/src/core/core_timing_util.h +++ b/src/core/core_timing_util.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index cbcc54891..0dd4c2196 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/fiber.h" #include "common/microprofile.h" @@ -9,6 +8,7 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/cpu_manager.h" +#include "core/hle/kernel/k_interrupt_manager.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" @@ -22,75 +22,51 @@ CpuManager::~CpuManager() = default; void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core) { - cpu_manager.RunThread(stop_token, core); + cpu_manager.RunThread(core); } void CpuManager::Initialize() { - running_mode = true; - if (is_multicore) { - for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { - core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core); - } - } else { - core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0); + num_cores = is_multicore ? Core::Hardware::NUM_CPU_CORES : 1; + gpu_barrier = std::make_unique<Common::Barrier>(num_cores + 1); + + for (std::size_t core = 0; core < num_cores; core++) { + core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core); } } void CpuManager::Shutdown() { - running_mode = false; - Pause(false); -} - -std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { - return GuestThreadFunction; -} - -std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() { - return IdleThreadFunction; -} - -std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() { - return SuspendThreadFunction; -} - -void CpuManager::GuestThreadFunction(void* cpu_manager_) { - CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); - if (cpu_manager->is_multicore) { - cpu_manager->MultiCoreRunGuestThread(); - } else { - cpu_manager->SingleCoreRunGuestThread(); + for (std::size_t core = 0; core < num_cores; core++) { + if (core_data[core].host_thread.joinable()) { + core_data[core].host_thread.join(); + } } } -void CpuManager::GuestRewindFunction(void* cpu_manager_) { - CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); - if (cpu_manager->is_multicore) { - cpu_manager->MultiCoreRunGuestLoop(); +void CpuManager::GuestThreadFunction() { + if (is_multicore) { + MultiCoreRunGuestThread(); } else { - cpu_manager->SingleCoreRunGuestLoop(); + SingleCoreRunGuestThread(); } } -void CpuManager::IdleThreadFunction(void* cpu_manager_) { - CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); - if (cpu_manager->is_multicore) { - cpu_manager->MultiCoreRunIdleThread(); +void CpuManager::IdleThreadFunction() { + if (is_multicore) { + MultiCoreRunIdleThread(); } else { - cpu_manager->SingleCoreRunIdleThread(); + SingleCoreRunIdleThread(); } } -void CpuManager::SuspendThreadFunction(void* cpu_manager_) { - CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); - if (cpu_manager->is_multicore) { - cpu_manager->MultiCoreRunSuspendThread(); - } else { - cpu_manager->SingleCoreRunSuspendThread(); - } +void CpuManager::ShutdownThreadFunction() { + ShutdownThread(); } -void* CpuManager::GetStartFuncParamater() { - return static_cast<void*>(this); +void CpuManager::HandleInterrupt() { + auto& kernel = system.Kernel(); + auto core_index = kernel.CurrentPhysicalCoreIndex(); + + Kernel::KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_index)); } /////////////////////////////////////////////////////////////////////////////// @@ -98,90 +74,37 @@ void* CpuManager::GetStartFuncParamater() { /////////////////////////////////////////////////////////////////////////////// void CpuManager::MultiCoreRunGuestThread() { + // Similar to UserModeThreadStarter in HOS auto& kernel = system.Kernel(); kernel.CurrentScheduler()->OnThreadStart(); - auto* thread = kernel.CurrentScheduler()->GetCurrentThread(); - auto& host_context = thread->GetHostContext(); - host_context->SetRewindPoint(GuestRewindFunction, this); - MultiCoreRunGuestLoop(); -} - -void CpuManager::MultiCoreRunGuestLoop() { - auto& kernel = system.Kernel(); while (true) { auto* physical_core = &kernel.CurrentPhysicalCore(); - system.EnterDynarmicProfile(); while (!physical_core->IsInterrupted()) { physical_core->Run(); physical_core = &kernel.CurrentPhysicalCore(); } - system.ExitDynarmicProfile(); - { - Kernel::KScopedDisableDispatch dd(kernel); - physical_core->ArmInterface().ClearExclusiveState(); - } + + HandleInterrupt(); } } void CpuManager::MultiCoreRunIdleThread() { - auto& kernel = system.Kernel(); - while (true) { - Kernel::KScopedDisableDispatch dd(kernel); - kernel.CurrentPhysicalCore().Idle(); - } -} + // Not accurate to HOS. Remove this entire method when singlecore is removed. + // See notes in KScheduler::ScheduleImpl for more information about why this + // is inaccurate. -void CpuManager::MultiCoreRunSuspendThread() { auto& kernel = system.Kernel(); kernel.CurrentScheduler()->OnThreadStart(); - while (true) { - auto core = kernel.CurrentPhysicalCoreIndex(); - auto& scheduler = *kernel.CurrentScheduler(); - Kernel::KThread* current_thread = scheduler.GetCurrentThread(); - Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context); - ASSERT(scheduler.ContextSwitchPending()); - ASSERT(core == kernel.CurrentPhysicalCoreIndex()); - scheduler.RescheduleCurrentCore(); - } -} -void CpuManager::MultiCorePause(bool paused) { - if (!paused) { - bool all_not_barrier = false; - while (!all_not_barrier) { - all_not_barrier = true; - for (const auto& data : core_data) { - all_not_barrier &= !data.is_running.load() && data.initialized.load(); - } - } - for (auto& data : core_data) { - data.enter_barrier->Set(); - } - if (paused_state.load()) { - bool all_barrier = false; - while (!all_barrier) { - all_barrier = true; - for (const auto& data : core_data) { - all_barrier &= data.is_paused.load() && data.initialized.load(); - } - } - for (auto& data : core_data) { - data.exit_barrier->Set(); - } - } - } else { - /// Wait until all cores are paused. - bool all_barrier = false; - while (!all_barrier) { - all_barrier = true; - for (const auto& data : core_data) { - all_barrier &= data.is_paused.load() && data.initialized.load(); - } + while (true) { + auto& physical_core = kernel.CurrentPhysicalCore(); + if (!physical_core.IsInterrupted()) { + physical_core.Idle(); } - /// Don't release the barrier + + HandleInterrupt(); } - paused_state = paused; } /////////////////////////////////////////////////////////////////////////////// @@ -191,175 +114,110 @@ void CpuManager::MultiCorePause(bool paused) { void CpuManager::SingleCoreRunGuestThread() { auto& kernel = system.Kernel(); kernel.CurrentScheduler()->OnThreadStart(); - auto* thread = kernel.CurrentScheduler()->GetCurrentThread(); - auto& host_context = thread->GetHostContext(); - host_context->SetRewindPoint(GuestRewindFunction, this); - SingleCoreRunGuestLoop(); -} -void CpuManager::SingleCoreRunGuestLoop() { - auto& kernel = system.Kernel(); while (true) { auto* physical_core = &kernel.CurrentPhysicalCore(); - system.EnterDynarmicProfile(); if (!physical_core->IsInterrupted()) { physical_core->Run(); physical_core = &kernel.CurrentPhysicalCore(); } - system.ExitDynarmicProfile(); + kernel.SetIsPhantomModeForSingleCore(true); system.CoreTiming().Advance(); kernel.SetIsPhantomModeForSingleCore(false); - physical_core->ArmInterface().ClearExclusiveState(); + PreemptSingleCore(); - auto& scheduler = kernel.Scheduler(current_core); - scheduler.RescheduleCurrentCore(); + HandleInterrupt(); } } void CpuManager::SingleCoreRunIdleThread() { auto& kernel = system.Kernel(); + kernel.CurrentScheduler()->OnThreadStart(); + while (true) { - auto& physical_core = kernel.CurrentPhysicalCore(); PreemptSingleCore(false); system.CoreTiming().AddTicks(1000U); idle_count++; - auto& scheduler = physical_core.Scheduler(); - scheduler.RescheduleCurrentCore(); + HandleInterrupt(); } } -void CpuManager::SingleCoreRunSuspendThread() { +void CpuManager::PreemptSingleCore(bool from_running_environment) { auto& kernel = system.Kernel(); - kernel.CurrentScheduler()->OnThreadStart(); - while (true) { - auto core = kernel.GetCurrentHostThreadID(); - auto& scheduler = *kernel.CurrentScheduler(); - Kernel::KThread* current_thread = scheduler.GetCurrentThread(); - Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[0].host_context); - ASSERT(scheduler.ContextSwitchPending()); - ASSERT(core == kernel.GetCurrentHostThreadID()); - scheduler.RescheduleCurrentCore(); - } -} -void CpuManager::PreemptSingleCore(bool from_running_enviroment) { - { - auto& kernel = system.Kernel(); - auto& scheduler = kernel.Scheduler(current_core); - Kernel::KThread* current_thread = scheduler.GetCurrentThread(); - if (idle_count >= 4 || from_running_enviroment) { - if (!from_running_enviroment) { - system.CoreTiming().Idle(); - idle_count = 0; - } - kernel.SetIsPhantomModeForSingleCore(true); - system.CoreTiming().Advance(); - kernel.SetIsPhantomModeForSingleCore(false); + if (idle_count >= 4 || from_running_environment) { + if (!from_running_environment) { + system.CoreTiming().Idle(); + idle_count = 0; } - current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES); - system.CoreTiming().ResetTicks(); - scheduler.Unload(scheduler.GetCurrentThread()); - - auto& next_scheduler = kernel.Scheduler(current_core); - Common::Fiber::YieldTo(current_thread->GetHostContext(), *next_scheduler.ControlContext()); + kernel.SetIsPhantomModeForSingleCore(true); + system.CoreTiming().Advance(); + kernel.SetIsPhantomModeForSingleCore(false); } + current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES); + system.CoreTiming().ResetTicks(); + kernel.Scheduler(current_core).PreemptSingleCore(); - // May have changed scheduler - { - auto& scheduler = system.Kernel().Scheduler(current_core); - scheduler.Reload(scheduler.GetCurrentThread()); - if (!scheduler.IsIdle()) { - idle_count = 0; - } + // We've now been scheduled again, and we may have exchanged schedulers. + // Reload the scheduler in case it's different. + if (!kernel.Scheduler(current_core).IsIdle()) { + idle_count = 0; } } -void CpuManager::SingleCorePause(bool paused) { - if (!paused) { - bool all_not_barrier = false; - while (!all_not_barrier) { - all_not_barrier = !core_data[0].is_running.load() && core_data[0].initialized.load(); - } - core_data[0].enter_barrier->Set(); - if (paused_state.load()) { - bool all_barrier = false; - while (!all_barrier) { - all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load(); - } - core_data[0].exit_barrier->Set(); - } - } else { - /// Wait until all cores are paused. - bool all_barrier = false; - while (!all_barrier) { - all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load(); - } - /// Don't release the barrier - } - paused_state = paused; +void CpuManager::GuestActivate() { + // Similar to the HorizonKernelMain callback in HOS + auto& kernel = system.Kernel(); + auto* scheduler = kernel.CurrentScheduler(); + + scheduler->Activate(); + UNREACHABLE(); } -void CpuManager::Pause(bool paused) { - if (is_multicore) { - MultiCorePause(paused); - } else { - SingleCorePause(paused); - } +void CpuManager::ShutdownThread() { + auto& kernel = system.Kernel(); + auto* thread = kernel.GetCurrentEmuThread(); + auto core = is_multicore ? kernel.CurrentPhysicalCoreIndex() : 0; + + Common::Fiber::YieldTo(thread->GetHostContext(), *core_data[core].host_context); + UNREACHABLE(); } -void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) { +void CpuManager::RunThread(std::size_t core) { /// Initialization system.RegisterCoreThread(core); std::string name; if (is_multicore) { - name = "yuzu:CPUCore_" + std::to_string(core); + name = "CPUCore_" + std::to_string(core); } else { - name = "yuzu:CPUThread"; + name = "CPUThread"; } MicroProfileOnThreadCreate(name.c_str()); Common::SetCurrentThreadName(name.c_str()); Common::SetCurrentThreadPriority(Common::ThreadPriority::High); auto& data = core_data[core]; - data.enter_barrier = std::make_unique<Common::Event>(); - data.exit_barrier = std::make_unique<Common::Event>(); data.host_context = Common::Fiber::ThreadToFiber(); - data.is_running = false; - data.initialized = true; - const bool sc_sync = !is_async_gpu && !is_multicore; - bool sc_sync_first_use = sc_sync; // Cleanup SCOPE_EXIT({ data.host_context->Exit(); - data.enter_barrier.reset(); - data.exit_barrier.reset(); - data.initialized = false; MicroProfileOnThreadExit(); }); - /// Running - while (running_mode) { - data.is_running = false; - data.enter_barrier->Wait(); - if (sc_sync_first_use) { - system.GPU().ObtainContext(); - sc_sync_first_use = false; - } + // Running + gpu_barrier->Sync(); - // Emulation was stopped - if (stop_token.stop_requested()) { - return; - } - - auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); - data.is_running = true; - Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext()); - data.is_running = false; - data.is_paused = true; - data.exit_barrier->Wait(); - data.is_paused = false; + if (!is_async_gpu && !is_multicore) { + system.GPU().ObtainContext(); } + + auto& kernel = system.Kernel(); + auto& scheduler = *kernel.CurrentScheduler(); + auto* thread = scheduler.GetSchedulerCurrentThread(); + Kernel::SetCurrentThread(kernel, thread); + + Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext()); } } // namespace Core diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index 9d92d4af0..95ea3ef39 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -44,15 +43,25 @@ public: is_async_gpu = is_async; } + void OnGpuReady() { + gpu_barrier->Sync(); + } + void Initialize(); void Shutdown(); - void Pause(bool paused); - - static std::function<void(void*)> GetGuestThreadStartFunc(); - static std::function<void(void*)> GetIdleThreadStartFunc(); - static std::function<void(void*)> GetSuspendThreadStartFunc(); - void* GetStartFuncParamater(); + std::function<void()> GetGuestActivateFunc() { + return [this] { GuestActivate(); }; + } + std::function<void()> GetGuestThreadFunc() { + return [this] { GuestThreadFunction(); }; + } + std::function<void()> GetIdleThreadStartFunc() { + return [this] { IdleThreadFunction(); }; + } + std::function<void()> GetShutdownThreadStartFunc() { + return [this] { ShutdownThreadFunction(); }; + } void PreemptSingleCore(bool from_running_enviroment = true); @@ -61,46 +70,36 @@ public: } private: - static void GuestThreadFunction(void* cpu_manager); - static void GuestRewindFunction(void* cpu_manager); - static void IdleThreadFunction(void* cpu_manager); - static void SuspendThreadFunction(void* cpu_manager); + void GuestThreadFunction(); + void IdleThreadFunction(); + void ShutdownThreadFunction(); void MultiCoreRunGuestThread(); - void MultiCoreRunGuestLoop(); void MultiCoreRunIdleThread(); - void MultiCoreRunSuspendThread(); - void MultiCorePause(bool paused); void SingleCoreRunGuestThread(); - void SingleCoreRunGuestLoop(); void SingleCoreRunIdleThread(); - void SingleCoreRunSuspendThread(); - void SingleCorePause(bool paused); static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core); - void RunThread(std::stop_token stop_token, std::size_t core); + void GuestActivate(); + void HandleInterrupt(); + void ShutdownThread(); + void RunThread(std::size_t core); struct CoreData { std::shared_ptr<Common::Fiber> host_context; - std::unique_ptr<Common::Event> enter_barrier; - std::unique_ptr<Common::Event> exit_barrier; - std::atomic<bool> is_running; - std::atomic<bool> is_paused; - std::atomic<bool> initialized; std::jthread host_thread; }; - std::atomic<bool> running_mode{}; - std::atomic<bool> paused_state{}; - + std::unique_ptr<Common::Barrier> gpu_barrier{}; std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{}; bool is_async_gpu{}; bool is_multicore{}; std::atomic<std::size_t> current_core{}; std::size_t idle_count{}; + std::size_t num_cores{}; static constexpr std::size_t max_cycle_runs = 5; System& system; diff --git a/src/core/crypto/aes_util.cpp b/src/core/crypto/aes_util.cpp index 85a666de9..cd7e15a58 100644 --- a/src/core/crypto/aes_util.cpp +++ b/src/core/crypto/aes_util.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> #include <mbedtls/cipher.h> diff --git a/src/core/crypto/aes_util.h b/src/core/crypto/aes_util.h index 230451b8f..a67ba5352 100644 --- a/src/core/crypto/aes_util.h +++ b/src/core/crypto/aes_util.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/crypto/ctr_encryption_layer.cpp b/src/core/crypto/ctr_encryption_layer.cpp index 3a2af4f50..b48c3f041 100644 --- a/src/core/crypto/ctr_encryption_layer.cpp +++ b/src/core/crypto/ctr_encryption_layer.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <cstring> diff --git a/src/core/crypto/ctr_encryption_layer.h b/src/core/crypto/ctr_encryption_layer.h index f86f01b6f..77f08d776 100644 --- a/src/core/crypto/ctr_encryption_layer.h +++ b/src/core/crypto/ctr_encryption_layer.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/crypto/encryption_layer.cpp b/src/core/crypto/encryption_layer.cpp index 4c377d7d4..cd10551ec 100644 --- a/src/core/crypto/encryption_layer.cpp +++ b/src/core/crypto/encryption_layer.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/crypto/encryption_layer.h" diff --git a/src/core/crypto/encryption_layer.h b/src/core/crypto/encryption_layer.h index 53619cb38..d3082ba53 100644 --- a/src/core/crypto/encryption_layer.h +++ b/src/core/crypto/encryption_layer.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index 9244907b5..443323390 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <array> @@ -141,7 +140,6 @@ u64 GetSignatureTypeDataSize(SignatureType type) { return 0x3C; } UNREACHABLE(); - return 0; } u64 GetSignatureTypePaddingSize(SignatureType type) { @@ -156,7 +154,6 @@ u64 GetSignatureTypePaddingSize(SignatureType type) { return 0x40; } UNREACHABLE(); - return 0; } SignatureType Ticket::GetSignatureType() const { diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index ac1eb8962..dbf9ebfe4 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp index d18252a54..97f5c8cea 100644 --- a/src/core/crypto/partition_data_manager.cpp +++ b/src/core/crypto/partition_data_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later // NOTE TO FUTURE MAINTAINERS: // When a new version of switch cryptography is released, diff --git a/src/core/crypto/partition_data_manager.h b/src/core/crypto/partition_data_manager.h index 7a7b5d038..057a70683 100644 --- a/src/core/crypto/partition_data_manager.h +++ b/src/core/crypto/partition_data_manager.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/crypto/sha_util.cpp b/src/core/crypto/sha_util.cpp index 180008a85..7a2c04838 100644 --- a/src/core/crypto/sha_util.cpp +++ b/src/core/crypto/sha_util.cpp @@ -1,5 +1,4 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later namespace Crypto {} // namespace Crypto diff --git a/src/core/crypto/sha_util.h b/src/core/crypto/sha_util.h index fa3fa9d33..5c2c43dbd 100644 --- a/src/core/crypto/sha_util.h +++ b/src/core/crypto/sha_util.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/crypto/xts_encryption_layer.cpp b/src/core/crypto/xts_encryption_layer.cpp index c2b7ea309..b60303412 100644 --- a/src/core/crypto/xts_encryption_layer.cpp +++ b/src/core/crypto/xts_encryption_layer.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <cstring> diff --git a/src/core/crypto/xts_encryption_layer.h b/src/core/crypto/xts_encryption_layer.h index 5f8f00fe7..735e660cb 100644 --- a/src/core/crypto/xts_encryption_layer.h +++ b/src/core/crypto/xts_encryption_layer.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp new file mode 100644 index 000000000..339f971e6 --- /dev/null +++ b/src/core/debugger/debugger.cpp @@ -0,0 +1,316 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <algorithm> +#include <mutex> +#include <thread> + +#include <boost/asio.hpp> +#include <boost/process/async_pipe.hpp> + +#include "common/logging/log.h" +#include "common/thread.h" +#include "core/core.h" +#include "core/debugger/debugger.h" +#include "core/debugger/debugger_interface.h" +#include "core/debugger/gdbstub.h" +#include "core/hle/kernel/global_scheduler_context.h" +#include "core/hle/kernel/k_scheduler.h" + +template <typename Readable, typename Buffer, typename Callback> +static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) { + static_assert(std::is_trivial_v<Buffer>); + auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))}; + r.async_read_some( + boost_buffer, [&, c](const boost::system::error_code& error, size_t bytes_read) { + if (!error.failed()) { + const u8* buffer_start = reinterpret_cast<const u8*>(&buffer); + std::span<const u8> received_data{buffer_start, buffer_start + bytes_read}; + c(received_data); + } + + AsyncReceiveInto(r, buffer, c); + }); +} + +template <typename Readable, typename Buffer> +static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) { + static_assert(std::is_trivial_v<Buffer>); + auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))}; + size_t bytes_read = r.read_some(boost_buffer); + const u8* buffer_start = reinterpret_cast<const u8*>(&buffer); + std::span<const u8> received_data{buffer_start, buffer_start + bytes_read}; + return received_data; +} + +enum class SignalType { + Stopped, + Watchpoint, + ShuttingDown, +}; + +struct SignalInfo { + SignalType type; + Kernel::KThread* thread; + const Kernel::DebugWatchpoint* watchpoint; +}; + +namespace Core { + +class DebuggerImpl : public DebuggerBackend { +public: + explicit DebuggerImpl(Core::System& system_, u16 port) + : system{system_}, signal_pipe{io_context}, client_socket{io_context} { + frontend = std::make_unique<GDBStub>(*this, system); + InitializeServer(port); + } + + ~DebuggerImpl() override { + ShutdownServer(); + } + + bool SignalDebugger(SignalInfo signal_info) { + { + std::scoped_lock lk{connection_lock}; + + if (stopped) { + // Do not notify the debugger about another event. + // It should be ignored. + return false; + } + + // Set up the state. + stopped = true; + info = signal_info; + } + + // Write a single byte into the pipe to wake up the debug interface. + boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped))); + return true; + } + + std::span<const u8> ReadFromClient() override { + return ReceiveInto(client_socket, client_data); + } + + void WriteToClient(std::span<const u8> data) override { + boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes())); + } + + void SetActiveThread(Kernel::KThread* thread) override { + active_thread = thread; + } + + Kernel::KThread* GetActiveThread() override { + return active_thread; + } + +private: + void InitializeServer(u16 port) { + using boost::asio::ip::tcp; + + LOG_INFO(Debug_GDBStub, "Starting server on port {}...", port); + + // Run the connection thread. + connection_thread = std::jthread([&, port](std::stop_token stop_token) { + try { + // Initialize the listening socket and accept a new client. + tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port}; + tcp::acceptor acceptor{io_context, endpoint}; + + acceptor.async_accept(client_socket, [](const auto&) {}); + io_context.run_one(); + io_context.restart(); + + if (stop_token.stop_requested()) { + return; + } + + ThreadLoop(stop_token); + } catch (const std::exception& ex) { + LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what()); + } + }); + } + + void ShutdownServer() { + connection_thread.request_stop(); + io_context.stop(); + connection_thread.join(); + } + + void ThreadLoop(std::stop_token stop_token) { + Common::SetCurrentThreadName("Debugger"); + + // Set up the client signals for new data. + AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); }); + AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); }); + + // Set the active thread. + UpdateActiveThread(); + + // Set up the frontend. + frontend->Connected(); + + // Main event loop. + while (!stop_token.stop_requested() && io_context.run()) { + } + } + + void PipeData(std::span<const u8> data) { + switch (info.type) { + case SignalType::Stopped: + case SignalType::Watchpoint: + // Stop emulation. + PauseEmulation(); + + // Notify the client. + active_thread = info.thread; + UpdateActiveThread(); + + if (info.type == SignalType::Watchpoint) { + frontend->Watchpoint(active_thread, *info.watchpoint); + } else { + frontend->Stopped(active_thread); + } + + break; + case SignalType::ShuttingDown: + frontend->ShuttingDown(); + + // Wait for emulation to shut down gracefully now. + signal_pipe.close(); + client_socket.shutdown(boost::asio::socket_base::shutdown_both); + LOG_INFO(Debug_GDBStub, "Shut down server"); + + break; + } + } + + void ClientData(std::span<const u8> data) { + const auto actions{frontend->ClientData(data)}; + for (const auto action : actions) { + switch (action) { + case DebuggerAction::Interrupt: { + { + std::scoped_lock lk{connection_lock}; + stopped = true; + } + PauseEmulation(); + UpdateActiveThread(); + frontend->Stopped(active_thread); + break; + } + case DebuggerAction::Continue: + MarkResumed([&] { ResumeEmulation(); }); + break; + case DebuggerAction::StepThreadUnlocked: + MarkResumed([&] { + active_thread->SetStepState(Kernel::StepState::StepPending); + active_thread->Resume(Kernel::SuspendType::Debug); + ResumeEmulation(active_thread); + }); + break; + case DebuggerAction::StepThreadLocked: { + MarkResumed([&] { + active_thread->SetStepState(Kernel::StepState::StepPending); + active_thread->Resume(Kernel::SuspendType::Debug); + }); + break; + } + case DebuggerAction::ShutdownEmulation: { + // Spawn another thread that will exit after shutdown, + // to avoid a deadlock + Core::System* system_ref{&system}; + std::thread t([system_ref] { system_ref->Exit(); }); + t.detach(); + break; + } + } + } + } + + void PauseEmulation() { + Kernel::KScopedSchedulerLock sl{system.Kernel()}; + + // Put all threads to sleep on next scheduler round. + for (auto* thread : ThreadList()) { + thread->RequestSuspend(Kernel::SuspendType::Debug); + } + } + + void ResumeEmulation(Kernel::KThread* except = nullptr) { + // Wake up all threads. + for (auto* thread : ThreadList()) { + if (thread == except) { + continue; + } + + thread->SetStepState(Kernel::StepState::NotStepping); + thread->Resume(Kernel::SuspendType::Debug); + } + } + + template <typename Callback> + void MarkResumed(Callback&& cb) { + Kernel::KScopedSchedulerLock sl{system.Kernel()}; + std::scoped_lock cl{connection_lock}; + stopped = false; + cb(); + } + + void UpdateActiveThread() { + const auto& threads{ThreadList()}; + if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) { + active_thread = threads[0]; + } + } + + const std::vector<Kernel::KThread*>& ThreadList() { + return system.GlobalSchedulerContext().GetThreadList(); + } + +private: + System& system; + std::unique_ptr<DebuggerFrontend> frontend; + + std::jthread connection_thread; + std::mutex connection_lock; + boost::asio::io_context io_context; + boost::process::async_pipe signal_pipe; + boost::asio::ip::tcp::socket client_socket; + + SignalInfo info; + Kernel::KThread* active_thread; + bool pipe_data; + bool stopped; + + std::array<u8, 4096> client_data; +}; + +Debugger::Debugger(Core::System& system, u16 port) { + try { + impl = std::make_unique<DebuggerImpl>(system, port); + } catch (const std::exception& ex) { + LOG_CRITICAL(Debug_GDBStub, "Failed to initialize debugger: {}", ex.what()); + } +} + +Debugger::~Debugger() = default; + +bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) { + return impl && impl->SignalDebugger(SignalInfo{SignalType::Stopped, thread, nullptr}); +} + +bool Debugger::NotifyThreadWatchpoint(Kernel::KThread* thread, + const Kernel::DebugWatchpoint& watch) { + return impl && impl->SignalDebugger(SignalInfo{SignalType::Watchpoint, thread, &watch}); +} + +void Debugger::NotifyShutdown() { + if (impl) { + impl->SignalDebugger(SignalInfo{SignalType::ShuttingDown, nullptr, nullptr}); + } +} + +} // namespace Core diff --git a/src/core/debugger/debugger.h b/src/core/debugger/debugger.h new file mode 100644 index 000000000..b2f503376 --- /dev/null +++ b/src/core/debugger/debugger.h @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> + +#include "common/common_types.h" + +namespace Kernel { +class KThread; +struct DebugWatchpoint; +} // namespace Kernel + +namespace Core { +class System; + +class DebuggerImpl; + +class Debugger { +public: + /** + * Blocks and waits for a connection on localhost, port `server_port`. + * Does not create the debugger if the port is already in use. + */ + explicit Debugger(Core::System& system, u16 server_port); + ~Debugger(); + + /** + * Notify the debugger that the given thread is stopped + * (due to a breakpoint, or due to stopping after a successful step). + * + * The debugger will asynchronously halt emulation after the notification has + * occurred. If another thread attempts to notify before emulation has stopped, + * it is ignored and this method will return false. Otherwise it will return true. + */ + bool NotifyThreadStopped(Kernel::KThread* thread); + + /** + * Notify the debugger that a shutdown is being performed now and disconnect. + */ + void NotifyShutdown(); + + /* + * Notify the debugger that the given thread has stopped due to hitting a watchpoint. + */ + bool NotifyThreadWatchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch); + +private: + std::unique_ptr<DebuggerImpl> impl; +}; +} // namespace Core diff --git a/src/core/debugger/debugger_interface.h b/src/core/debugger/debugger_interface.h new file mode 100644 index 000000000..5b31edc43 --- /dev/null +++ b/src/core/debugger/debugger_interface.h @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <functional> +#include <span> +#include <vector> + +#include "common/common_types.h" + +namespace Kernel { +class KThread; +struct DebugWatchpoint; +} // namespace Kernel + +namespace Core { + +enum class DebuggerAction { + Interrupt, ///< Stop emulation as soon as possible. + Continue, ///< Resume emulation. + StepThreadLocked, ///< Step the currently-active thread without resuming others. + StepThreadUnlocked, ///< Step the currently-active thread and resume others. + ShutdownEmulation, ///< Shut down the emulator. +}; + +class DebuggerBackend { +public: + virtual ~DebuggerBackend() = default; + + /** + * Can be invoked from a callback to synchronously wait for more data. + * Will return as soon as least one byte is received. Reads up to 4096 bytes. + */ + virtual std::span<const u8> ReadFromClient() = 0; + + /** + * Can be invoked from a callback to write data to the client. + * Returns immediately after the data is sent. + */ + virtual void WriteToClient(std::span<const u8> data) = 0; + + /** + * Gets the currently active thread when the debugger is stopped. + */ + virtual Kernel::KThread* GetActiveThread() = 0; + + /** + * Sets the currently active thread when the debugger is stopped. + */ + virtual void SetActiveThread(Kernel::KThread* thread) = 0; +}; + +class DebuggerFrontend { +public: + explicit DebuggerFrontend(DebuggerBackend& backend_) : backend{backend_} {} + + virtual ~DebuggerFrontend() = default; + + /** + * Called after the client has successfully connected to the port. + */ + virtual void Connected() = 0; + + /** + * Called when emulation has stopped. + */ + virtual void Stopped(Kernel::KThread* thread) = 0; + + /** + * Called when emulation is shutting down. + */ + virtual void ShuttingDown() = 0; + + /* + * Called when emulation has stopped on a watchpoint. + */ + virtual void Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) = 0; + + /** + * Called when new data is asynchronously received on the client socket. + * A list of actions to perform is returned. + */ + [[nodiscard]] virtual std::vector<DebuggerAction> ClientData(std::span<const u8> data) = 0; + +protected: + DebuggerBackend& backend; +}; + +} // namespace Core diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp new file mode 100644 index 000000000..884229c77 --- /dev/null +++ b/src/core/debugger/gdbstub.cpp @@ -0,0 +1,718 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <atomic> +#include <numeric> +#include <optional> +#include <thread> + +#include <boost/algorithm/string.hpp> + +#include "common/hex_util.h" +#include "common/logging/log.h" +#include "common/scope_exit.h" +#include "core/arm/arm_interface.h" +#include "core/core.h" +#include "core/debugger/gdbstub.h" +#include "core/debugger/gdbstub_arch.h" +#include "core/hle/kernel/k_page_table.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_thread.h" +#include "core/loader/loader.h" +#include "core/memory.h" + +namespace Core { + +constexpr char GDB_STUB_START = '$'; +constexpr char GDB_STUB_END = '#'; +constexpr char GDB_STUB_ACK = '+'; +constexpr char GDB_STUB_NACK = '-'; +constexpr char GDB_STUB_INT3 = 0x03; +constexpr int GDB_STUB_SIGTRAP = 5; + +constexpr char GDB_STUB_REPLY_ERR[] = "E01"; +constexpr char GDB_STUB_REPLY_OK[] = "OK"; +constexpr char GDB_STUB_REPLY_EMPTY[] = ""; + +static u8 CalculateChecksum(std::string_view data) { + return std::accumulate(data.begin(), data.end(), u8{0}, + [](u8 lhs, u8 rhs) { return static_cast<u8>(lhs + rhs); }); +} + +static std::string EscapeGDB(std::string_view data) { + std::string escaped; + escaped.reserve(data.size()); + + for (char c : data) { + switch (c) { + case '#': + escaped += "}\x03"; + break; + case '$': + escaped += "}\x04"; + break; + case '*': + escaped += "}\x0a"; + break; + case '}': + escaped += "}\x5d"; + break; + default: + escaped += c; + break; + } + } + + return escaped; +} + +static std::string EscapeXML(std::string_view data) { + std::string escaped; + escaped.reserve(data.size()); + + for (char c : data) { + switch (c) { + case '&': + escaped += "&"; + break; + case '"': + escaped += """; + break; + case '<': + escaped += "<"; + break; + case '>': + escaped += ">"; + break; + default: + escaped += c; + break; + } + } + + return escaped; +} + +GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_) + : DebuggerFrontend(backend_), system{system_} { + if (system.CurrentProcess()->Is64BitProcess()) { + arch = std::make_unique<GDBStubA64>(); + } else { + arch = std::make_unique<GDBStubA32>(); + } +} + +GDBStub::~GDBStub() = default; + +void GDBStub::Connected() {} + +void GDBStub::ShuttingDown() {} + +void GDBStub::Stopped(Kernel::KThread* thread) { + SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP)); +} + +void GDBStub::Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) { + const auto status{arch->ThreadStatus(thread, GDB_STUB_SIGTRAP)}; + + switch (watch.type) { + case Kernel::DebugWatchpointType::Read: + SendReply(fmt::format("{}rwatch:{:x};", status, watch.start_address)); + break; + case Kernel::DebugWatchpointType::Write: + SendReply(fmt::format("{}watch:{:x};", status, watch.start_address)); + break; + case Kernel::DebugWatchpointType::ReadOrWrite: + default: + SendReply(fmt::format("{}awatch:{:x};", status, watch.start_address)); + break; + } +} + +std::vector<DebuggerAction> GDBStub::ClientData(std::span<const u8> data) { + std::vector<DebuggerAction> actions; + current_command.insert(current_command.end(), data.begin(), data.end()); + + while (current_command.size() != 0) { + ProcessData(actions); + } + + return actions; +} + +void GDBStub::ProcessData(std::vector<DebuggerAction>& actions) { + const char c{current_command[0]}; + + // Acknowledgement + if (c == GDB_STUB_ACK || c == GDB_STUB_NACK) { + current_command.erase(current_command.begin()); + return; + } + + // Interrupt + if (c == GDB_STUB_INT3) { + LOG_INFO(Debug_GDBStub, "Received interrupt"); + current_command.erase(current_command.begin()); + actions.push_back(DebuggerAction::Interrupt); + SendStatus(GDB_STUB_ACK); + return; + } + + // Otherwise, require the data to be the start of a command + if (c != GDB_STUB_START) { + LOG_ERROR(Debug_GDBStub, "Invalid command buffer contents: {}", current_command.data()); + current_command.clear(); + SendStatus(GDB_STUB_NACK); + return; + } + + // Continue reading until command is complete + while (CommandEnd() == current_command.end()) { + const auto new_data{backend.ReadFromClient()}; + current_command.insert(current_command.end(), new_data.begin(), new_data.end()); + } + + // Execute and respond to GDB + const auto command{DetachCommand()}; + + if (command) { + SendStatus(GDB_STUB_ACK); + ExecuteCommand(*command, actions); + } else { + SendStatus(GDB_STUB_NACK); + } +} + +void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions) { + LOG_TRACE(Debug_GDBStub, "Executing command: {}", packet); + + if (packet.length() == 0) { + SendReply(GDB_STUB_REPLY_ERR); + return; + } + + if (packet.starts_with("vCont")) { + HandleVCont(packet.substr(5), actions); + return; + } + + std::string_view command{packet.substr(1, packet.size())}; + + switch (packet[0]) { + case 'H': { + Kernel::KThread* thread{nullptr}; + s64 thread_id{strtoll(command.data() + 1, nullptr, 16)}; + if (thread_id >= 1) { + thread = GetThreadByID(thread_id); + } else { + thread = backend.GetActiveThread(); + } + + if (thread) { + SendReply(GDB_STUB_REPLY_OK); + backend.SetActiveThread(thread); + } else { + SendReply(GDB_STUB_REPLY_ERR); + } + break; + } + case 'T': { + s64 thread_id{strtoll(command.data(), nullptr, 16)}; + if (GetThreadByID(thread_id)) { + SendReply(GDB_STUB_REPLY_OK); + } else { + SendReply(GDB_STUB_REPLY_ERR); + } + break; + } + case 'Q': + case 'q': + HandleQuery(command); + break; + case '?': + SendReply(arch->ThreadStatus(backend.GetActiveThread(), GDB_STUB_SIGTRAP)); + break; + case 'k': + LOG_INFO(Debug_GDBStub, "Shutting down emulation"); + actions.push_back(DebuggerAction::ShutdownEmulation); + break; + case 'g': + SendReply(arch->ReadRegisters(backend.GetActiveThread())); + break; + case 'G': + arch->WriteRegisters(backend.GetActiveThread(), command); + SendReply(GDB_STUB_REPLY_OK); + break; + case 'p': { + const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))}; + SendReply(arch->RegRead(backend.GetActiveThread(), reg)); + break; + } + case 'P': { + const auto sep{std::find(command.begin(), command.end(), '=') - command.begin() + 1}; + const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))}; + arch->RegWrite(backend.GetActiveThread(), reg, std::string_view(command).substr(sep)); + SendReply(GDB_STUB_REPLY_OK); + break; + } + case 'm': { + const auto sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; + const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))}; + const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))}; + + if (system.Memory().IsValidVirtualAddressRange(addr, size)) { + std::vector<u8> mem(size); + system.Memory().ReadBlock(addr, mem.data(), size); + + SendReply(Common::HexToString(mem)); + } else { + SendReply(GDB_STUB_REPLY_ERR); + } + break; + } + case 'M': { + const auto size_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; + const auto mem_sep{std::find(command.begin(), command.end(), ':') - command.begin() + 1}; + + const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))}; + const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))}; + + const auto mem_substr{std::string_view(command).substr(mem_sep)}; + const auto mem{Common::HexStringToVector(mem_substr, false)}; + + if (system.Memory().IsValidVirtualAddressRange(addr, size)) { + system.Memory().WriteBlock(addr, mem.data(), size); + system.InvalidateCpuInstructionCacheRange(addr, size); + SendReply(GDB_STUB_REPLY_OK); + } else { + SendReply(GDB_STUB_REPLY_ERR); + } + break; + } + case 's': + actions.push_back(DebuggerAction::StepThreadLocked); + break; + case 'C': + case 'c': + actions.push_back(DebuggerAction::Continue); + break; + case 'Z': + HandleBreakpointInsert(command); + break; + case 'z': + HandleBreakpointRemove(command); + break; + default: + SendReply(GDB_STUB_REPLY_EMPTY); + break; + } +} + +enum class BreakpointType { + Software = 0, + Hardware = 1, + WriteWatch = 2, + ReadWatch = 3, + AccessWatch = 4, +}; + +void GDBStub::HandleBreakpointInsert(std::string_view command) { + const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))}; + const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; + const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') - + command.begin() + 1}; + const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))}; + const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))}; + + if (!system.Memory().IsValidVirtualAddressRange(addr, size)) { + SendReply(GDB_STUB_REPLY_ERR); + return; + } + + bool success{}; + + switch (type) { + case BreakpointType::Software: + replaced_instructions[addr] = system.Memory().Read32(addr); + system.Memory().Write32(addr, arch->BreakpointInstruction()); + system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32)); + success = true; + break; + case BreakpointType::WriteWatch: + success = system.CurrentProcess()->InsertWatchpoint(system, addr, size, + Kernel::DebugWatchpointType::Write); + break; + case BreakpointType::ReadWatch: + success = system.CurrentProcess()->InsertWatchpoint(system, addr, size, + Kernel::DebugWatchpointType::Read); + break; + case BreakpointType::AccessWatch: + success = system.CurrentProcess()->InsertWatchpoint( + system, addr, size, Kernel::DebugWatchpointType::ReadOrWrite); + break; + case BreakpointType::Hardware: + default: + SendReply(GDB_STUB_REPLY_EMPTY); + return; + } + + if (success) { + SendReply(GDB_STUB_REPLY_OK); + } else { + SendReply(GDB_STUB_REPLY_ERR); + } +} + +void GDBStub::HandleBreakpointRemove(std::string_view command) { + const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))}; + const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; + const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') - + command.begin() + 1}; + const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))}; + const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))}; + + if (!system.Memory().IsValidVirtualAddressRange(addr, size)) { + SendReply(GDB_STUB_REPLY_ERR); + return; + } + + bool success{}; + + switch (type) { + case BreakpointType::Software: { + const auto orig_insn{replaced_instructions.find(addr)}; + if (orig_insn != replaced_instructions.end()) { + system.Memory().Write32(addr, orig_insn->second); + system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32)); + replaced_instructions.erase(addr); + success = true; + } + break; + } + case BreakpointType::WriteWatch: + success = system.CurrentProcess()->RemoveWatchpoint(system, addr, size, + Kernel::DebugWatchpointType::Write); + break; + case BreakpointType::ReadWatch: + success = system.CurrentProcess()->RemoveWatchpoint(system, addr, size, + Kernel::DebugWatchpointType::Read); + break; + case BreakpointType::AccessWatch: + success = system.CurrentProcess()->RemoveWatchpoint( + system, addr, size, Kernel::DebugWatchpointType::ReadOrWrite); + break; + case BreakpointType::Hardware: + default: + SendReply(GDB_STUB_REPLY_EMPTY); + return; + } + + if (success) { + SendReply(GDB_STUB_REPLY_OK); + } else { + SendReply(GDB_STUB_REPLY_ERR); + } +} + +// Structure offsets are from Atmosphere +// See osdbg_thread_local_region.os.horizon.hpp and osdbg_thread_type.os.horizon.hpp + +static std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory& memory, + const Kernel::KThread* thread) { + // Read thread type from TLS + const VAddr tls_thread_type{memory.Read32(thread->GetTLSAddress() + 0x1fc)}; + const VAddr argument_thread_type{thread->GetArgument()}; + + if (argument_thread_type && tls_thread_type != argument_thread_type) { + // Probably not created by nnsdk, no name available. + return std::nullopt; + } + + if (!tls_thread_type) { + return std::nullopt; + } + + const u16 version{memory.Read16(tls_thread_type + 0x26)}; + VAddr name_pointer{}; + if (version == 1) { + name_pointer = memory.Read32(tls_thread_type + 0xe4); + } else { + name_pointer = memory.Read32(tls_thread_type + 0xe8); + } + + if (!name_pointer) { + // No name provided. + return std::nullopt; + } + + return memory.ReadCString(name_pointer, 256); +} + +static std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory& memory, + const Kernel::KThread* thread) { + // Read thread type from TLS + const VAddr tls_thread_type{memory.Read64(thread->GetTLSAddress() + 0x1f8)}; + const VAddr argument_thread_type{thread->GetArgument()}; + + if (argument_thread_type && tls_thread_type != argument_thread_type) { + // Probably not created by nnsdk, no name available. + return std::nullopt; + } + + if (!tls_thread_type) { + return std::nullopt; + } + + const u16 version{memory.Read16(tls_thread_type + 0x46)}; + VAddr name_pointer{}; + if (version == 1) { + name_pointer = memory.Read64(tls_thread_type + 0x1a0); + } else { + name_pointer = memory.Read64(tls_thread_type + 0x1a8); + } + + if (!name_pointer) { + // No name provided. + return std::nullopt; + } + + return memory.ReadCString(name_pointer, 256); +} + +static std::optional<std::string> GetThreadName(Core::System& system, + const Kernel::KThread* thread) { + if (system.CurrentProcess()->Is64BitProcess()) { + return GetNameFromThreadType64(system.Memory(), thread); + } else { + return GetNameFromThreadType32(system.Memory(), thread); + } +} + +static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) { + switch (thread->GetWaitReasonForDebugging()) { + case Kernel::ThreadWaitReasonForDebugging::Sleep: + return "Sleep"; + case Kernel::ThreadWaitReasonForDebugging::IPC: + return "IPC"; + case Kernel::ThreadWaitReasonForDebugging::Synchronization: + return "Synchronization"; + case Kernel::ThreadWaitReasonForDebugging::ConditionVar: + return "ConditionVar"; + case Kernel::ThreadWaitReasonForDebugging::Arbitration: + return "Arbitration"; + case Kernel::ThreadWaitReasonForDebugging::Suspended: + return "Suspended"; + default: + return "Unknown"; + } +} + +static std::string GetThreadState(const Kernel::KThread* thread) { + switch (thread->GetState()) { + case Kernel::ThreadState::Initialized: + return "Initialized"; + case Kernel::ThreadState::Waiting: + return fmt::format("Waiting ({})", GetThreadWaitReason(thread)); + case Kernel::ThreadState::Runnable: + return "Runnable"; + case Kernel::ThreadState::Terminated: + return "Terminated"; + default: + return "Unknown"; + } +} + +static std::string PaginateBuffer(std::string_view buffer, std::string_view request) { + const auto amount{request.substr(request.find(',') + 1)}; + const auto offset_val{static_cast<u64>(strtoll(request.data(), nullptr, 16))}; + const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))}; + + if (offset_val + amount_val > buffer.size()) { + return fmt::format("l{}", buffer.substr(offset_val)); + } else { + return fmt::format("m{}", buffer.substr(offset_val, amount_val)); + } +} + +void GDBStub::HandleQuery(std::string_view command) { + if (command.starts_with("TStatus")) { + // no tracepoint support + SendReply("T0"); + } else if (command.starts_with("Supported")) { + SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;" + "vContSupported+;QStartNoAckMode+"); + } else if (command.starts_with("Xfer:features:read:target.xml:")) { + const auto target_xml{arch->GetTargetXML()}; + SendReply(PaginateBuffer(target_xml, command.substr(30))); + } else if (command.starts_with("Offsets")) { + Loader::AppLoader::Modules modules; + system.GetAppLoader().ReadNSOModules(modules); + + const auto main = std::find_if(modules.begin(), modules.end(), + [](const auto& key) { return key.second == "main"; }); + if (main != modules.end()) { + SendReply(fmt::format("TextSeg={:x}", main->first)); + } else { + SendReply(fmt::format("TextSeg={:x}", + system.CurrentProcess()->PageTable().GetCodeRegionStart())); + } + } else if (command.starts_with("Xfer:libraries:read::")) { + Loader::AppLoader::Modules modules; + system.GetAppLoader().ReadNSOModules(modules); + + std::string buffer; + buffer += R"(<?xml version="1.0"?>)"; + buffer += "<library-list>"; + for (const auto& [base, name] : modules) { + buffer += fmt::format(R"(<library name="{}"><segment address="{:#x}"/></library>)", + EscapeXML(name), base); + } + buffer += "</library-list>"; + + SendReply(PaginateBuffer(buffer, command.substr(21))); + } else if (command.starts_with("fThreadInfo")) { + // beginning of list + const auto& threads = system.GlobalSchedulerContext().GetThreadList(); + std::vector<std::string> thread_ids; + for (const auto& thread : threads) { + thread_ids.push_back(fmt::format("{:x}", thread->GetThreadID())); + } + SendReply(fmt::format("m{}", fmt::join(thread_ids, ","))); + } else if (command.starts_with("sThreadInfo")) { + // end of list + SendReply("l"); + } else if (command.starts_with("Xfer:threads:read::")) { + std::string buffer; + buffer += R"(<?xml version="1.0"?>)"; + buffer += "<threads>"; + + const auto& threads = system.GlobalSchedulerContext().GetThreadList(); + for (const auto* thread : threads) { + auto thread_name{GetThreadName(system, thread)}; + if (!thread_name) { + thread_name = fmt::format("Thread {:d}", thread->GetThreadID()); + } + + buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="{}">{}</thread>)", + thread->GetThreadID(), thread->GetActiveCore(), + EscapeXML(*thread_name), GetThreadState(thread)); + } + + buffer += "</threads>"; + + SendReply(PaginateBuffer(buffer, command.substr(19))); + } else if (command.starts_with("Attached")) { + SendReply("0"); + } else if (command.starts_with("StartNoAckMode")) { + no_ack = true; + SendReply(GDB_STUB_REPLY_OK); + } else { + SendReply(GDB_STUB_REPLY_EMPTY); + } +} + +void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions) { + if (command == "?") { + // Continuing and stepping are supported + // (signal is ignored, but required for GDB to use vCont) + SendReply("vCont;c;C;s;S"); + return; + } + + Kernel::KThread* stepped_thread{nullptr}; + bool lock_execution{true}; + + std::vector<std::string> entries; + boost::split(entries, command.substr(1), boost::is_any_of(";")); + for (const auto& thread_action : entries) { + std::vector<std::string> parts; + boost::split(parts, thread_action, boost::is_any_of(":")); + + if (parts.size() == 1 && (parts[0] == "c" || parts[0].starts_with("C"))) { + lock_execution = false; + } + if (parts.size() == 2 && (parts[0] == "s" || parts[0].starts_with("S"))) { + stepped_thread = GetThreadByID(strtoll(parts[1].data(), nullptr, 16)); + } + } + + if (stepped_thread) { + backend.SetActiveThread(stepped_thread); + actions.push_back(lock_execution ? DebuggerAction::StepThreadLocked + : DebuggerAction::StepThreadUnlocked); + } else { + actions.push_back(DebuggerAction::Continue); + } +} + +Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { + const auto& threads{system.GlobalSchedulerContext().GetThreadList()}; + for (auto* thread : threads) { + if (thread->GetThreadID() == thread_id) { + return thread; + } + } + + return nullptr; +} + +std::vector<char>::const_iterator GDBStub::CommandEnd() const { + // Find the end marker + const auto end{std::find(current_command.begin(), current_command.end(), GDB_STUB_END)}; + + // Require the checksum to be present + return std::min(end + 2, current_command.end()); +} + +std::optional<std::string> GDBStub::DetachCommand() { + // Slice the string part from the beginning to the end marker + const auto end{CommandEnd()}; + + // Extract possible command data + std::string data(current_command.data(), end - current_command.begin() + 1); + + // Shift over the remaining contents + current_command.erase(current_command.begin(), end + 1); + + // Validate received command + if (data[0] != GDB_STUB_START) { + LOG_ERROR(Debug_GDBStub, "Invalid start data: {}", data[0]); + return std::nullopt; + } + + u8 calculated = CalculateChecksum(std::string_view(data).substr(1, data.size() - 4)); + u8 received = static_cast<u8>(strtoll(data.data() + data.size() - 2, nullptr, 16)); + + // Verify checksum + if (calculated != received) { + LOG_ERROR(Debug_GDBStub, "Checksum mismatch: calculated {:02x}, received {:02x}", + calculated, received); + return std::nullopt; + } + + return data.substr(1, data.size() - 4); +} + +void GDBStub::SendReply(std::string_view data) { + const auto escaped{EscapeGDB(data)}; + const auto output{fmt::format("{}{}{}{:02x}", GDB_STUB_START, escaped, GDB_STUB_END, + CalculateChecksum(escaped))}; + LOG_TRACE(Debug_GDBStub, "Writing reply: {}", output); + + // C++ string support is complete rubbish + const u8* output_begin = reinterpret_cast<const u8*>(output.data()); + const u8* output_end = output_begin + output.size(); + backend.WriteToClient(std::span<const u8>(output_begin, output_end)); +} + +void GDBStub::SendStatus(char status) { + if (no_ack) { + return; + } + + std::array<u8, 1> buf = {static_cast<u8>(status)}; + LOG_TRACE(Debug_GDBStub, "Writing status: {}", status); + backend.WriteToClient(buf); +} + +} // namespace Core diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h new file mode 100644 index 000000000..0b0f56e4b --- /dev/null +++ b/src/core/debugger/gdbstub.h @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <map> +#include <memory> +#include <optional> +#include <string_view> +#include <vector> + +#include "core/debugger/debugger_interface.h" +#include "core/debugger/gdbstub_arch.h" + +namespace Core { + +class System; + +class GDBStub : public DebuggerFrontend { +public: + explicit GDBStub(DebuggerBackend& backend, Core::System& system); + ~GDBStub() override; + + void Connected() override; + void Stopped(Kernel::KThread* thread) override; + void ShuttingDown() override; + void Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) override; + std::vector<DebuggerAction> ClientData(std::span<const u8> data) override; + +private: + void ProcessData(std::vector<DebuggerAction>& actions); + void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions); + void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions); + void HandleQuery(std::string_view command); + void HandleBreakpointInsert(std::string_view command); + void HandleBreakpointRemove(std::string_view command); + std::vector<char>::const_iterator CommandEnd() const; + std::optional<std::string> DetachCommand(); + Kernel::KThread* GetThreadByID(u64 thread_id); + + void SendReply(std::string_view data); + void SendStatus(char status); + +private: + Core::System& system; + std::unique_ptr<GDBStubArch> arch; + std::vector<char> current_command; + std::map<VAddr, u32> replaced_instructions; + bool no_ack{}; +}; + +} // namespace Core diff --git a/src/core/debugger/gdbstub_arch.cpp b/src/core/debugger/gdbstub_arch.cpp new file mode 100644 index 000000000..4bef09bd7 --- /dev/null +++ b/src/core/debugger/gdbstub_arch.cpp @@ -0,0 +1,487 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/hex_util.h" +#include "core/debugger/gdbstub_arch.h" +#include "core/hle/kernel/k_thread.h" + +namespace Core { + +template <typename T> +static T HexToValue(std::string_view hex) { + static_assert(std::is_trivially_copyable_v<T>); + T value{}; + const auto mem{Common::HexStringToVector(hex, false)}; + std::memcpy(&value, mem.data(), std::min(mem.size(), sizeof(T))); + return value; +} + +template <typename T> +static std::string ValueToHex(const T value) { + static_assert(std::is_trivially_copyable_v<T>); + std::array<u8, sizeof(T)> mem{}; + std::memcpy(mem.data(), &value, sizeof(T)); + return Common::HexToString(mem); +} + +template <typename T> +static T GetSIMDRegister(const std::array<u32, 64>& simd_regs, size_t offset) { + static_assert(std::is_trivially_copyable_v<T>); + T value{}; + std::memcpy(&value, reinterpret_cast<const u8*>(simd_regs.data()) + sizeof(T) * offset, + sizeof(T)); + return value; +} + +template <typename T> +static void PutSIMDRegister(std::array<u32, 64>& simd_regs, size_t offset, const T value) { + static_assert(std::is_trivially_copyable_v<T>); + std::memcpy(reinterpret_cast<u8*>(simd_regs.data()) + sizeof(T) * offset, &value, sizeof(T)); +} + +// For sample XML files see the GDB source /gdb/features +// This XML defines what the registers are for this specific ARM device +std::string GDBStubA64::GetTargetXML() const { + constexpr const char* target_xml = + R"(<?xml version="1.0"?> +<!DOCTYPE target SYSTEM "gdb-target.dtd"> +<target version="1.0"> + <architecture>aarch64</architecture> + <feature name="org.gnu.gdb.aarch64.core"> + <reg name="x0" bitsize="64"/> + <reg name="x1" bitsize="64"/> + <reg name="x2" bitsize="64"/> + <reg name="x3" bitsize="64"/> + <reg name="x4" bitsize="64"/> + <reg name="x5" bitsize="64"/> + <reg name="x6" bitsize="64"/> + <reg name="x7" bitsize="64"/> + <reg name="x8" bitsize="64"/> + <reg name="x9" bitsize="64"/> + <reg name="x10" bitsize="64"/> + <reg name="x11" bitsize="64"/> + <reg name="x12" bitsize="64"/> + <reg name="x13" bitsize="64"/> + <reg name="x14" bitsize="64"/> + <reg name="x15" bitsize="64"/> + <reg name="x16" bitsize="64"/> + <reg name="x17" bitsize="64"/> + <reg name="x18" bitsize="64"/> + <reg name="x19" bitsize="64"/> + <reg name="x20" bitsize="64"/> + <reg name="x21" bitsize="64"/> + <reg name="x22" bitsize="64"/> + <reg name="x23" bitsize="64"/> + <reg name="x24" bitsize="64"/> + <reg name="x25" bitsize="64"/> + <reg name="x26" bitsize="64"/> + <reg name="x27" bitsize="64"/> + <reg name="x28" bitsize="64"/> + <reg name="x29" bitsize="64"/> + <reg name="x30" bitsize="64"/> + <reg name="sp" bitsize="64" type="data_ptr"/> + <reg name="pc" bitsize="64" type="code_ptr"/> + <flags id="cpsr_flags" size="4"> + <field name="SP" start="0" end="0"/> + <field name="" start="1" end="1"/> + <field name="EL" start="2" end="3"/> + <field name="nRW" start="4" end="4"/> + <field name="" start="5" end="5"/> + <field name="F" start="6" end="6"/> + <field name="I" start="7" end="7"/> + <field name="A" start="8" end="8"/> + <field name="D" start="9" end="9"/> + <field name="IL" start="20" end="20"/> + <field name="SS" start="21" end="21"/> + <field name="V" start="28" end="28"/> + <field name="C" start="29" end="29"/> + <field name="Z" start="30" end="30"/> + <field name="N" start="31" end="31"/> + </flags> + <reg name="cpsr" bitsize="32" type="cpsr_flags"/> + </feature> + <feature name="org.gnu.gdb.aarch64.fpu"> + <vector id="v2d" type="ieee_double" count="2"/> + <vector id="v2u" type="uint64" count="2"/> + <vector id="v2i" type="int64" count="2"/> + <vector id="v4f" type="ieee_single" count="4"/> + <vector id="v4u" type="uint32" count="4"/> + <vector id="v4i" type="int32" count="4"/> + <vector id="v8u" type="uint16" count="8"/> + <vector id="v8i" type="int16" count="8"/> + <vector id="v16u" type="uint8" count="16"/> + <vector id="v16i" type="int8" count="16"/> + <vector id="v1u" type="uint128" count="1"/> + <vector id="v1i" type="int128" count="1"/> + <union id="vnd"> + <field name="f" type="v2d"/> + <field name="u" type="v2u"/> + <field name="s" type="v2i"/> + </union> + <union id="vns"> + <field name="f" type="v4f"/> + <field name="u" type="v4u"/> + <field name="s" type="v4i"/> + </union> + <union id="vnh"> + <field name="u" type="v8u"/> + <field name="s" type="v8i"/> + </union> + <union id="vnb"> + <field name="u" type="v16u"/> + <field name="s" type="v16i"/> + </union> + <union id="vnq"> + <field name="u" type="v1u"/> + <field name="s" type="v1i"/> + </union> + <union id="aarch64v"> + <field name="d" type="vnd"/> + <field name="s" type="vns"/> + <field name="h" type="vnh"/> + <field name="b" type="vnb"/> + <field name="q" type="vnq"/> + </union> + <reg name="v0" bitsize="128" type="aarch64v" regnum="34"/> + <reg name="v1" bitsize="128" type="aarch64v" /> + <reg name="v2" bitsize="128" type="aarch64v" /> + <reg name="v3" bitsize="128" type="aarch64v" /> + <reg name="v4" bitsize="128" type="aarch64v" /> + <reg name="v5" bitsize="128" type="aarch64v" /> + <reg name="v6" bitsize="128" type="aarch64v" /> + <reg name="v7" bitsize="128" type="aarch64v" /> + <reg name="v8" bitsize="128" type="aarch64v" /> + <reg name="v9" bitsize="128" type="aarch64v" /> + <reg name="v10" bitsize="128" type="aarch64v"/> + <reg name="v11" bitsize="128" type="aarch64v"/> + <reg name="v12" bitsize="128" type="aarch64v"/> + <reg name="v13" bitsize="128" type="aarch64v"/> + <reg name="v14" bitsize="128" type="aarch64v"/> + <reg name="v15" bitsize="128" type="aarch64v"/> + <reg name="v16" bitsize="128" type="aarch64v"/> + <reg name="v17" bitsize="128" type="aarch64v"/> + <reg name="v18" bitsize="128" type="aarch64v"/> + <reg name="v19" bitsize="128" type="aarch64v"/> + <reg name="v20" bitsize="128" type="aarch64v"/> + <reg name="v21" bitsize="128" type="aarch64v"/> + <reg name="v22" bitsize="128" type="aarch64v"/> + <reg name="v23" bitsize="128" type="aarch64v"/> + <reg name="v24" bitsize="128" type="aarch64v"/> + <reg name="v25" bitsize="128" type="aarch64v"/> + <reg name="v26" bitsize="128" type="aarch64v"/> + <reg name="v27" bitsize="128" type="aarch64v"/> + <reg name="v28" bitsize="128" type="aarch64v"/> + <reg name="v29" bitsize="128" type="aarch64v"/> + <reg name="v30" bitsize="128" type="aarch64v"/> + <reg name="v31" bitsize="128" type="aarch64v"/> + <reg name="fpsr" bitsize="32"/> + <reg name="fpcr" bitsize="32"/> + </feature> +</target>)"; + + return target_xml; +} + +std::string GDBStubA64::RegRead(const Kernel::KThread* thread, size_t id) const { + if (!thread) { + return ""; + } + + const auto& context{thread->GetContext64()}; + const auto& gprs{context.cpu_registers}; + const auto& fprs{context.vector_registers}; + + if (id < SP_REGISTER) { + return ValueToHex(gprs[id]); + } else if (id == SP_REGISTER) { + return ValueToHex(context.sp); + } else if (id == PC_REGISTER) { + return ValueToHex(context.pc); + } else if (id == PSTATE_REGISTER) { + return ValueToHex(context.pstate); + } else if (id >= Q0_REGISTER && id < FPSR_REGISTER) { + return ValueToHex(fprs[id - Q0_REGISTER]); + } else if (id == FPSR_REGISTER) { + return ValueToHex(context.fpsr); + } else if (id == FPCR_REGISTER) { + return ValueToHex(context.fpcr); + } else { + return ""; + } +} + +void GDBStubA64::RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const { + if (!thread) { + return; + } + + auto& context{thread->GetContext64()}; + + if (id < SP_REGISTER) { + context.cpu_registers[id] = HexToValue<u64>(value); + } else if (id == SP_REGISTER) { + context.sp = HexToValue<u64>(value); + } else if (id == PC_REGISTER) { + context.pc = HexToValue<u64>(value); + } else if (id == PSTATE_REGISTER) { + context.pstate = HexToValue<u32>(value); + } else if (id >= Q0_REGISTER && id < FPSR_REGISTER) { + context.vector_registers[id - Q0_REGISTER] = HexToValue<u128>(value); + } else if (id == FPSR_REGISTER) { + context.fpsr = HexToValue<u32>(value); + } else if (id == FPCR_REGISTER) { + context.fpcr = HexToValue<u32>(value); + } +} + +std::string GDBStubA64::ReadRegisters(const Kernel::KThread* thread) const { + std::string output; + + for (size_t reg = 0; reg <= FPCR_REGISTER; reg++) { + output += RegRead(thread, reg); + } + + return output; +} + +void GDBStubA64::WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const { + for (size_t i = 0, reg = 0; reg <= FPCR_REGISTER; reg++) { + if (reg <= SP_REGISTER || reg == PC_REGISTER) { + RegWrite(thread, reg, register_data.substr(i, 16)); + i += 16; + } else if (reg == PSTATE_REGISTER || reg == FPCR_REGISTER || reg == FPSR_REGISTER) { + RegWrite(thread, reg, register_data.substr(i, 8)); + i += 8; + } else if (reg >= Q0_REGISTER && reg < FPCR_REGISTER) { + RegWrite(thread, reg, register_data.substr(i, 32)); + i += 32; + } + } +} + +std::string GDBStubA64::ThreadStatus(const Kernel::KThread* thread, u8 signal) const { + return fmt::format("T{:02x}{:02x}:{};{:02x}:{};{:02x}:{};thread:{:x};", signal, PC_REGISTER, + RegRead(thread, PC_REGISTER), SP_REGISTER, RegRead(thread, SP_REGISTER), + LR_REGISTER, RegRead(thread, LR_REGISTER), thread->GetThreadID()); +} + +u32 GDBStubA64::BreakpointInstruction() const { + // A64: brk #0 + return 0xd4200000; +} + +std::string GDBStubA32::GetTargetXML() const { + constexpr const char* target_xml = + R"(<?xml version="1.0"?> +<!DOCTYPE target SYSTEM "gdb-target.dtd"> +<target version="1.0"> + <architecture>arm</architecture> + <feature name="org.gnu.gdb.arm.core"> + <reg name="r0" bitsize="32" type="uint32"/> + <reg name="r1" bitsize="32" type="uint32"/> + <reg name="r2" bitsize="32" type="uint32"/> + <reg name="r3" bitsize="32" type="uint32"/> + <reg name="r4" bitsize="32" type="uint32"/> + <reg name="r5" bitsize="32" type="uint32"/> + <reg name="r6" bitsize="32" type="uint32"/> + <reg name="r7" bitsize="32" type="uint32"/> + <reg name="r8" bitsize="32" type="uint32"/> + <reg name="r9" bitsize="32" type="uint32"/> + <reg name="r10" bitsize="32" type="uint32"/> + <reg name="r11" bitsize="32" type="uint32"/> + <reg name="r12" bitsize="32" type="uint32"/> + <reg name="sp" bitsize="32" type="data_ptr"/> + <reg name="lr" bitsize="32" type="code_ptr"/> + <reg name="pc" bitsize="32" type="code_ptr"/> + <!-- The CPSR is register 25, rather than register 16, because + the FPA registers historically were placed between the PC + and the CPSR in the "g" packet. --> + <reg name="cpsr" bitsize="32" regnum="25"/> + </feature> + <feature name="org.gnu.gdb.arm.vfp"> + <vector id="neon_uint8x8" type="uint8" count="8"/> + <vector id="neon_uint16x4" type="uint16" count="4"/> + <vector id="neon_uint32x2" type="uint32" count="2"/> + <vector id="neon_float32x2" type="ieee_single" count="2"/> + <union id="neon_d"> + <field name="u8" type="neon_uint8x8"/> + <field name="u16" type="neon_uint16x4"/> + <field name="u32" type="neon_uint32x2"/> + <field name="u64" type="uint64"/> + <field name="f32" type="neon_float32x2"/> + <field name="f64" type="ieee_double"/> + </union> + <vector id="neon_uint8x16" type="uint8" count="16"/> + <vector id="neon_uint16x8" type="uint16" count="8"/> + <vector id="neon_uint32x4" type="uint32" count="4"/> + <vector id="neon_uint64x2" type="uint64" count="2"/> + <vector id="neon_float32x4" type="ieee_single" count="4"/> + <vector id="neon_float64x2" type="ieee_double" count="2"/> + <union id="neon_q"> + <field name="u8" type="neon_uint8x16"/> + <field name="u16" type="neon_uint16x8"/> + <field name="u32" type="neon_uint32x4"/> + <field name="u64" type="neon_uint64x2"/> + <field name="f32" type="neon_float32x4"/> + <field name="f64" type="neon_float64x2"/> + </union> + <reg name="d0" bitsize="64" type="neon_d" regnum="32"/> + <reg name="d1" bitsize="64" type="neon_d"/> + <reg name="d2" bitsize="64" type="neon_d"/> + <reg name="d3" bitsize="64" type="neon_d"/> + <reg name="d4" bitsize="64" type="neon_d"/> + <reg name="d5" bitsize="64" type="neon_d"/> + <reg name="d6" bitsize="64" type="neon_d"/> + <reg name="d7" bitsize="64" type="neon_d"/> + <reg name="d8" bitsize="64" type="neon_d"/> + <reg name="d9" bitsize="64" type="neon_d"/> + <reg name="d10" bitsize="64" type="neon_d"/> + <reg name="d11" bitsize="64" type="neon_d"/> + <reg name="d12" bitsize="64" type="neon_d"/> + <reg name="d13" bitsize="64" type="neon_d"/> + <reg name="d14" bitsize="64" type="neon_d"/> + <reg name="d15" bitsize="64" type="neon_d"/> + <reg name="d16" bitsize="64" type="neon_d"/> + <reg name="d17" bitsize="64" type="neon_d"/> + <reg name="d18" bitsize="64" type="neon_d"/> + <reg name="d19" bitsize="64" type="neon_d"/> + <reg name="d20" bitsize="64" type="neon_d"/> + <reg name="d21" bitsize="64" type="neon_d"/> + <reg name="d22" bitsize="64" type="neon_d"/> + <reg name="d23" bitsize="64" type="neon_d"/> + <reg name="d24" bitsize="64" type="neon_d"/> + <reg name="d25" bitsize="64" type="neon_d"/> + <reg name="d26" bitsize="64" type="neon_d"/> + <reg name="d27" bitsize="64" type="neon_d"/> + <reg name="d28" bitsize="64" type="neon_d"/> + <reg name="d29" bitsize="64" type="neon_d"/> + <reg name="d30" bitsize="64" type="neon_d"/> + <reg name="d31" bitsize="64" type="neon_d"/> + + <reg name="q0" bitsize="128" type="neon_q" regnum="64"/> + <reg name="q1" bitsize="128" type="neon_q"/> + <reg name="q2" bitsize="128" type="neon_q"/> + <reg name="q3" bitsize="128" type="neon_q"/> + <reg name="q4" bitsize="128" type="neon_q"/> + <reg name="q5" bitsize="128" type="neon_q"/> + <reg name="q6" bitsize="128" type="neon_q"/> + <reg name="q7" bitsize="128" type="neon_q"/> + <reg name="q8" bitsize="128" type="neon_q"/> + <reg name="q9" bitsize="128" type="neon_q"/> + <reg name="q10" bitsize="128" type="neon_q"/> + <reg name="q10" bitsize="128" type="neon_q"/> + <reg name="q12" bitsize="128" type="neon_q"/> + <reg name="q13" bitsize="128" type="neon_q"/> + <reg name="q14" bitsize="128" type="neon_q"/> + <reg name="q15" bitsize="128" type="neon_q"/> + + <reg name="fpscr" bitsize="32" type="int" group="float" regnum="80"/> + </feature> +</target>)"; + + return target_xml; +} + +std::string GDBStubA32::RegRead(const Kernel::KThread* thread, size_t id) const { + if (!thread) { + return ""; + } + + const auto& context{thread->GetContext32()}; + const auto& gprs{context.cpu_registers}; + const auto& fprs{context.extension_registers}; + + if (id <= PC_REGISTER) { + return ValueToHex(gprs[id]); + } else if (id == CPSR_REGISTER) { + return ValueToHex(context.cpsr); + } else if (id >= D0_REGISTER && id < Q0_REGISTER) { + const u64 dN{GetSIMDRegister<u64>(fprs, id - D0_REGISTER)}; + return ValueToHex(dN); + } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { + const u128 qN{GetSIMDRegister<u128>(fprs, id - Q0_REGISTER)}; + return ValueToHex(qN); + } else if (id == FPSCR_REGISTER) { + return ValueToHex(context.fpscr); + } else { + return ""; + } +} + +void GDBStubA32::RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const { + if (!thread) { + return; + } + + auto& context{thread->GetContext32()}; + auto& fprs{context.extension_registers}; + + if (id <= PC_REGISTER) { + context.cpu_registers[id] = HexToValue<u32>(value); + } else if (id == CPSR_REGISTER) { + context.cpsr = HexToValue<u32>(value); + } else if (id >= D0_REGISTER && id < Q0_REGISTER) { + PutSIMDRegister(fprs, id - D0_REGISTER, HexToValue<u64>(value)); + } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { + PutSIMDRegister(fprs, id - Q0_REGISTER, HexToValue<u128>(value)); + } else if (id == FPSCR_REGISTER) { + context.fpscr = HexToValue<u32>(value); + } +} + +std::string GDBStubA32::ReadRegisters(const Kernel::KThread* thread) const { + std::string output; + + for (size_t reg = 0; reg <= FPSCR_REGISTER; reg++) { + const bool gpr{reg <= PC_REGISTER}; + const bool dfpr{reg >= D0_REGISTER && reg < Q0_REGISTER}; + const bool qfpr{reg >= Q0_REGISTER && reg < FPSCR_REGISTER}; + + if (!(gpr || dfpr || qfpr || reg == CPSR_REGISTER || reg == FPSCR_REGISTER)) { + continue; + } + + output += RegRead(thread, reg); + } + + return output; +} + +void GDBStubA32::WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const { + for (size_t i = 0, reg = 0; reg <= FPSCR_REGISTER; reg++) { + const bool gpr{reg <= PC_REGISTER}; + const bool dfpr{reg >= D0_REGISTER && reg < Q0_REGISTER}; + const bool qfpr{reg >= Q0_REGISTER && reg < FPSCR_REGISTER}; + + if (gpr || reg == CPSR_REGISTER || reg == FPSCR_REGISTER) { + RegWrite(thread, reg, register_data.substr(i, 8)); + i += 8; + } else if (dfpr) { + RegWrite(thread, reg, register_data.substr(i, 16)); + i += 16; + } else if (qfpr) { + RegWrite(thread, reg, register_data.substr(i, 32)); + i += 32; + } + + if (reg == PC_REGISTER) { + reg = CPSR_REGISTER - 1; + } else if (reg == CPSR_REGISTER) { + reg = D0_REGISTER - 1; + } + } +} + +std::string GDBStubA32::ThreadStatus(const Kernel::KThread* thread, u8 signal) const { + return fmt::format("T{:02x}{:02x}:{};{:02x}:{};{:02x}:{};thread:{:x};", signal, PC_REGISTER, + RegRead(thread, PC_REGISTER), SP_REGISTER, RegRead(thread, SP_REGISTER), + LR_REGISTER, RegRead(thread, LR_REGISTER), thread->GetThreadID()); +} + +u32 GDBStubA32::BreakpointInstruction() const { + // A32: trap + // T32: trap + b #4 + return 0xe7ffdefe; +} + +} // namespace Core diff --git a/src/core/debugger/gdbstub_arch.h b/src/core/debugger/gdbstub_arch.h new file mode 100644 index 000000000..2540d6456 --- /dev/null +++ b/src/core/debugger/gdbstub_arch.h @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <string> + +#include "common/common_types.h" + +namespace Kernel { +class KThread; +} + +namespace Core { + +class GDBStubArch { +public: + virtual ~GDBStubArch() = default; + virtual std::string GetTargetXML() const = 0; + virtual std::string RegRead(const Kernel::KThread* thread, size_t id) const = 0; + virtual void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const = 0; + virtual std::string ReadRegisters(const Kernel::KThread* thread) const = 0; + virtual void WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const = 0; + virtual std::string ThreadStatus(const Kernel::KThread* thread, u8 signal) const = 0; + virtual u32 BreakpointInstruction() const = 0; +}; + +class GDBStubA64 final : public GDBStubArch { +public: + std::string GetTargetXML() const override; + std::string RegRead(const Kernel::KThread* thread, size_t id) const override; + void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const override; + std::string ReadRegisters(const Kernel::KThread* thread) const override; + void WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const override; + std::string ThreadStatus(const Kernel::KThread* thread, u8 signal) const override; + u32 BreakpointInstruction() const override; + +private: + static constexpr u32 LR_REGISTER = 30; + static constexpr u32 SP_REGISTER = 31; + static constexpr u32 PC_REGISTER = 32; + static constexpr u32 PSTATE_REGISTER = 33; + static constexpr u32 Q0_REGISTER = 34; + static constexpr u32 FPSR_REGISTER = 66; + static constexpr u32 FPCR_REGISTER = 67; +}; + +class GDBStubA32 final : public GDBStubArch { +public: + std::string GetTargetXML() const override; + std::string RegRead(const Kernel::KThread* thread, size_t id) const override; + void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const override; + std::string ReadRegisters(const Kernel::KThread* thread) const override; + void WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const override; + std::string ThreadStatus(const Kernel::KThread* thread, u8 signal) const override; + u32 BreakpointInstruction() const override; + +private: + static constexpr u32 SP_REGISTER = 13; + static constexpr u32 LR_REGISTER = 14; + static constexpr u32 PC_REGISTER = 15; + static constexpr u32 CPSR_REGISTER = 25; + static constexpr u32 D0_REGISTER = 32; + static constexpr u32 Q0_REGISTER = 64; + static constexpr u32 FPSCR_REGISTER = 80; +}; + +} // namespace Core diff --git a/src/core/device_memory.cpp b/src/core/device_memory.cpp index f19c0515f..f8b5be2b4 100644 --- a/src/core/device_memory.cpp +++ b/src/core/device_memory.cpp @@ -1,12 +1,14 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/device_memory.h" +#include "hle/kernel/board/nintendo/nx/k_system_control.h" namespace Core { -DeviceMemory::DeviceMemory() : buffer{DramMemoryMap::Size, 1ULL << 39} {} +DeviceMemory::DeviceMemory() + : buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(), + 1ULL << 39} {} DeviceMemory::~DeviceMemory() = default; } // namespace Core diff --git a/src/core/device_memory.h b/src/core/device_memory.h index c4d17705f..df61b0c0b 100644 --- a/src/core/device_memory.h +++ b/src/core/device_memory.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -12,12 +11,8 @@ namespace Core { namespace DramMemoryMap { enum : u64 { Base = 0x80000000ULL, - Size = 0x100000000ULL, - End = Base + Size, KernelReserveBase = Base + 0x60000, SlabHeapBase = KernelReserveBase + 0x85000, - SlapHeapSize = 0xa21000, - SlabHeapEnd = SlabHeapBase + SlapHeapSize, }; }; // namespace DramMemoryMap diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp index f3891acf1..c750c0da7 100644 --- a/src/core/file_sys/bis_factory.cpp +++ b/src/core/file_sys/bis_factory.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <fmt/format.h> #include "common/fs/path_util.h" diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h index 136485881..26f0c6e5e 100644 --- a/src/core/file_sys/bis_factory.h +++ b/src/core/file_sys/bis_factory.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index c6300be59..f23d9373b 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> #include <string> diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index 0fd9fa87c..1283f8216 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/common_funcs.h b/src/core/file_sys/common_funcs.h index 7ed97aa50..3dd8f1244 100644 --- a/src/core/file_sys/common_funcs.h +++ b/src/core/file_sys/common_funcs.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 7019a7a68..78e56bbbd 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <cstring> @@ -420,7 +419,7 @@ std::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type Core::Crypto::Mode::ECB); cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Core::Crypto::Op::Decrypt); - Core::Crypto::Key128 out; + Core::Crypto::Key128 out{}; if (type == NCASectionCryptoType::XTS) { std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin()); } else if (type == NCASectionCryptoType::CTR || type == NCASectionCryptoType::BKTR) { diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index e9eccdea3..7fdc45ea7 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index 05936f3c3..be25da2f6 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/string_util.h" #include "common/swap.h" diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index 3e0b45630..75295519c 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory.h index 21c7aefc8..a853c00f3 100644 --- a/src/core/file_sys/directory.h +++ b/src/core/file_sys/directory.h @@ -1,11 +1,9 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include <cstddef> -#include <iterator> #include "common/common_funcs.h" #include "common/common_types.h" diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h index 1a920b45d..7cee0c7df 100644 --- a/src/core/file_sys/errors.h +++ b/src/core/file_sys/errors.h @@ -1,6 +1,5 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2016 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,14 +7,14 @@ namespace FileSys { -constexpr ResultCode ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1}; -constexpr ResultCode ERROR_PATH_ALREADY_EXISTS{ErrorModule::FS, 2}; -constexpr ResultCode ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002}; -constexpr ResultCode ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001}; -constexpr ResultCode ERROR_OUT_OF_BOUNDS{ErrorModule::FS, 3005}; -constexpr ResultCode ERROR_FAILED_MOUNT_ARCHIVE{ErrorModule::FS, 3223}; -constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::FS, 6001}; -constexpr ResultCode ERROR_INVALID_OFFSET{ErrorModule::FS, 6061}; -constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::FS, 6062}; +constexpr Result ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1}; +constexpr Result ERROR_PATH_ALREADY_EXISTS{ErrorModule::FS, 2}; +constexpr Result ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002}; +constexpr Result ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001}; +constexpr Result ERROR_OUT_OF_BOUNDS{ErrorModule::FS, 3005}; +constexpr Result ERROR_FAILED_MOUNT_ARCHIVE{ErrorModule::FS, 3223}; +constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::FS, 6001}; +constexpr Result ERROR_INVALID_OFFSET{ErrorModule::FS, 6061}; +constexpr Result ERROR_INVALID_SIZE{ErrorModule::FS, 6062}; } // namespace FileSys diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp index 1ca1d536f..1ff83c08c 100644 --- a/src/core/file_sys/fsmitm_romfsbuild.cpp +++ b/src/core/file_sys/fsmitm_romfsbuild.cpp @@ -1,26 +1,5 @@ -/* - * Copyright (c) 2018 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * Adapted by DarkLordZach for use/interaction with yuzu - * - * Modifications Copyright 2018 yuzu emulator team - * Licensed under GPLv2 or any later version - * Refer to the license.txt file included. - */ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include <string_view> diff --git a/src/core/file_sys/fsmitm_romfsbuild.h b/src/core/file_sys/fsmitm_romfsbuild.h index 8d4d89fab..06e5d5a47 100644 --- a/src/core/file_sys/fsmitm_romfsbuild.h +++ b/src/core/file_sys/fsmitm_romfsbuild.h @@ -1,26 +1,5 @@ -/* - * Copyright (c) 2018 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * Adapted by DarkLordZach for use/interaction with yuzu - * - * Modifications Copyright 2018 yuzu emulator team - * Licensed under GPLv2 or any later version - * Refer to the license.txt file included. - */ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp index a6101f1c0..5aab428bb 100644 --- a/src/core/file_sys/ips_layer.cpp +++ b/src/core/file_sys/ips_layer.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <cstring> @@ -218,9 +217,7 @@ void IPSwitchCompiler::Parse() { break; } else if (StartsWith(line, "@nsobid-")) { // NSO Build ID Specifier - auto raw_build_id = line.substr(8); - if (raw_build_id.size() != 0x40) - raw_build_id.resize(0x40, '0'); + const auto raw_build_id = fmt::format("{:0<64}", line.substr(8)); nso_build_id = Common::HexStringToArray<0x20>(raw_build_id); } else if (StartsWith(line, "#")) { // Mandatory Comment @@ -288,7 +285,8 @@ void IPSwitchCompiler::Parse() { std::copy(value.begin(), value.end(), std::back_inserter(replace)); } else { // hex replacement - const auto value = patch_line.substr(9); + const auto value = + patch_line.substr(9, patch_line.find_first_of(" /\r\n", 9) - 9); replace = Common::HexStringToVector(value, is_little_endian); } diff --git a/src/core/file_sys/ips_layer.h b/src/core/file_sys/ips_layer.h index 450b2f71e..f2717bae7 100644 --- a/src/core/file_sys/ips_layer.h +++ b/src/core/file_sys/ips_layer.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/kernel_executable.cpp b/src/core/file_sys/kernel_executable.cpp index ef93ef3ed..70c062f4c 100644 --- a/src/core/file_sys/kernel_executable.cpp +++ b/src/core/file_sys/kernel_executable.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> diff --git a/src/core/file_sys/kernel_executable.h b/src/core/file_sys/kernel_executable.h index 79ca82f8b..d5b9199b5 100644 --- a/src/core/file_sys/kernel_executable.h +++ b/src/core/file_sys/kernel_executable.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/mode.h b/src/core/file_sys/mode.h index 6c49a64e2..9596ef4fd 100644 --- a/src/core/file_sys/mode.h +++ b/src/core/file_sys/mode.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp index f5cb4aa8c..52c78020c 100644 --- a/src/core/file_sys/nca_metadata.cpp +++ b/src/core/file_sys/nca_metadata.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include "common/common_types.h" diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h index 75c74ae28..c59ece010 100644 --- a/src/core/file_sys/nca_metadata.h +++ b/src/core/file_sys/nca_metadata.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp index b36827b75..2735d053b 100644 --- a/src/core/file_sys/nca_patch.cpp +++ b/src/core/file_sys/nca_patch.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <array> @@ -51,7 +50,7 @@ std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, const BlockTyp low = mid + 1; } } - UNREACHABLE_MSG("Offset could not be found in BKTR block."); + ASSERT_MSG(false, "Offset could not be found in BKTR block."); return {0, 0}; } } // Anonymous namespace diff --git a/src/core/file_sys/nca_patch.h b/src/core/file_sys/nca_patch.h index 503cf473e..595e3ef09 100644 --- a/src/core/file_sys/nca_patch.h +++ b/src/core/file_sys/nca_patch.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp index c5967049e..2527ae606 100644 --- a/src/core/file_sys/partition_filesystem.cpp +++ b/src/core/file_sys/partition_filesystem.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <cstddef> diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h index 0f831148e..b6e3a2b0c 100644 --- a/src/core/file_sys/partition_filesystem.h +++ b/src/core/file_sys/partition_filesystem.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 7c0950bb0..4c80e13a9 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <array> @@ -10,7 +9,10 @@ #include "common/hex_util.h" #include "common/logging/log.h" #include "common/settings.h" +#ifndef _WIN32 #include "common/string_util.h" +#endif + #include "core/core.h" #include "core/file_sys/common_funcs.h" #include "core/file_sys/content_archive.h" @@ -128,15 +130,6 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { if (exefs == nullptr) return exefs; - if (Settings::values.dump_exefs) { - LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id); - const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id); - if (dump_dir != nullptr) { - const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs"); - VfsRawCopyD(exefs, exefs_dir); - } - } - const auto& disabled = Settings::values.disabled_addons[title_id]; const auto update_disabled = std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend(); @@ -154,28 +147,41 @@ 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) { + LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id); + const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id); + if (dump_dir != nullptr) { + const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs"); + VfsRawCopyD(exefs, exefs_dir); } } @@ -185,6 +191,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, const std::string& build_id) const { const auto& disabled = Settings::values.disabled_addons[title_id]; + const auto nso_build_id = fmt::format("{:0<64}", build_id); std::vector<VirtualFile> out; out.reserve(patch_dirs.size()); @@ -197,21 +204,18 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD for (const auto& file : exefs_dir->GetFiles()) { if (file->GetExtension() == "ips") { auto name = file->GetName(); - const auto p1 = name.substr(0, name.find('.')); - const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1); - if (build_id == this_build_id) + const auto this_build_id = + fmt::format("{:0<64}", name.substr(0, name.find('.'))); + if (nso_build_id == this_build_id) out.push_back(file); } else if (file->GetExtension() == "pchtxt") { IPSwitchCompiler compiler{file}; if (!compiler.IsValid()) continue; - auto this_build_id = Common::HexToString(compiler.GetBuildID()); - this_build_id = - this_build_id.substr(0, this_build_id.find_last_not_of('0') + 1); - - if (build_id == this_build_id) + const auto this_build_id = Common::HexToString(compiler.GetBuildID()); + if (nso_build_id == this_build_id) out.push_back(file); } } @@ -533,11 +537,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/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index 3be871f35..69d15e2f8 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index 484d4baea..08d489eab 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstddef> #include <vector> diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index c89a1c445..2e8960b07 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h @@ -1,12 +1,13 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include <array> #include <vector> + #include "common/bit_field.h" +#include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" #include "core/file_sys/vfs_types.h" diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 7a646b5f1..878d832c2 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <random> @@ -109,7 +108,7 @@ ContentRecordType GetCRTypeFromNCAType(NCAContentType type) { // TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal. return ContentRecordType::HtmlDocument; default: - UNREACHABLE_MSG("Invalid NCAContentType={:02X}", type); + ASSERT_MSG(false, "Invalid NCAContentType={:02X}", type); return ContentRecordType{}; } } @@ -387,15 +386,17 @@ std::vector<NcaID> RegisteredCache::AccumulateFiles() const { continue; for (const auto& nca_dir : d2_dir->GetSubdirectories()) { - if (!FollowsNcaIdFormat(nca_dir->GetName())) + if (nca_dir == nullptr || !FollowsNcaIdFormat(nca_dir->GetName())) { continue; + } ids.push_back(Common::HexStringToArray<0x10, true>(nca_dir->GetName().substr(0, 0x20))); } for (const auto& nca_file : d2_dir->GetFiles()) { - if (!FollowsNcaIdFormat(nca_file->GetName())) + if (nca_file == nullptr || !FollowsNcaIdFormat(nca_file->GetName())) { continue; + } ids.push_back( Common::HexStringToArray<0x10, true>(nca_file->GetName().substr(0, 0x20))); diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index d042aef90..587f8cae8 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp index 120032134..ddcfe5980 100644 --- a/src/core/file_sys/romfs.cpp +++ b/src/core/file_sys/romfs.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h index 82e683782..5d7f0c2a8 100644 --- a/src/core/file_sys/romfs.h +++ b/src/core/file_sys/romfs.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp index 291b746b6..ae7a3511b 100644 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> #include "common/assert.h" diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h index 2c93a49a5..14936031f 100644 --- a/src/core/file_sys/romfs_factory.h +++ b/src/core/file_sys/romfs_factory.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index e6f8514c9..8c1b2523c 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> #include "common/assert.h" diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index de415b0c4..a763b94c8 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp index c0e13e56f..1df022c9e 100644 --- a/src/core/file_sys/sdmc_factory.cpp +++ b/src/core/file_sys/sdmc_factory.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> #include "core/file_sys/registered_cache.h" diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h index 3a3d11f3a..3aebfb25e 100644 --- a/src/core/file_sys/sdmc_factory.h +++ b/src/core/file_sys/sdmc_factory.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index f03124e3d..c90e6e372 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <cstring> diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index 030f36c09..3226b884a 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/system_archive/data/font_chinese_simplified.cpp b/src/core/file_sys/system_archive/data/font_chinese_simplified.cpp index a676867e5..a1daac3a9 100644 --- a/src/core/file_sys/system_archive/data/font_chinese_simplified.cpp +++ b/src/core/file_sys/system_archive/data/font_chinese_simplified.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/file_sys/system_archive/data/font_chinese_simplified.h" diff --git a/src/core/file_sys/system_archive/data/font_chinese_simplified.h b/src/core/file_sys/system_archive/data/font_chinese_simplified.h index 161de4542..33932c456 100644 --- a/src/core/file_sys/system_archive/data/font_chinese_simplified.h +++ b/src/core/file_sys/system_archive/data/font_chinese_simplified.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/system_archive/data/font_chinese_traditional.cpp b/src/core/file_sys/system_archive/data/font_chinese_traditional.cpp index cc70a4032..9b92ec0ff 100644 --- a/src/core/file_sys/system_archive/data/font_chinese_traditional.cpp +++ b/src/core/file_sys/system_archive/data/font_chinese_traditional.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/file_sys/system_archive/data/font_chinese_traditional.h" diff --git a/src/core/file_sys/system_archive/data/font_chinese_traditional.h b/src/core/file_sys/system_archive/data/font_chinese_traditional.h index b16c8e550..c2ebd4d96 100644 --- a/src/core/file_sys/system_archive/data/font_chinese_traditional.h +++ b/src/core/file_sys/system_archive/data/font_chinese_traditional.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/system_archive/data/font_extended_chinese_simplified.cpp b/src/core/file_sys/system_archive/data/font_extended_chinese_simplified.cpp index 18b8c87e7..9e43ef2c1 100644 --- a/src/core/file_sys/system_archive/data/font_extended_chinese_simplified.cpp +++ b/src/core/file_sys/system_archive/data/font_extended_chinese_simplified.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/file_sys/system_archive/data/font_extended_chinese_simplified.h" diff --git a/src/core/file_sys/system_archive/data/font_extended_chinese_simplified.h b/src/core/file_sys/system_archive/data/font_extended_chinese_simplified.h index de8ac57ac..d640cf755 100644 --- a/src/core/file_sys/system_archive/data/font_extended_chinese_simplified.h +++ b/src/core/file_sys/system_archive/data/font_extended_chinese_simplified.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/system_archive/data/font_korean.cpp b/src/core/file_sys/system_archive/data/font_korean.cpp index ae9228958..cba7fe0f8 100644 --- a/src/core/file_sys/system_archive/data/font_korean.cpp +++ b/src/core/file_sys/system_archive/data/font_korean.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/file_sys/system_archive/data/font_korean.h" diff --git a/src/core/file_sys/system_archive/data/font_korean.h b/src/core/file_sys/system_archive/data/font_korean.h index e1b02c4e5..1d9ab4b1f 100644 --- a/src/core/file_sys/system_archive/data/font_korean.h +++ b/src/core/file_sys/system_archive/data/font_korean.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/system_archive/data/font_nintendo_extended.cpp b/src/core/file_sys/system_archive/data/font_nintendo_extended.cpp index 29ef110a6..eaa8ec254 100644 --- a/src/core/file_sys/system_archive/data/font_nintendo_extended.cpp +++ b/src/core/file_sys/system_archive/data/font_nintendo_extended.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/file_sys/system_archive/data/font_nintendo_extended.h" diff --git a/src/core/file_sys/system_archive/data/font_nintendo_extended.h b/src/core/file_sys/system_archive/data/font_nintendo_extended.h index edb9df914..247ebb5af 100644 --- a/src/core/file_sys/system_archive/data/font_nintendo_extended.h +++ b/src/core/file_sys/system_archive/data/font_nintendo_extended.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/system_archive/data/font_standard.cpp b/src/core/file_sys/system_archive/data/font_standard.cpp index 8f4d8448e..b71ba8a83 100644 --- a/src/core/file_sys/system_archive/data/font_standard.cpp +++ b/src/core/file_sys/system_archive/data/font_standard.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/file_sys/system_archive/data/font_standard.h" diff --git a/src/core/file_sys/system_archive/data/font_standard.h b/src/core/file_sys/system_archive/data/font_standard.h index 757593c4b..ab44108d6 100644 --- a/src/core/file_sys/system_archive/data/font_standard.h +++ b/src/core/file_sys/system_archive/data/font_standard.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/system_archive/mii_model.cpp b/src/core/file_sys/system_archive/mii_model.cpp index d65c7d234..5c87b42f8 100644 --- a/src/core/file_sys/system_archive/mii_model.cpp +++ b/src/core/file_sys/system_archive/mii_model.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/file_sys/system_archive/mii_model.h" #include "core/file_sys/vfs_vector.h" diff --git a/src/core/file_sys/system_archive/mii_model.h b/src/core/file_sys/system_archive/mii_model.h index 6c2d9398b..b6cbefe24 100644 --- a/src/core/file_sys/system_archive/mii_model.h +++ b/src/core/file_sys/system_archive/mii_model.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/system_archive/ng_word.cpp b/src/core/file_sys/system_archive/ng_word.cpp index 8d86d563a..5cf6749da 100644 --- a/src/core/file_sys/system_archive/ng_word.cpp +++ b/src/core/file_sys/system_archive/ng_word.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <fmt/format.h> #include "common/common_types.h" diff --git a/src/core/file_sys/system_archive/ng_word.h b/src/core/file_sys/system_archive/ng_word.h index cd81e0abb..1d7b49532 100644 --- a/src/core/file_sys/system_archive/ng_word.h +++ b/src/core/file_sys/system_archive/ng_word.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/system_archive/shared_font.cpp b/src/core/file_sys/system_archive/shared_font.cpp index c5cdf7d9b..3210583f0 100644 --- a/src/core/file_sys/system_archive/shared_font.cpp +++ b/src/core/file_sys/system_archive/shared_font.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/file_sys/system_archive/data/font_chinese_simplified.h" #include "core/file_sys/system_archive/data/font_chinese_traditional.h" @@ -10,7 +9,7 @@ #include "core/file_sys/system_archive/data/font_standard.h" #include "core/file_sys/system_archive/shared_font.h" #include "core/file_sys/vfs_vector.h" -#include "core/hle/service/ns/pl_u.h" +#include "core/hle/service/ns/iplatform_service_manager.h" namespace FileSys::SystemArchive { diff --git a/src/core/file_sys/system_archive/shared_font.h b/src/core/file_sys/system_archive/shared_font.h index 6d8de565b..d1cd1dc44 100644 --- a/src/core/file_sys/system_archive/shared_font.h +++ b/src/core/file_sys/system_archive/shared_font.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/system_archive/system_archive.cpp b/src/core/file_sys/system_archive/system_archive.cpp index a6696024e..6abac793b 100644 --- a/src/core/file_sys/system_archive/system_archive.cpp +++ b/src/core/file_sys/system_archive/system_archive.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/file_sys/romfs.h" diff --git a/src/core/file_sys/system_archive/system_archive.h b/src/core/file_sys/system_archive/system_archive.h index 724a8eb17..02d9157bb 100644 --- a/src/core/file_sys/system_archive/system_archive.h +++ b/src/core/file_sys/system_archive/system_archive.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/system_archive/system_version.cpp b/src/core/file_sys/system_archive/system_version.cpp index 9b76d007e..bd493ecca 100644 --- a/src/core/file_sys/system_archive/system_version.cpp +++ b/src/core/file_sys/system_archive/system_version.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/file_sys/system_archive/system_version.h" #include "core/file_sys/vfs_vector.h" diff --git a/src/core/file_sys/system_archive/system_version.h b/src/core/file_sys/system_archive/system_version.h index deed79b26..21b5514a9 100644 --- a/src/core/file_sys/system_archive/system_version.h +++ b/src/core/file_sys/system_archive/system_version.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/system_archive/time_zone_binary.cpp b/src/core/file_sys/system_archive/time_zone_binary.cpp index 8fd005012..85383998d 100644 --- a/src/core/file_sys/system_archive/time_zone_binary.cpp +++ b/src/core/file_sys/system_archive/time_zone_binary.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> #include <vector> diff --git a/src/core/file_sys/system_archive/time_zone_binary.h b/src/core/file_sys/system_archive/time_zone_binary.h index 266c23537..d0e1a4acd 100644 --- a/src/core/file_sys/system_archive/time_zone_binary.h +++ b/src/core/file_sys/system_archive/time_zone_binary.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index f5ad10b15..0f6618b31 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <numeric> diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h index 1b9365853..8fc1738a4 100644 --- a/src/core/file_sys/vfs.h +++ b/src/core/file_sys/vfs.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs_concat.cpp index 5f8c09124..d23623aa0 100644 --- a/src/core/file_sys/vfs_concat.cpp +++ b/src/core/file_sys/vfs_concat.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <utility> diff --git a/src/core/file_sys/vfs_concat.h b/src/core/file_sys/vfs_concat.h index bd091451e..9be0261b6 100644 --- a/src/core/file_sys/vfs_concat.h +++ b/src/core/file_sys/vfs_concat.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/vfs_layered.cpp b/src/core/file_sys/vfs_layered.cpp index e093c4db2..da05dd395 100644 --- a/src/core/file_sys/vfs_layered.cpp +++ b/src/core/file_sys/vfs_layered.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <utility> diff --git a/src/core/file_sys/vfs_layered.h b/src/core/file_sys/vfs_layered.h index cd6baf28c..a62112e9d 100644 --- a/src/core/file_sys/vfs_layered.h +++ b/src/core/file_sys/vfs_layered.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp index 870ed1cf8..d950a6633 100644 --- a/src/core/file_sys/vfs_offset.cpp +++ b/src/core/file_sys/vfs_offset.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <utility> diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h index 7ce1eb336..6c051ca00 100644 --- a/src/core/file_sys/vfs_offset.h +++ b/src/core/file_sys/vfs_offset.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index f4073b76a..cc0076238 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <cstddef> @@ -145,7 +144,7 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_ LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path); } } else { - UNREACHABLE(); + ASSERT(false); return nullptr; } diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h index 746e624cb..acde1ac89 100644 --- a/src/core/file_sys/vfs_real.h +++ b/src/core/file_sys/vfs_real.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/vfs_static.h b/src/core/file_sys/vfs_static.h index f5b66cf71..ca3f989ef 100644 --- a/src/core/file_sys/vfs_static.h +++ b/src/core/file_sys/vfs_static.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/vfs_types.h b/src/core/file_sys/vfs_types.h index ed0724717..4a583ed64 100644 --- a/src/core/file_sys/vfs_types.h +++ b/src/core/file_sys/vfs_types.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp index f64b88639..251d9d7c9 100644 --- a/src/core/file_sys/vfs_vector.cpp +++ b/src/core/file_sys/vfs_vector.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <utility> diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h index 73f180070..bfedb6e42 100644 --- a/src/core/file_sys/vfs_vector.h +++ b/src/core/file_sys/vfs_vector.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp index d6fe1af47..ede0aa11a 100644 --- a/src/core/file_sys/xts_archive.cpp +++ b/src/core/file_sys/xts_archive.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <array> diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h index 63a032b68..abbe5f716 100644 --- a/src/core/file_sys/xts_archive.h +++ b/src/core/file_sys/xts_archive.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp index e1033b634..6c230f619 100644 --- a/src/core/frontend/applets/controller.cpp +++ b/src/core/frontend/applets/controller.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "common/logging/log.h" @@ -66,7 +65,7 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); controller->Connect(true); } else { - UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!"); + ASSERT_MSG(false, "Unable to add a new controller based on the given parameters!"); } } diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h index 014bc8901..1d2850ad5 100644 --- a/src/core/frontend/applets/controller.h +++ b/src/core/frontend/applets/controller.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/frontend/applets/error.cpp b/src/core/frontend/applets/error.cpp index dceb20ff8..f8b961098 100644 --- a/src/core/frontend/applets/error.cpp +++ b/src/core/frontend/applets/error.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/frontend/applets/error.h" @@ -9,12 +8,12 @@ namespace Core::Frontend { ErrorApplet::~ErrorApplet() = default; -void DefaultErrorApplet::ShowError(ResultCode error, std::function<void()> finished) const { +void DefaultErrorApplet::ShowError(Result error, std::function<void()> finished) const { LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})", error.module.Value(), error.description.Value(), error.raw); } -void DefaultErrorApplet::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time, +void DefaultErrorApplet::ShowErrorWithTimestamp(Result error, std::chrono::seconds time, std::function<void()> finished) const { LOG_CRITICAL( Service_Fatal, @@ -22,7 +21,7 @@ void DefaultErrorApplet::ShowErrorWithTimestamp(ResultCode error, std::chrono::s error.module.Value(), error.description.Value(), error.raw, time.count()); } -void DefaultErrorApplet::ShowCustomErrorText(ResultCode error, std::string main_text, +void DefaultErrorApplet::ShowCustomErrorText(Result error, std::string main_text, std::string detail_text, std::function<void()> finished) const { LOG_CRITICAL(Service_Fatal, diff --git a/src/core/frontend/applets/error.h b/src/core/frontend/applets/error.h index 699df940d..f378f8805 100644 --- a/src/core/frontend/applets/error.h +++ b/src/core/frontend/applets/error.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -15,22 +14,22 @@ class ErrorApplet { public: virtual ~ErrorApplet(); - virtual void ShowError(ResultCode error, std::function<void()> finished) const = 0; + virtual void ShowError(Result error, std::function<void()> finished) const = 0; - virtual void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time, + virtual void ShowErrorWithTimestamp(Result error, std::chrono::seconds time, std::function<void()> finished) const = 0; - virtual void ShowCustomErrorText(ResultCode error, std::string dialog_text, + virtual void ShowCustomErrorText(Result error, std::string dialog_text, std::string fullscreen_text, std::function<void()> finished) const = 0; }; class DefaultErrorApplet final : public ErrorApplet { public: - void ShowError(ResultCode error, std::function<void()> finished) const override; - void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time, + void ShowError(Result error, std::function<void()> finished) const override; + void ShowErrorWithTimestamp(Result error, std::chrono::seconds time, std::function<void()> finished) const override; - void ShowCustomErrorText(ResultCode error, std::string main_text, std::string detail_text, + void ShowCustomErrorText(Result error, std::string main_text, std::string detail_text, std::function<void()> finished) const override; }; diff --git a/src/core/frontend/applets/general_frontend.cpp b/src/core/frontend/applets/general_frontend.cpp index 7483ffb76..29a00fb6f 100644 --- a/src/core/frontend/applets/general_frontend.cpp +++ b/src/core/frontend/applets/general_frontend.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/frontend/applets/general_frontend.h" diff --git a/src/core/frontend/applets/general_frontend.h b/src/core/frontend/applets/general_frontend.h index 1647aa975..cbec8b4ad 100644 --- a/src/core/frontend/applets/general_frontend.h +++ b/src/core/frontend/applets/general_frontend.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/frontend/applets/mii_edit.cpp b/src/core/frontend/applets/mii_edit.cpp new file mode 100644 index 000000000..d37b5368a --- /dev/null +++ b/src/core/frontend/applets/mii_edit.cpp @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/frontend/applets/mii_edit.h" + +namespace Core::Frontend { + +MiiEditApplet::~MiiEditApplet() = default; + +void DefaultMiiEditApplet::ShowMiiEdit(const std::function<void()>& callback) const { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + callback(); +} + +} // namespace Core::Frontend diff --git a/src/core/frontend/applets/mii_edit.h b/src/core/frontend/applets/mii_edit.h new file mode 100644 index 000000000..58fa2039b --- /dev/null +++ b/src/core/frontend/applets/mii_edit.h @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <functional> + +namespace Core::Frontend { + +class MiiEditApplet { +public: + virtual ~MiiEditApplet(); + + virtual void ShowMiiEdit(const std::function<void()>& callback) const = 0; +}; + +class DefaultMiiEditApplet final : public MiiEditApplet { +public: + void ShowMiiEdit(const std::function<void()>& callback) const override; +}; + +} // namespace Core::Frontend diff --git a/src/core/frontend/applets/profile_select.cpp b/src/core/frontend/applets/profile_select.cpp index 4c58c310f..d11fbce0a 100644 --- a/src/core/frontend/applets/profile_select.cpp +++ b/src/core/frontend/applets/profile_select.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/settings.h" #include "core/frontend/applets/profile_select.h" diff --git a/src/core/frontend/applets/profile_select.h b/src/core/frontend/applets/profile_select.h index 3506b9885..8d6ee5279 100644 --- a/src/core/frontend/applets/profile_select.h +++ b/src/core/frontend/applets/profile_select.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/frontend/applets/software_keyboard.cpp b/src/core/frontend/applets/software_keyboard.cpp index c4863ee73..020c7fa5e 100644 --- a/src/core/frontend/applets/software_keyboard.cpp +++ b/src/core/frontend/applets/software_keyboard.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <thread> diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h index 490c55cc2..094d1e713 100644 --- a/src/core/frontend/applets/software_keyboard.h +++ b/src/core/frontend/applets/software_keyboard.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -18,6 +17,8 @@ struct KeyboardInitializeParameters { std::u16string sub_text; std::u16string guide_text; std::u16string initial_text; + char16_t left_optional_symbol_key; + char16_t right_optional_symbol_key; u32 max_text_length; u32 min_text_length; s32 initial_cursor_position; diff --git a/src/core/frontend/applets/web_browser.cpp b/src/core/frontend/applets/web_browser.cpp index be4736f47..27c7086be 100644 --- a/src/core/frontend/applets/web_browser.cpp +++ b/src/core/frontend/applets/web_browser.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/frontend/applets/web_browser.h" diff --git a/src/core/frontend/applets/web_browser.h b/src/core/frontend/applets/web_browser.h index b6a60c994..1411274f8 100644 --- a/src/core/frontend/applets/web_browser.h +++ b/src/core/frontend/applets/web_browser.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 57c6ffc43..1be2dccb0 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -1,6 +1,5 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2014 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <mutex> #include "core/frontend/emu_window.h" diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index e413a520a..ac1906d5e 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -1,6 +1,5 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2014 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -42,11 +41,20 @@ public: context.MakeCurrent(); } ~Scoped() { - context.DoneCurrent(); + if (active) { + context.DoneCurrent(); + } + } + + /// In the event that context was destroyed before the Scoped is destroyed, this provides a + /// mechanism to prevent calling a destroyed object's method during the deconstructor + void Cancel() { + active = false; } private: GraphicsContext& context; + bool active{true}; }; /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index 26a5b12aa..90dd68ff1 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cmath> diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h index 8e341e4e2..1561d994e 100644 --- a/src/core/frontend/framebuffer_layout.h +++ b/src/core/frontend/framebuffer_layout.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp deleted file mode 100644 index 290db505e..000000000 --- a/src/core/hardware_interrupt_manager.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2019 Yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hardware_interrupt_manager.h" -#include "core/hle/service/nvdrv/nvdrv_interface.h" -#include "core/hle/service/sm/sm.h" - -namespace Core::Hardware { - -InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { - gpu_interrupt_event = Core::Timing::CreateEvent( - "GPUInterrupt", [this](std::uintptr_t message, std::chrono::nanoseconds) { - auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv"); - const u32 syncpt = static_cast<u32>(message >> 32); - const u32 value = static_cast<u32>(message); - nvdrv->SignalGPUInterruptSyncpt(syncpt, value); - }); -} - -InterruptManager::~InterruptManager() = default; - -void InterruptManager::GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) { - const u64 msg = (static_cast<u64>(syncpoint_id) << 32ULL) | value; - system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{10}, gpu_interrupt_event, msg); -} - -} // namespace Core::Hardware diff --git a/src/core/hardware_interrupt_manager.h b/src/core/hardware_interrupt_manager.h deleted file mode 100644 index 5fa306ae0..000000000 --- a/src/core/hardware_interrupt_manager.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2019 Yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <memory> - -#include "common/common_types.h" - -namespace Core { -class System; -} - -namespace Core::Timing { -struct EventType; -} - -namespace Core::Hardware { - -class InterruptManager { -public: - explicit InterruptManager(Core::System& system); - ~InterruptManager(); - - void GPUInterruptSyncpt(u32 syncpoint_id, u32 value); - -private: - Core::System& system; - std::shared_ptr<Core::Timing::EventType> gpu_interrupt_event; -}; - -} // namespace Core::Hardware diff --git a/src/core/hardware_properties.h b/src/core/hardware_properties.h index 176a72c67..13cbdb734 100644 --- a/src/core/hardware_properties.h +++ b/src/core/hardware_properties.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -26,6 +25,9 @@ constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, }; +// Cortex-A57 supports 4 memory watchpoints +constexpr u64 NUM_WATCHPOINTS = 4; + } // namespace Hardware } // namespace Core diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp index eef0ff493..aac45907d 100644 --- a/src/core/hid/emulated_console.cpp +++ b/src/core/hid/emulated_console.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/settings.h" #include "core/hid/emulated_console.h" @@ -28,12 +27,19 @@ void EmulatedConsole::SetTouchParams() { // We can't use mouse as touch if native mouse is enabled touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"}; } - touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0"}; - touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1"}; + + touch_params[index++] = + Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0,touch_id:0"}; + touch_params[index++] = + Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1,touch_id:1"}; + touch_params[index++] = + Common::ParamPackage{"engine:touch,axis_x:4,axis_y:5,button:2,touch_id:2"}; + touch_params[index++] = + Common::ParamPackage{"engine:touch,axis_x:6,axis_y:7,button:3,touch_id:3"}; touch_params[index++] = - Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"}; + Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536,touch_id:0"}; touch_params[index++] = - Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"}; + Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072,touch_id:1"}; const auto button_index = static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue()); @@ -132,7 +138,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 +157,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 +173,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 +181,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 +198,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 +233,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..1c510cd19 100644 --- a/src/core/hid/emulated_console.h +++ b/src/core/hid/emulated_console.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -183,6 +182,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 2bee173b3..025f1c78e 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -1,7 +1,7 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#include "common/thread.h" #include "core/hid/emulated_controller.h" #include "core/hid/input_converter.h" @@ -85,23 +85,26 @@ void EmulatedController::ReloadFromSettings() { motion_params[index] = Common::ParamPackage(player.motions[index]); } + controller.colors_state.fullkey = { + .body = GetNpadColor(player.body_color_left), + .button = GetNpadColor(player.button_color_left), + }; controller.colors_state.left = { - .body = player.body_color_left, - .button = player.button_color_left, + .body = GetNpadColor(player.body_color_left), + .button = GetNpadColor(player.button_color_left), }; - controller.colors_state.right = { - .body = player.body_color_right, - .button = player.button_color_right, + .body = GetNpadColor(player.body_color_right), + .button = GetNpadColor(player.button_color_right), }; - controller.colors_state.fullkey = controller.colors_state.left; - // Other or debug controller should always be a pro controller if (npad_id_type != NpadIdType::Other) { SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type)); + original_npad_type = npad_type; } else { SetNpadStyleIndex(NpadStyleIndex::ProController); + original_npad_type = npad_type; } if (player.connected) { @@ -127,10 +130,17 @@ void EmulatedController::LoadDevices() { battery_params[LeftIndex].Set("battery", true); battery_params[RightIndex].Set("battery", true); + camera_params = Common::ParamPackage{"engine:camera,camera:1"}; + nfc_params = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"}; + output_params[LeftIndex] = left_joycon; output_params[RightIndex] = right_joycon; + output_params[2] = camera_params; + output_params[3] = nfc_params; output_params[LeftIndex].Set("output", true); output_params[RightIndex].Set("output", true); + output_params[2].Set("output", true); + output_params[3].Set("output", true); LoadTASParams(); @@ -147,6 +157,8 @@ void EmulatedController::LoadDevices() { Common::Input::CreateDevice<Common::Input::InputDevice>); std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>); + camera_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(camera_params); + nfc_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(nfc_params); std::transform(output_params.begin(), output_params.end(), output_devices.begin(), Common::Input::CreateDevice<Common::Input::OutputDevice>); @@ -268,6 +280,24 @@ void EmulatedController::ReloadInput() { motion_devices[index]->ForceUpdate(); } + if (camera_devices) { + camera_devices->SetCallback({ + .on_change = + [this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); }, + }); + camera_devices->ForceUpdate(); + } + + if (nfc_devices) { + if (npad_id_type == NpadIdType::Handheld || npad_id_type == NpadIdType::Player1) { + nfc_devices->SetCallback({ + .on_change = + [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); }, + }); + nfc_devices->ForceUpdate(); + } + } + // Use a common UUID for TAS static constexpr Common::UUID TAS_UUID = Common::UUID{ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; @@ -323,6 +353,8 @@ void EmulatedController::UnloadInput() { for (auto& stick : tas_stick_devices) { stick.reset(); } + camera_devices.reset(); + nfc_devices.reset(); } void EmulatedController::EnableConfiguration() { @@ -340,6 +372,7 @@ void EmulatedController::DisableConfiguration() { Disconnect(); } SetNpadStyleIndex(tmp_npad_type); + original_npad_type = tmp_npad_type; } // Apply temporary connected status to the real controller @@ -353,14 +386,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 +530,151 @@ 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; + 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; - } + // 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; + } - // Unlock button ready for next press - if (!new_status.value && current_status.locked) { - current_status.locked = false; - } + // Unlock button ready for next press + if (!new_status.value && current_status.locked) { + current_status.locked = false; } + } + + if (!value_changed) { + return; + } - if (!value_changed) { + if (is_configuring) { + controller.npad_button_state.raw = NpadButton::None; + controller.debug_pad_button_state.raw = 0; + lock.unlock(); + TriggerOnChange(ControllerTriggerType::Button, false); + return; + } + + // GC controllers have triggers not buttons + if (npad_type == NpadStyleIndex::GameCube) { + if (index == Settings::NativeButton::ZR) { return; } - - if (is_configuring) { - controller.npad_button_state.raw = NpadButton::None; - controller.debug_pad_button_state.raw = 0; - TriggerOnChange(ControllerTriggerType::Button, false); + if (index == Settings::NativeButton::ZL) { 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; - } - controller.home_button_state.home.Assign(current_status.value); + 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 +691,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 +707,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 +734,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, break; } + lock.unlock(); TriggerOnChange(ControllerTriggerType::Stick, true); } @@ -693,7 +743,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,10 +759,16 @@ 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; } + // Only GC controllers have analog triggers + if (npad_type != NpadStyleIndex::GameCube) { + return; + } + const auto& trigger = controller.trigger_values[index]; switch (index) { @@ -727,6 +783,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac break; } + lock.unlock(); TriggerOnChange(ControllerTriggerType::Trigger, true); } @@ -735,7 +792,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 +813,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 +825,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 +834,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,9 +895,49 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac }; break; } + + lock.unlock(); TriggerOnChange(ControllerTriggerType::Battery, true); } +void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) { + std::unique_lock lock{mutex}; + controller.camera_values = TransformToCamera(callback); + + if (is_configuring) { + lock.unlock(); + TriggerOnChange(ControllerTriggerType::IrSensor, false); + return; + } + + controller.camera_state.sample++; + controller.camera_state.format = + static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format); + controller.camera_state.data = controller.camera_values.data; + + lock.unlock(); + TriggerOnChange(ControllerTriggerType::IrSensor, true); +} + +void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) { + std::unique_lock lock{mutex}; + controller.nfc_values = TransformToNfc(callback); + + if (is_configuring) { + lock.unlock(); + TriggerOnChange(ControllerTriggerType::Nfc, false); + return; + } + + controller.nfc_state = { + controller.nfc_values.state, + controller.nfc_values.data, + }; + + lock.unlock(); + TriggerOnChange(ControllerTriggerType::Nfc, true); +} + bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { if (device_index >= output_devices.size()) { return false; @@ -871,18 +971,100 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v } bool EmulatedController::TestVibration(std::size_t device_index) { - static constexpr VibrationValue test_vibration = { + if (device_index >= output_devices.size()) { + return false; + } + if (!output_devices[device_index]) { + return false; + } + + const auto player_index = NpadIdTypeToIndex(npad_id_type); + const auto& player = Settings::values.players.GetValue()[player_index]; + + if (!player.vibration_enabled) { + return false; + } + + const Common::Input::VibrationStatus test_vibration = { .low_amplitude = 0.001f, - .low_frequency = 160.0f, + .low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency, .high_amplitude = 0.001f, - .high_frequency = 320.0f, + .high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency, + .type = Common::Input::VibrationAmplificationType::Test, + }; + + const Common::Input::VibrationStatus zero_vibration = { + .low_amplitude = DEFAULT_VIBRATION_VALUE.low_amplitude, + .low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency, + .high_amplitude = DEFAULT_VIBRATION_VALUE.high_amplitude, + .high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency, + .type = Common::Input::VibrationAmplificationType::Test, }; // Send a slight vibration to test for rumble support - SetVibration(device_index, test_vibration); + output_devices[device_index]->SetVibration(test_vibration); + + // Wait for about 15ms to ensure the controller is ready for the stop command + std::this_thread::sleep_for(std::chrono::milliseconds(15)); // Stop any vibration and return the result - return SetVibration(device_index, DEFAULT_VIBRATION_VALUE); + return output_devices[device_index]->SetVibration(zero_vibration) == + Common::Input::VibrationError::None; +} + +bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { + LOG_INFO(Service_HID, "Set polling mode {}", polling_mode); + auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; + auto& nfc_output_device = output_devices[3]; + + const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode); + const auto mapped_nfc_result = output_device->SetPollingMode(polling_mode); + + return virtual_nfc_result == Common::Input::PollingError::None || + mapped_nfc_result == Common::Input::PollingError::None; +} + +bool EmulatedController::SetCameraFormat( + Core::IrSensor::ImageTransferProcessorFormat camera_format) { + LOG_INFO(Service_HID, "Set camera format {}", camera_format); + + auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; + auto& camera_output_device = output_devices[2]; + + if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>( + camera_format)) == Common::Input::CameraError::None) { + return true; + } + + // Fallback to Qt camera if native device doesn't have support + return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>( + camera_format)) == Common::Input::CameraError::None; +} + +bool EmulatedController::HasNfc() const { + const auto& nfc_output_device = output_devices[3]; + + switch (npad_type) { + case NpadStyleIndex::JoyconRight: + case NpadStyleIndex::JoyconDual: + case NpadStyleIndex::ProController: + break; + default: + return false; + } + + const bool has_virtual_nfc = + npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld; + const bool is_virtual_nfc_supported = + nfc_output_device->SupportsNfc() != Common::Input::NfcState::NotSupported; + + return is_connected && (has_virtual_nfc && is_virtual_nfc_supported); +} + +bool EmulatedController::WriteNfc(const std::vector<u8>& data) { + auto& nfc_output_device = output_devices[3]; + + return nfc_output_device->WriteNfcData(data) == Common::Input::NfcState::Success; } void EmulatedController::SetLedPattern() { @@ -907,13 +1089,27 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) if (!is_connected) { return; } + + // Attempt to reconnect with the original type + if (npad_type != original_npad_type) { + Disconnect(); + const auto current_npad_type = npad_type; + SetNpadStyleIndex(original_npad_type); + if (IsControllerSupported()) { + Connect(); + return; + } + SetNpadStyleIndex(current_npad_type); + Connect(); + } + if (IsControllerSupported()) { return; } Disconnect(); - // Fallback fullkey controllers to Pro controllers + // Fallback Fullkey controllers to Pro controllers if (IsControllerFullkey() && supported_style_tag.fullkey) { LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); SetNpadStyleIndex(NpadStyleIndex::ProController); @@ -921,11 +1117,28 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) return; } + // Fallback Dual joycon controllers to Pro controllers + if (npad_type == NpadStyleIndex::JoyconDual && supported_style_tag.fullkey) { + LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); + SetNpadStyleIndex(NpadStyleIndex::ProController); + Connect(); + return; + } + + // Fallback Pro controllers to Dual joycon + if (npad_type == NpadStyleIndex::ProController && supported_style_tag.joycon_dual) { + LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type); + SetNpadStyleIndex(NpadStyleIndex::JoyconDual); + Connect(); + return; + } + LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller", npad_type); } 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: @@ -941,6 +1154,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: @@ -976,40 +1190,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; } @@ -1023,10 +1241,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; } @@ -1034,27 +1254,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); } @@ -1082,30 +1303,42 @@ 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; } +CameraValues EmulatedController::GetCameraValues() const { + std::scoped_lock lock{mutex}; + return controller.camera_values; +} + HomeButtonState EmulatedController::GetHomeButtons() const { + std::scoped_lock lock{mutex}; if (is_configuring) { return {}; } @@ -1113,6 +1346,7 @@ HomeButtonState EmulatedController::GetHomeButtons() const { } CaptureButtonState EmulatedController::GetCaptureButtons() const { + std::scoped_lock lock{mutex}; if (is_configuring) { return {}; } @@ -1120,6 +1354,7 @@ CaptureButtonState EmulatedController::GetCaptureButtons() const { } NpadButtonState EmulatedController::GetNpadButtons() const { + std::scoped_lock lock{mutex}; if (is_configuring) { return {}; } @@ -1127,6 +1362,7 @@ NpadButtonState EmulatedController::GetNpadButtons() const { } DebugPadButton EmulatedController::GetDebugPadButtons() const { + std::scoped_lock lock{mutex}; if (is_configuring) { return {}; } @@ -1134,20 +1370,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 {}; } @@ -1155,26 +1398,54 @@ 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; } +const CameraState& EmulatedController::GetCamera() const { + std::scoped_lock lock{mutex}; + return controller.camera_state; +} + +const NfcState& EmulatedController::GetNfc() const { + std::scoped_lock lock{mutex}; + return controller.nfc_state; +} + +NpadColor EmulatedController::GetNpadColor(u32 color) { + return { + .r = static_cast<u8>((color >> 16) & 0xFF), + .g = static_cast<u8>((color >> 8) & 0xFF), + .b = static_cast<u8>(color & 0xFF), + .a = 0xff, + }; +} + 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) { @@ -1187,13 +1458,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 d8642c5b3..319226bf8 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -16,10 +15,12 @@ #include "common/settings.h" #include "common/vector_math.h" #include "core/hid/hid_types.h" +#include "core/hid/irs_types.h" #include "core/hid/motion_input.h" namespace Core::HID { const std::size_t max_emulated_controllers = 2; +const std::size_t output_devices_size = 4; struct ControllerMotionInfo { Common::Input::MotionStatus raw_status{}; MotionInput emulated{}; @@ -35,15 +36,18 @@ using TriggerDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>; using BatteryDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; -using OutputDevices = - std::array<std::unique_ptr<Common::Input::OutputDevice>, max_emulated_controllers>; +using CameraDevices = std::unique_ptr<Common::Input::InputDevice>; +using NfcDevices = std::unique_ptr<Common::Input::InputDevice>; +using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices_size>; using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>; using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>; using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>; using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>; using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>; -using OutputParams = std::array<Common::ParamPackage, max_emulated_controllers>; +using CameraParams = Common::ParamPackage; +using NfcParams = Common::ParamPackage; +using OutputParams = std::array<Common::ParamPackage, output_devices_size>; using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>; using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>; @@ -52,6 +56,8 @@ using TriggerValues = using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>; using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>; using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>; +using CameraValues = Common::Input::CameraStatus; +using NfcValues = Common::Input::NfcStatus; using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>; struct AnalogSticks { @@ -71,6 +77,17 @@ struct BatteryLevelState { NpadPowerInfo right{}; }; +struct CameraState { + Core::IrSensor::ImageTransferProcessorFormat format{}; + std::vector<u8> data{}; + std::size_t sample{}; +}; + +struct NfcState { + Common::Input::NfcState state{}; + std::vector<u8> data{}; +}; + struct ControllerMotion { Common::Vec3f accel{}; Common::Vec3f gyro{}; @@ -97,6 +114,8 @@ struct ControllerStatus { ColorValues color_values{}; BatteryValues battery_values{}; VibrationValues vibration_values{}; + CameraValues camera_values{}; + NfcValues nfc_values{}; // Data for HID serices HomeButtonState home_button_state{}; @@ -108,6 +127,8 @@ struct ControllerStatus { NpadGcTriggerState gc_trigger_state{}; ControllerColors colors_state{}; BatteryLevelState battery_state{}; + CameraState camera_state{}; + NfcState nfc_state{}; }; enum class ControllerTriggerType { @@ -118,6 +139,8 @@ enum class ControllerTriggerType { Color, Battery, Vibration, + IrSensor, + Nfc, Connected, Disconnected, Type, @@ -270,6 +293,9 @@ public: /// Returns the latest battery status from the controller with parameters BatteryValues GetBatteryValues() const; + /// Returns the latest camera status from the controller with parameters + CameraValues GetCameraValues() const; + /// Returns the latest status of button input for the hid::HomeButton service HomeButtonState GetHomeButtons() const; @@ -297,18 +323,44 @@ public: /// Returns the latest battery status from the controller BatteryLevelState GetBattery() const; + /// Returns the latest camera status from the controller + const CameraState& GetCamera() const; + + /// Returns the latest ntag status from the controller + const NfcState& GetNfc() const; + /** * Sends a specific vibration to the output device - * @return returns true if vibration had no errors + * @return true if vibration had no errors */ bool SetVibration(std::size_t device_index, VibrationValue vibration); /** * Sends a small vibration to the output device - * @return returns true if SetVibration was successfull + * @return true if SetVibration was successfull */ bool TestVibration(std::size_t device_index); + /** + * Sets the desired data to be polled from a controller + * @param polling_mode type of input desired buttons, gyro, nfc, ir, etc. + * @return true if SetPollingMode was successfull + */ + bool SetPollingMode(Common::Input::PollingMode polling_mode); + + /** + * Sets the desired camera format to be polled from a controller + * @param camera_format size of each frame + * @return true if SetCameraFormat was successfull + */ + bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format); + + /// Returns true if the device has nfc support + bool HasNfc() const; + + /// Returns true if the nfc tag was written + bool WriteNfc(const std::vector<u8>& data); + /// Returns the led pattern corresponding to this emulated controller LedPattern GetLedPattern() const; @@ -387,14 +439,34 @@ private: void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index); /** + * Updates the camera status of the controller + * @param callback A CallbackStatus containing the camera status + */ + void SetCamera(const Common::Input::CallbackStatus& callback); + + /** + * Updates the nfc status of the controller + * @param callback A CallbackStatus containing the nfc status + */ + void SetNfc(const Common::Input::CallbackStatus& callback); + + /** + * Converts a color format from bgra to rgba + * @param color in bgra format + * @return NpadColor in rgba format + */ + NpadColor GetNpadColor(u32 color); + + /** * Triggers a callback that something has changed on the controller status * @param type Input type of the event to trigger * @param is_service_update indicates if this event should only be sent to HID services */ void TriggerOnChange(ControllerTriggerType type, bool is_service_update); - NpadIdType npad_id_type; + const NpadIdType npad_id_type; NpadStyleIndex npad_type{NpadStyleIndex::None}; + NpadStyleIndex original_npad_type{NpadStyleIndex::None}; NpadStyleTag supported_style_tag{NpadStyleSet::All}; bool is_connected{false}; bool is_configuring{false}; @@ -411,6 +483,8 @@ private: ControllerMotionParams motion_params; TriggerParams trigger_params; BatteryParams battery_params; + CameraParams camera_params; + NfcParams nfc_params; OutputParams output_params; ButtonDevices button_devices; @@ -418,6 +492,8 @@ private: ControllerMotionDevices motion_devices; TriggerDevices trigger_devices; BatteryDevices battery_devices; + CameraDevices camera_devices; + NfcDevices nfc_devices; OutputDevices output_devices; // TAS related variables @@ -427,6 +503,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..8d367b546 100644 --- a/src/core/hid/emulated_devices.cpp +++ b/src/core/hid/emulated_devices.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <fmt/format.h> @@ -15,6 +14,7 @@ EmulatedDevices::EmulatedDevices() = default; EmulatedDevices::~EmulatedDevices() = default; void EmulatedDevices::ReloadFromSettings() { + ring_params = Common::ParamPackage(Settings::values.ringcon_analogs); ReloadInput(); } @@ -66,6 +66,8 @@ void EmulatedDevices::ReloadInput() { key_index++; } + ring_analog_device = Common::Input::CreateDevice<Common::Input::InputDevice>(ring_params); + for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) { if (!mouse_button_devices[index]) { continue; @@ -120,6 +122,13 @@ void EmulatedDevices::ReloadInput() { }, }); } + + if (ring_analog_device) { + ring_analog_device->SetCallback({ + .on_change = + [this](const Common::Input::CallbackStatus& callback) { SetRingAnalog(callback); }, + }); + } } void EmulatedDevices::UnloadInput() { @@ -155,6 +164,7 @@ void EmulatedDevices::SaveCurrentConfig() { if (!is_configuring) { return; } + Settings::values.ringcon_analogs = ring_params.Serialize(); } void EmulatedDevices::RestoreConfig() { @@ -164,12 +174,21 @@ void EmulatedDevices::RestoreConfig() { ReloadFromSettings(); } +Common::ParamPackage EmulatedDevices::GetRingParam() const { + return ring_params; +} + +void EmulatedDevices::SetRingParam(Common::ParamPackage param) { + ring_params = std::move(param); + ReloadInput(); +} + void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback, std::size_t index) { 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 +220,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal } if (is_configuring) { + lock.unlock(); TriggerOnChange(DeviceTriggerType::Keyboard); return; } @@ -208,6 +228,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 +248,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 +280,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c } if (is_configuring) { + lock.unlock(); TriggerOnChange(DeviceTriggerType::KeyboardModdifier); return; } @@ -289,6 +311,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c break; } + lock.unlock(); TriggerOnChange(DeviceTriggerType::KeyboardModdifier); } @@ -297,7 +320,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 +352,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba } if (is_configuring) { + lock.unlock(); TriggerOnChange(DeviceTriggerType::Mouse); return; } @@ -351,6 +375,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba break; } + lock.unlock(); TriggerOnChange(DeviceTriggerType::Mouse); } @@ -359,13 +384,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 +405,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 +425,77 @@ 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); } +void EmulatedDevices::SetRingAnalog(const Common::Input::CallbackStatus& callback) { + std::lock_guard lock{mutex}; + const auto force_value = TransformToStick(callback); + + device_status.ring_analog_value = force_value.x; + + if (is_configuring) { + device_status.ring_analog_value = {}; + TriggerOnChange(DeviceTriggerType::RingController); + return; + } + + device_status.ring_analog_state.force = force_value.x.value; + + TriggerOnChange(DeviceTriggerType::RingController); +} + 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; } +RingAnalogValue EmulatedDevices::GetRingSensorValues() const { + return device_status.ring_analog_value; +} + 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; } +RingSensorForce EmulatedDevices::GetRingSensorForce() const { + return device_status.ring_analog_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 +505,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..4149eeced 100644 --- a/src/core/hid/emulated_devices.h +++ b/src/core/hid/emulated_devices.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -26,9 +25,11 @@ using MouseButtonDevices = std::array<std::unique_ptr<Common::Input::InputDevice using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeMouseWheel::NumMouseWheels>; using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>; +using RingAnalogDevice = std::unique_ptr<Common::Input::InputDevice>; using MouseButtonParams = std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>; +using RingAnalogParams = Common::ParamPackage; using KeyboardValues = std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>; @@ -39,12 +40,17 @@ using MouseButtonValues = using MouseAnalogValues = std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>; using MouseStickValue = Common::Input::TouchStatus; +using RingAnalogValue = Common::Input::AnalogStatus; struct MousePosition { f32 x; f32 y; }; +struct RingSensorForce { + f32 force; +}; + struct DeviceStatus { // Data from input_common KeyboardValues keyboard_values{}; @@ -52,6 +58,7 @@ struct DeviceStatus { MouseButtonValues mouse_button_values{}; MouseAnalogValues mouse_analog_values{}; MouseStickValue mouse_stick_value{}; + RingAnalogValue ring_analog_value{}; // Data for HID serices KeyboardKey keyboard_state{}; @@ -59,12 +66,14 @@ struct DeviceStatus { MouseButton mouse_button_state{}; MousePosition mouse_position_state{}; AnalogStickState mouse_wheel_state{}; + RingSensorForce ring_analog_state{}; }; enum class DeviceTriggerType { Keyboard, KeyboardModdifier, Mouse, + RingController, }; struct InterfaceUpdateCallback { @@ -110,6 +119,15 @@ public: /// Reverts any mapped changes made that weren't saved void RestoreConfig(); + // Returns the current mapped ring device + Common::ParamPackage GetRingParam() const; + + /** + * Updates the current mapped ring device + * @param param ParamPackage with ring sensor data to be mapped + */ + void SetRingParam(Common::ParamPackage param); + /// Returns the latest status of button input from the keyboard with parameters KeyboardValues GetKeyboardValues() const; @@ -119,6 +137,9 @@ public: /// Returns the latest status of button input from the mouse with parameters MouseButtonValues GetMouseButtonsValues() const; + /// Returns the latest status of analog input from the ring sensor with parameters + RingAnalogValue GetRingSensorValues() const; + /// Returns the latest status of button input from the keyboard KeyboardKey GetKeyboard() const; @@ -134,6 +155,9 @@ public: /// Returns the latest mouse wheel change AnalogStickState GetMouseWheel() const; + /// Returns the latest ringcon force sensor value + RingSensorForce GetRingSensorForce() const; + /** * Adds a callback to the list of events * @param update_callback InterfaceUpdateCallback that will be triggered @@ -186,6 +210,12 @@ private: void SetMouseStick(const Common::Input::CallbackStatus& callback); /** + * Updates the ring analog sensor status of the ring controller + * @param callback A CallbackStatus containing the force status + */ + void SetRingAnalog(const Common::Input::CallbackStatus& callback); + + /** * Triggers a callback that something has changed on the device status * @param type Input type of the event to trigger */ @@ -193,13 +223,17 @@ private: bool is_configuring{false}; + RingAnalogParams ring_params; + KeyboardDevices keyboard_devices; KeyboardModifierDevices keyboard_modifier_devices; MouseButtonDevices mouse_button_devices; MouseAnalogDevices mouse_analog_devices; MouseStickDevice mouse_stick_device; + RingAnalogDevice ring_analog_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/hid/hid_core.cpp b/src/core/hid/hid_core.cpp index a1c3bbb57..7d6373414 100644 --- a/src/core/hid/hid_core.cpp +++ b/src/core/hid/hid_core.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "core/hid/emulated_console.h" @@ -49,7 +48,7 @@ EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) { return handheld.get(); case NpadIdType::Invalid: default: - UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type); + ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type); return nullptr; } } @@ -78,7 +77,7 @@ const EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type return handheld.get(); case NpadIdType::Invalid: default: - UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type); + ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type); return nullptr; } } diff --git a/src/core/hid/hid_core.h b/src/core/hid/hid_core.h index 717f605e7..5fe36551e 100644 --- a/src/core/hid/hid_core.h +++ b/src/core/hid/hid_core.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h index 778b328b9..e3b1cfbc6 100644 --- a/src/core/hid/hid_types.h +++ b/src/core/hid/hid_types.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -273,6 +272,7 @@ enum class VibrationDeviceType : u32 { Unknown = 0, LinearResonantActuator = 1, GcErm = 2, + N64 = 3, }; // This is nn::hid::VibrationGcErmCommand @@ -317,27 +317,35 @@ static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size" // This is nn::hid::TouchState struct TouchState { - u64 delta_time; - TouchAttribute attribute; - u32 finger; - Common::Point<u32> position; - u32 diameter_x; - u32 diameter_y; - u32 rotation_angle; + u64 delta_time{}; + TouchAttribute attribute{}; + u32 finger{}; + Common::Point<u32> position{}; + u32 diameter_x{}; + u32 diameter_y{}; + u32 rotation_angle{}; }; static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); +struct NpadColor { + u8 r{}; + u8 g{}; + u8 b{}; + u8 a{}; +}; +static_assert(sizeof(NpadColor) == 4, "NpadColor is an invalid size"); + // This is nn::hid::NpadControllerColor struct NpadControllerColor { - u32 body; - u32 button; + NpadColor body{}; + NpadColor button{}; }; static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size"); // This is nn::hid::AnalogStickState struct AnalogStickState { - s32 x; - s32 y; + s32 x{}; + s32 y{}; }; static_assert(sizeof(AnalogStickState) == 8, "AnalogStickState is an invalid size"); @@ -355,10 +363,10 @@ static_assert(sizeof(NpadBatteryLevel) == 0x4, "NpadBatteryLevel is an invalid s // This is nn::hid::system::NpadPowerInfo struct NpadPowerInfo { - bool is_powered; - bool is_charging; + bool is_powered{}; + bool is_charging{}; INSERT_PADDING_BYTES(0x6); - NpadBatteryLevel battery_level; + NpadBatteryLevel battery_level{8}; }; static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size"); @@ -475,8 +483,8 @@ static_assert(sizeof(DebugPadButton) == 0x4, "DebugPadButton is an invalid size" // This is nn::hid::ConsoleSixAxisSensorHandle struct ConsoleSixAxisSensorHandle { - u8 unknown_1; - u8 unknown_2; + u8 unknown_1{}; + u8 unknown_2{}; INSERT_PADDING_BYTES_NOINIT(2); }; static_assert(sizeof(ConsoleSixAxisSensorHandle) == 4, @@ -484,35 +492,79 @@ static_assert(sizeof(ConsoleSixAxisSensorHandle) == 4, // This is nn::hid::SixAxisSensorHandle struct SixAxisSensorHandle { - NpadStyleIndex npad_type; - u8 npad_id; - DeviceIndex device_index; + NpadStyleIndex npad_type{NpadStyleIndex::None}; + u8 npad_id{}; + DeviceIndex device_index{DeviceIndex::None}; INSERT_PADDING_BYTES_NOINIT(1); }; static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size"); +// These parameters seem related to how much gyro/accelerometer is used struct SixAxisSensorFusionParameters { - f32 parameter1; - f32 parameter2; + f32 parameter1{0.03f}; // Range 0.0 to 1.0, default 0.03 + f32 parameter2{0.4f}; // Default 0.4 }; static_assert(sizeof(SixAxisSensorFusionParameters) == 8, "SixAxisSensorFusionParameters is an invalid size"); +// This is nn::hid::server::SixAxisSensorProperties +struct SixAxisSensorProperties { + union { + u8 raw{}; + BitField<0, 1, u8> is_newly_assigned; + BitField<1, 1, u8> is_firmware_update_available; + }; +}; +static_assert(sizeof(SixAxisSensorProperties) == 1, "SixAxisSensorProperties is an invalid size"); + +// This is nn::hid::SixAxisSensorCalibrationParameter +struct SixAxisSensorCalibrationParameter { + std::array<u8, 0x744> unknown_data{}; +}; +static_assert(sizeof(SixAxisSensorCalibrationParameter) == 0x744, + "SixAxisSensorCalibrationParameter is an invalid size"); + +// This is nn::hid::SixAxisSensorIcInformation +struct SixAxisSensorIcInformation { + f32 angular_rate{2000.0f}; // dps + std::array<f32, 6> unknown_gyro_data1{ + -10.0f, -10.0f, -10.0f, 10.0f, 10.0f, 10.0f, + }; // dps + std::array<f32, 9> unknown_gyro_data2{ + 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, + }; + std::array<f32, 9> unknown_gyro_data3{ + 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, + }; + f32 acceleration_range{8.0f}; // g force + std::array<f32, 6> unknown_accel_data1{ + -0.0612f, -0.0612f, -0.0612f, 0.0612f, 0.0612f, 0.0612f, + }; // g force + std::array<f32, 9> unknown_accel_data2{ + 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, + }; + std::array<f32, 9> unknown_accel_data3{ + 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, + }; +}; +static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8, + "SixAxisSensorIcInformation is an invalid size"); + // This is nn::hid::VibrationDeviceHandle struct VibrationDeviceHandle { - NpadStyleIndex npad_type; - u8 npad_id; - DeviceIndex device_index; + NpadStyleIndex npad_type{NpadStyleIndex::None}; + u8 npad_id{}; + DeviceIndex device_index{DeviceIndex::None}; INSERT_PADDING_BYTES_NOINIT(1); }; static_assert(sizeof(VibrationDeviceHandle) == 4, "SixAxisSensorHandle is an invalid size"); // This is nn::hid::VibrationValue struct VibrationValue { - f32 low_amplitude; - f32 low_frequency; - f32 high_amplitude; - f32 high_frequency; + f32 low_amplitude{}; + f32 low_frequency{}; + f32 high_amplitude{}; + f32 high_frequency{}; }; static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size."); @@ -561,7 +613,7 @@ static_assert(sizeof(KeyboardAttribute) == 0x4, "KeyboardAttribute is an invalid // This is nn::hid::KeyboardKey struct KeyboardKey { // This should be a 256 bit flag - std::array<u8, 32> key; + std::array<u8, 32> key{}; }; static_assert(sizeof(KeyboardKey) == 0x20, "KeyboardKey is an invalid size"); @@ -590,16 +642,16 @@ static_assert(sizeof(MouseAttribute) == 0x4, "MouseAttribute is an invalid size" // This is nn::hid::detail::MouseState struct MouseState { - s64 sampling_number; - s32 x; - s32 y; - s32 delta_x; - s32 delta_y; + s64 sampling_number{}; + s32 x{}; + s32 y{}; + s32 delta_x{}; + s32 delta_y{}; // Axis Order in HW is switched for the wheel - s32 delta_wheel_y; - s32 delta_wheel_x; - MouseButton button; - MouseAttribute attribute; + s32 delta_wheel_y{}; + s32 delta_wheel_x{}; + MouseButton button{}; + MouseAttribute attribute{}; }; static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size"); diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp index cd41607a7..fe9915abe 100644 --- a/src/core/hid/input_converter.cpp +++ b/src/core/hid/input_converter.cpp @@ -1,7 +1,7 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#include <algorithm> #include <random> #include "common/input.h" @@ -52,6 +52,9 @@ Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatu Common::Input::ButtonStatus status{}; switch (callback.type) { case Common::Input::InputType::Analog: + status.value = TransformToTrigger(callback).pressed.value; + status.toggle = callback.analog_status.properties.toggle; + break; case Common::Input::InputType::Trigger: status.value = TransformToTrigger(callback).pressed.value; break; @@ -197,6 +200,9 @@ Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& x = std::clamp(x, 0.0f, 1.0f); y = std::clamp(y, 0.0f, 1.0f); + // Limit id to maximum number of fingers + status.id = std::clamp(status.id, 0, 16); + if (status.pressed.inverted) { status.pressed.value = !status.pressed.value; } @@ -267,6 +273,34 @@ Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatu return status; } +Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback) { + Common::Input::CameraStatus camera{}; + switch (callback.type) { + case Common::Input::InputType::IrSensor: + camera = callback.camera_status; + break; + default: + LOG_ERROR(Input, "Conversion from type {} to camera not implemented", callback.type); + break; + } + + return camera; +} + +Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback) { + Common::Input::NfcStatus nfc{}; + switch (callback.type) { + case Common::Input::InputType::Nfc: + return callback.nfc_status; + break; + default: + LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type); + break; + } + + return nfc; +} + void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) { const auto& properties = analog.properties; float& raw_value = analog.raw_value; @@ -328,7 +362,7 @@ void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogS raw_y += properties_y.offset; // Apply X scale correction from offset - if (std::abs(properties_x.offset) < 0.5f) { + if (std::abs(properties_x.offset) < 0.75f) { if (raw_x > 0) { raw_x /= 1 + properties_x.offset; } else { @@ -337,7 +371,7 @@ void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogS } // Apply Y scale correction from offset - if (std::abs(properties_y.offset) < 0.5f) { + if (std::abs(properties_y.offset) < 0.75f) { if (raw_y > 0) { raw_y /= 1 + properties_y.offset; } else { diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h index d24582226..b7eb6e660 100644 --- a/src/core/hid/input_converter.h +++ b/src/core/hid/input_converter.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -78,6 +77,22 @@ Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackSta Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback); /** + * Converts raw input data into a valid camera status. + * + * @param callback Supported callbacks: Camera. + * @return A valid CameraObject object. + */ +Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback); + +/** + * Converts raw input data into a valid nfc status. + * + * @param callback Supported callbacks: Nfc. + * @return A valid CameraObject object. + */ +Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback); + +/** * Converts raw analog data into a valid analog value * @param analog An analog object containing raw data and properties * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f. diff --git a/src/core/hid/input_interpreter.cpp b/src/core/hid/input_interpreter.cpp index 2dbda8814..76d6b8ab0 100644 --- a/src/core/hid/input_interpreter.cpp +++ b/src/core/hid/input_interpreter.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" #include "core/hid/hid_types.h" diff --git a/src/core/hid/input_interpreter.h b/src/core/hid/input_interpreter.h index 70c34d474..8c521b381 100644 --- a/src/core/hid/input_interpreter.h +++ b/src/core/hid/input_interpreter.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hid/irs_types.h b/src/core/hid/irs_types.h new file mode 100644 index 000000000..88c5b016d --- /dev/null +++ b/src/core/hid/irs_types.h @@ -0,0 +1,301 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hid/hid_types.h" + +namespace Core::IrSensor { + +// This is nn::irsensor::CameraAmbientNoiseLevel +enum class CameraAmbientNoiseLevel : u32 { + Low, + Medium, + High, + Unkown3, // This level can't be reached +}; + +// This is nn::irsensor::CameraLightTarget +enum class CameraLightTarget : u32 { + AllLeds, + BrightLeds, + DimLeds, + None, +}; + +// This is nn::irsensor::PackedCameraLightTarget +enum class PackedCameraLightTarget : u8 { + AllLeds, + BrightLeds, + DimLeds, + None, +}; + +// This is nn::irsensor::AdaptiveClusteringMode +enum class AdaptiveClusteringMode : u32 { + StaticFov, + DynamicFov, +}; + +// This is nn::irsensor::AdaptiveClusteringTargetDistance +enum class AdaptiveClusteringTargetDistance : u32 { + Near, + Middle, + Far, +}; + +// This is nn::irsensor::ImageTransferProcessorFormat +enum class ImageTransferProcessorFormat : u32 { + Size320x240, + Size160x120, + Size80x60, + Size40x30, + Size20x15, +}; + +// This is nn::irsensor::PackedImageTransferProcessorFormat +enum class PackedImageTransferProcessorFormat : u8 { + Size320x240, + Size160x120, + Size80x60, + Size40x30, + Size20x15, +}; + +// This is nn::irsensor::IrCameraStatus +enum class IrCameraStatus : u32 { + Available, + Unsupported, + Unconnected, +}; + +// This is nn::irsensor::IrCameraInternalStatus +enum class IrCameraInternalStatus : u32 { + Stopped, + FirmwareUpdateNeeded, + Unkown2, + Unkown3, + Unkown4, + FirmwareVersionRequested, + FirmwareVersionIsInvalid, + Ready, + Setting, +}; + +// This is nn::irsensor::detail::StatusManager::IrSensorMode +enum class IrSensorMode : u64 { + None, + MomentProcessor, + ClusteringProcessor, + ImageTransferProcessor, + PointingProcessorMarker, + TeraPluginProcessor, + IrLedProcessor, +}; + +// This is nn::irsensor::ImageProcessorStatus +enum ImageProcessorStatus : u32 { + Stopped, + Running, +}; + +// This is nn::irsensor::HandAnalysisMode +enum class HandAnalysisMode : u32 { + None, + Silhouette, + Image, + SilhoueteAndImage, + SilhuetteOnly, +}; + +// This is nn::irsensor::IrSensorFunctionLevel +enum class IrSensorFunctionLevel : u8 { + unknown0, + unknown1, + unknown2, + unknown3, + unknown4, +}; + +// This is nn::irsensor::MomentProcessorPreprocess +enum class MomentProcessorPreprocess : u32 { + Unkown0, + Unkown1, +}; + +// This is nn::irsensor::PackedMomentProcessorPreprocess +enum class PackedMomentProcessorPreprocess : u8 { + Unkown0, + Unkown1, +}; + +// This is nn::irsensor::PointingStatus +enum class PointingStatus : u32 { + Unkown0, + Unkown1, +}; + +struct IrsRect { + s16 x; + s16 y; + s16 width; + s16 height; +}; + +struct IrsCentroid { + f32 x; + f32 y; +}; + +struct CameraConfig { + u64 exposure_time; + CameraLightTarget light_target; + u32 gain; + bool is_negative_used; + INSERT_PADDING_BYTES(7); +}; +static_assert(sizeof(CameraConfig) == 0x18, "CameraConfig is an invalid size"); + +struct PackedCameraConfig { + u64 exposure_time; + PackedCameraLightTarget light_target; + u8 gain; + bool is_negative_used; + INSERT_PADDING_BYTES(5); +}; +static_assert(sizeof(PackedCameraConfig) == 0x10, "PackedCameraConfig is an invalid size"); + +// This is nn::irsensor::IrCameraHandle +struct IrCameraHandle { + u8 npad_id{}; + Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None}; + INSERT_PADDING_BYTES(2); +}; +static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size"); + +// This is nn::irsensor::PackedMcuVersion +struct PackedMcuVersion { + u16 major; + u16 minor; +}; +static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size"); + +// This is nn::irsensor::PackedMomentProcessorConfig +struct PackedMomentProcessorConfig { + PackedCameraConfig camera_config; + IrsRect window_of_interest; + PackedMcuVersion required_mcu_version; + PackedMomentProcessorPreprocess preprocess; + u8 preprocess_intensity_threshold; + INSERT_PADDING_BYTES(2); +}; +static_assert(sizeof(PackedMomentProcessorConfig) == 0x20, + "PackedMomentProcessorConfig is an invalid size"); + +// This is nn::irsensor::PackedClusteringProcessorConfig +struct PackedClusteringProcessorConfig { + PackedCameraConfig camera_config; + IrsRect window_of_interest; + PackedMcuVersion required_mcu_version; + u32 pixel_count_min; + u32 pixel_count_max; + u8 object_intensity_min; + bool is_external_light_filter_enabled; + INSERT_PADDING_BYTES(2); +}; +static_assert(sizeof(PackedClusteringProcessorConfig) == 0x28, + "PackedClusteringProcessorConfig is an invalid size"); + +// This is nn::irsensor::PackedImageTransferProcessorConfig +struct PackedImageTransferProcessorConfig { + PackedCameraConfig camera_config; + PackedMcuVersion required_mcu_version; + PackedImageTransferProcessorFormat format; + INSERT_PADDING_BYTES(3); +}; +static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18, + "PackedImageTransferProcessorConfig is an invalid size"); + +// This is nn::irsensor::PackedTeraPluginProcessorConfig +struct PackedTeraPluginProcessorConfig { + PackedMcuVersion required_mcu_version; + u8 mode; + u8 unknown_1; + u8 unknown_2; + u8 unknown_3; +}; +static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8, + "PackedTeraPluginProcessorConfig is an invalid size"); + +// This is nn::irsensor::PackedPointingProcessorConfig +struct PackedPointingProcessorConfig { + IrsRect window_of_interest; + PackedMcuVersion required_mcu_version; +}; +static_assert(sizeof(PackedPointingProcessorConfig) == 0xC, + "PackedPointingProcessorConfig is an invalid size"); + +// This is nn::irsensor::PackedFunctionLevel +struct PackedFunctionLevel { + IrSensorFunctionLevel function_level; + INSERT_PADDING_BYTES(3); +}; +static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size"); + +// This is nn::irsensor::PackedImageTransferProcessorExConfig +struct PackedImageTransferProcessorExConfig { + PackedCameraConfig camera_config; + PackedMcuVersion required_mcu_version; + PackedImageTransferProcessorFormat origin_format; + PackedImageTransferProcessorFormat trimming_format; + u16 trimming_start_x; + u16 trimming_start_y; + bool is_external_light_filter_enabled; + INSERT_PADDING_BYTES(5); +}; +static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20, + "PackedImageTransferProcessorExConfig is an invalid size"); + +// This is nn::irsensor::PackedIrLedProcessorConfig +struct PackedIrLedProcessorConfig { + PackedMcuVersion required_mcu_version; + u8 light_target; + INSERT_PADDING_BYTES(3); +}; +static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8, + "PackedIrLedProcessorConfig is an invalid size"); + +// This is nn::irsensor::HandAnalysisConfig +struct HandAnalysisConfig { + HandAnalysisMode mode; +}; +static_assert(sizeof(HandAnalysisConfig) == 0x4, "HandAnalysisConfig is an invalid size"); + +// This is nn::irsensor::detail::ProcessorState contents are different for each processor +struct ProcessorState { + std::array<u8, 0xE20> processor_raw_data{}; +}; +static_assert(sizeof(ProcessorState) == 0xE20, "ProcessorState is an invalid size"); + +// This is nn::irsensor::detail::DeviceFormat +struct DeviceFormat { + Core::IrSensor::IrCameraStatus camera_status{Core::IrSensor::IrCameraStatus::Unconnected}; + Core::IrSensor::IrCameraInternalStatus camera_internal_status{ + Core::IrSensor::IrCameraInternalStatus::Ready}; + Core::IrSensor::IrSensorMode mode{Core::IrSensor::IrSensorMode::None}; + ProcessorState state{}; +}; +static_assert(sizeof(DeviceFormat) == 0xE30, "DeviceFormat is an invalid size"); + +// This is nn::irsensor::ImageTransferProcessorState +struct ImageTransferProcessorState { + u64 sampling_number; + Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; + INSERT_PADDING_BYTES(4); +}; +static_assert(sizeof(ImageTransferProcessorState) == 0x10, + "ImageTransferProcessorState is an invalid size"); + +} // namespace Core::IrSensor diff --git a/src/core/hid/motion_input.cpp b/src/core/hid/motion_input.cpp index 05042fd99..b1f658e62 100644 --- a/src/core/hid/motion_input.cpp +++ b/src/core/hid/motion_input.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/math_util.h" #include "core/hid/motion_input.h" diff --git a/src/core/hid/motion_input.h b/src/core/hid/motion_input.h index bca4520fa..f5fd90db5 100644 --- a/src/core/hid/motion_input.h +++ b/src/core/hid/motion_input.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/api_version.h b/src/core/hle/api_version.h index 626e30753..bd15606e1 100644 --- a/src/core/hle/api_version.h +++ b/src/core/hle/api_version.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index 602e12606..416da15ec 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h @@ -1,6 +1,5 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2016 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 026257115..d631c0357 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -1,6 +1,5 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2016 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -19,7 +18,7 @@ namespace IPC { -constexpr ResultCode ERR_REMOTE_PROCESS_DEAD{ErrorModule::HIPC, 301}; +constexpr Result ERR_REMOTE_PROCESS_DEAD{ErrorModule::HIPC, 301}; class RequestHelperBase { protected: @@ -176,7 +175,7 @@ public: void PushImpl(float value); void PushImpl(double value); void PushImpl(bool value); - void PushImpl(ResultCode value); + void PushImpl(Result value); template <typename T> void Push(T value) { @@ -251,7 +250,7 @@ void ResponseBuilder::PushRaw(const T& value) { index += (sizeof(T) + 3) / 4; // round up to word length } -inline void ResponseBuilder::PushImpl(ResultCode value) { +inline void ResponseBuilder::PushImpl(Result value) { // Result codes are actually 64-bit in the IPC buffer, but only the high part is discarded. Push(value.raw); Push<u32>(0); @@ -385,7 +384,7 @@ public: T PopRaw(); template <class T> - std::shared_ptr<T> PopIpcInterface() { + std::weak_ptr<T> PopIpcInterface() { ASSERT(context->Session()->IsDomain()); ASSERT(context->GetDomainMessageHeader().input_object_count > 0); return context->GetDomainHandler<T>(Pop<u32>() - 1); @@ -481,8 +480,8 @@ inline bool RequestParser::Pop() { } template <> -inline ResultCode RequestParser::Pop() { - return ResultCode{Pop<u32>()}; +inline Result RequestParser::Pop() { + return Result{Pop<u32>()}; } template <typename T> diff --git a/src/core/hle/kernel/arch/arm64/k_memory_region_device_types.inc b/src/core/hle/kernel/arch/arm64/k_memory_region_device_types.inc index 857b512ba..2a0f2d5f7 100644 --- a/src/core/hle/kernel/arch/arm64/k_memory_region_device_types.inc +++ b/src/core/hle/kernel/arch/arm64/k_memory_region_device_types.inc @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later // All architectures must define NumArchitectureDeviceRegions. constexpr inline const auto NumArchitectureDeviceRegions = 3; diff --git a/src/core/hle/kernel/board/nintendo/nx/k_memory_layout.h b/src/core/hle/kernel/board/nintendo/nx/k_memory_layout.h new file mode 100644 index 000000000..d02ee61c3 --- /dev/null +++ b/src/core/hle/kernel/board/nintendo/nx/k_memory_layout.h @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace Kernel { + +constexpr inline PAddr MainMemoryAddress = 0x80000000; + +} // namespace Kernel diff --git a/src/core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc b/src/core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc index 58d6c0b16..5f5ec6d5b 100644 --- a/src/core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc +++ b/src/core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later // All architectures must define NumBoardDeviceRegions. constexpr inline const auto NumBoardDeviceRegions = 6; 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 6f335c251..c10b7bf30 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 @@ -1,10 +1,10 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <random> #include "common/literals.h" +#include "common/settings.h" #include "core/hle/kernel/board/nintendo/nx/k_system_control.h" #include "core/hle/kernel/board/nintendo/nx/secure_monitor.h" @@ -28,33 +28,20 @@ namespace { using namespace Common::Literals; -u32 GetMemoryModeForInit() { - return 0x01; -} - u32 GetMemorySizeForInit() { - return 0; + return Settings::values.use_extended_memory_layout ? Smc::MemorySize_6GB : Smc::MemorySize_4GB; } Smc::MemoryArrangement GetMemoryArrangeForInit() { - switch (GetMemoryModeForInit() & 0x3F) { - case 0x01: - default: - return Smc::MemoryArrangement_4GB; - case 0x02: - return Smc::MemoryArrangement_4GBForAppletDev; - case 0x03: - return Smc::MemoryArrangement_4GBForSystemDev; - case 0x11: - return Smc::MemoryArrangement_6GB; - case 0x12: - return Smc::MemoryArrangement_6GBForAppletDev; - case 0x21: - return Smc::MemoryArrangement_8GB; - } + return Settings::values.use_extended_memory_layout ? Smc::MemoryArrangement_6GB + : Smc::MemoryArrangement_4GB; } } // namespace +size_t KSystemControl::Init::GetRealMemorySize() { + return GetIntendedMemorySize(); +} + // Initialization. size_t KSystemControl::Init::GetIntendedMemorySize() { switch (GetMemorySizeForInit()) { @@ -69,7 +56,13 @@ size_t KSystemControl::Init::GetIntendedMemorySize() { } PAddr KSystemControl::Init::GetKernelPhysicalBaseAddress(u64 base_address) { - return base_address; + const size_t real_dram_size = KSystemControl::Init::GetRealMemorySize(); + const size_t intended_dram_size = KSystemControl::Init::GetIntendedMemorySize(); + if (intended_dram_size * 2 < real_dram_size) { + return base_address; + } else { + return base_address + ((real_dram_size - intended_dram_size) / 2); + } } bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() { @@ -154,9 +147,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/board/nintendo/nx/k_system_control.h b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h index 52f230ced..fe375769e 100644 --- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h +++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -13,6 +12,7 @@ public: class Init { public: // Initialization. + static std::size_t GetRealMemorySize(); static std::size_t GetIntendedMemorySize(); static PAddr GetKernelPhysicalBaseAddress(u64 base_address); static bool ShouldIncreaseThreadResourceLimit(); diff --git a/src/core/hle/kernel/board/nintendo/nx/secure_monitor.h b/src/core/hle/kernel/board/nintendo/nx/secure_monitor.h index f77a91dec..b0e4123f0 100644 --- a/src/core/hle/kernel/board/nintendo/nx/secure_monitor.h +++ b/src/core/hle/kernel/board/nintendo/nx/secure_monitor.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/code_set.cpp b/src/core/hle/kernel/code_set.cpp index 1f434e9af..41386048b 100644 --- a/src/core/hle/kernel/code_set.cpp +++ b/src/core/hle/kernel/code_set.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/code_set.h" diff --git a/src/core/hle/kernel/code_set.h b/src/core/hle/kernel/code_set.h index 5cc3b9829..5220dbcb6 100644 --- a/src/core/hle/kernel/code_set.h +++ b/src/core/hle/kernel/code_set.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/global_scheduler_context.cpp b/src/core/hle/kernel/global_scheduler_context.cpp index baad2c5d6..65576b8c4 100644 --- a/src/core/hle/kernel/global_scheduler_context.cpp +++ b/src/core/hle/kernel/global_scheduler_context.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <mutex> @@ -42,12 +41,7 @@ void GlobalSchedulerContext::PreemptThreads() { ASSERT(IsLocked()); for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { const u32 priority = preemption_priorities[core_id]; - kernel.Scheduler(core_id).RotateScheduledQueue(core_id, priority); - - // Signal an interrupt occurred. For core 3, this is a certainty, as preemption will result - // in the rotator thread being scheduled. For cores 0-2, this is to simulate or system - // interrupts that may have occurred. - kernel.PhysicalCore(core_id).Interrupt(); + KScheduler::RotateScheduledQueue(kernel, core_id, priority); } } diff --git a/src/core/hle/kernel/global_scheduler_context.h b/src/core/hle/kernel/global_scheduler_context.h index 6f44b534f..67bb9852d 100644 --- a/src/core/hle/kernel/global_scheduler_context.h +++ b/src/core/hle/kernel/global_scheduler_context.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,7 +7,6 @@ #include <vector> #include "common/common_types.h" -#include "common/spin_lock.h" #include "core/hardware_properties.h" #include "core/hle/kernel/k_priority_queue.h" #include "core/hle/kernel/k_scheduler_lock.h" @@ -80,7 +78,7 @@ private: /// Lists all thread ids that aren't deleted/etc. std::vector<KThread*> thread_list; - Common::SpinLock global_list_guard{}; + std::mutex global_list_guard; }; } // namespace Kernel diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index e19544c54..5b3feec66 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <array> @@ -17,7 +16,6 @@ #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_process.h" -#include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_server_session.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" @@ -25,8 +23,15 @@ namespace Kernel { -SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_) - : kernel{kernel_}, service_thread{kernel.CreateServiceThread(service_name_)} {} +SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_, + ServiceThreadType thread_type) + : kernel{kernel_} { + if (thread_type == ServiceThreadType::CreateNew) { + service_thread = kernel.CreateServiceThread(service_name_); + } else { + service_thread = kernel.GetDefaultServiceThread(); + } +} SessionRequestHandler::~SessionRequestHandler() { kernel.ReleaseServiceThread(service_thread); @@ -45,7 +50,7 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co LOG_CRITICAL(IPC, "object_id {} is too big!", object_id); return false; } - return DomainHandler(object_id - 1) != nullptr; + return !DomainHandler(object_id - 1).expired(); } else { return session_handler != nullptr; } @@ -55,7 +60,7 @@ void SessionRequestHandler::ClientConnected(KServerSession* session) { session->ClientConnected(shared_from_this()); // Ensure our server session is tracked globally. - kernel.RegisterServerSession(session); + kernel.RegisterServerObject(session); } void SessionRequestHandler::ClientDisconnected(KServerSession* session) { @@ -183,8 +188,8 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32 rp.Skip(1, false); // The command is actually an u64, but we don't use the high part. } -ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table, - u32_le* src_cmdbuf) { +Result HLERequestContext::PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table, + u32_le* src_cmdbuf) { ParseCommandBuffer(handle_table, src_cmdbuf, true); if (command_header->IsCloseCommand()) { @@ -197,7 +202,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const KHandleTab return ResultSuccess; } -ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_thread) { +Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_thread) { auto current_offset = handles_offset; auto& owner_process = *requesting_thread.GetOwnerProcess(); auto& handle_table = owner_process.GetHandleTable(); @@ -282,15 +287,49 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, BufferDescriptorB().size() > buffer_index && BufferDescriptorB()[buffer_index].Size() >= size, { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", buffer_index, size); - memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); + WriteBufferB(buffer, size, buffer_index); } else { ASSERT_OR_EXECUTE_MSG( BufferDescriptorC().size() > buffer_index && BufferDescriptorC()[buffer_index].Size() >= size, { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", buffer_index, size); - memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); + WriteBufferC(buffer, size, buffer_index); + } + + return size; +} + +std::size_t HLERequestContext::WriteBufferB(const void* buffer, std::size_t size, + std::size_t buffer_index) const { + if (buffer_index >= BufferDescriptorB().size() || size == 0) { + return 0; + } + + const auto buffer_size{BufferDescriptorB()[buffer_index].Size()}; + if (size > buffer_size) { + LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size, + buffer_size); + size = buffer_size; // TODO(bunnei): This needs to be HW tested + } + + memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); + return size; +} + +std::size_t HLERequestContext::WriteBufferC(const void* buffer, std::size_t size, + std::size_t buffer_index) const { + if (buffer_index >= BufferDescriptorC().size() || size == 0) { + return 0; + } + + const auto buffer_size{BufferDescriptorC()[buffer_index].Size()}; + if (size > buffer_size) { + LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size, + buffer_size); + size = buffer_size; // TODO(bunnei): This needs to be HW tested } + memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); return size; } diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 754b41ff6..99265ce90 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -19,7 +18,7 @@ #include "core/hle/ipc.h" #include "core/hle/kernel/svc_common.h" -union ResultCode; +union Result; namespace Core::Memory { class Memory; @@ -33,6 +32,11 @@ namespace Service { class ServiceFrameworkBase; } +enum class ServiceThreadType { + Default, + CreateNew, +}; + namespace Kernel { class Domain; @@ -57,7 +61,8 @@ enum class ThreadWakeupReason; */ class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> { public: - SessionRequestHandler(KernelCore& kernel, const char* service_name_); + SessionRequestHandler(KernelCore& kernel_, const char* service_name_, + ServiceThreadType thread_type); virtual ~SessionRequestHandler(); /** @@ -66,10 +71,10 @@ public: * it should be used to differentiate which client (As in ClientSession) we're answering to. * TODO(Subv): Use a wrapper structure to hold all the information relevant to * this request (ServerSession, Originator thread, Translated command buffer, etc). - * @returns ResultCode the result code of the translate operation. + * @returns Result the result code of the translate operation. */ - virtual ResultCode HandleSyncRequest(Kernel::KServerSession& session, - Kernel::HLERequestContext& context) = 0; + virtual Result HandleSyncRequest(Kernel::KServerSession& session, + Kernel::HLERequestContext& context) = 0; /** * Signals that a client has just connected to this HLE handler and keeps the @@ -94,6 +99,7 @@ protected: std::weak_ptr<ServiceThread> service_thread; }; +using SessionRequestHandlerWeakPtr = std::weak_ptr<SessionRequestHandler>; using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>; /** @@ -135,11 +141,11 @@ public: if (index < DomainHandlerCount()) { domain_handlers[index] = nullptr; } else { - UNREACHABLE_MSG("Unexpected handler index {}", index); + ASSERT_MSG(false, "Unexpected handler index {}", index); } } - SessionRequestHandlerPtr DomainHandler(std::size_t index) const { + SessionRequestHandlerWeakPtr DomainHandler(std::size_t index) const { ASSERT_MSG(index < DomainHandlerCount(), "Unexpected handler index {}", index); return domain_handlers.at(index); } @@ -206,11 +212,10 @@ public: } /// Populates this context with data from the requesting process/thread. - ResultCode PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table, - u32_le* src_cmdbuf); + Result PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table, u32_le* src_cmdbuf); /// Writes data from this context back to the requesting process/thread. - ResultCode WriteToOutgoingCommandBuffer(KThread& requesting_thread); + Result WriteToOutgoingCommandBuffer(KThread& requesting_thread); u32_le GetHipcCommand() const { return command; @@ -272,6 +277,14 @@ public: std::size_t WriteBuffer(const void* buffer, std::size_t size, std::size_t buffer_index = 0) const; + /// Helper function to write buffer B + std::size_t WriteBufferB(const void* buffer, std::size_t size, + std::size_t buffer_index = 0) const; + + /// Helper function to write buffer C + std::size_t WriteBufferC(const void* buffer, std::size_t size, + std::size_t buffer_index = 0) const; + /* Helper function to write a buffer using the appropriate buffer descriptor * * @tparam T an arbitrary container that satisfies the @@ -328,10 +341,10 @@ public: template <typename T> std::shared_ptr<T> GetDomainHandler(std::size_t index) const { - return std::static_pointer_cast<T>(manager->DomainHandler(index)); + return std::static_pointer_cast<T>(manager.lock()->DomainHandler(index).lock()); } - void SetSessionRequestManager(std::shared_ptr<SessionRequestManager> manager_) { + void SetSessionRequestManager(std::weak_ptr<SessionRequestManager> manager_) { manager = std::move(manager_); } @@ -374,7 +387,7 @@ private: u32 handles_offset{}; u32 domain_offset{}; - std::shared_ptr<SessionRequestManager> manager; + std::weak_ptr<SessionRequestManager> manager; KernelCore& kernel; Core::Memory::Memory& memory; diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp index 36fc0944a..9b6b284d0 100644 --- a/src/core/hle/kernel/init/init_slab_setup.cpp +++ b/src/core/hle/kernel/init/init_slab_setup.cpp @@ -1,25 +1,28 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/alignment.h" #include "common/assert.h" #include "common/common_funcs.h" #include "common/common_types.h" #include "core/core.h" +#include "core/device_memory.h" #include "core/hardware_properties.h" #include "core/hle/kernel/init/init_slab_setup.h" #include "core/hle/kernel/k_code_memory.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_memory_manager.h" +#include "core/hle/kernel/k_page_buffer.h" #include "core/hle/kernel/k_port.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_session.h" #include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/kernel/k_shared_memory_info.h" #include "core/hle/kernel/k_system_control.h" #include "core/hle/kernel/k_thread.h" +#include "core/hle/kernel/k_thread_local_page.h" #include "core/hle/kernel/k_transfer_memory.h" namespace Kernel::Init { @@ -32,9 +35,13 @@ namespace Kernel::Init { HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \ HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \ HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \ + HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \ HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \ HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \ + HANDLER(KThreadLocalPage, \ + (SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \ + ##__VA_ARGS__) \ HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__) namespace { @@ -50,38 +57,46 @@ enum KSlabType : u32 { // Constexpr counts. constexpr size_t SlabCountKProcess = 80; constexpr size_t SlabCountKThread = 800; -constexpr size_t SlabCountKEvent = 700; +constexpr size_t SlabCountKEvent = 900; constexpr size_t SlabCountKInterruptEvent = 100; -constexpr size_t SlabCountKPort = 256 + 0x20; // Extra 0x20 ports over Nintendo for homebrew. +constexpr size_t SlabCountKPort = 384; constexpr size_t SlabCountKSharedMemory = 80; constexpr size_t SlabCountKTransferMemory = 200; constexpr size_t SlabCountKCodeMemory = 10; constexpr size_t SlabCountKDeviceAddressSpace = 300; -constexpr size_t SlabCountKSession = 933; +constexpr size_t SlabCountKSession = 1133; constexpr size_t SlabCountKLightSession = 100; constexpr size_t SlabCountKObjectName = 7; constexpr size_t SlabCountKResourceLimit = 5; constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES; -constexpr size_t SlabCountKAlpha = 1; -constexpr size_t SlabCountKBeta = 6; +constexpr size_t SlabCountKIoPool = 1; +constexpr size_t SlabCountKIoRegion = 6; constexpr size_t SlabCountExtraKThread = 160; +/// Helper function to translate from the slab virtual address to the reserved location in physical +/// memory. +static PAddr TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout, VAddr slab_addr) { + slab_addr -= memory_layout.GetSlabRegionAddress(); + return slab_addr + Core::DramMemoryMap::SlabHeapBase; +} + template <typename T> VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAddr address, size_t num_objects) { - // TODO(bunnei): This is just a place holder. We should initialize the appropriate KSlabHeap for - // kernel object type T with the backing kernel memory pointer once we emulate kernel memory. const size_t size = Common::AlignUp(sizeof(T) * num_objects, alignof(void*)); VAddr start = Common::AlignUp(address, alignof(T)); - // This is intentionally empty. Once KSlabHeap is fully implemented, we can replace this with - // the pointer to emulated memory to pass along. Until then, KSlabHeap will just allocate/free - // host memory. - void* backing_kernel_memory{}; + // This should use the virtual memory address passed in, but currently, we do not setup the + // kernel virtual memory layout. Instead, we simply map these at a region of physical memory + // that we reserve for the slab heaps. + // TODO(bunnei): Fix this once we support the kernel virtual memory layout. if (size > 0) { + void* backing_kernel_memory{ + system.DeviceMemory().GetPointer(TranslateSlabAddrToPhysical(memory_layout, start))}; + const KMemoryRegion* region = memory_layout.FindVirtual(start + size - 1); ASSERT(region != nullptr); ASSERT(region->IsDerivedFrom(KMemoryRegionType_KernelSlab)); @@ -91,6 +106,12 @@ VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAd return start + size; } +size_t CalculateSlabHeapGapSize() { + constexpr size_t KernelSlabHeapGapSize = 2_MiB - 296_KiB; + static_assert(KernelSlabHeapGapSize <= KernelSlabHeapGapsSizeMax); + return KernelSlabHeapGapSize; +} + } // namespace KSlabResourceCounts KSlabResourceCounts::CreateDefault() { @@ -109,8 +130,8 @@ KSlabResourceCounts KSlabResourceCounts::CreateDefault() { .num_KObjectName = SlabCountKObjectName, .num_KResourceLimit = SlabCountKResourceLimit, .num_KDebug = SlabCountKDebug, - .num_KAlpha = SlabCountKAlpha, - .num_KBeta = SlabCountKBeta, + .num_KIoPool = SlabCountKIoPool, + .num_KIoRegion = SlabCountKIoRegion, }; } @@ -136,11 +157,34 @@ size_t CalculateTotalSlabHeapSize(const KernelCore& kernel) { #undef ADD_SLAB_SIZE // Add the reserved size. - size += KernelSlabHeapGapsSize; + size += CalculateSlabHeapGapSize(); return size; } +void InitializeKPageBufferSlabHeap(Core::System& system) { + auto& kernel = system.Kernel(); + + const auto& counts = kernel.SlabResourceCounts(); + const size_t num_pages = + counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8; + const size_t slab_size = num_pages * PageSize; + + // Reserve memory from the system resource limit. + ASSERT(kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemory, slab_size)); + + // Allocate memory for the slab. + constexpr auto AllocateOption = KMemoryManager::EncodeOption( + KMemoryManager::Pool::System, KMemoryManager::Direction::FromFront); + const PAddr slab_address = + kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, AllocateOption); + ASSERT(slab_address != 0); + + // Initialize the slabheap. + KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer(slab_address), + slab_size); +} + void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) { auto& kernel = system.Kernel(); @@ -160,13 +204,13 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) { } // Create an array to represent the gaps between the slabs. - const size_t total_gap_size = KernelSlabHeapGapsSize; + const size_t total_gap_size = CalculateSlabHeapGapSize(); std::array<size_t, slab_types.size()> slab_gaps; - for (size_t i = 0; i < slab_gaps.size(); i++) { + for (auto& slab_gap : slab_gaps) { // Note: This is an off-by-one error from Nintendo's intention, because GenerateRandomRange // is inclusive. However, Nintendo also has the off-by-one error, and it's "harmless", so we // will include it ourselves. - slab_gaps[i] = KSystemControl::GenerateRandomRange(0, total_gap_size); + slab_gap = KSystemControl::GenerateRandomRange(0, total_gap_size); } // Sort the array, so that we can treat differences between values as offsets to the starts of @@ -177,13 +221,21 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) { } } - for (size_t i = 0; i < slab_types.size(); i++) { + // Track the gaps, so that we can free them to the unused slab tree. + VAddr gap_start = address; + size_t gap_size = 0; + + for (size_t i = 0; i < slab_gaps.size(); i++) { // Add the random gap to the address. - address += (i == 0) ? slab_gaps[0] : slab_gaps[i] - slab_gaps[i - 1]; + const auto cur_gap = (i == 0) ? slab_gaps[0] : slab_gaps[i] - slab_gaps[i - 1]; + address += cur_gap; + gap_size += cur_gap; #define INITIALIZE_SLAB_HEAP(NAME, COUNT, ...) \ case KSlabType_##NAME: \ - address = InitializeSlabHeap<NAME>(system, memory_layout, address, COUNT); \ + if (COUNT > 0) { \ + address = InitializeSlabHeap<NAME>(system, memory_layout, address, COUNT); \ + } \ break; // Initialize the slabheap. @@ -192,7 +244,13 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) { FOREACH_SLAB_TYPE(INITIALIZE_SLAB_HEAP) // If we somehow get an invalid type, abort. default: - UNREACHABLE(); + ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]); + } + + // If we've hit the end of a gap, free it. + if (gap_start + gap_size != address) { + gap_start = address; + gap_size = 0; } } } diff --git a/src/core/hle/kernel/init/init_slab_setup.h b/src/core/hle/kernel/init/init_slab_setup.h index a8f7e0918..13be63c87 100644 --- a/src/core/hle/kernel/init/init_slab_setup.h +++ b/src/core/hle/kernel/init/init_slab_setup.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -32,12 +31,13 @@ struct KSlabResourceCounts { size_t num_KObjectName; size_t num_KResourceLimit; size_t num_KDebug; - size_t num_KAlpha; - size_t num_KBeta; + size_t num_KIoPool; + size_t num_KIoRegion; }; void InitializeSlabResourceCounts(KernelCore& kernel); size_t CalculateTotalSlabHeapSize(const KernelCore& kernel); +void InitializeKPageBufferSlabHeap(Core::System& system); void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout); } // namespace Kernel::Init diff --git a/src/core/hle/kernel/initial_process.h b/src/core/hle/kernel/initial_process.h new file mode 100644 index 000000000..af0fb23b6 --- /dev/null +++ b/src/core/hle/kernel/initial_process.h @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "common/literals.h" +#include "core/hle/kernel/board/nintendo/nx/k_memory_layout.h" +#include "core/hle/kernel/board/nintendo/nx/k_system_control.h" + +namespace Kernel { + +using namespace Common::Literals; + +constexpr std::size_t InitialProcessBinarySizeMax = 12_MiB; + +static inline PAddr GetInitialProcessBinaryPhysicalAddress() { + return Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetKernelPhysicalBaseAddress( + MainMemoryAddress); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp index 783c69858..f85b11557 100644 --- a/src/core/hle/kernel/k_address_arbiter.cpp +++ b/src/core/hle/kernel/k_address_arbiter.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/arm/exclusive_monitor.h" #include "core/core.h" @@ -49,7 +48,7 @@ bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 valu } } else { // Otherwise, clear our exclusive hold and finish - monitor.ClearExclusive(); + monitor.ClearExclusive(current_core); } // We're done. @@ -78,7 +77,7 @@ bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 } } else { // Otherwise, clear our exclusive hold and finish. - monitor.ClearExclusive(); + monitor.ClearExclusive(current_core); } // We're done. @@ -91,8 +90,7 @@ public: explicit ThreadQueueImplForKAddressArbiter(KernelCore& kernel_, KAddressArbiter::ThreadTree* t) : KThreadQueue(kernel_), m_tree(t) {} - void CancelWait(KThread* waiting_thread, ResultCode wait_result, - bool cancel_timer_task) override { + void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // If the thread is waiting on an address arbiter, remove it from the tree. if (waiting_thread->IsWaitingForAddressArbiter()) { m_tree->erase(m_tree->iterator_to(*waiting_thread)); @@ -109,13 +107,13 @@ private: } // namespace -ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) { +Result KAddressArbiter::Signal(VAddr addr, s32 count) { // Perform signaling. s32 num_waiters{}; { KScopedSchedulerLock sl(kernel); - auto it = thread_tree.nfind_light({addr, -1}); + auto it = thread_tree.nfind_key({addr, -1}); while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetAddressArbiterKey() == addr)) { // End the thread's wait. @@ -132,7 +130,7 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) { return ResultSuccess; } -ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count) { +Result KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count) { // Perform signaling. s32 num_waiters{}; { @@ -148,7 +146,7 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 return ResultInvalidState; } - auto it = thread_tree.nfind_light({addr, -1}); + auto it = thread_tree.nfind_key({addr, -1}); while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetAddressArbiterKey() == addr)) { // End the thread's wait. @@ -165,13 +163,13 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 return ResultSuccess; } -ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count) { +Result KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count) { // Perform signaling. s32 num_waiters{}; { [[maybe_unused]] const KScopedSchedulerLock sl(kernel); - auto it = thread_tree.nfind_light({addr, -1}); + auto it = thread_tree.nfind_key({addr, -1}); // Determine the updated value. s32 new_value{}; if (count <= 0) { @@ -233,9 +231,9 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 return ResultSuccess; } -ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) { +Result KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) { // Prepare to wait. - KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); + KThread* cur_thread = GetCurrentThreadPointer(kernel); ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree)); { @@ -286,9 +284,9 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement return cur_thread->GetWaitResult(); } -ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { +Result KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { // Prepare to wait. - KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); + KThread* cur_thread = GetCurrentThreadPointer(kernel); ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree)); { diff --git a/src/core/hle/kernel/k_address_arbiter.h b/src/core/hle/kernel/k_address_arbiter.h index bf8b46665..e4085ae22 100644 --- a/src/core/hle/kernel/k_address_arbiter.h +++ b/src/core/hle/kernel/k_address_arbiter.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -9,7 +8,7 @@ #include "core/hle/kernel/k_condition_variable.h" #include "core/hle/kernel/svc_types.h" -union ResultCode; +union Result; namespace Core { class System; @@ -26,8 +25,7 @@ public: explicit KAddressArbiter(Core::System& system_); ~KAddressArbiter(); - [[nodiscard]] ResultCode SignalToAddress(VAddr addr, Svc::SignalType type, s32 value, - s32 count) { + [[nodiscard]] Result SignalToAddress(VAddr addr, Svc::SignalType type, s32 value, s32 count) { switch (type) { case Svc::SignalType::Signal: return Signal(addr, count); @@ -36,12 +34,12 @@ public: case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual: return SignalAndModifyByWaitingCountIfEqual(addr, value, count); } - UNREACHABLE(); + ASSERT(false); return ResultUnknown; } - [[nodiscard]] ResultCode WaitForAddress(VAddr addr, Svc::ArbitrationType type, s32 value, - s64 timeout) { + [[nodiscard]] Result WaitForAddress(VAddr addr, Svc::ArbitrationType type, s32 value, + s64 timeout) { switch (type) { case Svc::ArbitrationType::WaitIfLessThan: return WaitIfLessThan(addr, value, false, timeout); @@ -50,16 +48,16 @@ public: case Svc::ArbitrationType::WaitIfEqual: return WaitIfEqual(addr, value, timeout); } - UNREACHABLE(); + ASSERT(false); return ResultUnknown; } private: - [[nodiscard]] ResultCode Signal(VAddr addr, s32 count); - [[nodiscard]] ResultCode SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count); - [[nodiscard]] ResultCode SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count); - [[nodiscard]] ResultCode WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout); - [[nodiscard]] ResultCode WaitIfEqual(VAddr addr, s32 value, s64 timeout); + [[nodiscard]] Result Signal(VAddr addr, s32 count); + [[nodiscard]] Result SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count); + [[nodiscard]] Result SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count); + [[nodiscard]] Result WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout); + [[nodiscard]] Result WaitIfEqual(VAddr addr, s32 value, s64 timeout); ThreadTree thread_tree; diff --git a/src/core/hle/kernel/k_address_space_info.cpp b/src/core/hle/kernel/k_address_space_info.cpp index ca29edc88..3e612a207 100644 --- a/src/core/hle/kernel/k_address_space_info.cpp +++ b/src/core/hle/kernel/k_address_space_info.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> @@ -85,7 +84,7 @@ u64 KAddressSpaceInfo::GetAddressSpaceStart(std::size_t width, Type type) { ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices39Bit[index])); return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].address; } - UNREACHABLE(); + ASSERT(false); return 0; } @@ -102,7 +101,7 @@ std::size_t KAddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type) ASSERT(IsAllowed39BitType(type)); return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].size; } - UNREACHABLE(); + ASSERT(false); return 0; } diff --git a/src/core/hle/kernel/k_address_space_info.h b/src/core/hle/kernel/k_address_space_info.h index 06f31c6d5..69e9d77f2 100644 --- a/src/core/hle/kernel/k_address_space_info.h +++ b/src/core/hle/kernel/k_address_space_info.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_affinity_mask.h b/src/core/hle/kernel/k_affinity_mask.h index cf704ce87..b58716e90 100644 --- a/src/core/hle/kernel/k_affinity_mask.h +++ b/src/core/hle/kernel/k_affinity_mask.h @@ -1,9 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -// This file references various implementation details from Atmosphere, an open-source firmware for -// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_auto_object.cpp b/src/core/hle/kernel/k_auto_object.cpp index c99a9ebb7..691af8ccb 100644 --- a/src/core/hle/kernel/k_auto_object.cpp +++ b/src/core/hle/kernel/k_auto_object.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/kernel.h" diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h index 05779f2d5..2827763d5 100644 --- a/src/core/hle/kernel/k_auto_object.h +++ b/src/core/hle/kernel/k_auto_object.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -19,7 +18,7 @@ namespace Kernel { class KernelCore; class KProcess; -#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \ +#define KERNEL_AUTOOBJECT_TRAITS_IMPL(CLASS, BASE_CLASS, ATTRIBUTE) \ \ private: \ friend class ::Kernel::KClassTokenGenerator; \ @@ -41,16 +40,19 @@ public: static constexpr const char* GetStaticTypeName() { \ return TypeName; \ } \ - virtual TypeObj GetTypeObj() const { \ + virtual TypeObj GetTypeObj() ATTRIBUTE { \ return GetStaticTypeObj(); \ } \ - virtual const char* GetTypeName() const { \ + virtual const char* GetTypeName() ATTRIBUTE { \ return GetStaticTypeName(); \ } \ \ private: \ constexpr bool operator!=(const TypeObj& rhs) +#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \ + KERNEL_AUTOOBJECT_TRAITS_IMPL(CLASS, BASE_CLASS, const override) + class KAutoObject { protected: class TypeObj { @@ -83,15 +85,13 @@ protected: }; private: - KERNEL_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject); + KERNEL_AUTOOBJECT_TRAITS_IMPL(KAutoObject, KAutoObject, const); public: explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) { RegisterWithKernel(); } - virtual ~KAutoObject() { - UnregisterWithKernel(); - } + virtual ~KAutoObject() = default; static KAutoObject* Create(KAutoObject* ptr); @@ -163,11 +163,12 @@ 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) { this->Destroy(); + this->UnregisterWithKernel(); } } diff --git a/src/core/hle/kernel/k_auto_object_container.cpp b/src/core/hle/kernel/k_auto_object_container.cpp index d5f80d5b2..636b3f993 100644 --- a/src/core/hle/kernel/k_auto_object_container.cpp +++ b/src/core/hle/kernel/k_auto_object_container.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> diff --git a/src/core/hle/kernel/k_auto_object_container.h b/src/core/hle/kernel/k_auto_object_container.h index 697cc4289..badd75d2a 100644 --- a/src/core/hle/kernel/k_auto_object_container.h +++ b/src/core/hle/kernel/k_auto_object_container.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_class_token.cpp b/src/core/hle/kernel/k_class_token.cpp index 21e2fe494..cc2a0f7ca 100644 --- a/src/core/hle/kernel/k_class_token.cpp +++ b/src/core/hle/kernel/k_class_token.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_class_token.h" diff --git a/src/core/hle/kernel/k_class_token.h b/src/core/hle/kernel/k_class_token.h index 980010150..c9001ae3d 100644 --- a/src/core/hle/kernel/k_class_token.h +++ b/src/core/hle/kernel/k_class_token.h @@ -1,11 +1,8 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include <atomic> - #include "common/bit_util.h" #include "common/common_types.h" @@ -52,6 +49,7 @@ private: } } } + UNREACHABLE(); }(); template <typename T> diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp index ef168fe87..3cb22ff4d 100644 --- a/src/core/hle/kernel/k_client_port.cpp +++ b/src/core/hle/kernel/k_client_port.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/scope_exit.h" #include "core/hle/kernel/hle_ipc.h" @@ -59,8 +58,8 @@ bool KClientPort::IsSignaled() const { return num_sessions < max_sessions; } -ResultCode KClientPort::CreateSession(KClientSession** out, - std::shared_ptr<SessionRequestManager> session_manager) { +Result KClientPort::CreateSession(KClientSession** out, + std::shared_ptr<SessionRequestManager> session_manager) { // Reserve a new session from the resource limit. KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(), LimitableResource::Sessions); diff --git a/src/core/hle/kernel/k_client_port.h b/src/core/hle/kernel/k_client_port.h index 54bb05e20..e17eff28f 100644 --- a/src/core/hle/kernel/k_client_port.h +++ b/src/core/hle/kernel/k_client_port.h @@ -1,6 +1,5 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2016 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -53,8 +52,8 @@ public: void Destroy() override; bool IsSignaled() const override; - ResultCode CreateSession(KClientSession** out, - std::shared_ptr<SessionRequestManager> session_manager = nullptr); + Result CreateSession(KClientSession** out, + std::shared_ptr<SessionRequestManager> session_manager = nullptr); private: std::atomic<s32> num_sessions{}; diff --git a/src/core/hle/kernel/k_client_session.cpp b/src/core/hle/kernel/k_client_session.cpp index 242582f8f..b2a887b14 100644 --- a/src/core/hle/kernel/k_client_session.cpp +++ b/src/core/hle/kernel/k_client_session.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/k_client_session.h" @@ -22,8 +21,8 @@ void KClientSession::Destroy() { void KClientSession::OnServerClosed() {} -ResultCode KClientSession::SendSyncRequest(KThread* thread, Core::Memory::Memory& memory, - Core::Timing::CoreTiming& core_timing) { +Result KClientSession::SendSyncRequest(KThread* thread, Core::Memory::Memory& memory, + Core::Timing::CoreTiming& core_timing) { // Signal the server session that new data is available return parent->GetServerSession().HandleSyncRequest(thread, memory, core_timing); } diff --git a/src/core/hle/kernel/k_client_session.h b/src/core/hle/kernel/k_client_session.h index ad6cc4ed1..0c750d756 100644 --- a/src/core/hle/kernel/k_client_session.h +++ b/src/core/hle/kernel/k_client_session.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -10,7 +9,7 @@ #include "core/hle/kernel/slab_helpers.h" #include "core/hle/result.h" -union ResultCode; +union Result; namespace Core::Memory { class Memory; @@ -47,8 +46,8 @@ public: return parent; } - ResultCode SendSyncRequest(KThread* thread, Core::Memory::Memory& memory, - Core::Timing::CoreTiming& core_timing); + Result SendSyncRequest(KThread* thread, Core::Memory::Memory& memory, + Core::Timing::CoreTiming& core_timing); void OnServerClosed(); diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp index 0b225e8e0..da57ceb21 100644 --- a/src/core/hle/kernel/k_code_memory.cpp +++ b/src/core/hle/kernel/k_code_memory.cpp @@ -1,15 +1,13 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/alignment.h" #include "common/common_types.h" #include "core/device_memory.h" -#include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_code_memory.h" #include "core/hle/kernel/k_light_lock.h" #include "core/hle/kernel/k_memory_block.h" -#include "core/hle/kernel/k_page_linked_list.h" +#include "core/hle/kernel/k_page_group.h" #include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/slab_helpers.h" @@ -21,7 +19,7 @@ namespace Kernel { KCodeMemory::KCodeMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_}, m_lock(kernel_) {} -ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, size_t size) { +Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, size_t size) { // Set members. m_owner = kernel.CurrentProcess(); @@ -29,10 +27,10 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr auto& page_table = m_owner->PageTable(); // Construct the page group. - m_page_group = KPageLinkedList(addr, Common::DivideUp(size, PageSize)); + m_page_group = {}; // Lock the memory. - R_TRY(page_table.LockForCodeMemory(addr, size)) + R_TRY(page_table.LockForCodeMemory(&m_page_group, addr, size)) // Clear the memory. for (const auto& block : m_page_group.Nodes()) { @@ -40,6 +38,7 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr } // Set remaining tracking members. + m_owner->Open(); m_address = addr; m_is_initialized = true; m_is_owner_mapped = false; @@ -53,11 +52,17 @@ void KCodeMemory::Finalize() { // Unlock. if (!m_is_mapped && !m_is_owner_mapped) { const size_t size = m_page_group.GetNumPages() * PageSize; - m_owner->PageTable().UnlockForCodeMemory(m_address, size); + m_owner->PageTable().UnlockForCodeMemory(m_address, size, m_page_group); } + + // Close the page group. + m_page_group = {}; + + // Close our reference to our owner. + m_owner->Close(); } -ResultCode KCodeMemory::Map(VAddr address, size_t size) { +Result KCodeMemory::Map(VAddr address, size_t size) { // Validate the size. R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); @@ -77,7 +82,7 @@ ResultCode KCodeMemory::Map(VAddr address, size_t size) { return ResultSuccess; } -ResultCode KCodeMemory::Unmap(VAddr address, size_t size) { +Result KCodeMemory::Unmap(VAddr address, size_t size) { // Validate the size. R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); @@ -94,7 +99,7 @@ ResultCode KCodeMemory::Unmap(VAddr address, size_t size) { return ResultSuccess; } -ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) { +Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) { // Validate the size. R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); @@ -114,7 +119,8 @@ ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermis k_perm = KMemoryPermission::UserReadExecute; break; default: - break; + // Already validated by ControlCodeMemory svc + UNREACHABLE(); } // Map the memory. @@ -127,7 +133,7 @@ ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermis return ResultSuccess; } -ResultCode KCodeMemory::UnmapFromOwner(VAddr address, size_t size) { +Result KCodeMemory::UnmapFromOwner(VAddr address, size_t size) { // Validate the size. R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); diff --git a/src/core/hle/kernel/k_code_memory.h b/src/core/hle/kernel/k_code_memory.h index e0ba19a53..2e7e1436a 100644 --- a/src/core/hle/kernel/k_code_memory.h +++ b/src/core/hle/kernel/k_code_memory.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,7 +7,7 @@ #include "core/device_memory.h" #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_light_lock.h" -#include "core/hle/kernel/k_page_linked_list.h" +#include "core/hle/kernel/k_page_group.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/slab_helpers.h" #include "core/hle/kernel/svc_types.h" @@ -30,20 +29,20 @@ class KCodeMemory final public: explicit KCodeMemory(KernelCore& kernel_); - ResultCode Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size); - void Finalize(); + Result Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size); + void Finalize() override; - ResultCode Map(VAddr address, size_t size); - ResultCode Unmap(VAddr address, size_t size); - ResultCode MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm); - ResultCode UnmapFromOwner(VAddr address, size_t size); + Result Map(VAddr address, size_t size); + Result Unmap(VAddr address, size_t size); + Result MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm); + Result UnmapFromOwner(VAddr address, size_t size); - bool IsInitialized() const { + bool IsInitialized() const override { return m_is_initialized; } static void PostDestroy([[maybe_unused]] uintptr_t arg) {} - KProcess* GetOwner() const { + KProcess* GetOwner() const override { return m_owner; } VAddr GetSourceAddress() const { @@ -54,7 +53,7 @@ public: } private: - KPageLinkedList m_page_group{}; + KPageGroup m_page_group{}; KProcess* m_owner{}; VAddr m_address{}; KLightLock m_lock; diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp index aadcc297a..124149697 100644 --- a/src/core/hle/kernel/k_condition_variable.cpp +++ b/src/core/hle/kernel/k_condition_variable.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/arm/exclusive_monitor.h" #include "core/core.h" @@ -9,7 +8,6 @@ #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" -#include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread_queue.h" #include "core/hle/kernel/kernel.h" @@ -63,8 +61,7 @@ public: explicit ThreadQueueImplForKConditionVariableWaitForAddress(KernelCore& kernel_) : KThreadQueue(kernel_) {} - void CancelWait(KThread* waiting_thread, ResultCode wait_result, - bool cancel_timer_task) override { + void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // Remove the thread as a waiter from its owner. waiting_thread->GetLockOwner()->RemoveWaiter(waiting_thread); @@ -82,8 +79,7 @@ public: KernelCore& kernel_, KConditionVariable::ThreadTree* t) : KThreadQueue(kernel_), m_tree(t) {} - void CancelWait(KThread* waiting_thread, ResultCode wait_result, - bool cancel_timer_task) override { + void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // Remove the thread as a waiter from its owner. if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) { owner->RemoveWaiter(waiting_thread); @@ -107,8 +103,8 @@ KConditionVariable::KConditionVariable(Core::System& system_) KConditionVariable::~KConditionVariable() = default; -ResultCode KConditionVariable::SignalToAddress(VAddr addr) { - KThread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread(); +Result KConditionVariable::SignalToAddress(VAddr addr) { + KThread* owner_thread = GetCurrentThreadPointer(kernel); // Signal the address. { @@ -128,7 +124,7 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) { } // Write the value to userspace. - ResultCode result{ResultSuccess}; + Result result{ResultSuccess}; if (WriteToUser(system, addr, std::addressof(next_value))) [[likely]] { result = ResultSuccess; } else { @@ -148,8 +144,8 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) { } } -ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) { - KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); +Result KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) { + KThread* cur_thread = GetCurrentThreadPointer(kernel); ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel); // Wait for the address. @@ -244,7 +240,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) { { KScopedSchedulerLock sl(kernel); - auto it = thread_tree.nfind_light({cv_key, -1}); + auto it = thread_tree.nfind_key({cv_key, -1}); while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetConditionVariableKey() == cv_key)) { KThread* target_thread = std::addressof(*it); @@ -263,7 +259,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) { } } -ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) { +Result KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) { // Prepare to wait. KThread* cur_thread = GetCurrentThreadPointer(kernel); ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue( diff --git a/src/core/hle/kernel/k_condition_variable.h b/src/core/hle/kernel/k_condition_variable.h index 5e4815d08..fad4ed011 100644 --- a/src/core/hle/kernel/k_condition_variable.h +++ b/src/core/hle/kernel/k_condition_variable.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -26,12 +25,12 @@ public: ~KConditionVariable(); // Arbitration - [[nodiscard]] ResultCode SignalToAddress(VAddr addr); - [[nodiscard]] ResultCode WaitForAddress(Handle handle, VAddr addr, u32 value); + [[nodiscard]] Result SignalToAddress(VAddr addr); + [[nodiscard]] Result WaitForAddress(Handle handle, VAddr addr, u32 value); // Condition variable void Signal(u64 cv_key, s32 count); - [[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout); + [[nodiscard]] Result Wait(VAddr addr, u64 key, u32 value, s64 timeout); private: void SignalImpl(KThread* thread); diff --git a/src/core/hle/kernel/k_event.cpp b/src/core/hle/kernel/k_event.cpp index 0720efece..e52fafbc7 100644 --- a/src/core/hle/kernel/k_event.cpp +++ b/src/core/hle/kernel/k_event.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_process.h" @@ -14,7 +13,7 @@ KEvent::KEvent(KernelCore& kernel_) KEvent::~KEvent() = default; -void KEvent::Initialize(std::string&& name_) { +void KEvent::Initialize(std::string&& name_, KProcess* owner_) { // Increment reference count. // Because reference count is one on creation, this will result // in a reference count of two. Thus, when both readable and @@ -30,10 +29,8 @@ void KEvent::Initialize(std::string&& name_) { writable_event.Initialize(this, name_ + ":Writable"); // Set our owner process. - owner = kernel.CurrentProcess(); - if (owner) { - owner->Open(); - } + owner = owner_; + owner->Open(); // Mark initialized. name = std::move(name_); @@ -47,10 +44,8 @@ void KEvent::Finalize() { void KEvent::PostDestroy(uintptr_t arg) { // Release the event count resource the owner process holds. KProcess* owner = reinterpret_cast<KProcess*>(arg); - if (owner) { - owner->GetResourceLimit()->Release(LimitableResource::Events, 1); - owner->Close(); - } + owner->GetResourceLimit()->Release(LimitableResource::Events, 1); + owner->Close(); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_event.h b/src/core/hle/kernel/k_event.h index 3d3ec99e2..2ff828feb 100644 --- a/src/core/hle/kernel/k_event.h +++ b/src/core/hle/kernel/k_event.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -22,7 +21,7 @@ public: explicit KEvent(KernelCore& kernel_); ~KEvent() override; - void Initialize(std::string&& name); + void Initialize(std::string&& name, KProcess* owner_); void Finalize() override; diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp index cf95f0852..e830ca46e 100644 --- a/src/core/hle/kernel/k_handle_table.cpp +++ b/src/core/hle/kernel/k_handle_table.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_handle_table.h" @@ -9,7 +8,7 @@ namespace Kernel { KHandleTable::KHandleTable(KernelCore& kernel_) : kernel{kernel_} {} KHandleTable::~KHandleTable() = default; -ResultCode KHandleTable::Finalize() { +Result KHandleTable::Finalize() { // Get the table and clear our record of it. u16 saved_table_size = 0; { @@ -63,7 +62,7 @@ bool KHandleTable::Remove(Handle handle) { return true; } -ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { +Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) { KScopedDisableDispatch dd(kernel); KScopedSpinLock lk(m_lock); @@ -75,7 +74,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { const auto linear_id = this->AllocateLinearId(); const auto index = this->AllocateEntry(); - m_entry_infos[index].info = {.linear_id = linear_id, .type = type}; + m_entry_infos[index].linear_id = linear_id; m_objects[index] = obj; obj->Open(); @@ -86,7 +85,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { return ResultSuccess; } -ResultCode KHandleTable::Reserve(Handle* out_handle) { +Result KHandleTable::Reserve(Handle* out_handle) { KScopedDisableDispatch dd(kernel); KScopedSpinLock lk(m_lock); @@ -116,7 +115,7 @@ void KHandleTable::Unreserve(Handle handle) { } } -void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) { +void KHandleTable::Register(Handle handle, KAutoObject* obj) { KScopedDisableDispatch dd(kernel); KScopedSpinLock lk(m_lock); @@ -132,7 +131,7 @@ void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) { // Set the entry. ASSERT(m_objects[index] == nullptr); - m_entry_infos[index].info = {.linear_id = static_cast<u16>(linear_id), .type = type}; + m_entry_infos[index].linear_id = static_cast<u16>(linear_id); m_objects[index] = obj; obj->Open(); diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h index 87004a0f9..0864a737c 100644 --- a/src/core/hle/kernel/k_handle_table.h +++ b/src/core/hle/kernel/k_handle_table.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -31,7 +30,7 @@ public: explicit KHandleTable(KernelCore& kernel_); ~KHandleTable(); - ResultCode Initialize(s32 size) { + Result Initialize(s32 size) { R_UNLESS(size <= static_cast<s32>(MaxTableSize), ResultOutOfMemory); // Initialize all fields. @@ -42,7 +41,7 @@ public: m_free_head_index = -1; // Free all entries. - for (s32 i = 0; i < static_cast<s32>(m_table_size); ++i) { + for (s16 i = 0; i < static_cast<s16>(m_table_size); ++i) { m_objects[i] = nullptr; m_entry_infos[i].next_free_index = i - 1; m_free_head_index = i; @@ -61,7 +60,7 @@ public: return m_max_count; } - ResultCode Finalize(); + Result Finalize(); bool Remove(Handle handle); template <typename T = KAutoObject> @@ -101,20 +100,11 @@ public: return this->template GetObjectWithoutPseudoHandle<T>(handle); } - ResultCode Reserve(Handle* out_handle); + Result Reserve(Handle* out_handle); void Unreserve(Handle handle); - template <typename T> - ResultCode Add(Handle* out_handle, T* obj) { - static_assert(std::is_base_of_v<KAutoObject, T>); - return this->Add(out_handle, obj, obj->GetTypeObj().GetClassToken()); - } - - template <typename T> - void Register(Handle handle, T* obj) { - static_assert(std::is_base_of_v<KAutoObject, T>); - return this->Register(handle, obj, obj->GetTypeObj().GetClassToken()); - } + Result Add(Handle* out_handle, KAutoObject* obj); + void Register(Handle handle, KAutoObject* obj); template <typename T> bool GetMultipleObjects(T** out, const Handle* handles, size_t num_handles) const { @@ -160,9 +150,6 @@ public: } private: - ResultCode Add(Handle* out_handle, KAutoObject* obj, u16 type); - void Register(Handle handle, KAutoObject* obj, u16 type); - s32 AllocateEntry() { ASSERT(m_count < m_table_size); @@ -179,7 +166,7 @@ private: ASSERT(m_count > 0); m_objects[index] = nullptr; - m_entry_infos[index].next_free_index = m_free_head_index; + m_entry_infos[index].next_free_index = static_cast<s16>(m_free_head_index); m_free_head_index = index; @@ -278,19 +265,13 @@ private: } union EntryInfo { - struct { - u16 linear_id; - u16 type; - } info; - s32 next_free_index; + u16 linear_id; + s16 next_free_index; constexpr u16 GetLinearId() const { - return info.linear_id; - } - constexpr u16 GetType() const { - return info.type; + return linear_id; } - constexpr s32 GetNextFreeIndex() const { + constexpr s16 GetNextFreeIndex() const { return next_free_index; } }; diff --git a/src/core/hle/kernel/k_interrupt_manager.cpp b/src/core/hle/kernel/k_interrupt_manager.cpp index e5dd39751..1b577a5b3 100644 --- a/src/core/hle/kernel/k_interrupt_manager.cpp +++ b/src/core/hle/kernel/k_interrupt_manager.cpp @@ -1,12 +1,12 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_interrupt_manager.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/physical_core.h" namespace Kernel::KInterruptManager { @@ -16,8 +16,10 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) { return; } - auto& scheduler = kernel.Scheduler(core_id); - auto& current_thread = *scheduler.GetCurrentThread(); + // Acknowledge the interrupt. + kernel.PhysicalCore(core_id).ClearInterrupt(); + + auto& current_thread = GetCurrentThread(kernel); // If the user disable count is set, we may need to pin the current thread. if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) { @@ -27,8 +29,11 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) { process->PinCurrentThread(core_id); // Set the interrupt flag for the thread. - scheduler.GetCurrentThread()->SetInterruptFlag(); + GetCurrentThread(kernel).SetInterruptFlag(); } + + // Request interrupt scheduling. + kernel.CurrentScheduler()->RequestScheduleOnInterrupt(); } } // namespace Kernel::KInterruptManager diff --git a/src/core/hle/kernel/k_interrupt_manager.h b/src/core/hle/kernel/k_interrupt_manager.h index 05924801e..f103dfe3f 100644 --- a/src/core/hle/kernel/k_interrupt_manager.h +++ b/src/core/hle/kernel/k_interrupt_manager.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_light_condition_variable.cpp b/src/core/hle/kernel/k_light_condition_variable.cpp index a8001fffc..cade99cfd 100644 --- a/src/core/hle/kernel/k_light_condition_variable.cpp +++ b/src/core/hle/kernel/k_light_condition_variable.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_light_condition_variable.h" #include "core/hle/kernel/k_scheduler.h" @@ -18,8 +17,7 @@ public: bool term) : KThreadQueue(kernel_), m_wait_list(wl), m_allow_terminating_thread(term) {} - void CancelWait(KThread* waiting_thread, ResultCode wait_result, - bool cancel_timer_task) override { + void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // Only process waits if we're allowed to. if (ResultTerminationRequested == wait_result && m_allow_terminating_thread) { return; diff --git a/src/core/hle/kernel/k_light_condition_variable.h b/src/core/hle/kernel/k_light_condition_variable.h index 5d6d7f128..3cabd6b4f 100644 --- a/src/core/hle/kernel/k_light_condition_variable.h +++ b/src/core/hle/kernel/k_light_condition_variable.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_light_lock.cpp b/src/core/hle/kernel/k_light_lock.cpp index 4620342eb..43185320d 100644 --- a/src/core/hle/kernel/k_light_lock.cpp +++ b/src/core/hle/kernel/k_light_lock.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_light_lock.h" #include "core/hle/kernel/k_scheduler.h" @@ -16,8 +15,7 @@ class ThreadQueueImplForKLightLock final : public KThreadQueue { public: explicit ThreadQueueImplForKLightLock(KernelCore& kernel_) : KThreadQueue(kernel_) {} - void CancelWait(KThread* waiting_thread, ResultCode wait_result, - bool cancel_timer_task) override { + void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // Remove the thread as a waiter from its owner. if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) { owner->RemoveWaiter(waiting_thread); diff --git a/src/core/hle/kernel/k_light_lock.h b/src/core/hle/kernel/k_light_lock.h index 4163b8a85..7edd950c0 100644 --- a/src/core/hle/kernel/k_light_lock.h +++ b/src/core/hle/kernel/k_light_lock.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_linked_list.h b/src/core/hle/kernel/k_linked_list.h index 6adfe1e34..78859ced3 100644 --- a/src/core/hle/kernel/k_linked_list.h +++ b/src/core/hle/kernel/k_linked_list.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h index dcca47f1b..18df1f836 100644 --- a/src/core/hle/kernel/k_memory_block.h +++ b/src/core/hle/kernel/k_memory_block.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_memory_block_manager.cpp b/src/core/hle/kernel/k_memory_block_manager.cpp index fc7033564..3ddb9984f 100644 --- a/src/core/hle/kernel/k_memory_block_manager.cpp +++ b/src/core/hle/kernel/k_memory_block_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_memory_block_manager.h" #include "core/hle/kernel/memory_types.h" diff --git a/src/core/hle/kernel/k_memory_block_manager.h b/src/core/hle/kernel/k_memory_block_manager.h index d222da919..e14741b89 100644 --- a/src/core/hle/kernel/k_memory_block_manager.h +++ b/src/core/hle/kernel/k_memory_block_manager.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp b/src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp index af652af58..098ba6eac 100644 --- a/src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp +++ b/src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/alignment.h" #include "common/literals.h" diff --git a/src/core/hle/kernel/k_memory_layout.cpp b/src/core/hle/kernel/k_memory_layout.cpp index fb1e2435f..55dc296d0 100644 --- a/src/core/hle/kernel/k_memory_layout.cpp +++ b/src/core/hle/kernel/k_memory_layout.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> diff --git a/src/core/hle/kernel/k_memory_layout.h b/src/core/hle/kernel/k_memory_layout.h index 57ff538cc..884fc623a 100644 --- a/src/core/hle/kernel/k_memory_layout.h +++ b/src/core/hle/kernel/k_memory_layout.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -57,11 +56,11 @@ constexpr std::size_t KernelPageTableHeapSize = GetMaximumOverheadSize(MainMemor constexpr std::size_t KernelInitialPageHeapSize = 128_KiB; constexpr std::size_t KernelSlabHeapDataSize = 5_MiB; -constexpr std::size_t KernelSlabHeapGapsSize = 2_MiB - 64_KiB; -constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSize; +constexpr std::size_t KernelSlabHeapGapsSizeMax = 2_MiB - 64_KiB; +constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSizeMax; // NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860. -constexpr std::size_t KernelSlabHeapAdditionalSize = 416_KiB; +constexpr std::size_t KernelSlabHeapAdditionalSize = 0x68000; constexpr std::size_t KernelResourceSize = KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize; @@ -173,6 +172,10 @@ public: return Dereference(FindVirtualLinear(address)); } + const KMemoryRegion& GetPhysicalLinearRegion(PAddr address) const { + return Dereference(FindPhysicalLinear(address)); + } + const KMemoryRegion* GetPhysicalKernelTraceBufferRegion() const { return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_KernelTraceBuffer); } diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp index 1b44541b1..5b0a9963a 100644 --- a/src/core/hle/kernel/k_memory_manager.cpp +++ b/src/core/hle/kernel/k_memory_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> @@ -10,189 +9,411 @@ #include "common/scope_exit.h" #include "core/core.h" #include "core/device_memory.h" +#include "core/hle/kernel/initial_process.h" #include "core/hle/kernel/k_memory_manager.h" -#include "core/hle/kernel/k_page_linked_list.h" +#include "core/hle/kernel/k_page_group.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/svc_results.h" namespace Kernel { -KMemoryManager::KMemoryManager(Core::System& system_) : system{system_} {} +namespace { + +constexpr KMemoryManager::Pool GetPoolFromMemoryRegionType(u32 type) { + if ((type | KMemoryRegionType_DramApplicationPool) == type) { + return KMemoryManager::Pool::Application; + } else if ((type | KMemoryRegionType_DramAppletPool) == type) { + return KMemoryManager::Pool::Applet; + } else if ((type | KMemoryRegionType_DramSystemPool) == type) { + return KMemoryManager::Pool::System; + } else if ((type | KMemoryRegionType_DramSystemNonSecurePool) == type) { + return KMemoryManager::Pool::SystemNonSecure; + } else { + ASSERT_MSG(false, "InvalidMemoryRegionType for conversion to Pool"); + return {}; + } +} -std::size_t KMemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u64 end_address) { - const auto size{end_address - start_address}; +} // namespace + +KMemoryManager::KMemoryManager(Core::System& system_) + : system{system_}, pool_locks{ + KLightLock{system_.Kernel()}, + KLightLock{system_.Kernel()}, + KLightLock{system_.Kernel()}, + KLightLock{system_.Kernel()}, + } {} + +void KMemoryManager::Initialize(VAddr management_region, size_t management_region_size) { + + // Clear the management region to zero. + const VAddr management_region_end = management_region + management_region_size; + + // Reset our manager count. + num_managers = 0; + + // Traverse the virtual memory layout tree, initializing each manager as appropriate. + while (num_managers != MaxManagerCount) { + // Locate the region that should initialize the current manager. + PAddr region_address = 0; + size_t region_size = 0; + Pool region_pool = Pool::Count; + for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) { + // We only care about regions that we need to create managers for. + if (!it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) { + continue; + } - // Calculate metadata sizes - const auto ref_count_size{(size / PageSize) * sizeof(u16)}; - const auto optimize_map_size{(Common::AlignUp((size / PageSize), 64) / 64) * sizeof(u64)}; - const auto manager_size{Common::AlignUp(optimize_map_size + ref_count_size, PageSize)}; - const auto page_heap_size{KPageHeap::CalculateManagementOverheadSize(size)}; - const auto total_metadata_size{manager_size + page_heap_size}; - ASSERT(manager_size <= total_metadata_size); - ASSERT(Common::IsAligned(total_metadata_size, PageSize)); + // We want to initialize the managers in order. + if (it.GetAttributes() != num_managers) { + continue; + } - // Setup region - pool = new_pool; + const PAddr cur_start = it.GetAddress(); + const PAddr cur_end = it.GetEndAddress(); + + // Validate the region. + ASSERT(cur_end != 0); + ASSERT(cur_start != 0); + ASSERT(it.GetSize() > 0); + + // Update the region's extents. + if (region_address == 0) { + region_address = cur_start; + region_size = it.GetSize(); + region_pool = GetPoolFromMemoryRegionType(it.GetType()); + } else { + ASSERT(cur_start == region_address + region_size); + + // Update the size. + region_size = cur_end - region_address; + ASSERT(GetPoolFromMemoryRegionType(it.GetType()) == region_pool); + } + } + + // If we didn't find a region, we're done. + if (region_size == 0) { + break; + } - // Initialize the manager's KPageHeap - heap.Initialize(start_address, size, page_heap_size); + // Initialize a new manager for the region. + Impl* manager = std::addressof(managers[num_managers++]); + ASSERT(num_managers <= managers.size()); + + const size_t cur_size = manager->Initialize(region_address, region_size, management_region, + management_region_end, region_pool); + management_region += cur_size; + ASSERT(management_region <= management_region_end); + + // Insert the manager into the pool list. + const auto region_pool_index = static_cast<u32>(region_pool); + if (pool_managers_tail[region_pool_index] == nullptr) { + pool_managers_head[region_pool_index] = manager; + } else { + pool_managers_tail[region_pool_index]->SetNext(manager); + manager->SetPrev(pool_managers_tail[region_pool_index]); + } + pool_managers_tail[region_pool_index] = manager; + } - // Free the memory to the heap - heap.Free(start_address, size / PageSize); + // Free each region to its corresponding heap. + size_t reserved_sizes[MaxManagerCount] = {}; + const PAddr ini_start = GetInitialProcessBinaryPhysicalAddress(); + const PAddr ini_end = ini_start + InitialProcessBinarySizeMax; + const PAddr ini_last = ini_end - 1; + for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) { + if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) { + // Get the manager for the region. + auto index = it.GetAttributes(); + auto& manager = managers[index]; + + const PAddr cur_start = it.GetAddress(); + const PAddr cur_last = it.GetLastAddress(); + const PAddr cur_end = it.GetEndAddress(); + + if (cur_start <= ini_start && ini_last <= cur_last) { + // Free memory before the ini to the heap. + if (cur_start != ini_start) { + manager.Free(cur_start, (ini_start - cur_start) / PageSize); + } - // Update the heap's used size - heap.UpdateUsedSize(); + // Open/reserve the ini memory. + manager.OpenFirst(ini_start, InitialProcessBinarySizeMax / PageSize); + reserved_sizes[it.GetAttributes()] += InitialProcessBinarySizeMax; - return total_metadata_size; -} + // Free memory after the ini to the heap. + if (ini_last != cur_last) { + ASSERT(cur_end != 0); + manager.Free(ini_end, cur_end - ini_end); + } + } else { + // Ensure there's no partial overlap with the ini image. + if (cur_start <= ini_last) { + ASSERT(cur_last < ini_start); + } else { + // Otherwise, check the region for general validity. + ASSERT(cur_end != 0); + } -void KMemoryManager::InitializeManager(Pool pool, u64 start_address, u64 end_address) { - ASSERT(pool < Pool::Count); - managers[static_cast<std::size_t>(pool)].Initialize(pool, start_address, end_address); + // Free the memory to the heap. + manager.Free(cur_start, it.GetSize() / PageSize); + } + } + } + + // Update the used size for all managers. + for (size_t i = 0; i < num_managers; ++i) { + managers[i].SetInitialUsedHeapSize(reserved_sizes[i]); + } } -VAddr KMemoryManager::AllocateAndOpenContinuous(std::size_t num_pages, std::size_t align_pages, - u32 option) { - // Early return if we're allocating no pages +PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option) { + // Early return if we're allocating no pages. if (num_pages == 0) { - return {}; + return 0; } - // Lock the pool that we're allocating from + // Lock the pool that we're allocating from. const auto [pool, dir] = DecodeOption(option); - const auto pool_index{static_cast<std::size_t>(pool)}; - std::lock_guard lock{pool_locks[pool_index]}; - - // Choose a heap based on our page size request - const s32 heap_index{KPageHeap::GetAlignedBlockIndex(num_pages, align_pages)}; - - // Loop, trying to iterate from each block - // TODO (bunnei): Support multiple managers - Impl& chosen_manager{managers[pool_index]}; - VAddr allocated_block{chosen_manager.AllocateBlock(heap_index, false)}; + KScopedLightLock lk(pool_locks[static_cast<std::size_t>(pool)]); + + // Choose a heap based on our page size request. + const s32 heap_index = KPageHeap::GetAlignedBlockIndex(num_pages, align_pages); + + // Loop, trying to iterate from each block. + Impl* chosen_manager = nullptr; + PAddr allocated_block = 0; + for (chosen_manager = this->GetFirstManager(pool, dir); chosen_manager != nullptr; + chosen_manager = this->GetNextManager(chosen_manager, dir)) { + allocated_block = chosen_manager->AllocateBlock(heap_index, true); + if (allocated_block != 0) { + break; + } + } - // If we failed to allocate, quit now - if (!allocated_block) { - return {}; + // If we failed to allocate, quit now. + if (allocated_block == 0) { + return 0; } - // If we allocated more than we need, free some - const auto allocated_pages{KPageHeap::GetBlockNumPages(heap_index)}; + // If we allocated more than we need, free some. + const size_t allocated_pages = KPageHeap::GetBlockNumPages(heap_index); if (allocated_pages > num_pages) { - chosen_manager.Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages); + chosen_manager->Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages); } + // Open the first reference to the pages. + chosen_manager->OpenFirst(allocated_block, num_pages); + return allocated_block; } -ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, - Direction dir, u32 heap_fill_value) { - ASSERT(page_list.GetNumPages() == 0); +Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool, + Direction dir, bool random) { + // Choose a heap based on our page size request. + const s32 heap_index = KPageHeap::GetBlockIndex(num_pages); + R_UNLESS(0 <= heap_index, ResultOutOfMemory); + + // Ensure that we don't leave anything un-freed. + auto group_guard = SCOPE_GUARD({ + for (const auto& it : out->Nodes()) { + auto& manager = this->GetManager(system.Kernel().MemoryLayout(), it.GetAddress()); + const size_t num_pages_to_free = + std::min(it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize); + manager.Free(it.GetAddress(), num_pages_to_free); + } + }); - // Early return if we're allocating no pages - if (num_pages == 0) { - return ResultSuccess; - } + // Keep allocating until we've allocated all our pages. + for (s32 index = heap_index; index >= 0 && num_pages > 0; index--) { + const size_t pages_per_alloc = KPageHeap::GetBlockNumPages(index); + for (Impl* cur_manager = this->GetFirstManager(pool, dir); cur_manager != nullptr; + cur_manager = this->GetNextManager(cur_manager, dir)) { + while (num_pages >= pages_per_alloc) { + // Allocate a block. + PAddr allocated_block = cur_manager->AllocateBlock(index, random); + if (allocated_block == 0) { + break; + } - // Lock the pool that we're allocating from - const auto pool_index{static_cast<std::size_t>(pool)}; - std::lock_guard lock{pool_locks[pool_index]}; + // Safely add it to our group. + { + auto block_guard = + SCOPE_GUARD({ cur_manager->Free(allocated_block, pages_per_alloc); }); + R_TRY(out->AddBlock(allocated_block, pages_per_alloc)); + block_guard.Cancel(); + } - // Choose a heap based on our page size request - const s32 heap_index{KPageHeap::GetBlockIndex(num_pages)}; - if (heap_index < 0) { - return ResultOutOfMemory; + num_pages -= pages_per_alloc; + } + } } - // TODO (bunnei): Support multiple managers - Impl& chosen_manager{managers[pool_index]}; + // Only succeed if we allocated as many pages as we wanted. + R_UNLESS(num_pages == 0, ResultOutOfMemory); - // Ensure that we don't leave anything un-freed - auto group_guard = detail::ScopeExit([&] { - for (const auto& it : page_list.Nodes()) { - const auto min_num_pages{std::min<size_t>( - it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)}; - chosen_manager.Free(it.GetAddress(), min_num_pages); - } - }); + // We succeeded! + group_guard.Cancel(); + return ResultSuccess; +} - // Keep allocating until we've allocated all our pages - for (s32 index{heap_index}; index >= 0 && num_pages > 0; index--) { - const auto pages_per_alloc{KPageHeap::GetBlockNumPages(index)}; +Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option) { + ASSERT(out != nullptr); + ASSERT(out->GetNumPages() == 0); - while (num_pages >= pages_per_alloc) { - // Allocate a block - VAddr allocated_block{chosen_manager.AllocateBlock(index, false)}; - if (!allocated_block) { - break; - } + // Early return if we're allocating no pages. + R_SUCCEED_IF(num_pages == 0); - // Safely add it to our group - { - auto block_guard = detail::ScopeExit( - [&] { chosen_manager.Free(allocated_block, pages_per_alloc); }); + // Lock the pool that we're allocating from. + const auto [pool, dir] = DecodeOption(option); + KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]); + + // Allocate the page group. + R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false)); + + // Open the first reference to the pages. + for (const auto& block : out->Nodes()) { + PAddr cur_address = block.GetAddress(); + size_t remaining_pages = block.GetNumPages(); + while (remaining_pages > 0) { + // Get the manager for the current address. + auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address); + + // Process part or all of the block. + const size_t cur_pages = + std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address)); + manager.OpenFirst(cur_address, cur_pages); + + // Advance. + cur_address += cur_pages * PageSize; + remaining_pages -= cur_pages; + } + } - if (const ResultCode result{page_list.AddBlock(allocated_block, pages_per_alloc)}; - result.IsError()) { - return result; - } + return ResultSuccess; +} - block_guard.Cancel(); - } +Result KMemoryManager::AllocateAndOpenForProcess(KPageGroup* out, size_t num_pages, u32 option, + u64 process_id, u8 fill_pattern) { + ASSERT(out != nullptr); + ASSERT(out->GetNumPages() == 0); - num_pages -= pages_per_alloc; - } - } + // Decode the option. + const auto [pool, dir] = DecodeOption(option); - // Clear allocated memory. - for (const auto& it : page_list.Nodes()) { - std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value, - it.GetSize()); + // Allocate the memory. + { + // Lock the pool that we're allocating from. + KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]); + + // Allocate the page group. + R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false)); + + // Open the first reference to the pages. + for (const auto& block : out->Nodes()) { + PAddr cur_address = block.GetAddress(); + size_t remaining_pages = block.GetNumPages(); + while (remaining_pages > 0) { + // Get the manager for the current address. + auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address); + + // Process part or all of the block. + const size_t cur_pages = + std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address)); + manager.OpenFirst(cur_address, cur_pages); + + // Advance. + cur_address += cur_pages * PageSize; + remaining_pages -= cur_pages; + } + } } - // Only succeed if we allocated as many pages as we wanted - if (num_pages) { - return ResultOutOfMemory; + // Set all the allocated memory. + for (const auto& block : out->Nodes()) { + std::memset(system.DeviceMemory().GetPointer(block.GetAddress()), fill_pattern, + block.GetSize()); } - // We succeeded! - group_guard.Cancel(); - return ResultSuccess; } -ResultCode KMemoryManager::Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, - Direction dir, u32 heap_fill_value) { - // Early return if we're freeing no pages - if (!num_pages) { - return ResultSuccess; +void KMemoryManager::Open(PAddr address, size_t num_pages) { + // Repeatedly open references until we've done so for all pages. + while (num_pages) { + auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address); + const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address)); + + { + KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]); + manager.Open(address, cur_pages); + } + + num_pages -= cur_pages; + address += cur_pages * PageSize; } +} - // Lock the pool that we're freeing from - const auto pool_index{static_cast<std::size_t>(pool)}; - std::lock_guard lock{pool_locks[pool_index]}; +void KMemoryManager::Close(PAddr address, size_t num_pages) { + // Repeatedly close references until we've done so for all pages. + while (num_pages) { + auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address); + const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address)); - // TODO (bunnei): Support multiple managers - Impl& chosen_manager{managers[pool_index]}; + { + KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]); + manager.Close(address, cur_pages); + } - // Free all of the pages - for (const auto& it : page_list.Nodes()) { - const auto min_num_pages{std::min<size_t>( - it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)}; - chosen_manager.Free(it.GetAddress(), min_num_pages); + num_pages -= cur_pages; + address += cur_pages * PageSize; } +} - return ResultSuccess; +void KMemoryManager::Close(const KPageGroup& pg) { + for (const auto& node : pg.Nodes()) { + Close(node.GetAddress(), node.GetNumPages()); + } +} +void KMemoryManager::Open(const KPageGroup& pg) { + for (const auto& node : pg.Nodes()) { + Open(node.GetAddress(), node.GetNumPages()); + } +} + +size_t KMemoryManager::Impl::Initialize(PAddr address, size_t size, VAddr management, + VAddr management_end, Pool p) { + // Calculate management sizes. + const size_t ref_count_size = (size / PageSize) * sizeof(u16); + const size_t optimize_map_size = CalculateOptimizedProcessOverheadSize(size); + const size_t manager_size = Common::AlignUp(optimize_map_size + ref_count_size, PageSize); + const size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(size); + const size_t total_management_size = manager_size + page_heap_size; + ASSERT(manager_size <= total_management_size); + ASSERT(management + total_management_size <= management_end); + ASSERT(Common::IsAligned(total_management_size, PageSize)); + + // Setup region. + pool = p; + management_region = management; + page_reference_counts.resize( + Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize() / PageSize); + ASSERT(Common::IsAligned(management_region, PageSize)); + + // Initialize the manager's KPageHeap. + heap.Initialize(address, size, management + manager_size, page_heap_size); + + return total_management_size; } -std::size_t KMemoryManager::Impl::CalculateManagementOverheadSize(std::size_t region_size) { - const std::size_t ref_count_size = (region_size / PageSize) * sizeof(u16); - const std::size_t optimize_map_size = +size_t KMemoryManager::Impl::CalculateManagementOverheadSize(size_t region_size) { + const size_t ref_count_size = (region_size / PageSize) * sizeof(u16); + const size_t optimize_map_size = (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) / Common::BitSize<u64>()) * sizeof(u64); - const std::size_t manager_meta_size = - Common::AlignUp(optimize_map_size + ref_count_size, PageSize); - const std::size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(region_size); + const size_t manager_meta_size = Common::AlignUp(optimize_map_size + ref_count_size, PageSize); + const size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(region_size); return manager_meta_size + page_heap_size; } diff --git a/src/core/hle/kernel/k_memory_manager.h b/src/core/hle/kernel/k_memory_manager.h index 17c7690f1..dcb9b6348 100644 --- a/src/core/hle/kernel/k_memory_manager.h +++ b/src/core/hle/kernel/k_memory_manager.h @@ -1,15 +1,15 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include <array> -#include <mutex> #include <tuple> #include "common/common_funcs.h" #include "common/common_types.h" +#include "core/hle/kernel/k_light_lock.h" +#include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_page_heap.h" #include "core/hle/result.h" @@ -19,7 +19,7 @@ class System; namespace Kernel { -class KPageLinkedList; +class KPageGroup; class KMemoryManager final { public: @@ -52,22 +52,33 @@ public: explicit KMemoryManager(Core::System& system_); - constexpr std::size_t GetSize(Pool pool) const { - return managers[static_cast<std::size_t>(pool)].GetSize(); + void Initialize(VAddr management_region, size_t management_region_size); + + constexpr size_t GetSize(Pool pool) const { + constexpr Direction GetSizeDirection = Direction::FromFront; + size_t total = 0; + for (auto* manager = this->GetFirstManager(pool, GetSizeDirection); manager != nullptr; + manager = this->GetNextManager(manager, GetSizeDirection)) { + total += manager->GetSize(); + } + return total; } - void InitializeManager(Pool pool, u64 start_address, u64 end_address); + PAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option); + Result AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option); + Result AllocateAndOpenForProcess(KPageGroup* out, size_t num_pages, u32 option, u64 process_id, + u8 fill_pattern); + + static constexpr size_t MaxManagerCount = 10; - VAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option); - ResultCode Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, Direction dir, - u32 heap_fill_value = 0); - ResultCode Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, Direction dir, - u32 heap_fill_value = 0); + void Close(PAddr address, size_t num_pages); + void Close(const KPageGroup& pg); - static constexpr std::size_t MaxManagerCount = 10; + void Open(PAddr address, size_t num_pages); + void Open(const KPageGroup& pg); public: - static std::size_t CalculateManagementOverheadSize(std::size_t region_size) { + static size_t CalculateManagementOverheadSize(size_t region_size) { return Impl::CalculateManagementOverheadSize(region_size); } @@ -100,17 +111,26 @@ private: Impl() = default; ~Impl() = default; - std::size_t Initialize(Pool new_pool, u64 start_address, u64 end_address); + size_t Initialize(PAddr address, size_t size, VAddr management, VAddr management_end, + Pool p); VAddr AllocateBlock(s32 index, bool random) { return heap.AllocateBlock(index, random); } - void Free(VAddr addr, std::size_t num_pages) { + void Free(VAddr addr, size_t num_pages) { heap.Free(addr, num_pages); } - constexpr std::size_t GetSize() const { + void SetInitialUsedHeapSize(size_t reserved_size) { + heap.SetInitialUsedSize(reserved_size); + } + + constexpr Pool GetPool() const { + return pool; + } + + constexpr size_t GetSize() const { return heap.GetSize(); } @@ -122,10 +142,88 @@ private: return heap.GetEndAddress(); } - static std::size_t CalculateManagementOverheadSize(std::size_t region_size); + constexpr size_t GetPageOffset(PAddr address) const { + return heap.GetPageOffset(address); + } + + constexpr size_t GetPageOffsetToEnd(PAddr address) const { + return heap.GetPageOffsetToEnd(address); + } + + constexpr void SetNext(Impl* n) { + next = n; + } + + constexpr void SetPrev(Impl* n) { + prev = n; + } + + constexpr Impl* GetNext() const { + return next; + } + + constexpr Impl* GetPrev() const { + return prev; + } + + void OpenFirst(PAddr address, size_t num_pages) { + size_t index = this->GetPageOffset(address); + const size_t end = index + num_pages; + while (index < end) { + const RefCount ref_count = (++page_reference_counts[index]); + ASSERT(ref_count == 1); - static constexpr std::size_t CalculateOptimizedProcessOverheadSize( - std::size_t region_size) { + index++; + } + } + + void Open(PAddr address, size_t num_pages) { + size_t index = this->GetPageOffset(address); + const size_t end = index + num_pages; + while (index < end) { + const RefCount ref_count = (++page_reference_counts[index]); + ASSERT(ref_count > 1); + + index++; + } + } + + void Close(PAddr address, size_t num_pages) { + size_t index = this->GetPageOffset(address); + const size_t end = index + num_pages; + + size_t free_start = 0; + size_t free_count = 0; + while (index < end) { + ASSERT(page_reference_counts[index] > 0); + const RefCount ref_count = (--page_reference_counts[index]); + + // Keep track of how many zero refcounts we see in a row, to minimize calls to free. + if (ref_count == 0) { + if (free_count > 0) { + free_count++; + } else { + free_start = index; + free_count = 1; + } + } else { + if (free_count > 0) { + this->Free(heap.GetAddress() + free_start * PageSize, free_count); + free_count = 0; + } + } + + index++; + } + + if (free_count > 0) { + this->Free(heap.GetAddress() + free_start * PageSize, free_count); + } + } + + static size_t CalculateManagementOverheadSize(size_t region_size); + + static constexpr size_t CalculateOptimizedProcessOverheadSize(size_t region_size) { return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) / Common::BitSize<u64>()) * sizeof(u64); @@ -135,13 +233,45 @@ private: using RefCount = u16; KPageHeap heap; + std::vector<RefCount> page_reference_counts; + VAddr management_region{}; Pool pool{}; + Impl* next{}; + Impl* prev{}; }; private: + Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) { + return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()]; + } + + const Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) const { + return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()]; + } + + constexpr Impl* GetFirstManager(Pool pool, Direction dir) const { + return dir == Direction::FromBack ? pool_managers_tail[static_cast<size_t>(pool)] + : pool_managers_head[static_cast<size_t>(pool)]; + } + + constexpr Impl* GetNextManager(Impl* cur, Direction dir) const { + if (dir == Direction::FromBack) { + return cur->GetPrev(); + } else { + return cur->GetNext(); + } + } + + Result AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool, Direction dir, + bool random); + +private: Core::System& system; - std::array<std::mutex, static_cast<std::size_t>(Pool::Count)> pool_locks; + std::array<KLightLock, static_cast<size_t>(Pool::Count)> pool_locks; + std::array<Impl*, MaxManagerCount> pool_managers_head{}; + std::array<Impl*, MaxManagerCount> pool_managers_tail{}; std::array<Impl, MaxManagerCount> managers; + size_t num_managers{}; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_memory_region.h b/src/core/hle/kernel/k_memory_region.h index e9bdf4e59..5037e657f 100644 --- a/src/core/hle/kernel/k_memory_region.h +++ b/src/core/hle/kernel/k_memory_region.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_memory_region_type.h b/src/core/hle/kernel/k_memory_region_type.h index a05e66677..7e2fcccdc 100644 --- a/src/core/hle/kernel/k_memory_region_type.h +++ b/src/core/hle/kernel/k_memory_region_type.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -14,7 +13,8 @@ namespace Kernel { enum KMemoryRegionType : u32 { - KMemoryRegionAttr_CarveoutProtected = 0x04000000, + KMemoryRegionAttr_CarveoutProtected = 0x02000000, + KMemoryRegionAttr_Uncached = 0x04000000, KMemoryRegionAttr_DidKernelMap = 0x08000000, KMemoryRegionAttr_ShouldKernelMap = 0x10000000, KMemoryRegionAttr_UserReadOnly = 0x20000000, @@ -239,6 +239,11 @@ static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A); static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A); static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A); +// UNUSED: .DeriveSparse(2, 2, 0); +constexpr auto KMemoryRegionType_VirtualDramUnknownDebug = + KMemoryRegionType_Dram.DeriveSparse(2, 2, 1); +static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52)); + constexpr auto KMemoryRegionType_VirtualDramKernelInitPt = KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0); constexpr auto KMemoryRegionType_VirtualDramPoolManagement = @@ -330,6 +335,8 @@ constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) { return KMemoryRegionType_VirtualDramKernelTraceBuffer; } else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) { return KMemoryRegionType_VirtualDramKernelPtHeap; + } else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) { + return KMemoryRegionType_VirtualDramUnknownDebug; } else { return KMemoryRegionType_Dram; } diff --git a/src/core/hle/kernel/k_page_bitmap.h b/src/core/hle/kernel/k_page_bitmap.h index c75d667c9..c97b3dc0b 100644 --- a/src/core/hle/kernel/k_page_bitmap.h +++ b/src/core/hle/kernel/k_page_bitmap.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_page_buffer.cpp b/src/core/hle/kernel/k_page_buffer.cpp new file mode 100644 index 000000000..1a0bf4439 --- /dev/null +++ b/src/core/hle/kernel/k_page_buffer.cpp @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/alignment.h" +#include "common/assert.h" +#include "core/core.h" +#include "core/device_memory.h" +#include "core/hle/kernel/k_page_buffer.h" +#include "core/hle/kernel/memory_types.h" + +namespace Kernel { + +KPageBuffer* KPageBuffer::FromPhysicalAddress(Core::System& system, PAddr phys_addr) { + ASSERT(Common::IsAligned(phys_addr, PageSize)); + return reinterpret_cast<KPageBuffer*>(system.DeviceMemory().GetPointer(phys_addr)); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_page_buffer.h b/src/core/hle/kernel/k_page_buffer.h new file mode 100644 index 000000000..7e50dc1d1 --- /dev/null +++ b/src/core/hle/kernel/k_page_buffer.h @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> + +#include "common/common_types.h" +#include "core/hle/kernel/memory_types.h" +#include "core/hle/kernel/slab_helpers.h" + +namespace Kernel { + +class KPageBuffer final : public KSlabAllocated<KPageBuffer> { +public: + KPageBuffer() = default; + + static KPageBuffer* FromPhysicalAddress(Core::System& system, PAddr phys_addr); + +private: + [[maybe_unused]] alignas(PageSize) std::array<u8, PageSize> m_buffer{}; +}; + +static_assert(sizeof(KPageBuffer) == PageSize); +static_assert(alignof(KPageBuffer) == PageSize); + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_page_group.h b/src/core/hle/kernel/k_page_group.h new file mode 100644 index 000000000..968753992 --- /dev/null +++ b/src/core/hle/kernel/k_page_group.h @@ -0,0 +1,99 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <list> + +#include "common/assert.h" +#include "common/common_types.h" +#include "core/hle/kernel/memory_types.h" +#include "core/hle/result.h" + +namespace Kernel { + +class KPageGroup final { +public: + class Node final { + public: + constexpr Node(u64 addr_, std::size_t num_pages_) : addr{addr_}, num_pages{num_pages_} {} + + constexpr u64 GetAddress() const { + return addr; + } + + constexpr std::size_t GetNumPages() const { + return num_pages; + } + + constexpr std::size_t GetSize() const { + return GetNumPages() * PageSize; + } + + private: + u64 addr{}; + std::size_t num_pages{}; + }; + +public: + KPageGroup() = default; + KPageGroup(u64 address, u64 num_pages) { + ASSERT(AddBlock(address, num_pages).IsSuccess()); + } + + constexpr std::list<Node>& Nodes() { + return nodes; + } + + constexpr const std::list<Node>& Nodes() const { + return nodes; + } + + std::size_t GetNumPages() const { + std::size_t num_pages = 0; + for (const Node& node : nodes) { + num_pages += node.GetNumPages(); + } + return num_pages; + } + + bool IsEqual(KPageGroup& other) const { + auto this_node = nodes.begin(); + auto other_node = other.nodes.begin(); + while (this_node != nodes.end() && other_node != other.nodes.end()) { + if (this_node->GetAddress() != other_node->GetAddress() || + this_node->GetNumPages() != other_node->GetNumPages()) { + return false; + } + this_node = std::next(this_node); + other_node = std::next(other_node); + } + + return this_node == nodes.end() && other_node == other.nodes.end(); + } + + Result AddBlock(u64 address, u64 num_pages) { + if (!num_pages) { + return ResultSuccess; + } + if (!nodes.empty()) { + const auto node = nodes.back(); + if (node.GetAddress() + node.GetNumPages() * PageSize == address) { + address = node.GetAddress(); + num_pages += node.GetNumPages(); + nodes.pop_back(); + } + } + nodes.push_back({address, num_pages}); + return ResultSuccess; + } + + bool Empty() const { + return nodes.empty(); + } + +private: + std::list<Node> nodes; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_page_heap.cpp b/src/core/hle/kernel/k_page_heap.cpp index 29d996d62..5ede60168 100644 --- a/src/core/hle/kernel/k_page_heap.cpp +++ b/src/core/hle/kernel/k_page_heap.cpp @@ -1,41 +1,56 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" #include "core/hle/kernel/k_page_heap.h" namespace Kernel { -void KPageHeap::Initialize(VAddr address, std::size_t size, std::size_t metadata_size) { - // Check our assumptions - ASSERT(Common::IsAligned((address), PageSize)); +void KPageHeap::Initialize(PAddr address, size_t size, VAddr management_address, + size_t management_size, const size_t* block_shifts, + size_t num_block_shifts) { + // Check our assumptions. + ASSERT(Common::IsAligned(address, PageSize)); ASSERT(Common::IsAligned(size, PageSize)); + ASSERT(0 < num_block_shifts && num_block_shifts <= NumMemoryBlockPageShifts); + const VAddr management_end = management_address + management_size; - // Set our members - heap_address = address; - heap_size = size; - - // Setup bitmaps - metadata.resize(metadata_size / sizeof(u64)); - u64* cur_bitmap_storage{metadata.data()}; - for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) { - const std::size_t cur_block_shift{MemoryBlockPageShifts[i]}; - const std::size_t next_block_shift{ - (i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0}; - cur_bitmap_storage = blocks[i].Initialize(heap_address, heap_size, cur_block_shift, - next_block_shift, cur_bitmap_storage); + // Set our members. + m_heap_address = address; + m_heap_size = size; + m_num_blocks = num_block_shifts; + + // Setup bitmaps. + m_management_data.resize(management_size / sizeof(u64)); + u64* cur_bitmap_storage{m_management_data.data()}; + for (size_t i = 0; i < num_block_shifts; i++) { + const size_t cur_block_shift = block_shifts[i]; + const size_t next_block_shift = (i != num_block_shifts - 1) ? block_shifts[i + 1] : 0; + cur_bitmap_storage = m_blocks[i].Initialize(m_heap_address, m_heap_size, cur_block_shift, + next_block_shift, cur_bitmap_storage); } + + // Ensure we didn't overextend our bounds. + ASSERT(VAddr(cur_bitmap_storage) <= management_end); +} + +size_t KPageHeap::GetNumFreePages() const { + size_t num_free = 0; + + for (size_t i = 0; i < m_num_blocks; i++) { + num_free += m_blocks[i].GetNumFreePages(); + } + + return num_free; } -VAddr KPageHeap::AllocateBlock(s32 index, bool random) { - const std::size_t needed_size{blocks[index].GetSize()}; +PAddr KPageHeap::AllocateBlock(s32 index, bool random) { + const size_t needed_size = m_blocks[index].GetSize(); - for (s32 i{index}; i < static_cast<s32>(MemoryBlockPageShifts.size()); i++) { - if (const VAddr addr{blocks[i].PopBlock(random)}; addr) { - if (const std::size_t allocated_size{blocks[i].GetSize()}; - allocated_size > needed_size) { - Free(addr + needed_size, (allocated_size - needed_size) / PageSize); + for (s32 i = index; i < static_cast<s32>(m_num_blocks); i++) { + if (const PAddr addr = m_blocks[i].PopBlock(random); addr != 0) { + if (const size_t allocated_size = m_blocks[i].GetSize(); allocated_size > needed_size) { + this->Free(addr + needed_size, (allocated_size - needed_size) / PageSize); } return addr; } @@ -44,34 +59,34 @@ VAddr KPageHeap::AllocateBlock(s32 index, bool random) { return 0; } -void KPageHeap::FreeBlock(VAddr block, s32 index) { +void KPageHeap::FreeBlock(PAddr block, s32 index) { do { - block = blocks[index++].PushBlock(block); + block = m_blocks[index++].PushBlock(block); } while (block != 0); } -void KPageHeap::Free(VAddr addr, std::size_t num_pages) { - // Freeing no pages is a no-op +void KPageHeap::Free(PAddr addr, size_t num_pages) { + // Freeing no pages is a no-op. if (num_pages == 0) { return; } - // Find the largest block size that we can free, and free as many as possible - s32 big_index{static_cast<s32>(MemoryBlockPageShifts.size()) - 1}; - const VAddr start{addr}; - const VAddr end{(num_pages * PageSize) + addr}; - VAddr before_start{start}; - VAddr before_end{start}; - VAddr after_start{end}; - VAddr after_end{end}; + // Find the largest block size that we can free, and free as many as possible. + s32 big_index = static_cast<s32>(m_num_blocks) - 1; + const PAddr start = addr; + const PAddr end = addr + num_pages * PageSize; + PAddr before_start = start; + PAddr before_end = start; + PAddr after_start = end; + PAddr after_end = end; while (big_index >= 0) { - const std::size_t block_size{blocks[big_index].GetSize()}; - const VAddr big_start{Common::AlignUp((start), block_size)}; - const VAddr big_end{Common::AlignDown((end), block_size)}; + const size_t block_size = m_blocks[big_index].GetSize(); + const PAddr big_start = Common::AlignUp(start, block_size); + const PAddr big_end = Common::AlignDown(end, block_size); if (big_start < big_end) { - // Free as many big blocks as we can - for (auto block{big_start}; block < big_end; block += block_size) { - FreeBlock(block, big_index); + // Free as many big blocks as we can. + for (auto block = big_start; block < big_end; block += block_size) { + this->FreeBlock(block, big_index); } before_end = big_start; after_start = big_end; @@ -81,31 +96,31 @@ void KPageHeap::Free(VAddr addr, std::size_t num_pages) { } ASSERT(big_index >= 0); - // Free space before the big blocks - for (s32 i{big_index - 1}; i >= 0; i--) { - const std::size_t block_size{blocks[i].GetSize()}; + // Free space before the big blocks. + for (s32 i = big_index - 1; i >= 0; i--) { + const size_t block_size = m_blocks[i].GetSize(); while (before_start + block_size <= before_end) { before_end -= block_size; - FreeBlock(before_end, i); + this->FreeBlock(before_end, i); } } - // Free space after the big blocks - for (s32 i{big_index - 1}; i >= 0; i--) { - const std::size_t block_size{blocks[i].GetSize()}; + // Free space after the big blocks. + for (s32 i = big_index - 1; i >= 0; i--) { + const size_t block_size = m_blocks[i].GetSize(); while (after_start + block_size <= after_end) { - FreeBlock(after_start, i); + this->FreeBlock(after_start, i); after_start += block_size; } } } -std::size_t KPageHeap::CalculateManagementOverheadSize(std::size_t region_size) { - std::size_t overhead_size = 0; - for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) { - const std::size_t cur_block_shift{MemoryBlockPageShifts[i]}; - const std::size_t next_block_shift{ - (i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0}; +size_t KPageHeap::CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts, + size_t num_block_shifts) { + size_t overhead_size = 0; + for (size_t i = 0; i < num_block_shifts; i++) { + const size_t cur_block_shift = block_shifts[i]; + const size_t next_block_shift = (i != num_block_shifts - 1) ? block_shifts[i + 1] : 0; overhead_size += KPageHeap::Block::CalculateManagementOverheadSize( region_size, cur_block_shift, next_block_shift); } diff --git a/src/core/hle/kernel/k_page_heap.h b/src/core/hle/kernel/k_page_heap.h index a65aa28a0..0917a8bed 100644 --- a/src/core/hle/kernel/k_page_heap.h +++ b/src/core/hle/kernel/k_page_heap.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -23,54 +22,73 @@ public: KPageHeap() = default; ~KPageHeap() = default; - constexpr VAddr GetAddress() const { - return heap_address; + constexpr PAddr GetAddress() const { + return m_heap_address; } - constexpr std::size_t GetSize() const { - return heap_size; + constexpr size_t GetSize() const { + return m_heap_size; } - constexpr VAddr GetEndAddress() const { - return GetAddress() + GetSize(); + constexpr PAddr GetEndAddress() const { + return this->GetAddress() + this->GetSize(); } - constexpr std::size_t GetPageOffset(VAddr block) const { - return (block - GetAddress()) / PageSize; + constexpr size_t GetPageOffset(PAddr block) const { + return (block - this->GetAddress()) / PageSize; + } + constexpr size_t GetPageOffsetToEnd(PAddr block) const { + return (this->GetEndAddress() - block) / PageSize; + } + + void Initialize(PAddr heap_address, size_t heap_size, VAddr management_address, + size_t management_size) { + return this->Initialize(heap_address, heap_size, management_address, management_size, + MemoryBlockPageShifts.data(), NumMemoryBlockPageShifts); + } + + size_t GetFreeSize() const { + return this->GetNumFreePages() * PageSize; } - void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size); - VAddr AllocateBlock(s32 index, bool random); - void Free(VAddr addr, std::size_t num_pages); + void SetInitialUsedSize(size_t reserved_size) { + // Check that the reserved size is valid. + const size_t free_size = this->GetNumFreePages() * PageSize; + ASSERT(m_heap_size >= free_size + reserved_size); - void UpdateUsedSize() { - used_size = heap_size - (GetNumFreePages() * PageSize); + // Set the initial used size. + m_initial_used_size = m_heap_size - free_size - reserved_size; } - static std::size_t CalculateManagementOverheadSize(std::size_t region_size); + PAddr AllocateBlock(s32 index, bool random); + void Free(PAddr addr, size_t num_pages); + + static size_t CalculateManagementOverheadSize(size_t region_size) { + return CalculateManagementOverheadSize(region_size, MemoryBlockPageShifts.data(), + NumMemoryBlockPageShifts); + } - static constexpr s32 GetAlignedBlockIndex(std::size_t num_pages, std::size_t align_pages) { - const auto target_pages{std::max(num_pages, align_pages)}; - for (std::size_t i = 0; i < NumMemoryBlockPageShifts; i++) { - if (target_pages <= - (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) { + static constexpr s32 GetAlignedBlockIndex(size_t num_pages, size_t align_pages) { + const size_t target_pages = std::max(num_pages, align_pages); + for (size_t i = 0; i < NumMemoryBlockPageShifts; i++) { + if (target_pages <= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) { return static_cast<s32>(i); } } return -1; } - static constexpr s32 GetBlockIndex(std::size_t num_pages) { - for (s32 i{static_cast<s32>(NumMemoryBlockPageShifts) - 1}; i >= 0; i--) { - if (num_pages >= (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) { + static constexpr s32 GetBlockIndex(size_t num_pages) { + for (s32 i = static_cast<s32>(NumMemoryBlockPageShifts) - 1; i >= 0; i--) { + if (num_pages >= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) { return i; } } return -1; } - static constexpr std::size_t GetBlockSize(std::size_t index) { - return static_cast<std::size_t>(1) << MemoryBlockPageShifts[index]; + static constexpr size_t GetBlockSize(size_t index) { + return size_t(1) << MemoryBlockPageShifts[index]; } - static constexpr std::size_t GetBlockNumPages(std::size_t index) { + static constexpr size_t GetBlockNumPages(size_t index) { return GetBlockSize(index) / PageSize; } @@ -83,114 +101,116 @@ private: Block() = default; ~Block() = default; - constexpr std::size_t GetShift() const { - return block_shift; + constexpr size_t GetShift() const { + return m_block_shift; } - constexpr std::size_t GetNextShift() const { - return next_block_shift; + constexpr size_t GetNextShift() const { + return m_next_block_shift; } - constexpr std::size_t GetSize() const { - return static_cast<std::size_t>(1) << GetShift(); + constexpr size_t GetSize() const { + return u64(1) << this->GetShift(); } - constexpr std::size_t GetNumPages() const { - return GetSize() / PageSize; + constexpr size_t GetNumPages() const { + return this->GetSize() / PageSize; } - constexpr std::size_t GetNumFreeBlocks() const { - return bitmap.GetNumBits(); + constexpr size_t GetNumFreeBlocks() const { + return m_bitmap.GetNumBits(); } - constexpr std::size_t GetNumFreePages() const { - return GetNumFreeBlocks() * GetNumPages(); + constexpr size_t GetNumFreePages() const { + return this->GetNumFreeBlocks() * this->GetNumPages(); } - u64* Initialize(VAddr addr, std::size_t size, std::size_t bs, std::size_t nbs, - u64* bit_storage) { - // Set shifts - block_shift = bs; - next_block_shift = nbs; - - // Align up the address - VAddr end{addr + size}; - const auto align{(next_block_shift != 0) ? (1ULL << next_block_shift) - : (1ULL << block_shift)}; - addr = Common::AlignDown((addr), align); - end = Common::AlignUp((end), align); - - heap_address = addr; - end_offset = (end - addr) / (1ULL << block_shift); - return bitmap.Initialize(bit_storage, end_offset); + u64* Initialize(PAddr addr, size_t size, size_t bs, size_t nbs, u64* bit_storage) { + // Set shifts. + m_block_shift = bs; + m_next_block_shift = nbs; + + // Align up the address. + PAddr end = addr + size; + const size_t align = (m_next_block_shift != 0) ? (u64(1) << m_next_block_shift) + : (u64(1) << m_block_shift); + addr = Common::AlignDown(addr, align); + end = Common::AlignUp(end, align); + + m_heap_address = addr; + m_end_offset = (end - addr) / (u64(1) << m_block_shift); + return m_bitmap.Initialize(bit_storage, m_end_offset); } - VAddr PushBlock(VAddr address) { - // Set the bit for the free block - std::size_t offset{(address - heap_address) >> GetShift()}; - bitmap.SetBit(offset); + PAddr PushBlock(PAddr address) { + // Set the bit for the free block. + size_t offset = (address - m_heap_address) >> this->GetShift(); + m_bitmap.SetBit(offset); - // If we have a next shift, try to clear the blocks below and return the address - if (GetNextShift()) { - const auto diff{1ULL << (GetNextShift() - GetShift())}; + // If we have a next shift, try to clear the blocks below this one and return the new + // address. + if (this->GetNextShift()) { + const size_t diff = u64(1) << (this->GetNextShift() - this->GetShift()); offset = Common::AlignDown(offset, diff); - if (bitmap.ClearRange(offset, diff)) { - return heap_address + (offset << GetShift()); + if (m_bitmap.ClearRange(offset, diff)) { + return m_heap_address + (offset << this->GetShift()); } } - // We couldn't coalesce, or we're already as big as possible - return 0; + // We couldn't coalesce, or we're already as big as possible. + return {}; } - VAddr PopBlock(bool random) { - // Find a free block - const s64 soffset{bitmap.FindFreeBlock(random)}; + PAddr PopBlock(bool random) { + // Find a free block. + s64 soffset = m_bitmap.FindFreeBlock(random); if (soffset < 0) { - return 0; + return {}; } - const auto offset{static_cast<std::size_t>(soffset)}; + const size_t offset = static_cast<size_t>(soffset); - // Update our tracking and return it - bitmap.ClearBit(offset); - return heap_address + (offset << GetShift()); + // Update our tracking and return it. + m_bitmap.ClearBit(offset); + return m_heap_address + (offset << this->GetShift()); } - static constexpr std::size_t CalculateManagementOverheadSize(std::size_t region_size, - std::size_t cur_block_shift, - std::size_t next_block_shift) { - const auto cur_block_size{(1ULL << cur_block_shift)}; - const auto next_block_size{(1ULL << next_block_shift)}; - const auto align{(next_block_shift != 0) ? next_block_size : cur_block_size}; + public: + static constexpr size_t CalculateManagementOverheadSize(size_t region_size, + size_t cur_block_shift, + size_t next_block_shift) { + const size_t cur_block_size = (u64(1) << cur_block_shift); + const size_t next_block_size = (u64(1) << next_block_shift); + const size_t align = (next_block_shift != 0) ? next_block_size : cur_block_size; return KPageBitmap::CalculateManagementOverheadSize( (align * 2 + Common::AlignUp(region_size, align)) / cur_block_size); } private: - KPageBitmap bitmap; - VAddr heap_address{}; - uintptr_t end_offset{}; - std::size_t block_shift{}; - std::size_t next_block_shift{}; + KPageBitmap m_bitmap; + PAddr m_heap_address{}; + uintptr_t m_end_offset{}; + size_t m_block_shift{}; + size_t m_next_block_shift{}; }; - constexpr std::size_t GetNumFreePages() const { - std::size_t num_free{}; - - for (const auto& block : blocks) { - num_free += block.GetNumFreePages(); - } - - return num_free; - } +private: + void Initialize(PAddr heap_address, size_t heap_size, VAddr management_address, + size_t management_size, const size_t* block_shifts, size_t num_block_shifts); + size_t GetNumFreePages() const; - void FreeBlock(VAddr block, s32 index); + void FreeBlock(PAddr block, s32 index); - static constexpr std::size_t NumMemoryBlockPageShifts{7}; - static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{ + static constexpr size_t NumMemoryBlockPageShifts{7}; + static constexpr std::array<size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{ 0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E, }; - VAddr heap_address{}; - std::size_t heap_size{}; - std::size_t used_size{}; - std::array<Block, NumMemoryBlockPageShifts> blocks{}; - std::vector<u64> metadata; +private: + static size_t CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts, + size_t num_block_shifts); + +private: + PAddr m_heap_address{}; + size_t m_heap_size{}; + size_t m_initial_used_size{}; + size_t m_num_blocks{}; + std::array<Block, NumMemoryBlockPageShifts> m_blocks{}; + std::vector<u64> m_management_data; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_page_linked_list.h b/src/core/hle/kernel/k_page_linked_list.h deleted file mode 100644 index 0e2ae582a..000000000 --- a/src/core/hle/kernel/k_page_linked_list.h +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <list> - -#include "common/assert.h" -#include "common/common_types.h" -#include "core/hle/kernel/memory_types.h" -#include "core/hle/result.h" - -namespace Kernel { - -class KPageLinkedList final { -public: - class Node final { - public: - constexpr Node(u64 addr_, std::size_t num_pages_) : addr{addr_}, num_pages{num_pages_} {} - - constexpr u64 GetAddress() const { - return addr; - } - - constexpr std::size_t GetNumPages() const { - return num_pages; - } - - constexpr std::size_t GetSize() const { - return GetNumPages() * PageSize; - } - - private: - u64 addr{}; - std::size_t num_pages{}; - }; - -public: - KPageLinkedList() = default; - KPageLinkedList(u64 address, u64 num_pages) { - ASSERT(AddBlock(address, num_pages).IsSuccess()); - } - - constexpr std::list<Node>& Nodes() { - return nodes; - } - - constexpr const std::list<Node>& Nodes() const { - return nodes; - } - - std::size_t GetNumPages() const { - std::size_t num_pages = 0; - for (const Node& node : nodes) { - num_pages += node.GetNumPages(); - } - return num_pages; - } - - bool IsEqual(KPageLinkedList& other) const { - auto this_node = nodes.begin(); - auto other_node = other.nodes.begin(); - while (this_node != nodes.end() && other_node != other.nodes.end()) { - if (this_node->GetAddress() != other_node->GetAddress() || - this_node->GetNumPages() != other_node->GetNumPages()) { - return false; - } - this_node = std::next(this_node); - other_node = std::next(other_node); - } - - return this_node == nodes.end() && other_node == other.nodes.end(); - } - - ResultCode AddBlock(u64 address, u64 num_pages) { - if (!num_pages) { - return ResultSuccess; - } - if (!nodes.empty()) { - const auto node = nodes.back(); - if (node.GetAddress() + node.GetNumPages() * PageSize == address) { - address = node.GetAddress(); - num_pages += node.GetNumPages(); - nodes.pop_back(); - } - } - nodes.push_back({address, num_pages}); - return ResultSuccess; - } - -private: - std::list<Node> nodes; -}; - -} // namespace Kernel diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 912853e5c..d975de844 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/alignment.h" #include "common/assert.h" @@ -10,7 +9,7 @@ #include "core/hle/kernel/k_address_space_info.h" #include "core/hle/kernel/k_memory_block.h" #include "core/hle/kernel/k_memory_block_manager.h" -#include "core/hle/kernel/k_page_linked_list.h" +#include "core/hle/kernel/k_page_group.h" #include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_resource_limit.h" @@ -36,29 +35,11 @@ constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceT case FileSys::ProgramAddressSpaceType::Is39Bit: return 39; default: - UNREACHABLE(); + ASSERT(false); return {}; } } -constexpr u64 GetAddressInRange(const KMemoryInfo& info, VAddr addr) { - if (info.GetAddress() < addr) { - return addr; - } - return info.GetAddress(); -} - -constexpr std::size_t GetSizeInRange(const KMemoryInfo& info, VAddr start, VAddr end) { - std::size_t size{info.GetSize()}; - if (info.GetAddress() < start) { - size -= start - info.GetAddress(); - } - if (info.GetEndAddress() > end) { - size -= info.GetEndAddress() - end; - } - return size; -} - } // namespace KPageTable::KPageTable(Core::System& system_) @@ -66,9 +47,9 @@ KPageTable::KPageTable(Core::System& system_) KPageTable::~KPageTable() = default; -ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, - bool enable_aslr, VAddr code_addr, - std::size_t code_size, KMemoryManager::Pool pool) { +Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, + VAddr code_addr, std::size_t code_size, + KMemoryManager::Pool pool) { const auto GetSpaceStart = [this](KAddressSpaceInfo::Type type) { return KAddressSpaceInfo::GetAddressSpaceStart(address_space_width, type); @@ -84,7 +65,6 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_ std::size_t alias_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Alias)}; std::size_t heap_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Heap)}; - ASSERT(start <= code_addr); ASSERT(code_addr < code_addr + code_size); ASSERT(code_addr + code_size - 1 <= end - 1); @@ -147,7 +127,7 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_ const std::size_t needed_size{ (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size)}; if (alloc_size < needed_size) { - UNREACHABLE(); + ASSERT(false); return ResultOutOfMemory; } @@ -277,8 +257,8 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_ return InitializeMemoryLayout(start, end); } -ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemoryState state, - KMemoryPermission perm) { +Result KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemoryState state, + KMemoryPermission perm) { const u64 size{num_pages * PageSize}; // Validate the mapping request. @@ -291,89 +271,367 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::None, KMemoryAttribute::None)); + KPageGroup pg; + R_TRY(system.Kernel().MemoryManager().AllocateAndOpen( + &pg, num_pages, + KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, allocation_option))); - KPageLinkedList page_linked_list; - R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool, - allocation_option)); - R_TRY(Operate(addr, num_pages, page_linked_list, OperationType::MapGroup)); + R_TRY(Operate(addr, num_pages, pg, OperationType::MapGroup)); block_manager->Update(addr, num_pages, state, perm); return ResultSuccess; } -ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { +Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) { + // Validate the mapping request. + R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), + ResultInvalidMemoryRegion); + + // Lock the table. KScopedLightLock lk(general_lock); - const std::size_t num_pages{size / PageSize}; + // Verify that the source memory is normal heap. + KMemoryState src_state{}; + KMemoryPermission src_perm{}; + std::size_t num_src_allocator_blocks{}; + R_TRY(this->CheckMemoryState(&src_state, &src_perm, nullptr, &num_src_allocator_blocks, + src_address, size, KMemoryState::All, KMemoryState::Normal, + KMemoryPermission::All, KMemoryPermission::UserReadWrite, + KMemoryAttribute::All, KMemoryAttribute::None)); - KMemoryState state{}; - KMemoryPermission perm{}; - CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, nullptr, src_addr, size, - KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All, - KMemoryPermission::UserReadWrite, KMemoryAttribute::Mask, - KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); + // Verify that the destination memory is unmapped. + std::size_t num_dst_allocator_blocks{}; + R_TRY(this->CheckMemoryState(&num_dst_allocator_blocks, dst_address, size, KMemoryState::All, + KMemoryState::Free, KMemoryPermission::None, + KMemoryPermission::None, KMemoryAttribute::None, + KMemoryAttribute::None)); - if (IsRegionMapped(dst_addr, size)) { - return ResultInvalidCurrentMemory; + // Map the code memory. + { + // Determine the number of pages being operated on. + const std::size_t num_pages = size / PageSize; + + // Create page groups for the memory being mapped. + KPageGroup pg; + AddRegionToPages(src_address, num_pages, pg); + + // Reprotect the source as kernel-read/not mapped. + const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead | + KMemoryPermission::NotMapped); + R_TRY(Operate(src_address, num_pages, new_perm, OperationType::ChangePermissions)); + + // Ensure that we unprotect the source pages on failure. + auto unprot_guard = SCOPE_GUARD({ + ASSERT(this->Operate(src_address, num_pages, src_perm, OperationType::ChangePermissions) + .IsSuccess()); + }); + + // Map the alias pages. + R_TRY(MapPages(dst_address, pg, new_perm)); + + // We successfully mapped the alias pages, so we don't need to unprotect the src pages on + // failure. + unprot_guard.Cancel(); + + // Apply the memory block updates. + block_manager->Update(src_address, num_pages, src_state, new_perm, + KMemoryAttribute::Locked); + block_manager->Update(dst_address, num_pages, KMemoryState::AliasCode, new_perm, + KMemoryAttribute::None); } - KPageLinkedList page_linked_list; - AddRegionToPages(src_addr, num_pages, page_linked_list); + return ResultSuccess; +} + +Result 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); + + // Lock the table. + KScopedLightLock lk(general_lock); + + // Verify that the source memory is locked normal heap. + std::size_t num_src_allocator_blocks{}; + R_TRY(this->CheckMemoryState(std::addressof(num_src_allocator_blocks), src_address, size, + KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None, + KMemoryPermission::None, KMemoryAttribute::All, + KMemoryAttribute::Locked)); + + // Verify that the destination memory is aliasable code. + std::size_t num_dst_allocator_blocks{}; + R_TRY(this->CheckMemoryStateContiguous( + std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias, + KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None, + KMemoryAttribute::All, KMemoryAttribute::None)); + // Determine whether any pages being unmapped are code. + bool any_code_pages = false; { - auto block_guard = detail::ScopeExit( - [&] { Operate(src_addr, num_pages, perm, OperationType::ChangePermissions); }); + KMemoryBlockManager::const_iterator it = block_manager->FindIterator(dst_address); + while (true) { + // Get the memory info. + const KMemoryInfo info = it->GetMemoryInfo(); + + // Check if the memory has code flag. + if ((info.GetState() & KMemoryState::FlagCode) != KMemoryState::None) { + any_code_pages = true; + break; + } - CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::None, - OperationType::ChangePermissions)); - CASCADE_CODE(MapPages(dst_addr, page_linked_list, KMemoryPermission::None)); + // Check if we're done. + if (dst_address + size - 1 <= info.GetLastAddress()) { + break; + } - block_guard.Cancel(); + // Advance. + ++it; + } } - block_manager->Update(src_addr, num_pages, state, KMemoryPermission::None, - KMemoryAttribute::Locked); - block_manager->Update(dst_addr, num_pages, KMemoryState::AliasCode); + // Ensure that we maintain the instruction cache. + bool reprotected_pages = false; + SCOPE_EXIT({ + if (reprotected_pages && any_code_pages) { + if (icache_invalidation_strategy == ICacheInvalidationStrategy::InvalidateRange) { + system.InvalidateCpuInstructionCacheRange(dst_address, size); + } else { + system.InvalidateCpuInstructionCaches(); + } + } + }); + + // Unmap. + { + // Determine the number of pages being operated on. + const std::size_t num_pages = size / PageSize; + + // Unmap the aliased copy of the pages. + R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap)); + + // Try to set the permissions for the source pages back to what they should be. + R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, + OperationType::ChangePermissions)); + + // Apply the memory block updates. + block_manager->Update(dst_address, num_pages, KMemoryState::None); + block_manager->Update(src_address, num_pages, KMemoryState::Normal, + KMemoryPermission::UserReadWrite); + + // Note that we reprotected pages. + reprotected_pages = true; + } return ResultSuccess; } -ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { - KScopedLightLock lk(general_lock); +VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages, + std::size_t num_pages, std::size_t alignment, std::size_t offset, + std::size_t guard_pages) { + VAddr address = 0; + + if (num_pages <= region_num_pages) { + if (this->IsAslrEnabled()) { + // Try to directly find a free area up to 8 times. + for (std::size_t i = 0; i < 8; i++) { + const std::size_t random_offset = + KSystemControl::GenerateRandomRange( + 0, (region_num_pages - num_pages - guard_pages) * PageSize / alignment) * + alignment; + const VAddr candidate = + Common::AlignDown((region_start + random_offset), alignment) + offset; + + KMemoryInfo info = this->QueryInfoImpl(candidate); + + if (info.state != KMemoryState::Free) { + continue; + } + if (region_start > candidate) { + continue; + } + if (info.GetAddress() + guard_pages * PageSize > candidate) { + continue; + } + + const VAddr candidate_end = candidate + (num_pages + guard_pages) * PageSize - 1; + if (candidate_end > info.GetLastAddress()) { + continue; + } + if (candidate_end > region_start + region_num_pages * PageSize - 1) { + continue; + } + + address = candidate; + break; + } + // Fall back to finding the first free area with a random offset. + if (address == 0) { + // NOTE: Nintendo does not account for guard pages here. + // This may theoretically cause an offset to be chosen that cannot be mapped. We + // will account for guard pages. + const std::size_t offset_pages = KSystemControl::GenerateRandomRange( + 0, region_num_pages - num_pages - guard_pages); + address = block_manager->FindFreeArea(region_start + offset_pages * PageSize, + region_num_pages - offset_pages, num_pages, + alignment, offset, guard_pages); + } + } - if (!size) { - return ResultSuccess; + // Find the first free area. + if (address == 0) { + address = block_manager->FindFreeArea(region_start, region_num_pages, num_pages, + alignment, offset, guard_pages); + } } - const std::size_t num_pages{size / PageSize}; + return address; +} - CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, nullptr, src_addr, size, - KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None, - KMemoryPermission::None, KMemoryAttribute::Mask, - KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped)); +Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) { + ASSERT(this->IsLockedByCurrentThread()); - KMemoryState state{}; - CASCADE_CODE(CheckMemoryState( - &state, nullptr, nullptr, nullptr, dst_addr, PageSize, KMemoryState::FlagCanCodeAlias, - KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None, - KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); - CASCADE_CODE(CheckMemoryState(dst_addr, size, KMemoryState::All, state, KMemoryPermission::None, - KMemoryPermission::None, KMemoryAttribute::Mask, - KMemoryAttribute::None)); - CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap)); + const size_t size = num_pages * PageSize; - block_manager->Update(dst_addr, num_pages, KMemoryState::Free); - block_manager->Update(src_addr, num_pages, KMemoryState::Normal, - KMemoryPermission::UserReadWrite); + // We're making a new group, not adding to an existing one. + R_UNLESS(pg.Empty(), ResultInvalidCurrentMemory); + + // Begin traversal. + Common::PageTable::TraversalContext context; + Common::PageTable::TraversalEntry next_entry; + R_UNLESS(page_table_impl.BeginTraversal(next_entry, context, addr), ResultInvalidCurrentMemory); - system.InvalidateCpuInstructionCacheRange(dst_addr, size); + // Prepare tracking variables. + PAddr cur_addr = next_entry.phys_addr; + size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1)); + size_t tot_size = cur_size; + + // Iterate, adding to group as we go. + const auto& memory_layout = system.Kernel().MemoryLayout(); + while (tot_size < size) { + R_UNLESS(page_table_impl.ContinueTraversal(next_entry, context), + ResultInvalidCurrentMemory); + + if (next_entry.phys_addr != (cur_addr + cur_size)) { + const size_t cur_pages = cur_size / PageSize; + + R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory); + R_TRY(pg.AddBlock(cur_addr, cur_pages)); + + cur_addr = next_entry.phys_addr; + cur_size = next_entry.block_size; + } else { + cur_size += next_entry.block_size; + } + + tot_size += next_entry.block_size; + } + + // Ensure we add the right amount for the last block. + if (tot_size > size) { + cur_size -= (tot_size - size); + } + + // Add the last block. + const size_t cur_pages = cur_size / PageSize; + R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory); + R_TRY(pg.AddBlock(cur_addr, cur_pages)); return ResultSuccess; } -ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, - KPageTable& src_page_table, VAddr src_addr) { +bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t num_pages) { + ASSERT(this->IsLockedByCurrentThread()); + + const size_t size = num_pages * PageSize; + const auto& pg = pg_ll.Nodes(); + const auto& memory_layout = system.Kernel().MemoryLayout(); + + // Empty groups are necessarily invalid. + if (pg.empty()) { + return false; + } + + // We're going to validate that the group we'd expect is the group we see. + auto cur_it = pg.begin(); + PAddr cur_block_address = cur_it->GetAddress(); + size_t cur_block_pages = cur_it->GetNumPages(); + + auto UpdateCurrentIterator = [&]() { + if (cur_block_pages == 0) { + if ((++cur_it) == pg.end()) { + return false; + } + + cur_block_address = cur_it->GetAddress(); + cur_block_pages = cur_it->GetNumPages(); + } + return true; + }; + + // Begin traversal. + Common::PageTable::TraversalContext context; + Common::PageTable::TraversalEntry next_entry; + if (!page_table_impl.BeginTraversal(next_entry, context, addr)) { + return false; + } + + // Prepare tracking variables. + PAddr cur_addr = next_entry.phys_addr; + size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1)); + size_t tot_size = cur_size; + + // Iterate, comparing expected to actual. + while (tot_size < size) { + if (!page_table_impl.ContinueTraversal(next_entry, context)) { + return false; + } + + if (next_entry.phys_addr != (cur_addr + cur_size)) { + const size_t cur_pages = cur_size / PageSize; + + if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) { + return false; + } + + if (!UpdateCurrentIterator()) { + return false; + } + + if (cur_block_address != cur_addr || cur_block_pages < cur_pages) { + return false; + } + + cur_block_address += cur_size; + cur_block_pages -= cur_pages; + cur_addr = next_entry.phys_addr; + cur_size = next_entry.block_size; + } else { + cur_size += next_entry.block_size; + } + + tot_size += next_entry.block_size; + } + + // Ensure we compare the right amount for the last block. + if (tot_size > size) { + cur_size -= (tot_size - size); + } + + if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) { + return false; + } + + if (!UpdateCurrentIterator()) { + return false; + } + + return cur_block_address == cur_addr && cur_block_pages == (cur_size / PageSize); +} + +Result KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, + VAddr src_addr) { KScopedLightLock lk(general_lock); const std::size_t num_pages{size / PageSize}; @@ -397,156 +655,486 @@ 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; } -ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) { +Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) { // Lock the physical memory lock. KScopedLightLock map_phys_mem_lk(map_physical_memory_lock); - // Lock the table. - KScopedLightLock lk(general_lock); - - std::size_t mapped_size{}; - const VAddr end_addr{addr + size}; + // Calculate the last address for convenience. + const VAddr last_address = address + size - 1; - block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) { - if (info.state != KMemoryState::Free) { - mapped_size += GetSizeInRange(info, addr, end_addr); - } - }); + // Define iteration variables. + VAddr cur_address; + std::size_t mapped_size; - if (mapped_size == size) { - return ResultSuccess; - } + // The entire mapping process can be retried. + while (true) { + // Check if the memory is already mapped. + { + // Lock the table. + KScopedLightLock lk(general_lock); + + // Iterate over the memory. + cur_address = address; + mapped_size = 0; + + auto it = block_manager->FindIterator(cur_address); + while (true) { + // Check that the iterator is valid. + ASSERT(it != block_manager->end()); + + // Get the memory info. + const KMemoryInfo info = it->GetMemoryInfo(); + + // Check if we're done. + if (last_address <= info.GetLastAddress()) { + if (info.GetState() != KMemoryState::Free) { + mapped_size += (last_address + 1 - cur_address); + } + break; + } + + // Track the memory if it's mapped. + if (info.GetState() != KMemoryState::Free) { + mapped_size += VAddr(info.GetEndAddress()) - cur_address; + } + + // Advance. + cur_address = info.GetEndAddress(); + ++it; + } - const std::size_t remaining_size{size - mapped_size}; - const std::size_t remaining_pages{remaining_size / PageSize}; + // If the size mapped is the size requested, we've nothing to do. + R_SUCCEED_IF(size == mapped_size); + } - // Reserve the memory from the process resource limit. - KScopedResourceReservation memory_reservation( - system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory, - remaining_size); - if (!memory_reservation.Succeeded()) { - LOG_ERROR(Kernel, "Could not reserve remaining {:X} bytes", remaining_size); - return ResultLimitReached; + // Allocate and map the memory. + { + // Reserve the memory from the process resource limit. + KScopedResourceReservation memory_reservation( + system.Kernel().CurrentProcess()->GetResourceLimit(), + LimitableResource::PhysicalMemory, size - mapped_size); + R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); + + // Allocate pages for the new memory. + KPageGroup pg; + R_TRY(system.Kernel().MemoryManager().AllocateAndOpenForProcess( + &pg, (size - mapped_size) / PageSize, + KMemoryManager::EncodeOption(memory_pool, allocation_option), 0, 0)); + + // Map the memory. + { + // Lock the table. + KScopedLightLock lk(general_lock); + + size_t num_allocator_blocks = 0; + + // Verify that nobody has mapped memory since we first checked. + { + // Iterate over the memory. + size_t checked_mapped_size = 0; + cur_address = address; + + auto it = block_manager->FindIterator(cur_address); + while (true) { + // Check that the iterator is valid. + ASSERT(it != block_manager->end()); + + // Get the memory info. + const KMemoryInfo info = it->GetMemoryInfo(); + + const bool is_free = info.GetState() == KMemoryState::Free; + if (is_free) { + if (info.GetAddress() < address) { + ++num_allocator_blocks; + } + if (last_address < info.GetLastAddress()) { + ++num_allocator_blocks; + } + } + + // Check if we're done. + if (last_address <= info.GetLastAddress()) { + if (!is_free) { + checked_mapped_size += (last_address + 1 - cur_address); + } + break; + } + + // Track the memory if it's mapped. + if (!is_free) { + checked_mapped_size += VAddr(info.GetEndAddress()) - cur_address; + } + + // Advance. + cur_address = info.GetEndAddress(); + ++it; + } + + // If the size now isn't what it was before, somebody mapped or unmapped + // concurrently. If this happened, retry. + if (mapped_size != checked_mapped_size) { + continue; + } + } + + // Reset the current tracking address, and make sure we clean up on failure. + cur_address = address; + auto unmap_guard = detail::ScopeExit([&] { + if (cur_address > address) { + const VAddr last_unmap_address = cur_address - 1; + + // Iterate, unmapping the pages. + cur_address = address; + + auto it = block_manager->FindIterator(cur_address); + while (true) { + // Check that the iterator is valid. + ASSERT(it != block_manager->end()); + + // Get the memory info. + const KMemoryInfo info = it->GetMemoryInfo(); + + // If the memory state is free, we mapped it and need to unmap it. + if (info.GetState() == KMemoryState::Free) { + // Determine the range to unmap. + const size_t cur_pages = + std::min(VAddr(info.GetEndAddress()) - cur_address, + last_unmap_address + 1 - cur_address) / + PageSize; + + // Unmap. + ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, + OperationType::Unmap) + .IsSuccess()); + } + + // Check if we're done. + if (last_unmap_address <= info.GetLastAddress()) { + break; + } + + // Advance. + cur_address = info.GetEndAddress(); + ++it; + } + } + }); + + // Iterate over the memory. + auto pg_it = pg.Nodes().begin(); + PAddr pg_phys_addr = pg_it->GetAddress(); + size_t pg_pages = pg_it->GetNumPages(); + + auto it = block_manager->FindIterator(cur_address); + while (true) { + // Check that the iterator is valid. + ASSERT(it != block_manager->end()); + + // Get the memory info. + const KMemoryInfo info = it->GetMemoryInfo(); + + // If it's unmapped, we need to map it. + if (info.GetState() == KMemoryState::Free) { + // Determine the range to map. + size_t map_pages = std::min(VAddr(info.GetEndAddress()) - cur_address, + last_address + 1 - cur_address) / + PageSize; + + // While we have pages to map, map them. + while (map_pages > 0) { + // Check if we're at the end of the physical block. + if (pg_pages == 0) { + // Ensure there are more pages to map. + ASSERT(pg_it != pg.Nodes().end()); + + // Advance our physical block. + ++pg_it; + pg_phys_addr = pg_it->GetAddress(); + pg_pages = pg_it->GetNumPages(); + } + + // Map whatever we can. + const size_t cur_pages = std::min(pg_pages, map_pages); + R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite, + OperationType::Map, pg_phys_addr)); + + // Advance. + cur_address += cur_pages * PageSize; + map_pages -= cur_pages; + + pg_phys_addr += cur_pages * PageSize; + pg_pages -= cur_pages; + } + } + + // Check if we're done. + if (last_address <= info.GetLastAddress()) { + break; + } + + // Advance. + cur_address = info.GetEndAddress(); + ++it; + } + + // We succeeded, so commit the memory reservation. + memory_reservation.Commit(); + + // Increase our tracked mapped size. + mapped_physical_memory_size += (size - mapped_size); + + // Update the relevant memory blocks. + block_manager->Update(address, size / PageSize, KMemoryState::Free, + KMemoryPermission::None, KMemoryAttribute::None, + KMemoryState::Normal, KMemoryPermission::UserReadWrite, + KMemoryAttribute::None); + + // Cancel our guard. + unmap_guard.Cancel(); + + return ResultSuccess; + } + } } +} - KPageLinkedList page_linked_list; +Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) { + // Lock the physical memory lock. + KScopedLightLock map_phys_mem_lk(map_physical_memory_lock); - CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages, - memory_pool, allocation_option)); + // Lock the table. + KScopedLightLock lk(general_lock); - // We succeeded, so commit the memory reservation. - memory_reservation.Commit(); + // Calculate the last address for convenience. + const VAddr last_address = address + size - 1; - // Map the memory. - auto node{page_linked_list.Nodes().begin()}; - PAddr map_addr{node->GetAddress()}; - std::size_t src_num_pages{node->GetNumPages()}; - block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) { - if (info.state != KMemoryState::Free) { - return; - } + // Define iteration variables. + VAddr cur_address = 0; + std::size_t mapped_size = 0; + std::size_t num_allocator_blocks = 0; - std::size_t dst_num_pages{GetSizeInRange(info, addr, end_addr) / PageSize}; - VAddr dst_addr{GetAddressInRange(info, addr)}; + // Check if the memory is mapped. + { + // Iterate over the memory. + cur_address = address; + mapped_size = 0; + + auto it = block_manager->FindIterator(cur_address); + while (true) { + // Check that the iterator is valid. + ASSERT(it != block_manager->end()); + + // Get the memory info. + const KMemoryInfo info = it->GetMemoryInfo(); + + // Verify the memory's state. + const bool is_normal = info.GetState() == KMemoryState::Normal && + info.GetAttribute() == KMemoryAttribute::None; + const bool is_free = info.GetState() == KMemoryState::Free; + R_UNLESS(is_normal || is_free, ResultInvalidCurrentMemory); + + if (is_normal) { + R_UNLESS(info.GetAttribute() == KMemoryAttribute::None, ResultInvalidCurrentMemory); + + if (info.GetAddress() < address) { + ++num_allocator_blocks; + } + if (last_address < info.GetLastAddress()) { + ++num_allocator_blocks; + } + } - while (dst_num_pages) { - if (!src_num_pages) { - node = std::next(node); - map_addr = node->GetAddress(); - src_num_pages = node->GetNumPages(); + // Check if we're done. + if (last_address <= info.GetLastAddress()) { + if (is_normal) { + mapped_size += (last_address + 1 - cur_address); + } + break; } - const std::size_t num_pages{std::min(src_num_pages, dst_num_pages)}; - Operate(dst_addr, num_pages, KMemoryPermission::UserReadWrite, OperationType::Map, - map_addr); + // Track the memory if it's mapped. + if (is_normal) { + mapped_size += VAddr(info.GetEndAddress()) - cur_address; + } - dst_addr += num_pages * PageSize; - map_addr += num_pages * PageSize; - src_num_pages -= num_pages; - dst_num_pages -= num_pages; + // Advance. + cur_address = info.GetEndAddress(); + ++it; } - }); - mapped_physical_memory_size += remaining_size; - - const std::size_t num_pages{size / PageSize}; - block_manager->Update(addr, num_pages, KMemoryState::Free, KMemoryPermission::None, - KMemoryAttribute::None, KMemoryState::Normal, - KMemoryPermission::UserReadWrite, KMemoryAttribute::None); - - return ResultSuccess; -} + // If there's nothing mapped, we've nothing to do. + R_SUCCEED_IF(mapped_size == 0); + } -ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) { - // Lock the physical memory lock. - KScopedLightLock map_phys_mem_lk(map_physical_memory_lock); + // Make a page group for the unmap region. + KPageGroup pg; + { + auto& impl = this->PageTableImpl(); + + // Begin traversal. + Common::PageTable::TraversalContext context; + Common::PageTable::TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0}; + bool cur_valid = false; + Common::PageTable::TraversalEntry next_entry; + bool next_valid = false; + size_t tot_size = 0; + + cur_address = address; + next_valid = impl.BeginTraversal(next_entry, context, cur_address); + next_entry.block_size = + (next_entry.block_size - (next_entry.phys_addr & (next_entry.block_size - 1))); + + // Iterate, building the group. + while (true) { + if ((!next_valid && !cur_valid) || + (next_valid && cur_valid && + next_entry.phys_addr == cur_entry.phys_addr + cur_entry.block_size)) { + cur_entry.block_size += next_entry.block_size; + } else { + if (cur_valid) { + // ASSERT(IsHeapPhysicalAddress(cur_entry.phys_addr)); + R_TRY(pg.AddBlock(cur_entry.phys_addr, cur_entry.block_size / PageSize)); + } + + // Update tracking variables. + tot_size += cur_entry.block_size; + cur_entry = next_entry; + cur_valid = next_valid; + } - // Lock the table. - KScopedLightLock lk(general_lock); + if (cur_entry.block_size + tot_size >= size) { + break; + } - const VAddr end_addr{addr + size}; - ResultCode result{ResultSuccess}; - std::size_t mapped_size{}; + next_valid = impl.ContinueTraversal(next_entry, context); + } - // Verify that the region can be unmapped - block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) { - if (info.state == KMemoryState::Normal) { - if (info.attribute != KMemoryAttribute::None) { - result = ResultInvalidCurrentMemory; - return; + // Add the last block. + if (cur_valid) { + // ASSERT(IsHeapPhysicalAddress(cur_entry.phys_addr)); + R_TRY(pg.AddBlock(cur_entry.phys_addr, (size - tot_size) / PageSize)); + } + } + ASSERT(pg.GetNumPages() == mapped_size / PageSize); + + // Reset the current tracking address, and make sure we clean up on failure. + cur_address = address; + auto remap_guard = detail::ScopeExit([&] { + if (cur_address > address) { + const VAddr last_map_address = cur_address - 1; + cur_address = address; + + // Iterate over the memory we unmapped. + auto it = block_manager->FindIterator(cur_address); + auto pg_it = pg.Nodes().begin(); + PAddr pg_phys_addr = pg_it->GetAddress(); + size_t pg_pages = pg_it->GetNumPages(); + + while (true) { + // Get the memory info for the pages we unmapped, convert to property. + const KMemoryInfo info = it->GetMemoryInfo(); + + // If the memory is normal, we unmapped it and need to re-map it. + if (info.GetState() == KMemoryState::Normal) { + // Determine the range to map. + size_t map_pages = std::min(VAddr(info.GetEndAddress()) - cur_address, + last_map_address + 1 - cur_address) / + PageSize; + + // While we have pages to map, map them. + while (map_pages > 0) { + // Check if we're at the end of the physical block. + if (pg_pages == 0) { + // Ensure there are more pages to map. + ASSERT(pg_it != pg.Nodes().end()); + + // Advance our physical block. + ++pg_it; + pg_phys_addr = pg_it->GetAddress(); + pg_pages = pg_it->GetNumPages(); + } + + // Map whatever we can. + const size_t cur_pages = std::min(pg_pages, map_pages); + ASSERT(this->Operate(cur_address, cur_pages, info.GetPermission(), + OperationType::Map, pg_phys_addr) == ResultSuccess); + + // Advance. + cur_address += cur_pages * PageSize; + map_pages -= cur_pages; + + pg_phys_addr += cur_pages * PageSize; + pg_pages -= cur_pages; + } + } + + // Check if we're done. + if (last_map_address <= info.GetLastAddress()) { + break; + } + + // Advance. + ++it; } - mapped_size += GetSizeInRange(info, addr, end_addr); - } else if (info.state != KMemoryState::Free) { - result = ResultInvalidCurrentMemory; } }); - if (result.IsError()) { - return result; - } - - if (!mapped_size) { - return ResultSuccess; - } + // Iterate over the memory, unmapping as we go. + auto it = block_manager->FindIterator(cur_address); + while (true) { + // Check that the iterator is valid. + ASSERT(it != block_manager->end()); - // Unmap each region within the range - KPageLinkedList page_linked_list; - block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) { - if (info.state == KMemoryState::Normal) { - const std::size_t block_size{GetSizeInRange(info, addr, end_addr)}; - const std::size_t block_num_pages{block_size / PageSize}; - const VAddr block_addr{GetAddressInRange(info, addr)}; + // Get the memory info. + const KMemoryInfo info = it->GetMemoryInfo(); - AddRegionToPages(block_addr, block_size / PageSize, page_linked_list); + // If the memory state is normal, we need to unmap it. + if (info.GetState() == KMemoryState::Normal) { + // Determine the range to unmap. + const size_t cur_pages = std::min(VAddr(info.GetEndAddress()) - cur_address, + last_address + 1 - cur_address) / + PageSize; - if (result = Operate(block_addr, block_num_pages, KMemoryPermission::None, - OperationType::Unmap); - result.IsError()) { - return; - } + // Unmap. + R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap)); } - }); - if (result.IsError()) { - return result; - } - const std::size_t num_pages{size / PageSize}; - system.Kernel().MemoryManager().Free(page_linked_list, num_pages, memory_pool, - allocation_option); + // Check if we're done. + if (last_address <= info.GetLastAddress()) { + break; + } - block_manager->Update(addr, num_pages, KMemoryState::Free); + // Advance. + cur_address = info.GetEndAddress(); + ++it; + } + // Release the memory resource. + mapped_physical_memory_size -= mapped_size; auto process{system.Kernel().CurrentProcess()}; process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size); - mapped_physical_memory_size -= mapped_size; + + // Update memory blocks. + block_manager->Update(address, size / PageSize, KMemoryState::Free, KMemoryPermission::None, + KMemoryAttribute::None); + + // TODO(bunnei): This is a workaround until the next set of changes, where we add reference + // counting for mapped pages. Until then, we must manually close the reference to the page + // group. + system.Kernel().MemoryManager().Close(pg); + + // We succeeded. + remap_guard.Cancel(); return ResultSuccess; } -ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { +Result KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { KScopedLightLock lk(general_lock); KMemoryState src_state{}; @@ -559,7 +1147,7 @@ ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t siz return ResultInvalidCurrentMemory; } - KPageLinkedList page_linked_list; + KPageGroup page_linked_list; const std::size_t num_pages{size / PageSize}; AddRegionToPages(src_addr, num_pages, page_linked_list); @@ -585,7 +1173,7 @@ ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t siz return ResultSuccess; } -ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { +Result KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { KScopedLightLock lk(general_lock); KMemoryState src_state{}; @@ -600,8 +1188,8 @@ ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t s KMemoryPermission::None, KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); - KPageLinkedList src_pages; - KPageLinkedList dst_pages; + KPageGroup src_pages; + KPageGroup dst_pages; const std::size_t num_pages{size / PageSize}; AddRegionToPages(src_addr, num_pages, src_pages); @@ -627,8 +1215,8 @@ ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t s return ResultSuccess; } -ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_list, - KMemoryPermission perm) { +Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list, + KMemoryPermission perm) { ASSERT(this->IsLockedByCurrentThread()); VAddr cur_addr{addr}; @@ -651,8 +1239,8 @@ ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_l return ResultSuccess; } -ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list, - KMemoryState state, KMemoryPermission perm) { +Result KPageTable::MapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state, + KMemoryPermission perm) { // Check that the map is in range. const std::size_t num_pages{page_linked_list.GetNumPages()}; const std::size_t size{num_pages * PageSize}; @@ -675,15 +1263,54 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list return ResultSuccess; } -ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) { +Result KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, + PAddr phys_addr, bool is_pa_valid, VAddr region_start, + std::size_t region_num_pages, KMemoryState state, + KMemoryPermission perm) { + ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize); + + // Ensure this is a valid map request. + R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state), + ResultInvalidCurrentMemory); + R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory); + + // Lock the table. + KScopedLightLock lk(general_lock); + + // Find a random address to map at. + VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0, + this->GetNumGuardPages()); + R_UNLESS(addr != 0, ResultOutOfMemory); + ASSERT(Common::IsAligned(addr, alignment)); + ASSERT(this->CanContain(addr, num_pages * PageSize, state)); + ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free, + KMemoryPermission::None, KMemoryPermission::None, + KMemoryAttribute::None, KMemoryAttribute::None) + .IsSuccess()); + + // Perform mapping operation. + if (is_pa_valid) { + R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr)); + } else { + UNIMPLEMENTED(); + } + + // Update the blocks. + block_manager->Update(addr, num_pages, state, perm); + + // We successfully mapped the pages. + *out_addr = addr; + return ResultSuccess; +} + +Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) { ASSERT(this->IsLockedByCurrentThread()); VAddr cur_addr{addr}; for (const auto& node : page_linked_list.Nodes()) { - const std::size_t num_pages{(addr - cur_addr) / PageSize}; - if (const auto result{ - Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap)}; + if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None, + OperationType::Unmap)}; result.IsError()) { return result; } @@ -694,8 +1321,7 @@ ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked return ResultSuccess; } -ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, - KMemoryState state) { +Result KPageTable::UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state) { // Check that the unmap is in range. const std::size_t num_pages{page_linked_list.GetNumPages()}; const std::size_t size{num_pages * PageSize}; @@ -718,8 +1344,57 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, return ResultSuccess; } -ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, - Svc::MemoryPermission svc_perm) { +Result KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state) { + // Check that the unmap is in range. + const std::size_t size = num_pages * PageSize; + R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); + + // Lock the table. + KScopedLightLock lk(general_lock); + + // Check the memory state. + std::size_t num_allocator_blocks{}; + R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, + KMemoryState::All, state, KMemoryPermission::None, + KMemoryPermission::None, KMemoryAttribute::All, + KMemoryAttribute::None)); + + // Perform the unmap. + R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap)); + + // Update the blocks. + block_manager->Update(address, num_pages, KMemoryState::Free, KMemoryPermission::None); + + return ResultSuccess; +} + +Result KPageTable::MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages, + KMemoryState state_mask, KMemoryState state, + KMemoryPermission perm_mask, KMemoryPermission perm, + KMemoryAttribute attr_mask, KMemoryAttribute attr) { + // Ensure that the page group isn't null. + ASSERT(out != nullptr); + + // Make sure that the region we're mapping is valid for the table. + const size_t size = num_pages * PageSize; + R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); + + // Lock the table. + KScopedLightLock lk(general_lock); + + // Check if state allows us to create the group. + R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState::FlagReferenceCounted, + state | KMemoryState::FlagReferenceCounted, perm_mask, perm, + attr_mask, attr)); + + // Create a new page group for the region. + R_TRY(this->MakePageGroup(*out, address, num_pages)); + + return ResultSuccess; +} + +Result KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, + Svc::MemoryPermission svc_perm) { const size_t num_pages = size / PageSize; // Lock the table. @@ -753,7 +1428,7 @@ ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, new_state = KMemoryState::AliasCodeData; break; default: - UNREACHABLE(); + ASSERT(false); } } @@ -791,7 +1466,7 @@ KMemoryInfo KPageTable::QueryInfo(VAddr addr) { return QueryInfoImpl(addr); } -ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm) { +Result KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm) { KScopedLightLock lk(general_lock); KMemoryState state{}; @@ -809,7 +1484,7 @@ ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemo return ResultSuccess; } -ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) { +Result KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) { KScopedLightLock lk(general_lock); KMemoryState state{}; @@ -824,8 +1499,8 @@ ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) { return ResultSuccess; } -ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size, - Svc::MemoryPermission svc_perm) { +Result KPageTable::SetMemoryPermission(VAddr addr, std::size_t size, + Svc::MemoryPermission svc_perm) { const size_t num_pages = size / PageSize; // Lock the table. @@ -852,7 +1527,7 @@ ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size, return ResultSuccess; } -ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr) { +Result KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr) { const size_t num_pages = size / PageSize; ASSERT((static_cast<KMemoryAttribute>(mask) | KMemoryAttribute::SetMask) == KMemoryAttribute::SetMask); @@ -887,7 +1562,7 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask return ResultSuccess; } -ResultCode KPageTable::SetMaxHeapSize(std::size_t size) { +Result KPageTable::SetMaxHeapSize(std::size_t size) { // Lock the table. KScopedLightLock lk(general_lock); @@ -899,7 +1574,7 @@ ResultCode KPageTable::SetMaxHeapSize(std::size_t size) { return ResultSuccess; } -ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) { +Result KPageTable::SetHeapSize(VAddr* out, std::size_t size) { // Lock the physical memory mutex. KScopedLightLock map_phys_mem_lk(map_physical_memory_lock); @@ -966,9 +1641,16 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) { R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); // Allocate pages for the heap extension. - KPageLinkedList page_linked_list; - R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, allocation_size / PageSize, - memory_pool, allocation_option)); + KPageGroup pg; + R_TRY(system.Kernel().MemoryManager().AllocateAndOpen( + &pg, allocation_size / PageSize, + KMemoryManager::EncodeOption(memory_pool, allocation_option))); + + // Clear all the newly allocated pages. + for (const auto& it : pg.Nodes()) { + std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value, + it.GetSize()); + } // Map the pages. { @@ -987,7 +1669,7 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) { // Map the pages. const auto num_pages = allocation_size / PageSize; - R_TRY(Operate(current_heap_end, num_pages, page_linked_list, OperationType::MapGroup)); + R_TRY(Operate(current_heap_end, num_pages, pg, OperationType::MapGroup)); // Clear all the newly allocated pages. for (std::size_t cur_page = 0; cur_page < num_pages; ++cur_page) { @@ -1034,9 +1716,10 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, if (is_map_only) { R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr)); } else { - KPageLinkedList page_group; - R_TRY(system.Kernel().MemoryManager().Allocate(page_group, needed_num_pages, memory_pool, - allocation_option)); + KPageGroup page_group; + R_TRY(system.Kernel().MemoryManager().AllocateAndOpenForProcess( + &page_group, needed_num_pages, + KMemoryManager::EncodeOption(memory_pool, allocation_option), 0, 0)); R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup)); } @@ -1045,11 +1728,11 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, return addr; } -ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) { +Result KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) { KScopedLightLock lk(general_lock); KMemoryPermission perm{}; - if (const ResultCode result{CheckMemoryState( + if (const Result result{CheckMemoryState( nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute, KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None, @@ -1068,11 +1751,11 @@ ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) { return ResultSuccess; } -ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) { +Result KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) { KScopedLightLock lk(general_lock); KMemoryPermission perm{}; - if (const ResultCode result{CheckMemoryState( + if (const Result result{CheckMemoryState( nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute, KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None, @@ -1091,61 +1774,24 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) return ResultSuccess; } -ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) { - KScopedLightLock lk(general_lock); - - KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite; - - KMemoryPermission old_perm{}; - - if (const ResultCode result{CheckMemoryState( - nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, - KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, - KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)}; - result.IsError()) { - return result; - } - - new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; - - block_manager->UpdateLock( - addr, size / PageSize, - [](KMemoryBlockManager::iterator block, KMemoryPermission permission) { - block->ShareToDevice(permission); - }, - new_perm); - - return ResultSuccess; +Result KPageTable::LockForCodeMemory(KPageGroup* out, VAddr addr, std::size_t size) { + return this->LockMemoryAndOpen( + out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, + KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All, + KMemoryAttribute::None, + static_cast<KMemoryPermission>(KMemoryPermission::NotMapped | + KMemoryPermission::KernelReadWrite), + KMemoryAttribute::Locked); } -ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) { - KScopedLightLock lk(general_lock); - - KMemoryPermission new_perm = KMemoryPermission::UserReadWrite; - - KMemoryPermission old_perm{}; - - if (const ResultCode result{CheckMemoryState( - nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, - KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None, - KMemoryAttribute::All, KMemoryAttribute::Locked)}; - result.IsError()) { - return result; - } - - new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; - - block_manager->UpdateLock( - addr, size / PageSize, - [](KMemoryBlockManager::iterator block, KMemoryPermission permission) { - block->UnshareToDevice(permission); - }, - new_perm); - - return ResultSuccess; +Result KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size, const KPageGroup& pg) { + return this->UnlockMemory( + addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, + KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All, + KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, KMemoryAttribute::Locked, &pg); } -ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { +Result KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { block_manager = std::make_unique<KMemoryBlockManager>(start, end); return ResultSuccess; @@ -1170,13 +1816,11 @@ bool KPageTable::IsRegionContiguous(VAddr addr, u64 size) const { } void KPageTable::AddRegionToPages(VAddr start, std::size_t num_pages, - KPageLinkedList& page_linked_list) { + KPageGroup& page_linked_list) { VAddr addr{start}; while (addr < start + (num_pages * PageSize)) { const PAddr paddr{GetPhysicalAddr(addr)}; - if (!paddr) { - UNREACHABLE(); - } + ASSERT(paddr != 0); page_linked_list.AddBlock(paddr, 1); addr += PageSize; } @@ -1191,8 +1835,8 @@ VAddr KPageTable::AllocateVirtualMemory(VAddr start, std::size_t region_num_page IsKernel() ? 1 : 4); } -ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLinkedList& page_group, - OperationType operation) { +Result KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageGroup& page_group, + OperationType operation) { ASSERT(this->IsLockedByCurrentThread()); ASSERT(Common::IsAligned(addr, PageSize)); @@ -1207,7 +1851,7 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLin system.Memory().MapMemoryRegion(page_table_impl, addr, size, node.GetAddress()); break; default: - UNREACHABLE(); + ASSERT(false); } addr += size; @@ -1216,8 +1860,8 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLin return ResultSuccess; } -ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm, - OperationType operation, PAddr map_addr) { +Result KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm, + OperationType operation, PAddr map_addr) { ASSERT(this->IsLockedByCurrentThread()); ASSERT(num_pages > 0); @@ -1238,12 +1882,12 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermiss case OperationType::ChangePermissionsAndRefresh: break; default: - UNREACHABLE(); + ASSERT(false); } return ResultSuccess; } -constexpr VAddr KPageTable::GetRegionAddress(KMemoryState state) const { +VAddr KPageTable::GetRegionAddress(KMemoryState state) const { switch (state) { case KMemoryState::Free: case KMemoryState::Kernel: @@ -1275,11 +1919,10 @@ constexpr VAddr KPageTable::GetRegionAddress(KMemoryState state) const { return code_region_start; default: UNREACHABLE(); - return {}; } } -constexpr std::size_t KPageTable::GetRegionSize(KMemoryState state) const { +std::size_t KPageTable::GetRegionSize(KMemoryState state) const { switch (state) { case KMemoryState::Free: case KMemoryState::Kernel: @@ -1311,7 +1954,6 @@ constexpr std::size_t KPageTable::GetRegionSize(KMemoryState state) const { return code_region_end - code_region_start; default: UNREACHABLE(); - return {}; } } @@ -1361,10 +2003,10 @@ bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) co } } -ResultCode KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, - KMemoryState state, KMemoryPermission perm_mask, - KMemoryPermission perm, KMemoryAttribute attr_mask, - KMemoryAttribute attr) const { +Result KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, + KMemoryState state, KMemoryPermission perm_mask, + KMemoryPermission perm, KMemoryAttribute attr_mask, + KMemoryAttribute attr) const { // Validate the states match expectation. R_UNLESS((info.state & state_mask) == state, ResultInvalidCurrentMemory); R_UNLESS((info.perm & perm_mask) == perm, ResultInvalidCurrentMemory); @@ -1373,12 +2015,11 @@ ResultCode KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState st return ResultSuccess; } -ResultCode KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, - std::size_t size, KMemoryState state_mask, - KMemoryState state, KMemoryPermission perm_mask, - KMemoryPermission perm, - KMemoryAttribute attr_mask, - KMemoryAttribute attr) const { +Result KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, + std::size_t size, KMemoryState state_mask, + KMemoryState state, KMemoryPermission perm_mask, + KMemoryPermission perm, KMemoryAttribute attr_mask, + KMemoryAttribute attr) const { ASSERT(this->IsLockedByCurrentThread()); // Get information about the first block. @@ -1416,12 +2057,12 @@ ResultCode KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed return ResultSuccess; } -ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, - KMemoryAttribute* out_attr, std::size_t* out_blocks_needed, - VAddr addr, std::size_t size, KMemoryState state_mask, - KMemoryState state, KMemoryPermission perm_mask, - KMemoryPermission perm, KMemoryAttribute attr_mask, - KMemoryAttribute attr, KMemoryAttribute ignore_attr) const { +Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, + KMemoryAttribute* out_attr, std::size_t* out_blocks_needed, + VAddr addr, std::size_t size, KMemoryState state_mask, + KMemoryState state, KMemoryPermission perm_mask, + KMemoryPermission perm, KMemoryAttribute attr_mask, + KMemoryAttribute attr, KMemoryAttribute ignore_attr) const { ASSERT(this->IsLockedByCurrentThread()); // Get information about the first block. @@ -1478,4 +2119,109 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi return ResultSuccess; } +Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr addr, size_t size, + KMemoryState state_mask, KMemoryState state, + KMemoryPermission perm_mask, KMemoryPermission perm, + KMemoryAttribute attr_mask, KMemoryAttribute attr, + KMemoryPermission new_perm, KMemoryAttribute lock_attr) { + // Validate basic preconditions. + ASSERT((lock_attr & attr) == KMemoryAttribute::None); + ASSERT((lock_attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) == + KMemoryAttribute::None); + + // Validate the lock request. + const size_t num_pages = size / PageSize; + R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); + + // Lock the table. + KScopedLightLock lk(general_lock); + + // Check that the output page group is empty, if it exists. + if (out_pg) { + ASSERT(out_pg->GetNumPages() == 0); + } + + // Check the state. + KMemoryState old_state{}; + KMemoryPermission old_perm{}; + KMemoryAttribute old_attr{}; + size_t num_allocator_blocks{}; + R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), + std::addressof(old_attr), std::addressof(num_allocator_blocks), + addr, size, state_mask | KMemoryState::FlagReferenceCounted, + state | KMemoryState::FlagReferenceCounted, perm_mask, perm, + attr_mask, attr)); + + // Get the physical address, if we're supposed to. + if (out_paddr != nullptr) { + ASSERT(this->GetPhysicalAddressLocked(out_paddr, addr)); + } + + // Make the page group, if we're supposed to. + if (out_pg != nullptr) { + R_TRY(this->MakePageGroup(*out_pg, addr, num_pages)); + } + + // Decide on new perm and attr. + new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; + KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr | lock_attr); + + // Update permission, if we need to. + if (new_perm != old_perm) { + R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions)); + } + + // Apply the memory block updates. + block_manager->Update(addr, num_pages, old_state, new_perm, new_attr); + + return ResultSuccess; +} + +Result KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, + KMemoryState state, KMemoryPermission perm_mask, + KMemoryPermission perm, KMemoryAttribute attr_mask, + KMemoryAttribute attr, KMemoryPermission new_perm, + KMemoryAttribute lock_attr, const KPageGroup* pg) { + // Validate basic preconditions. + ASSERT((attr_mask & lock_attr) == lock_attr); + ASSERT((attr & lock_attr) == lock_attr); + + // Validate the unlock request. + const size_t num_pages = size / PageSize; + R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); + + // Lock the table. + KScopedLightLock lk(general_lock); + + // Check the state. + KMemoryState old_state{}; + KMemoryPermission old_perm{}; + KMemoryAttribute old_attr{}; + size_t num_allocator_blocks{}; + R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), + std::addressof(old_attr), std::addressof(num_allocator_blocks), + addr, size, state_mask | KMemoryState::FlagReferenceCounted, + state | KMemoryState::FlagReferenceCounted, perm_mask, perm, + attr_mask, attr)); + + // Check the page group. + if (pg != nullptr) { + R_UNLESS(this->IsValidPageGroup(*pg, addr, num_pages), ResultInvalidMemoryRegion); + } + + // Decide on new perm and attr. + new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; + KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr & ~lock_attr); + + // Update permission, if we need to. + if (new_perm != old_perm) { + R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions)); + } + + // Apply the memory block updates. + block_manager->Update(addr, num_pages, old_state, new_perm, new_attr); + + return ResultSuccess; +} + } // namespace Kernel diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index c98887d34..25774f232 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -12,6 +11,7 @@ #include "core/file_sys/program_metadata.h" #include "core/hle/kernel/k_light_lock.h" #include "core/hle/kernel/k_memory_block.h" +#include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_memory_manager.h" #include "core/hle/result.h" @@ -25,45 +25,57 @@ class KMemoryBlockManager; class KPageTable final { public: + enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll }; + YUZU_NON_COPYABLE(KPageTable); YUZU_NON_MOVEABLE(KPageTable); explicit KPageTable(Core::System& system_); ~KPageTable(); - ResultCode InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, - VAddr code_addr, std::size_t code_size, - KMemoryManager::Pool pool); - ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state, - KMemoryPermission perm); - ResultCode MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); - ResultCode UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); - ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, - VAddr src_addr); - ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); - ResultCode UnmapPhysicalMemory(VAddr addr, std::size_t size); - ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); - ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); - ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state, - KMemoryPermission perm); - ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state); - ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size, - Svc::MemoryPermission svc_perm); + Result InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, + VAddr code_addr, std::size_t code_size, KMemoryManager::Pool pool); + Result MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state, + KMemoryPermission perm); + Result MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size); + Result UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size, + ICacheInvalidationStrategy icache_invalidation_strategy); + Result UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, + VAddr src_addr); + Result MapPhysicalMemory(VAddr addr, std::size_t size); + Result UnmapPhysicalMemory(VAddr addr, std::size_t size); + Result MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); + Result UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); + Result MapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state, + KMemoryPermission perm); + Result MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, PAddr phys_addr, + KMemoryState state, KMemoryPermission perm) { + return this->MapPages(out_addr, num_pages, alignment, phys_addr, true, + this->GetRegionAddress(state), this->GetRegionSize(state) / PageSize, + state, perm); + } + Result UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state); + Result UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state); + Result SetProcessMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission svc_perm); KMemoryInfo QueryInfo(VAddr addr); - ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm); - ResultCode ResetTransferMemory(VAddr addr, std::size_t size); - ResultCode SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm); - ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr); - ResultCode SetMaxHeapSize(std::size_t size); - ResultCode SetHeapSize(VAddr* out, std::size_t size); + Result ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm); + Result ResetTransferMemory(VAddr addr, std::size_t size); + Result SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm); + Result SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr); + Result SetMaxHeapSize(std::size_t size); + Result SetHeapSize(VAddr* out, std::size_t size); ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align, bool is_map_only, VAddr region_start, std::size_t region_num_pages, KMemoryState state, KMemoryPermission perm, PAddr map_addr = 0); - ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size); - ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); - ResultCode LockForCodeMemory(VAddr addr, std::size_t size); - ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size); + Result LockForDeviceAddressSpace(VAddr addr, std::size_t size); + Result UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); + Result LockForCodeMemory(KPageGroup* out, VAddr addr, std::size_t size); + Result UnlockForCodeMemory(VAddr addr, std::size_t size, const KPageGroup& pg); + Result MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages, + KMemoryState state_mask, KMemoryState state, + KMemoryPermission perm_mask, KMemoryPermission perm, + KMemoryAttribute attr_mask, KMemoryAttribute attr); Common::PageTable& PageTableImpl() { return page_table_impl; @@ -88,68 +100,97 @@ private: KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared; - ResultCode InitializeMemoryLayout(VAddr start, VAddr end); - ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list, - KMemoryPermission perm); - ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list); + Result InitializeMemoryLayout(VAddr start, VAddr end); + Result MapPages(VAddr addr, const KPageGroup& page_linked_list, KMemoryPermission perm); + Result MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, PAddr phys_addr, + bool is_pa_valid, VAddr region_start, std::size_t region_num_pages, + KMemoryState state, KMemoryPermission perm); + Result UnmapPages(VAddr addr, const KPageGroup& page_linked_list); bool IsRegionMapped(VAddr address, u64 size); bool IsRegionContiguous(VAddr addr, u64 size) const; - void AddRegionToPages(VAddr start, std::size_t num_pages, KPageLinkedList& page_linked_list); + void AddRegionToPages(VAddr start, std::size_t num_pages, KPageGroup& page_linked_list); KMemoryInfo QueryInfoImpl(VAddr addr); VAddr AllocateVirtualMemory(VAddr start, std::size_t region_num_pages, u64 needed_num_pages, std::size_t align); - ResultCode Operate(VAddr addr, std::size_t num_pages, const KPageLinkedList& page_group, - OperationType operation); - ResultCode Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm, - OperationType operation, PAddr map_addr = 0); - constexpr VAddr GetRegionAddress(KMemoryState state) const; - constexpr std::size_t GetRegionSize(KMemoryState state) const; - - ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, - std::size_t size, KMemoryState state_mask, - KMemoryState state, KMemoryPermission perm_mask, - KMemoryPermission perm, KMemoryAttribute attr_mask, - KMemoryAttribute attr) const; - ResultCode CheckMemoryStateContiguous(VAddr addr, std::size_t size, KMemoryState state_mask, - KMemoryState state, KMemoryPermission perm_mask, - KMemoryPermission perm, KMemoryAttribute attr_mask, - KMemoryAttribute attr) const { + Result Operate(VAddr addr, std::size_t num_pages, const KPageGroup& page_group, + OperationType operation); + Result Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm, + OperationType operation, PAddr map_addr = 0); + VAddr GetRegionAddress(KMemoryState state) const; + std::size_t GetRegionSize(KMemoryState state) const; + + VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages, + std::size_t alignment, std::size_t offset, std::size_t guard_pages); + + Result CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, std::size_t size, + KMemoryState state_mask, KMemoryState state, + KMemoryPermission perm_mask, KMemoryPermission perm, + KMemoryAttribute attr_mask, KMemoryAttribute attr) const; + Result CheckMemoryStateContiguous(VAddr addr, std::size_t size, KMemoryState state_mask, + KMemoryState state, KMemoryPermission perm_mask, + KMemoryPermission perm, KMemoryAttribute attr_mask, + KMemoryAttribute attr) const { return this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask, perm, attr_mask, attr); } - ResultCode CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, - KMemoryState state, KMemoryPermission perm_mask, - KMemoryPermission perm, KMemoryAttribute attr_mask, - KMemoryAttribute attr) const; - ResultCode CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, - KMemoryAttribute* out_attr, std::size_t* out_blocks_needed, - VAddr addr, std::size_t size, KMemoryState state_mask, - KMemoryState state, KMemoryPermission perm_mask, - KMemoryPermission perm, KMemoryAttribute attr_mask, - KMemoryAttribute attr, - KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const; - ResultCode CheckMemoryState(std::size_t* out_blocks_needed, VAddr addr, std::size_t size, - KMemoryState state_mask, KMemoryState state, - KMemoryPermission perm_mask, KMemoryPermission perm, - KMemoryAttribute attr_mask, KMemoryAttribute attr, - KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { + Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state, + KMemoryPermission perm_mask, KMemoryPermission perm, + KMemoryAttribute attr_mask, KMemoryAttribute attr) const; + Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, + KMemoryAttribute* out_attr, std::size_t* out_blocks_needed, VAddr addr, + std::size_t size, KMemoryState state_mask, KMemoryState state, + KMemoryPermission perm_mask, KMemoryPermission perm, + KMemoryAttribute attr_mask, KMemoryAttribute attr, + KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const; + Result CheckMemoryState(std::size_t* out_blocks_needed, VAddr addr, std::size_t size, + KMemoryState state_mask, KMemoryState state, + KMemoryPermission perm_mask, KMemoryPermission perm, + KMemoryAttribute attr_mask, KMemoryAttribute attr, + KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size, state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr); } - ResultCode CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask, - KMemoryState state, KMemoryPermission perm_mask, - KMemoryPermission perm, KMemoryAttribute attr_mask, - KMemoryAttribute attr, - KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { + Result CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask, + KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, + KMemoryAttribute attr_mask, KMemoryAttribute attr, + KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { return this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr); } + Result LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr addr, size_t size, + KMemoryState state_mask, KMemoryState state, + KMemoryPermission perm_mask, KMemoryPermission perm, + KMemoryAttribute attr_mask, KMemoryAttribute attr, + KMemoryPermission new_perm, KMemoryAttribute lock_attr); + Result UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state, + KMemoryPermission perm_mask, KMemoryPermission perm, + KMemoryAttribute attr_mask, KMemoryAttribute attr, + KMemoryPermission new_perm, KMemoryAttribute lock_attr, + const KPageGroup* pg); + + Result MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages); + bool IsValidPageGroup(const KPageGroup& pg, VAddr addr, size_t num_pages); + bool IsLockedByCurrentThread() const { return general_lock.IsLockedByCurrentThread(); } + bool IsHeapPhysicalAddress(const KMemoryLayout& layout, PAddr phys_addr) { + ASSERT(this->IsLockedByCurrentThread()); + + return layout.IsHeapPhysicalAddress(cached_physical_heap_region, phys_addr); + } + + bool GetPhysicalAddressLocked(PAddr* out, VAddr virt_addr) const { + ASSERT(this->IsLockedByCurrentThread()); + + *out = GetPhysicalAddr(virt_addr); + + return *out != 0; + } + mutable KLightLock general_lock; mutable KLightLock map_physical_memory_lock; @@ -210,7 +251,7 @@ public: constexpr VAddr GetAliasCodeRegionSize() const { return alias_code_region_end - alias_code_region_start; } - size_t GetNormalMemorySize() { + std::size_t GetNormalMemorySize() { KScopedLightLock lk(general_lock); return GetHeapSize() + mapped_physical_memory_size; } @@ -253,9 +294,10 @@ public: constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const { return !IsOutsideASLRRegion(address, size); } - - PAddr GetPhysicalAddr(VAddr addr) { - ASSERT(IsLockedByCurrentThread()); + constexpr std::size_t GetNumGuardPages() const { + return IsKernel() ? 1 : 4; + } + PAddr GetPhysicalAddr(VAddr addr) const { const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits]; ASSERT(backing_addr); return backing_addr + addr; @@ -276,10 +318,6 @@ private: return is_aslr_enabled; } - constexpr std::size_t GetNumGuardPages() const { - return IsKernel() ? 1 : 4; - } - constexpr bool ContainsPages(VAddr addr, std::size_t num_pages) const { return (address_space_start <= addr) && (num_pages <= (address_space_end - address_space_start) / PageSize) && @@ -311,6 +349,9 @@ private: bool is_kernel{}; bool is_aslr_enabled{}; + u32 heap_fill_value{}; + const KMemoryRegion* cached_physical_heap_region{}; + KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application}; KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront}; diff --git a/src/core/hle/kernel/k_port.cpp b/src/core/hle/kernel/k_port.cpp index a8ba09c4a..7a5a9dc2a 100644 --- a/src/core/hle/kernel/k_port.cpp +++ b/src/core/hle/kernel/k_port.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/k_port.h" @@ -51,13 +50,18 @@ bool KPort::IsServerClosed() const { return state == State::ServerClosed; } -ResultCode KPort::EnqueueSession(KServerSession* session) { +Result KPort::EnqueueSession(KServerSession* session) { KScopedSchedulerLock sl{kernel}; R_UNLESS(state == State::Normal, ResultPortClosed); server.EnqueueSession(session); - server.GetSessionRequestHandler()->ClientConnected(server.AcceptSession()); + + if (auto session_ptr = server.GetSessionRequestHandler().lock()) { + session_ptr->ClientConnected(server.AcceptSession()); + } else { + ASSERT(false); + } return ResultSuccess; } diff --git a/src/core/hle/kernel/k_port.h b/src/core/hle/kernel/k_port.h index b6e4a1fcd..0cfc16dab 100644 --- a/src/core/hle/kernel/k_port.h +++ b/src/core/hle/kernel/k_port.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -35,7 +34,7 @@ public: bool IsServerClosed() const; - ResultCode EnqueueSession(KServerSession* session); + Result EnqueueSession(KServerSession* session); KClientPort& GetClientPort() { return client; diff --git a/src/core/hle/kernel/k_priority_queue.h b/src/core/hle/kernel/k_priority_queue.h index bd779739d..cb2512b0b 100644 --- a/src/core/hle/kernel/k_priority_queue.h +++ b/src/core/hle/kernel/k_priority_queue.h @@ -1,9 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -// This file references various implementation details from Atmosphere, an open-source firmware for -// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 85c506979..d3e99665f 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -1,6 +1,5 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2015 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <bitset> @@ -13,7 +12,6 @@ #include "common/scope_exit.h" #include "common/settings.h" #include "core/core.h" -#include "core/device_memory.h" #include "core/file_sys/program_metadata.h" #include "core/hle/kernel/code_set.h" #include "core/hle/kernel/k_memory_block_manager.h" @@ -24,7 +22,6 @@ #include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_shared_memory_info.h" -#include "core/hle/kernel/k_slab_heap.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/svc_results.h" @@ -59,76 +56,22 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority thread->GetContext64().cpu_registers[0] = 0; thread->GetContext32().cpu_registers[1] = thread_handle; thread->GetContext64().cpu_registers[1] = thread_handle; - thread->DisableDispatch(); - auto& kernel = system.Kernel(); - // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires - { - KScopedSchedulerLock lock{kernel}; - thread->SetState(ThreadState::Runnable); + if (system.DebuggerEnabled()) { + thread->RequestSuspend(SuspendType::Debug); } + + // Run our thread. + void(thread->Run()); } } // Anonymous namespace -// Represents a page used for thread-local storage. -// -// Each TLS page contains slots that may be used by processes and threads. -// Every process and thread is created with a slot in some arbitrary page -// (whichever page happens to have an available slot). -class TLSPage { -public: - static constexpr std::size_t num_slot_entries = - Core::Memory::PAGE_SIZE / Core::Memory::TLS_ENTRY_SIZE; - - explicit TLSPage(VAddr address) : base_address{address} {} - - bool HasAvailableSlots() const { - return !is_slot_used.all(); - } - - VAddr GetBaseAddress() const { - return base_address; - } - - std::optional<VAddr> ReserveSlot() { - for (std::size_t i = 0; i < is_slot_used.size(); i++) { - if (is_slot_used[i]) { - continue; - } - - is_slot_used[i] = true; - return base_address + (i * Core::Memory::TLS_ENTRY_SIZE); - } - - return std::nullopt; - } - - void ReleaseSlot(VAddr address) { - // Ensure that all given addresses are consistent with how TLS pages - // are intended to be used when releasing slots. - ASSERT(IsWithinPage(address)); - ASSERT((address % Core::Memory::TLS_ENTRY_SIZE) == 0); - - const std::size_t index = (address - base_address) / Core::Memory::TLS_ENTRY_SIZE; - is_slot_used[index] = false; - } - -private: - bool IsWithinPage(VAddr address) const { - return base_address <= address && address < base_address + Core::Memory::PAGE_SIZE; - } - - VAddr base_address; - std::bitset<num_slot_entries> is_slot_used; -}; - -ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name, - ProcessType type) { +Result KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name, + ProcessType type, KResourceLimit* res_limit) { auto& kernel = system.Kernel(); process->name = std::move(process_name); - - process->resource_limit = kernel.GetSystemResourceLimit(); + process->resource_limit = res_limit; process->status = ProcessStatus::Created; process->program_id = 0; process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID() @@ -143,9 +86,6 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st kernel.AppendNewProcess(process); - // Open a reference to the resource limit. - process->resource_limit->Open(); - // Clear remaining fields. process->num_running_threads = 0; process->is_signaled = false; @@ -153,6 +93,9 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st process->is_suspended = false; process->schedule_count = 0; + // Open a reference to the resource limit. + process->resource_limit->Open(); + return ResultSuccess; } @@ -217,7 +160,7 @@ bool KProcess::ReleaseUserException(KThread* thread) { std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(exception_thread))); next != nullptr) { - next->SetState(ThreadState::Runnable); + next->EndWait(ResultSuccess); } KScheduler::SetSchedulerUpdateNeeded(kernel); @@ -232,7 +175,8 @@ void KProcess::PinCurrentThread(s32 core_id) { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); // Get the current thread. - KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread(); + KThread* cur_thread = + kernel.Scheduler(static_cast<std::size_t>(core_id)).GetSchedulerCurrentThread(); // If the thread isn't terminated, pin it. if (!cur_thread->IsTerminationRequested()) { @@ -249,7 +193,8 @@ void KProcess::UnpinCurrentThread(s32 core_id) { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); // Get the current thread. - KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread(); + KThread* cur_thread = + kernel.Scheduler(static_cast<std::size_t>(core_id)).GetSchedulerCurrentThread(); // Unpin it. cur_thread->Unpin(); @@ -273,8 +218,8 @@ void KProcess::UnpinThread(KThread* thread) { KScheduler::SetSchedulerUpdateNeeded(kernel); } -ResultCode KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address, - [[maybe_unused]] size_t size) { +Result KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address, + [[maybe_unused]] size_t size) { // Lock ourselves, to prevent concurrent access. KScopedLightLock lk(state_lock); @@ -326,15 +271,19 @@ void KProcess::RemoveSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr a shmem->Close(); } -void KProcess::RegisterThread(const KThread* thread) { +void KProcess::RegisterThread(KThread* thread) { + KScopedLightLock lk{list_lock}; + thread_list.push_back(thread); } -void KProcess::UnregisterThread(const KThread* thread) { +void KProcess::UnregisterThread(KThread* thread) { + KScopedLightLock lk{list_lock}; + thread_list.remove(thread); } -ResultCode KProcess::Reset() { +Result KProcess::Reset() { // Lock the process and the scheduler. KScopedLightLock lk(state_lock); KScopedSchedulerLock sl{kernel}; @@ -348,8 +297,51 @@ ResultCode KProcess::Reset() { return ResultSuccess; } -ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, - std::size_t code_size) { +Result KProcess::SetActivity(ProcessActivity activity) { + // Lock ourselves and the scheduler. + KScopedLightLock lk{state_lock}; + KScopedLightLock list_lk{list_lock}; + KScopedSchedulerLock sl{kernel}; + + // Validate our state. + R_UNLESS(status != ProcessStatus::Exiting, ResultInvalidState); + R_UNLESS(status != ProcessStatus::Exited, ResultInvalidState); + + // Either pause or resume. + if (activity == ProcessActivity::Paused) { + // Verify that we're not suspended. + if (is_suspended) { + return ResultInvalidState; + } + + // Suspend all threads. + for (auto* thread : GetThreadList()) { + thread->RequestSuspend(SuspendType::Process); + } + + // Set ourselves as suspended. + SetSuspended(true); + } else { + ASSERT(activity == ProcessActivity::Runnable); + + // Verify that we're suspended. + if (!is_suspended) { + return ResultInvalidState; + } + + // Resume all threads. + for (auto* thread : GetThreadList()) { + thread->Resume(SuspendType::Process); + } + + // Set ourselves as resumed. + SetSuspended(false); + } + + return ResultSuccess; +} + +Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size) { program_id = metadata.GetTitleID(); ideal_core = metadata.GetMainThreadCore(); is_64bit_process = metadata.Is64BitProgram(); @@ -364,24 +356,24 @@ ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, return ResultLimitReached; } // Initialize proces address space - if (const ResultCode result{ - page_table->InitializeForProcess(metadata.GetAddressSpaceType(), false, 0x8000000, - code_size, KMemoryManager::Pool::Application)}; + if (const Result result{page_table->InitializeForProcess(metadata.GetAddressSpaceType(), false, + 0x8000000, code_size, + KMemoryManager::Pool::Application)}; result.IsError()) { return result; } // Map process code region - if (const ResultCode result{page_table->MapProcessCode(page_table->GetCodeRegionStart(), - code_size / PageSize, KMemoryState::Code, - KMemoryPermission::None)}; + if (const Result result{page_table->MapProcessCode(page_table->GetCodeRegionStart(), + code_size / PageSize, KMemoryState::Code, + KMemoryPermission::None)}; result.IsError()) { return result; } // Initialize process capabilities const auto& caps{metadata.GetKernelCapabilities()}; - if (const ResultCode result{ + if (const Result result{ capabilities.InitializeForUserProcess(caps.data(), caps.size(), *page_table)}; result.IsError()) { return result; @@ -401,11 +393,11 @@ ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, break; default: - UNREACHABLE(); + ASSERT(false); } // Create TLS region - tls_region_address = CreateTLSRegion(); + R_TRY(this->CreateThreadLocalRegion(std::addressof(tls_region_address))); memory_reservation.Commit(); return handle_table.Initialize(capabilities.GetHandleTableSize()); @@ -428,11 +420,11 @@ void KProcess::PrepareForTermination() { ChangeStatus(ProcessStatus::Exiting); const auto stop_threads = [this](const std::vector<KThread*>& in_thread_list) { - for (auto& thread : in_thread_list) { + for (auto* thread : in_thread_list) { if (thread->GetOwnerProcess() != this) continue; - if (thread == kernel.CurrentScheduler()->GetCurrentThread()) + if (thread == GetCurrentThreadPointer(kernel)) continue; // TODO(Subv): When are the other running/ready threads terminated? @@ -445,7 +437,7 @@ void KProcess::PrepareForTermination() { stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList()); - FreeTLSRegion(tls_region_address); + this->DeleteThreadLocalRegion(tls_region_address); tls_region_address = 0; if (resource_limit) { @@ -457,9 +449,6 @@ void KProcess::PrepareForTermination() { } void KProcess::Finalize() { - // Finalize the handle table and close any open handles. - handle_table.Finalize(); - // Free all shared memory infos. { auto it = shared_memory_list.begin(); @@ -484,67 +473,156 @@ void KProcess::Finalize() { resource_limit = nullptr; } + // Finalize the page table. + page_table.reset(); + // Perform inherited finalization. KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize(); } -/** - * Attempts to find a TLS page that contains a free slot for - * use by a thread. - * - * @returns If a page with an available slot is found, then an iterator - * pointing to the page is returned. Otherwise the end iterator - * is returned instead. - */ -static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) { - return std::find_if(tls_pages.begin(), tls_pages.end(), - [](const auto& page) { return page.HasAvailableSlots(); }); +Result KProcess::CreateThreadLocalRegion(VAddr* out) { + KThreadLocalPage* tlp = nullptr; + VAddr tlr = 0; + + // See if we can get a region from a partially used TLP. + { + KScopedSchedulerLock sl{kernel}; + + if (auto it = partially_used_tlp_tree.begin(); it != partially_used_tlp_tree.end()) { + tlr = it->Reserve(); + ASSERT(tlr != 0); + + if (it->IsAllUsed()) { + tlp = std::addressof(*it); + partially_used_tlp_tree.erase(it); + fully_used_tlp_tree.insert(*tlp); + } + + *out = tlr; + return ResultSuccess; + } + } + + // Allocate a new page. + tlp = KThreadLocalPage::Allocate(kernel); + R_UNLESS(tlp != nullptr, ResultOutOfMemory); + auto tlp_guard = SCOPE_GUARD({ KThreadLocalPage::Free(kernel, tlp); }); + + // Initialize the new page. + R_TRY(tlp->Initialize(kernel, this)); + + // Reserve a TLR. + tlr = tlp->Reserve(); + ASSERT(tlr != 0); + + // Insert into our tree. + { + KScopedSchedulerLock sl{kernel}; + if (tlp->IsAllUsed()) { + fully_used_tlp_tree.insert(*tlp); + } else { + partially_used_tlp_tree.insert(*tlp); + } + } + + // We succeeded! + tlp_guard.Cancel(); + *out = tlr; + return ResultSuccess; } -VAddr KProcess::CreateTLSRegion() { - KScopedSchedulerLock lock(kernel); - if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)}; - tls_page_iter != tls_pages.cend()) { - return *tls_page_iter->ReserveSlot(); +Result KProcess::DeleteThreadLocalRegion(VAddr addr) { + KThreadLocalPage* page_to_free = nullptr; + + // Release the region. + { + KScopedSchedulerLock sl{kernel}; + + // Try to find the page in the partially used list. + auto it = partially_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize)); + if (it == partially_used_tlp_tree.end()) { + // If we don't find it, it has to be in the fully used list. + it = fully_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize)); + R_UNLESS(it != fully_used_tlp_tree.end(), ResultInvalidAddress); + + // Release the region. + it->Release(addr); + + // Move the page out of the fully used list. + KThreadLocalPage* tlp = std::addressof(*it); + fully_used_tlp_tree.erase(it); + if (tlp->IsAllFree()) { + page_to_free = tlp; + } else { + partially_used_tlp_tree.insert(*tlp); + } + } else { + // Release the region. + it->Release(addr); + + // Handle the all-free case. + KThreadLocalPage* tlp = std::addressof(*it); + if (tlp->IsAllFree()) { + partially_used_tlp_tree.erase(it); + page_to_free = tlp; + } + } + } + + // If we should free the page it was in, do so. + if (page_to_free != nullptr) { + page_to_free->Finalize(); + + KThreadLocalPage::Free(kernel, page_to_free); } - Page* const tls_page_ptr{kernel.GetUserSlabHeapPages().Allocate()}; - ASSERT(tls_page_ptr); + return ResultSuccess; +} - const VAddr start{page_table->GetKernelMapRegionStart()}; - const VAddr size{page_table->GetKernelMapRegionEnd() - start}; - const PAddr tls_map_addr{kernel.System().DeviceMemory().GetPhysicalAddr(tls_page_ptr)}; - const VAddr tls_page_addr{page_table - ->AllocateAndMapMemory(1, PageSize, true, start, size / PageSize, - KMemoryState::ThreadLocal, - KMemoryPermission::UserReadWrite, - tls_map_addr) - .ValueOr(0)}; +bool KProcess::InsertWatchpoint(Core::System& system, VAddr addr, u64 size, + DebugWatchpointType type) { + const auto watch{std::find_if(watchpoints.begin(), watchpoints.end(), [&](const auto& wp) { + return wp.type == DebugWatchpointType::None; + })}; - ASSERT(tls_page_addr); + if (watch == watchpoints.end()) { + return false; + } - std::memset(tls_page_ptr, 0, PageSize); - tls_pages.emplace_back(tls_page_addr); + watch->start_address = addr; + watch->end_address = addr + size; + watch->type = type; - const auto reserve_result{tls_pages.back().ReserveSlot()}; - ASSERT(reserve_result.has_value()); + for (VAddr page = Common::AlignDown(addr, PageSize); page < addr + size; page += PageSize) { + debug_page_refcounts[page]++; + system.Memory().MarkRegionDebug(page, PageSize, true); + } - return *reserve_result; + return true; } -void KProcess::FreeTLSRegion(VAddr tls_address) { - KScopedSchedulerLock lock(kernel); - const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE); - auto iter = - std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) { - return page.GetBaseAddress() == aligned_address; - }); +bool KProcess::RemoveWatchpoint(Core::System& system, VAddr addr, u64 size, + DebugWatchpointType type) { + const auto watch{std::find_if(watchpoints.begin(), watchpoints.end(), [&](const auto& wp) { + return wp.start_address == addr && wp.end_address == addr + size && wp.type == type; + })}; - // Something has gone very wrong if we're freeing a region - // with no actual page available. - ASSERT(iter != tls_pages.cend()); + if (watch == watchpoints.end()) { + return false; + } + + watch->start_address = 0; + watch->end_address = 0; + watch->type = DebugWatchpointType::None; + + for (VAddr page = Common::AlignDown(addr, PageSize); page < addr + size; page += PageSize) { + debug_page_refcounts[page]--; + if (!debug_page_refcounts[page]) { + system.Memory().MarkRegionDebug(page, PageSize, false); + } + } - iter->ReleaseSlot(tls_address); + return true; } void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) { @@ -567,9 +645,10 @@ bool KProcess::IsSignaled() const { } KProcess::KProcess(KernelCore& kernel_) - : KAutoObjectWithSlabHeapAndContainer{kernel_}, - page_table{std::make_unique<KPageTable>(kernel_.System())}, handle_table{kernel_}, - address_arbiter{kernel_.System()}, condition_var{kernel_.System()}, state_lock{kernel_} {} + : KAutoObjectWithSlabHeapAndContainer{kernel_}, page_table{std::make_unique<KPageTable>( + kernel_.System())}, + handle_table{kernel_}, address_arbiter{kernel_.System()}, condition_var{kernel_.System()}, + state_lock{kernel_}, list_lock{kernel_} {} KProcess::~KProcess() = default; @@ -583,7 +662,7 @@ void KProcess::ChangeStatus(ProcessStatus new_status) { NotifyAvailable(); } -ResultCode KProcess::AllocateMainThreadStack(std::size_t stack_size) { +Result KProcess::AllocateMainThreadStack(std::size_t stack_size) { ASSERT(stack_size); // The kernel always ensures that the given stack size is page aligned. diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index 38b446350..d56d73bab 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -1,20 +1,20 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2015 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include <array> #include <cstddef> #include <list> +#include <map> #include <string> -#include <vector> #include "common/common_types.h" #include "core/hle/kernel/k_address_arbiter.h" #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_condition_variable.h" #include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_synchronization_object.h" +#include "core/hle/kernel/k_thread_local_page.h" #include "core/hle/kernel/k_worker_task.h" #include "core/hle/kernel/process_capability.h" #include "core/hle/kernel/slab_helpers.h" @@ -63,6 +63,25 @@ enum class ProcessStatus { DebugBreak, }; +enum class ProcessActivity : u32 { + Runnable, + Paused, +}; + +enum class DebugWatchpointType : u8 { + None = 0, + Read = 1 << 0, + Write = 1 << 1, + ReadOrWrite = Read | Write, +}; +DECLARE_ENUM_FLAG_OPERATORS(DebugWatchpointType); + +struct DebugWatchpoint { + VAddr start_address; + VAddr end_address; + DebugWatchpointType type; +}; + class KProcess final : public KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask> { KERNEL_AUTOOBJECT_TRAITS(KProcess, KSynchronizationObject); @@ -90,8 +109,8 @@ public: static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4; - static ResultCode Initialize(KProcess* process, Core::System& system, std::string process_name, - ProcessType type); + static Result Initialize(KProcess* process, Core::System& system, std::string process_name, + ProcessType type, KResourceLimit* res_limit); /// Gets a reference to the process' page table. KPageTable& PageTable() { @@ -113,11 +132,11 @@ public: return handle_table; } - ResultCode SignalToAddress(VAddr address) { + Result SignalToAddress(VAddr address) { return condition_var.SignalToAddress(address); } - ResultCode WaitForAddress(Handle handle, VAddr address, u32 tag) { + Result WaitForAddress(Handle handle, VAddr address, u32 tag) { return condition_var.WaitForAddress(handle, address, tag); } @@ -125,17 +144,16 @@ public: return condition_var.Signal(cv_key, count); } - ResultCode WaitConditionVariable(VAddr address, u64 cv_key, u32 tag, s64 ns) { + Result WaitConditionVariable(VAddr address, u64 cv_key, u32 tag, s64 ns) { return condition_var.Wait(address, cv_key, tag, ns); } - ResultCode SignalAddressArbiter(VAddr address, Svc::SignalType signal_type, s32 value, - s32 count) { + Result SignalAddressArbiter(VAddr address, Svc::SignalType signal_type, s32 value, s32 count) { return address_arbiter.SignalToAddress(address, signal_type, value, count); } - ResultCode WaitAddressArbiter(VAddr address, Svc::ArbitrationType arb_type, s32 value, - s64 timeout) { + Result WaitAddressArbiter(VAddr address, Svc::ArbitrationType arb_type, s32 value, + s64 timeout) { return address_arbiter.WaitForAddress(address, arb_type, value, timeout); } @@ -282,17 +300,17 @@ public: u64 GetTotalPhysicalMemoryUsedWithoutSystemResource() const; /// Gets the list of all threads created with this process as their owner. - const std::list<const KThread*>& GetThreadList() const { + std::list<KThread*>& GetThreadList() { return thread_list; } /// Registers a thread as being created under this process, /// adding it to this process' thread list. - void RegisterThread(const KThread* thread); + void RegisterThread(KThread* thread); /// Unregisters a thread from this process, removing it /// from this process' thread list. - void UnregisterThread(const KThread* thread); + void UnregisterThread(KThread* thread); /// Clears the signaled state of the process if and only if it's signaled. /// @@ -302,7 +320,7 @@ public: /// @pre The process must be in a signaled state. If this is called on a /// process instance that is not signaled, ERR_INVALID_STATE will be /// returned. - ResultCode Reset(); + Result Reset(); /** * Loads process-specifics configuration info with metadata provided @@ -313,7 +331,7 @@ public: * @returns ResultSuccess if all relevant metadata was able to be * loaded and parsed. Otherwise, an error code is returned. */ - ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size); + Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size); /** * Starts the main application thread for this process. @@ -347,6 +365,8 @@ public: void DoWorkerTaskImpl(); + Result SetActivity(ProcessActivity activity); + void PinCurrentThread(s32 core_id); void UnpinCurrentThread(s32 core_id); void UnpinThread(KThread* thread); @@ -355,17 +375,30 @@ public: return state_lock; } - ResultCode AddSharedMemory(KSharedMemory* shmem, VAddr address, size_t size); + Result AddSharedMemory(KSharedMemory* shmem, VAddr address, size_t size); void RemoveSharedMemory(KSharedMemory* shmem, VAddr address, size_t size); /////////////////////////////////////////////////////////////////////////////////////////////// // Thread-local storage management // Marks the next available region as used and returns the address of the slot. - [[nodiscard]] VAddr CreateTLSRegion(); + [[nodiscard]] Result CreateThreadLocalRegion(VAddr* out); // Frees a used TLS slot identified by the given address - void FreeTLSRegion(VAddr tls_address); + Result DeleteThreadLocalRegion(VAddr addr); + + /////////////////////////////////////////////////////////////////////////////////////////////// + // Debug watchpoint management + + // Attempts to insert a watchpoint into a free slot. Returns false if none are available. + bool InsertWatchpoint(Core::System& system, VAddr addr, u64 size, DebugWatchpointType type); + + // Attempts to remove the watchpoint specified by the given parameters. + bool RemoveWatchpoint(Core::System& system, VAddr addr, u64 size, DebugWatchpointType type); + + const std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>& GetWatchpoints() const { + return watchpoints; + } private: void PinThread(s32 core_id, KThread* thread) { @@ -388,7 +421,7 @@ private: void ChangeStatus(ProcessStatus new_status); /// Allocates the main thread stack for the process, given the stack size in bytes. - ResultCode AllocateMainThreadStack(std::size_t stack_size); + Result AllocateMainThreadStack(std::size_t stack_size); /// Memory manager for this process std::unique_ptr<KPageTable> page_table; @@ -413,13 +446,6 @@ private: /// The ideal CPU core for this process, threads are scheduled on this core by default. u8 ideal_core = 0; - /// The Thread Local Storage area is allocated as processes create threads, - /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part - /// holds the TLS for a specific thread. This vector contains which parts are in use for each - /// page as a bitmask. - /// This vector will grow as more pages are allocated for new threads. - std::vector<TLSPage> tls_pages; - /// Contains the parsed process capability descriptors. ProcessCapabilities capabilities; @@ -429,7 +455,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; @@ -449,7 +475,7 @@ private: std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{}; /// List of threads that are running with this process as their owner. - std::list<const KThread*> thread_list; + std::list<KThread*> thread_list; /// List of shared memory that are running with this process as their owner. std::list<KSharedMemoryInfo*> shared_memory_list; @@ -478,10 +504,19 @@ private: std::array<KThread*, Core::Hardware::NUM_CPU_CORES> running_threads{}; std::array<u64, Core::Hardware::NUM_CPU_CORES> running_thread_idle_counts{}; std::array<KThread*, Core::Hardware::NUM_CPU_CORES> pinned_threads{}; + std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS> watchpoints{}; + std::map<VAddr, u64> debug_page_refcounts; KThread* exception_thread{}; KLightLock state_lock; + KLightLock list_lock; + + using TLPTree = + Common::IntrusiveRedBlackTreeBaseTraits<KThreadLocalPage>::TreeType<KThreadLocalPage>; + using TLPIterator = TLPTree::iterator; + TLPTree fully_used_tlp_tree; + TLPTree partially_used_tlp_tree; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_readable_event.cpp b/src/core/hle/kernel/k_readable_event.cpp index bf1db10d4..94c5464fe 100644 --- a/src/core/hle/kernel/k_readable_event.cpp +++ b/src/core/hle/kernel/k_readable_event.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "core/hle/kernel/k_event.h" @@ -28,7 +27,7 @@ void KReadableEvent::Destroy() { } } -ResultCode KReadableEvent::Signal() { +Result KReadableEvent::Signal() { KScopedSchedulerLock lk{kernel}; if (!is_signaled) { @@ -39,13 +38,13 @@ ResultCode KReadableEvent::Signal() { return ResultSuccess; } -ResultCode KReadableEvent::Clear() { +Result KReadableEvent::Clear() { Reset(); return ResultSuccess; } -ResultCode KReadableEvent::Reset() { +Result KReadableEvent::Reset() { KScopedSchedulerLock lk{kernel}; if (!is_signaled) { diff --git a/src/core/hle/kernel/k_readable_event.h b/src/core/hle/kernel/k_readable_event.h index 149fa78dd..18dcad289 100644 --- a/src/core/hle/kernel/k_readable_event.h +++ b/src/core/hle/kernel/k_readable_event.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -34,9 +33,9 @@ public: bool IsSignaled() const override; void Destroy() override; - ResultCode Signal(); - ResultCode Clear(); - ResultCode Reset(); + Result Signal(); + Result Clear(); + Result Reset(); private: bool is_signaled{}; diff --git a/src/core/hle/kernel/k_resource_limit.cpp b/src/core/hle/kernel/k_resource_limit.cpp index 0c4bba66b..010dcf99e 100644 --- a/src/core/hle/kernel/k_resource_limit.cpp +++ b/src/core/hle/kernel/k_resource_limit.cpp @@ -1,8 +1,8 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" +#include "core/core.h" #include "core/core_timing.h" #include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/svc_results.h" @@ -73,7 +73,7 @@ s64 KResourceLimit::GetFreeValue(LimitableResource which) const { return value; } -ResultCode KResourceLimit::SetLimitValue(LimitableResource which, s64 value) { +Result KResourceLimit::SetLimitValue(LimitableResource which, s64 value) { const auto index = static_cast<std::size_t>(which); KScopedLightLock lk(lock); R_UNLESS(current_values[index] <= value, ResultInvalidState); @@ -151,4 +151,22 @@ void KResourceLimit::Release(LimitableResource which, s64 value, s64 hint) { } } +KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size) { + auto* resource_limit = KResourceLimit::Create(system.Kernel()); + resource_limit->Initialize(&system.CoreTiming()); + + // Initialize default resource limit values. + // TODO(bunnei): These values are the system defaults, the limits for service processes are + // lower. These should use the correct limit values. + + ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, physical_memory_size) + .IsSuccess()); + ASSERT(resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess()); + ASSERT(resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess()); + ASSERT(resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200).IsSuccess()); + ASSERT(resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess()); + + return resource_limit; +} + } // namespace Kernel diff --git a/src/core/hle/kernel/k_resource_limit.h b/src/core/hle/kernel/k_resource_limit.h index fab6005ff..65c98c979 100644 --- a/src/core/hle/kernel/k_resource_limit.h +++ b/src/core/hle/kernel/k_resource_limit.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -9,7 +8,7 @@ #include "core/hle/kernel/k_light_condition_variable.h" #include "core/hle/kernel/k_light_lock.h" -union ResultCode; +union Result; namespace Core::Timing { class CoreTiming; @@ -47,7 +46,7 @@ public: s64 GetPeakValue(LimitableResource which) const; s64 GetFreeValue(LimitableResource which) const; - ResultCode SetLimitValue(LimitableResource which, s64 value); + Result SetLimitValue(LimitableResource which, s64 value); bool Reserve(LimitableResource which, s64 value); bool Reserve(LimitableResource which, s64 value, s64 timeout); @@ -67,4 +66,7 @@ private: KLightConditionVariable cond_var; const Core::Timing::CoreTiming* core_timing{}; }; + +KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size); + } // namespace Kernel diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index c96520828..c34ce7a17 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -1,9 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -// This file references various implementation details from Atmosphere, an open-source firmware for -// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <bit> @@ -22,7 +18,6 @@ #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/physical_core.h" -#include "core/hle/kernel/time_manager.h" namespace Kernel { @@ -32,69 +27,185 @@ static void IncrementScheduledCount(Kernel::KThread* thread) { } } -void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule) { - auto scheduler = kernel.CurrentScheduler(); +KScheduler::KScheduler(KernelCore& kernel_) : kernel{kernel_} { + m_switch_fiber = std::make_shared<Common::Fiber>([this] { + while (true) { + ScheduleImplFiber(); + } + }); + + m_state.needs_scheduling = true; +} + +KScheduler::~KScheduler() = default; - u32 current_core{0xF}; - bool must_context_switch{}; - if (scheduler) { - current_core = scheduler->core_id; - // TODO(bunnei): Should be set to true when we deprecate single core - must_context_switch = !kernel.IsPhantomModeForSingleCore(); +void KScheduler::SetInterruptTaskRunnable() { + m_state.interrupt_task_runnable = true; + m_state.needs_scheduling = true; +} + +void KScheduler::RequestScheduleOnInterrupt() { + m_state.needs_scheduling = true; + + if (CanSchedule(kernel)) { + ScheduleOnInterrupt(); } +} - while (cores_pending_reschedule != 0) { - const auto core = static_cast<u32>(std::countr_zero(cores_pending_reschedule)); - ASSERT(core < Core::Hardware::NUM_CPU_CORES); - if (!must_context_switch || core != current_core) { - auto& phys_core = kernel.PhysicalCore(core); - phys_core.Interrupt(); - } - cores_pending_reschedule &= ~(1ULL << core); +void KScheduler::DisableScheduling(KernelCore& kernel) { + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0); + GetCurrentThread(kernel).DisableDispatch(); +} + +void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) { + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 1); + + auto* scheduler{kernel.CurrentScheduler()}; + + if (!scheduler || kernel.IsPhantomModeForSingleCore()) { + KScheduler::RescheduleCores(kernel, cores_needing_scheduling); + KScheduler::RescheduleCurrentHLEThread(kernel); + return; } - for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; ++core_id) { - if (kernel.PhysicalCore(core_id).IsInterrupted()) { - KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_id)); - } + scheduler->RescheduleOtherCores(cores_needing_scheduling); + + if (GetCurrentThread(kernel).GetDisableDispatchCount() > 1) { + GetCurrentThread(kernel).EnableDispatch(); + } else { + scheduler->RescheduleCurrentCore(); } +} + +void KScheduler::RescheduleCurrentHLEThread(KernelCore& kernel) { + // HACK: we cannot schedule from this thread, it is not a core thread + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1); + + // Special case to ensure dummy threads that are waiting block + GetCurrentThread(kernel).IfDummyThreadTryWait(); - if (must_context_switch) { - auto core_scheduler = kernel.CurrentScheduler(); - kernel.ExitSVCProfile(); - core_scheduler->RescheduleCurrentCore(); - kernel.EnterSVCProfile(); + ASSERT(GetCurrentThread(kernel).GetState() != ThreadState::Waiting); + GetCurrentThread(kernel).EnableDispatch(); +} + +u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) { + if (IsSchedulerUpdateNeeded(kernel)) { + return UpdateHighestPriorityThreadsImpl(kernel); + } else { + return 0; } } +void KScheduler::Schedule() { + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1); + ASSERT(m_core_id == GetCurrentCoreId(kernel)); + + ScheduleImpl(); +} + +void KScheduler::ScheduleOnInterrupt() { + GetCurrentThread(kernel).DisableDispatch(); + Schedule(); + GetCurrentThread(kernel).EnableDispatch(); +} + +void KScheduler::PreemptSingleCore() { + GetCurrentThread(kernel).DisableDispatch(); + + auto* thread = GetCurrentThreadPointer(kernel); + auto& previous_scheduler = kernel.Scheduler(thread->GetCurrentCore()); + previous_scheduler.Unload(thread); + + Common::Fiber::YieldTo(thread->GetHostContext(), *m_switch_fiber); + + GetCurrentThread(kernel).EnableDispatch(); +} + +void KScheduler::RescheduleCurrentCore() { + ASSERT(!kernel.IsPhantomModeForSingleCore()); + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1); + + GetCurrentThread(kernel).EnableDispatch(); + + if (m_state.needs_scheduling.load()) { + // Disable interrupts, and then check again if rescheduling is needed. + // KScopedInterruptDisable intr_disable; + + kernel.CurrentScheduler()->RescheduleCurrentCoreImpl(); + } +} + +void KScheduler::RescheduleCurrentCoreImpl() { + // Check that scheduling is needed. + if (m_state.needs_scheduling.load()) [[likely]] { + GetCurrentThread(kernel).DisableDispatch(); + Schedule(); + GetCurrentThread(kernel).EnableDispatch(); + } +} + +void KScheduler::Initialize(KThread* main_thread, KThread* idle_thread, s32 core_id) { + // Set core ID/idle thread/interrupt task manager. + m_core_id = core_id; + m_idle_thread = idle_thread; + // m_state.idle_thread_stack = m_idle_thread->GetStackTop(); + // m_state.interrupt_task_manager = &kernel.GetInterruptTaskManager(); + + // Insert the main thread into the priority queue. + // { + // KScopedSchedulerLock lk{kernel}; + // GetPriorityQueue(kernel).PushBack(GetCurrentThreadPointer(kernel)); + // SetSchedulerUpdateNeeded(kernel); + // } + + // Bind interrupt handler. + // kernel.GetInterruptManager().BindHandler( + // GetSchedulerInterruptHandler(kernel), KInterruptName::Scheduler, m_core_id, + // KInterruptController::PriorityLevel::Scheduler, false, false); + + // Set the current thread. + m_current_thread = main_thread; +} + +void KScheduler::Activate() { + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1); + + // m_state.should_count_idle = KTargetSystem::IsDebugMode(); + m_is_active = true; + RescheduleCurrentCore(); +} + +void KScheduler::OnThreadStart() { + GetCurrentThread(kernel).EnableDispatch(); +} + u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) { - KScopedSpinLock lk{guard}; - if (KThread* prev_highest_thread = state.highest_priority_thread; - prev_highest_thread != highest_thread) { - if (prev_highest_thread != nullptr) { + if (KThread* prev_highest_thread = m_state.highest_priority_thread; + prev_highest_thread != highest_thread) [[likely]] { + if (prev_highest_thread != nullptr) [[likely]] { IncrementScheduledCount(prev_highest_thread); - prev_highest_thread->SetLastScheduledTick(system.CoreTiming().GetCPUTicks()); + prev_highest_thread->SetLastScheduledTick(kernel.System().CoreTiming().GetCPUTicks()); } - if (state.should_count_idle) { - if (highest_thread != nullptr) { + if (m_state.should_count_idle) { + if (highest_thread != nullptr) [[likely]] { if (KProcess* process = highest_thread->GetOwnerProcess(); process != nullptr) { - process->SetRunningThread(core_id, highest_thread, state.idle_count); + process->SetRunningThread(m_core_id, highest_thread, m_state.idle_count); } } else { - state.idle_count++; + m_state.idle_count++; } } - state.highest_priority_thread = highest_thread; - state.needs_scheduling.store(true); - return (1ULL << core_id); + m_state.highest_priority_thread = highest_thread; + m_state.needs_scheduling = true; + return (1ULL << m_core_id); } else { return 0; } } u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + ASSERT(IsSchedulerLockedByCurrentThread(kernel)); // Clear that we need to update. ClearSchedulerUpdateNeeded(kernel); @@ -103,18 +214,20 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { KThread* top_threads[Core::Hardware::NUM_CPU_CORES]; auto& priority_queue = GetPriorityQueue(kernel); - /// We want to go over all cores, finding the highest priority thread and determining if - /// scheduling is needed for that core. + // We want to go over all cores, finding the highest priority thread and determining if + // scheduling is needed for that core. for (size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { KThread* top_thread = priority_queue.GetScheduledFront(static_cast<s32>(core_id)); if (top_thread != nullptr) { - // If the thread has no waiters, we need to check if the process has a thread pinned. - if (top_thread->GetNumKernelWaiters() == 0) { - if (KProcess* parent = top_thread->GetOwnerProcess(); parent != nullptr) { - if (KThread* pinned = parent->GetPinnedThread(static_cast<s32>(core_id)); - pinned != nullptr && pinned != top_thread) { - // We prefer our parent's pinned thread if possible. However, we also don't - // want to schedule un-runnable threads. + // We need to check if the thread's process has a pinned thread. + if (KProcess* parent = top_thread->GetOwnerProcess()) { + // Check that there's a pinned thread other than the current top thread. + if (KThread* pinned = parent->GetPinnedThread(static_cast<s32>(core_id)); + pinned != nullptr && pinned != top_thread) { + // We need to prefer threads with kernel waiters to the pinned thread. + if (top_thread->GetNumKernelWaiters() == + 0 /* && top_thread != parent->GetExceptionThread() */) { + // If the pinned thread is runnable, use it. if (pinned->GetRawState() == ThreadState::Runnable) { top_thread = pinned; } else { @@ -134,7 +247,8 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { // Idle cores are bad. We're going to try to migrate threads to each idle core in turn. while (idle_cores != 0) { - const auto core_id = static_cast<u32>(std::countr_zero(idle_cores)); + const s32 core_id = static_cast<s32>(std::countr_zero(idle_cores)); + if (KThread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) { s32 migration_candidates[Core::Hardware::NUM_CPU_CORES]; size_t num_candidates = 0; @@ -155,7 +269,6 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { // The suggested thread isn't bound to its core, so we can migrate it! suggested->SetActiveCore(core_id); priority_queue.ChangeCore(suggested_core, suggested); - top_threads[core_id] = suggested; cores_needing_scheduling |= kernel.Scheduler(core_id).UpdateHighestPriorityThread(top_threads[core_id]); @@ -188,7 +301,6 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { // Perform the migration. suggested->SetActiveCore(core_id); priority_queue.ChangeCore(candidate_core, suggested); - top_threads[core_id] = suggested; cores_needing_scheduling |= kernel.Scheduler(core_id).UpdateHighestPriorityThread( @@ -205,24 +317,210 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { return cores_needing_scheduling; } +void KScheduler::SwitchThread(KThread* next_thread) { + KProcess* const cur_process = kernel.CurrentProcess(); + KThread* const cur_thread = GetCurrentThreadPointer(kernel); + + // We never want to schedule a null thread, so use the idle thread if we don't have a next. + if (next_thread == nullptr) { + next_thread = m_idle_thread; + } + + if (next_thread->GetCurrentCore() != m_core_id) { + next_thread->SetCurrentCore(m_core_id); + } + + // If we're not actually switching thread, there's nothing to do. + if (next_thread == cur_thread) { + return; + } + + // Next thread is now known not to be nullptr, and must not be dispatchable. + ASSERT(next_thread->GetDisableDispatchCount() == 1); + ASSERT(!next_thread->IsDummyThread()); + + // Update the CPU time tracking variables. + const s64 prev_tick = m_last_context_switch_time; + const s64 cur_tick = kernel.System().CoreTiming().GetCPUTicks(); + const s64 tick_diff = cur_tick - prev_tick; + cur_thread->AddCpuTime(m_core_id, tick_diff); + if (cur_process != nullptr) { + cur_process->UpdateCPUTimeTicks(tick_diff); + } + m_last_context_switch_time = cur_tick; + + // Update our previous thread. + if (cur_process != nullptr) { + if (!cur_thread->IsTerminationRequested() && cur_thread->GetActiveCore() == m_core_id) + [[likely]] { + m_state.prev_thread = cur_thread; + } else { + m_state.prev_thread = nullptr; + } + } + + // Switch the current process, if we're switching processes. + // if (KProcess *next_process = next_thread->GetOwnerProcess(); next_process != cur_process) { + // KProcess::Switch(cur_process, next_process); + // } + + // Set the new thread. + SetCurrentThread(kernel, next_thread); + m_current_thread = next_thread; + + // Set the new Thread Local region. + // cpu::SwitchThreadLocalRegion(GetInteger(next_thread->GetThreadLocalRegionAddress())); +} + +void KScheduler::ScheduleImpl() { + // First, clear the needs scheduling bool. + m_state.needs_scheduling.store(false, std::memory_order_seq_cst); + + // Load the appropriate thread pointers for scheduling. + KThread* const cur_thread{GetCurrentThreadPointer(kernel)}; + KThread* highest_priority_thread{m_state.highest_priority_thread}; + + // Check whether there are runnable interrupt tasks. + if (m_state.interrupt_task_runnable) { + // The interrupt task is runnable. + // We want to switch to the interrupt task/idle thread. + highest_priority_thread = nullptr; + } + + // If there aren't, we want to check if the highest priority thread is the same as the current + // thread. + if (highest_priority_thread == cur_thread) { + // If they're the same, then we can just return. + return; + } + + // The highest priority thread is not the same as the current thread. + // Jump to the switcher and continue executing from there. + m_switch_cur_thread = cur_thread; + m_switch_highest_priority_thread = highest_priority_thread; + m_switch_from_schedule = true; + Common::Fiber::YieldTo(cur_thread->host_context, *m_switch_fiber); + + // Returning from ScheduleImpl occurs after this thread has been scheduled again. +} + +void KScheduler::ScheduleImplFiber() { + KThread* const cur_thread{m_switch_cur_thread}; + KThread* highest_priority_thread{m_switch_highest_priority_thread}; + + // If we're not coming from scheduling (i.e., we came from SC preemption), + // we should restart the scheduling loop directly. Not accurate to HOS. + if (!m_switch_from_schedule) { + goto retry; + } + + // Mark that we are not coming from scheduling anymore. + m_switch_from_schedule = false; + + // Save the original thread context. + Unload(cur_thread); + + // The current thread's context has been entirely taken care of. + // Now we want to loop until we successfully switch the thread context. + while (true) { + // We're starting to try to do the context switch. + // Check if the highest priority thread is null. + if (!highest_priority_thread) { + // The next thread is nullptr! + + // Switch to the idle thread. Note: HOS treats idling as a special case for + // performance. This is not *required* for yuzu's purposes, and for singlecore + // compatibility, we can just move the logic that would go here into the execution + // of the idle thread. If we ever remove singlecore, we should implement this + // accurately to HOS. + highest_priority_thread = m_idle_thread; + } + + // We want to try to lock the highest priority thread's context. + // Try to take it. + while (!highest_priority_thread->context_guard.try_lock()) { + // The highest priority thread's context is already locked. + // Check if we need scheduling. If we don't, we can retry directly. + if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) { + // If we do, another core is interfering, and we must start again. + goto retry; + } + } + + // It's time to switch the thread. + // Switch to the highest priority thread. + SwitchThread(highest_priority_thread); + + // Check if we need scheduling. If we do, then we can't complete the switch and should + // retry. + if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) { + // Our switch failed. + // We should unlock the thread context, and then retry. + highest_priority_thread->context_guard.unlock(); + goto retry; + } else { + break; + } + + retry: + + // We failed to successfully do the context switch, and need to retry. + // Clear needs_scheduling. + m_state.needs_scheduling.store(false, std::memory_order_seq_cst); + + // Refresh the highest priority thread. + highest_priority_thread = m_state.highest_priority_thread; + } + + // Reload the guest thread context. + Reload(highest_priority_thread); + + // Reload the host thread. + Common::Fiber::YieldTo(m_switch_fiber, *highest_priority_thread->host_context); +} + +void KScheduler::Unload(KThread* thread) { + auto& cpu_core = kernel.System().ArmInterface(m_core_id); + cpu_core.SaveContext(thread->GetContext32()); + cpu_core.SaveContext(thread->GetContext64()); + // Save the TPIDR_EL0 system register in case it was modified. + thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); + cpu_core.ClearExclusiveState(); + + // Check if the thread is terminated by checking the DPC flags. + if ((thread->GetStackParameters().dpc_flags & static_cast<u32>(DpcFlag::Terminated)) == 0) { + // The thread isn't terminated, so we want to unlock it. + thread->context_guard.unlock(); + } +} + +void KScheduler::Reload(KThread* thread) { + auto& cpu_core = kernel.System().ArmInterface(m_core_id); + cpu_core.LoadContext(thread->GetContext32()); + cpu_core.LoadContext(thread->GetContext64()); + cpu_core.SetTlsAddress(thread->GetTLSAddress()); + cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0()); + cpu_core.LoadWatchpointArray(thread->GetOwnerProcess()->GetWatchpoints()); + cpu_core.ClearExclusiveState(); +} + void KScheduler::ClearPreviousThread(KernelCore& kernel, KThread* thread) { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + ASSERT(IsSchedulerLockedByCurrentThread(kernel)); for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; ++i) { // Get an atomic reference to the core scheduler's previous thread. - std::atomic_ref<KThread*> prev_thread(kernel.Scheduler(static_cast<s32>(i)).prev_thread); - static_assert(std::atomic_ref<KThread*>::is_always_lock_free); + auto& prev_thread{kernel.Scheduler(i).m_state.prev_thread}; // Atomically clear the previous thread if it's our target. KThread* compare = thread; - prev_thread.compare_exchange_strong(compare, nullptr); + prev_thread.compare_exchange_strong(compare, nullptr, std::memory_order_seq_cst); } } void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state) { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + ASSERT(IsSchedulerLockedByCurrentThread(kernel)); // Check if the state has changed, because if it hasn't there's nothing to do. - const auto cur_state = thread->GetRawState(); + const ThreadState cur_state = thread->GetRawState(); if (cur_state == old_state) { return; } @@ -242,12 +540,12 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, Threa } void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority) { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + ASSERT(IsSchedulerLockedByCurrentThread(kernel)); // If the thread is runnable, we want to change its priority in the queue. if (thread->GetRawState() == ThreadState::Runnable) { GetPriorityQueue(kernel).ChangePriority(old_priority, - thread == kernel.GetCurrentEmuThread(), thread); + thread == GetCurrentThreadPointer(kernel), thread); IncrementScheduledCount(thread); SetSchedulerUpdateNeeded(kernel); } @@ -255,7 +553,7 @@ void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s3 void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread, const KAffinityMask& old_affinity, s32 old_core) { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + ASSERT(IsSchedulerLockedByCurrentThread(kernel)); // If the thread is runnable, we want to change its affinity in the queue. if (thread->GetRawState() == ThreadState::Runnable) { @@ -265,15 +563,14 @@ void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread } } -void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) { - ASSERT(system.GlobalSchedulerContext().IsLocked()); +void KScheduler::RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 priority) { + ASSERT(IsSchedulerLockedByCurrentThread(kernel)); // Get a reference to the priority queue. - auto& kernel = system.Kernel(); auto& priority_queue = GetPriorityQueue(kernel); // Rotate the front of the queue to the end. - KThread* top_thread = priority_queue.GetScheduledFront(cpu_core_id, priority); + KThread* top_thread = priority_queue.GetScheduledFront(core_id, priority); KThread* next_thread = nullptr; if (top_thread != nullptr) { next_thread = priority_queue.MoveToScheduledBack(top_thread); @@ -285,7 +582,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) { // While we have a suggested thread, try to migrate it! { - KThread* suggested = priority_queue.GetSuggestedFront(cpu_core_id, priority); + KThread* suggested = priority_queue.GetSuggestedFront(core_id, priority); while (suggested != nullptr) { // Check if the suggested thread is the top thread on its core. const s32 suggested_core = suggested->GetActiveCore(); @@ -306,7 +603,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) { // to the front of the queue. if (top_on_suggested_core == nullptr || top_on_suggested_core->GetPriority() >= HighestCoreMigrationAllowedPriority) { - suggested->SetActiveCore(cpu_core_id); + suggested->SetActiveCore(core_id); priority_queue.ChangeCore(suggested_core, suggested, true); IncrementScheduledCount(suggested); break; @@ -314,22 +611,21 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) { } // Get the next suggestion. - suggested = priority_queue.GetSamePriorityNext(cpu_core_id, suggested); + suggested = priority_queue.GetSamePriorityNext(core_id, suggested); } } // Now that we might have migrated a thread with the same priority, check if we can do better. - { - KThread* best_thread = priority_queue.GetScheduledFront(cpu_core_id); - if (best_thread == GetCurrentThread()) { - best_thread = priority_queue.GetScheduledNext(cpu_core_id, best_thread); + KThread* best_thread = priority_queue.GetScheduledFront(core_id); + if (best_thread == GetCurrentThreadPointer(kernel)) { + best_thread = priority_queue.GetScheduledNext(core_id, best_thread); } // If the best thread we can choose has a priority the same or worse than ours, try to // migrate a higher priority thread. if (best_thread != nullptr && best_thread->GetPriority() >= priority) { - KThread* suggested = priority_queue.GetSuggestedFront(cpu_core_id); + KThread* suggested = priority_queue.GetSuggestedFront(core_id); while (suggested != nullptr) { // If the suggestion's priority is the same as ours, don't bother. if (suggested->GetPriority() >= best_thread->GetPriority()) { @@ -348,7 +644,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) { if (top_on_suggested_core == nullptr || top_on_suggested_core->GetPriority() >= HighestCoreMigrationAllowedPriority) { - suggested->SetActiveCore(cpu_core_id); + suggested->SetActiveCore(core_id); priority_queue.ChangeCore(suggested_core, suggested, true); IncrementScheduledCount(suggested); break; @@ -356,7 +652,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) { } // Get the next suggestion. - suggested = priority_queue.GetSuggestedNext(cpu_core_id, suggested); + suggested = priority_queue.GetSuggestedNext(core_id, suggested); } } } @@ -365,71 +661,13 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) { SetSchedulerUpdateNeeded(kernel); } -bool KScheduler::CanSchedule(KernelCore& kernel) { - return kernel.GetCurrentEmuThread()->GetDisableDispatchCount() <= 1; -} - -bool KScheduler::IsSchedulerUpdateNeeded(const KernelCore& kernel) { - return kernel.GlobalSchedulerContext().scheduler_update_needed.load(std::memory_order_acquire); -} - -void KScheduler::SetSchedulerUpdateNeeded(KernelCore& kernel) { - kernel.GlobalSchedulerContext().scheduler_update_needed.store(true, std::memory_order_release); -} - -void KScheduler::ClearSchedulerUpdateNeeded(KernelCore& kernel) { - kernel.GlobalSchedulerContext().scheduler_update_needed.store(false, std::memory_order_release); -} - -void KScheduler::DisableScheduling(KernelCore& kernel) { - // If we are shutting down the kernel, none of this is relevant anymore. - if (kernel.IsShuttingDown()) { - return; - } - - ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 0); - GetCurrentThreadPointer(kernel)->DisableDispatch(); -} - -void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) { - // If we are shutting down the kernel, none of this is relevant anymore. - if (kernel.IsShuttingDown()) { - return; - } - - auto* current_thread = GetCurrentThreadPointer(kernel); - - ASSERT(current_thread->GetDisableDispatchCount() >= 1); - - if (current_thread->GetDisableDispatchCount() > 1) { - current_thread->EnableDispatch(); - } else { - RescheduleCores(kernel, cores_needing_scheduling); - } - - // Special case to ensure dummy threads that are waiting block. - current_thread->IfDummyThreadTryWait(); -} - -u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) { - if (IsSchedulerUpdateNeeded(kernel)) { - return UpdateHighestPriorityThreadsImpl(kernel); - } else { - return 0; - } -} - -KSchedulerPriorityQueue& KScheduler::GetPriorityQueue(KernelCore& kernel) { - return kernel.GlobalSchedulerContext().priority_queue; -} - void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) { // Validate preconditions. ASSERT(CanSchedule(kernel)); ASSERT(kernel.CurrentProcess() != nullptr); // Get the current thread and process. - KThread& cur_thread = Kernel::GetCurrentThread(kernel); + KThread& cur_thread = GetCurrentThread(kernel); KProcess& cur_process = *kernel.CurrentProcess(); // If the thread's yield count matches, there's nothing for us to do. @@ -442,7 +680,7 @@ void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) { // Perform the yield. { - KScopedSchedulerLock lock(kernel); + KScopedSchedulerLock sl{kernel}; const auto cur_state = cur_thread.GetRawState(); if (cur_state == ThreadState::Runnable) { @@ -468,7 +706,7 @@ void KScheduler::YieldWithCoreMigration(KernelCore& kernel) { ASSERT(kernel.CurrentProcess() != nullptr); // Get the current thread and process. - KThread& cur_thread = Kernel::GetCurrentThread(kernel); + KThread& cur_thread = GetCurrentThread(kernel); KProcess& cur_process = *kernel.CurrentProcess(); // If the thread's yield count matches, there's nothing for us to do. @@ -481,7 +719,7 @@ void KScheduler::YieldWithCoreMigration(KernelCore& kernel) { // Perform the yield. { - KScopedSchedulerLock lock(kernel); + KScopedSchedulerLock sl{kernel}; const auto cur_state = cur_thread.GetRawState(); if (cur_state == ThreadState::Runnable) { @@ -501,7 +739,7 @@ void KScheduler::YieldWithCoreMigration(KernelCore& kernel) { if (KThread* running_on_suggested_core = (suggested_core >= 0) - ? kernel.Scheduler(suggested_core).state.highest_priority_thread + ? kernel.Scheduler(suggested_core).m_state.highest_priority_thread : nullptr; running_on_suggested_core != suggested) { // If the current thread's priority is higher than our suggestion's we prefer @@ -556,7 +794,7 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) { ASSERT(kernel.CurrentProcess() != nullptr); // Get the current thread and process. - KThread& cur_thread = Kernel::GetCurrentThread(kernel); + KThread& cur_thread = GetCurrentThread(kernel); KProcess& cur_process = *kernel.CurrentProcess(); // If the thread's yield count matches, there's nothing for us to do. @@ -569,7 +807,7 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) { // Perform the yield. { - KScopedSchedulerLock lock(kernel); + KScopedSchedulerLock sl{kernel}; const auto cur_state = cur_thread.GetRawState(); if (cur_state == ThreadState::Runnable) { @@ -626,219 +864,19 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) { } } -KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, core_id{core_id_} { - switch_fiber = std::make_shared<Common::Fiber>(OnSwitch, this); - state.needs_scheduling.store(true); - state.interrupt_task_thread_runnable = false; - state.should_count_idle = false; - state.idle_count = 0; - state.idle_thread_stack = nullptr; - state.highest_priority_thread = nullptr; -} - -void KScheduler::Finalize() { - if (idle_thread) { - idle_thread->Close(); - idle_thread = nullptr; - } -} - -KScheduler::~KScheduler() { - ASSERT(!idle_thread); -} - -KThread* KScheduler::GetCurrentThread() const { - if (auto result = current_thread.load(); result) { - return result; - } - return idle_thread; -} - -u64 KScheduler::GetLastContextSwitchTicks() const { - return last_context_switch_time; -} - -void KScheduler::RescheduleCurrentCore() { - ASSERT(GetCurrentThread()->GetDisableDispatchCount() == 1); - - auto& phys_core = system.Kernel().PhysicalCore(core_id); - if (phys_core.IsInterrupted()) { - phys_core.ClearInterrupt(); +void KScheduler::RescheduleOtherCores(u64 cores_needing_scheduling) { + if (const u64 core_mask = cores_needing_scheduling & ~(1ULL << m_core_id); core_mask != 0) { + RescheduleCores(kernel, core_mask); } - - guard.Lock(); - if (state.needs_scheduling.load()) { - Schedule(); - } else { - GetCurrentThread()->EnableDispatch(); - guard.Unlock(); - } -} - -void KScheduler::OnThreadStart() { - SwitchContextStep2(); } -void KScheduler::Unload(KThread* thread) { - ASSERT(thread); - - LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr"); - - if (thread->IsCallingSvc()) { - thread->ClearIsCallingSvc(); - } - - auto& physical_core = system.Kernel().PhysicalCore(core_id); - if (!physical_core.IsInitialized()) { - return; - } - - Core::ARM_Interface& cpu_core = physical_core.ArmInterface(); - cpu_core.SaveContext(thread->GetContext32()); - cpu_core.SaveContext(thread->GetContext64()); - // Save the TPIDR_EL0 system register in case it was modified. - thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); - cpu_core.ClearExclusiveState(); - - if (!thread->IsTerminationRequested() && thread->GetActiveCore() == core_id) { - prev_thread = thread; - } else { - prev_thread = nullptr; - } - - thread->context_guard.Unlock(); -} - -void KScheduler::Reload(KThread* thread) { - LOG_TRACE(Kernel, "core {}, reload thread {}", core_id, thread->GetName()); - - Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); - cpu_core.LoadContext(thread->GetContext32()); - cpu_core.LoadContext(thread->GetContext64()); - cpu_core.SetTlsAddress(thread->GetTLSAddress()); - cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0()); - cpu_core.ClearExclusiveState(); -} - -void KScheduler::SwitchContextStep2() { - // Load context of new thread - Reload(GetCurrentThread()); - - RescheduleCurrentCore(); -} - -void KScheduler::ScheduleImpl() { - KThread* previous_thread = GetCurrentThread(); - KThread* next_thread = state.highest_priority_thread; - - state.needs_scheduling.store(false); - - // We never want to schedule a null thread, so use the idle thread if we don't have a next. - if (next_thread == nullptr) { - next_thread = idle_thread; - } - - if (next_thread->GetCurrentCore() != core_id) { - next_thread->SetCurrentCore(core_id); - } - - // We never want to schedule a dummy thread, as these are only used by host threads for locking. - if (next_thread->GetThreadType() == ThreadType::Dummy) { - ASSERT_MSG(false, "Dummy threads should never be scheduled!"); - next_thread = idle_thread; - } - - // If we're not actually switching thread, there's nothing to do. - if (next_thread == current_thread.load()) { - previous_thread->EnableDispatch(); - guard.Unlock(); - return; - } - - // Update the CPU time tracking variables. - KProcess* const previous_process = system.Kernel().CurrentProcess(); - UpdateLastContextSwitchTime(previous_thread, previous_process); - - // Save context for previous thread - Unload(previous_thread); - - std::shared_ptr<Common::Fiber>* old_context; - old_context = &previous_thread->GetHostContext(); - - // Set the new thread. - current_thread.store(next_thread); - - guard.Unlock(); - - Common::Fiber::YieldTo(*old_context, *switch_fiber); - /// When a thread wakes up, the scheduler may have changed to other in another core. - auto& next_scheduler = *system.Kernel().CurrentScheduler(); - next_scheduler.SwitchContextStep2(); -} - -void KScheduler::OnSwitch(void* this_scheduler) { - KScheduler* sched = static_cast<KScheduler*>(this_scheduler); - sched->SwitchToCurrent(); -} - -void KScheduler::SwitchToCurrent() { - while (true) { - { - KScopedSpinLock lk{guard}; - current_thread.store(state.highest_priority_thread); - state.needs_scheduling.store(false); +void KScheduler::RescheduleCores(KernelCore& kernel, u64 core_mask) { + // Send IPI + for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { + if (core_mask & (1ULL << i)) { + kernel.PhysicalCore(i).Interrupt(); } - const auto is_switch_pending = [this] { - KScopedSpinLock lk{guard}; - return state.needs_scheduling.load(); - }; - do { - auto next_thread = current_thread.load(); - if (next_thread != nullptr) { - const auto locked = next_thread->context_guard.TryLock(); - if (state.needs_scheduling.load()) { - next_thread->context_guard.Unlock(); - break; - } - if (next_thread->GetActiveCore() != core_id) { - next_thread->context_guard.Unlock(); - break; - } - if (!locked) { - continue; - } - } - auto thread = next_thread ? next_thread : idle_thread; - Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext()); - } while (!is_switch_pending()); } } -void KScheduler::UpdateLastContextSwitchTime(KThread* thread, KProcess* process) { - const u64 prev_switch_ticks = last_context_switch_time; - const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks(); - const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks; - - if (thread != nullptr) { - thread->AddCpuTime(core_id, update_ticks); - } - - if (process != nullptr) { - process->UpdateCPUTimeTicks(update_ticks); - } - - last_context_switch_time = most_recent_switch_ticks; -} - -void KScheduler::Initialize() { - idle_thread = KThread::Create(system.Kernel()); - ASSERT(KThread::InitializeIdleThread(system, idle_thread, core_id).IsSuccess()); - idle_thread->SetName(fmt::format("IdleThread:{}", core_id)); -} - -KScopedSchedulerLock::KScopedSchedulerLock(KernelCore& kernel) - : KScopedLock(kernel.GlobalSchedulerContext().SchedulerLock()) {} - -KScopedSchedulerLock::~KScopedSchedulerLock() = default; - } // namespace Kernel diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h index 82fcd99e7..534321d8d 100644 --- a/src/core/hle/kernel/k_scheduler.h +++ b/src/core/hle/kernel/k_scheduler.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -12,6 +11,7 @@ #include "core/hle/kernel/k_scheduler_lock.h" #include "core/hle/kernel/k_scoped_lock.h" #include "core/hle/kernel/k_spin_lock.h" +#include "core/hle/kernel/k_thread.h" namespace Common { class Fiber; @@ -24,188 +24,150 @@ class System; namespace Kernel { class KernelCore; +class KInterruptTaskManager; class KProcess; -class SchedulerLock; class KThread; +class KScopedDisableDispatch; +class KScopedSchedulerLock; +class KScopedSchedulerLockAndSleep; class KScheduler final { public: - explicit KScheduler(Core::System& system_, s32 core_id_); - ~KScheduler(); - - void Finalize(); + YUZU_NON_COPYABLE(KScheduler); + YUZU_NON_MOVEABLE(KScheduler); - /// Reschedules to the next available thread (call after current thread is suspended) - void RescheduleCurrentCore(); + using LockType = KAbstractSchedulerLock<KScheduler>; - /// Reschedules cores pending reschedule, to be called on EnableScheduling. - static void RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule); + explicit KScheduler(KernelCore& kernel); + ~KScheduler(); - /// The next two are for SingleCore Only. - /// Unload current thread before preempting core. + void Initialize(KThread* main_thread, KThread* idle_thread, s32 core_id); + void Activate(); + void OnThreadStart(); void Unload(KThread* thread); - - /// Reload current thread after core preemption. void Reload(KThread* thread); - /// Gets the current running thread - [[nodiscard]] KThread* GetCurrentThread() const; + void SetInterruptTaskRunnable(); + void RequestScheduleOnInterrupt(); + void PreemptSingleCore(); - /// Gets the idle thread - [[nodiscard]] KThread* GetIdleThread() const { - return idle_thread; + u64 GetIdleCount() { + return m_state.idle_count; } - /// Returns true if the scheduler is idle - [[nodiscard]] bool IsIdle() const { - return GetCurrentThread() == idle_thread; + KThread* GetIdleThread() const { + return m_idle_thread; } - /// Gets the timestamp for the last context switch in ticks. - [[nodiscard]] u64 GetLastContextSwitchTicks() const; - - [[nodiscard]] bool ContextSwitchPending() const { - return state.needs_scheduling.load(std::memory_order_relaxed); + bool IsIdle() const { + return m_current_thread.load() == m_idle_thread; } - void Initialize(); - - void OnThreadStart(); + KThread* GetPreviousThread() const { + return m_state.prev_thread; + } - [[nodiscard]] std::shared_ptr<Common::Fiber>& ControlContext() { - return switch_fiber; + KThread* GetSchedulerCurrentThread() const { + return m_current_thread.load(); } - [[nodiscard]] const std::shared_ptr<Common::Fiber>& ControlContext() const { - return switch_fiber; + s64 GetLastContextSwitchTime() const { + return m_last_context_switch_time; } - [[nodiscard]] u64 UpdateHighestPriorityThread(KThread* highest_thread); + // Static public API. + static bool CanSchedule(KernelCore& kernel) { + return GetCurrentThread(kernel).GetDisableDispatchCount() == 0; + } + static bool IsSchedulerLockedByCurrentThread(KernelCore& kernel) { + return kernel.GlobalSchedulerContext().scheduler_lock.IsLockedByCurrentThread(); + } - /** - * Takes a thread and moves it to the back of the it's priority list. - * - * @note This operation can be redundant and no scheduling is changed if marked as so. - */ - static void YieldWithoutCoreMigration(KernelCore& kernel); + static bool IsSchedulerUpdateNeeded(KernelCore& kernel) { + return kernel.GlobalSchedulerContext().scheduler_update_needed; + } + static void SetSchedulerUpdateNeeded(KernelCore& kernel) { + kernel.GlobalSchedulerContext().scheduler_update_needed = true; + } + static void ClearSchedulerUpdateNeeded(KernelCore& kernel) { + kernel.GlobalSchedulerContext().scheduler_update_needed = false; + } - /** - * Takes a thread and moves it to the back of the it's priority list. - * Afterwards, tries to pick a suggested thread from the suggested queue that has worse time or - * a better priority than the next thread in the core. - * - * @note This operation can be redundant and no scheduling is changed if marked as so. - */ - static void YieldWithCoreMigration(KernelCore& kernel); + static void DisableScheduling(KernelCore& kernel); + static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling); - /** - * Takes a thread and moves it out of the scheduling queue. - * and into the suggested queue. If no thread can be scheduled afterwards in that core, - * a suggested thread is obtained instead. - * - * @note This operation can be redundant and no scheduling is changed if marked as so. - */ - static void YieldToAnyThread(KernelCore& kernel); + static u64 UpdateHighestPriorityThreads(KernelCore& kernel); static void ClearPreviousThread(KernelCore& kernel, KThread* thread); - /// Notify the scheduler a thread's status has changed. static void OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state); - - /// Notify the scheduler a thread's priority has changed. static void OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority); - - /// Notify the scheduler a thread's core and/or affinity mask has changed. static void OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread, const KAffinityMask& old_affinity, s32 old_core); - static bool CanSchedule(KernelCore& kernel); - static bool IsSchedulerUpdateNeeded(const KernelCore& kernel); - static void SetSchedulerUpdateNeeded(KernelCore& kernel); - static void ClearSchedulerUpdateNeeded(KernelCore& kernel); - static void DisableScheduling(KernelCore& kernel); - static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling); - [[nodiscard]] static u64 UpdateHighestPriorityThreads(KernelCore& kernel); + static void RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 priority); + static void RescheduleCores(KernelCore& kernel, u64 cores_needing_scheduling); + + static void YieldWithoutCoreMigration(KernelCore& kernel); + static void YieldWithCoreMigration(KernelCore& kernel); + static void YieldToAnyThread(KernelCore& kernel); private: - friend class GlobalSchedulerContext; - - /** - * Takes care of selecting the new scheduled threads in three steps: - * - * 1. First a thread is selected from the top of the priority queue. If no thread - * is obtained then we move to step two, else we are done. - * - * 2. Second we try to get a suggested thread that's not assigned to any core or - * that is not the top thread in that core. - * - * 3. Third is no suggested thread is found, we do a second pass and pick a running - * thread in another core and swap it with its current thread. - * - * returns the cores needing scheduling. - */ - [[nodiscard]] static u64 UpdateHighestPriorityThreadsImpl(KernelCore& kernel); - - [[nodiscard]] static KSchedulerPriorityQueue& GetPriorityQueue(KernelCore& kernel); - - void RotateScheduledQueue(s32 cpu_core_id, s32 priority); - - void Schedule() { - ASSERT(GetCurrentThread()->GetDisableDispatchCount() == 1); - this->ScheduleImpl(); + // Static private API. + static KSchedulerPriorityQueue& GetPriorityQueue(KernelCore& kernel) { + return kernel.GlobalSchedulerContext().priority_queue; } + static u64 UpdateHighestPriorityThreadsImpl(KernelCore& kernel); - /// Switches the CPU's active thread context to that of the specified thread - void ScheduleImpl(); - - /// When a thread wakes up, it must run this through it's new scheduler - void SwitchContextStep2(); + static void RescheduleCurrentHLEThread(KernelCore& kernel); - /** - * Called on every context switch to update the internal timestamp - * This also updates the running time ticks for the given thread and - * process using the following difference: - * - * ticks += most_recent_ticks - last_context_switch_ticks - * - * The internal tick timestamp for the scheduler is simply the - * most recent tick count retrieved. No special arithmetic is - * applied to it. - */ - void UpdateLastContextSwitchTime(KThread* thread, KProcess* process); + // Instanced private API. + void ScheduleImpl(); + void ScheduleImplFiber(); + void SwitchThread(KThread* next_thread); - static void OnSwitch(void* this_scheduler); - void SwitchToCurrent(); + void Schedule(); + void ScheduleOnInterrupt(); - KThread* prev_thread{}; - std::atomic<KThread*> current_thread{}; + void RescheduleOtherCores(u64 cores_needing_scheduling); + void RescheduleCurrentCore(); + void RescheduleCurrentCoreImpl(); - KThread* idle_thread{}; + u64 UpdateHighestPriorityThread(KThread* thread); - std::shared_ptr<Common::Fiber> switch_fiber{}; +private: + friend class KScopedDisableDispatch; struct SchedulingState { - std::atomic<bool> needs_scheduling{}; - bool interrupt_task_thread_runnable{}; - bool should_count_idle{}; - u64 idle_count{}; - KThread* highest_priority_thread{}; - void* idle_thread_stack{}; + std::atomic<bool> needs_scheduling{false}; + bool interrupt_task_runnable{false}; + bool should_count_idle{false}; + u64 idle_count{0}; + KThread* highest_priority_thread{nullptr}; + void* idle_thread_stack{nullptr}; + std::atomic<KThread*> prev_thread{nullptr}; + KInterruptTaskManager* interrupt_task_manager{nullptr}; }; - SchedulingState state; - - Core::System& system; - u64 last_context_switch_time{}; - const s32 core_id; - - KSpinLock guard{}; + KernelCore& kernel; + SchedulingState m_state; + bool m_is_active{false}; + s32 m_core_id{0}; + s64 m_last_context_switch_time{0}; + KThread* m_idle_thread{nullptr}; + std::atomic<KThread*> m_current_thread{nullptr}; + + std::shared_ptr<Common::Fiber> m_switch_fiber{}; + KThread* m_switch_cur_thread{}; + KThread* m_switch_highest_priority_thread{}; + bool m_switch_from_schedule{}; }; -class [[nodiscard]] KScopedSchedulerLock : KScopedLock<GlobalSchedulerContext::LockType> { +class KScopedSchedulerLock : public KScopedLock<KScheduler::LockType> { public: - explicit KScopedSchedulerLock(KernelCore& kernel); - ~KScopedSchedulerLock(); + explicit KScopedSchedulerLock(KernelCore& kernel) + : KScopedLock(kernel.GlobalSchedulerContext().scheduler_lock) {} + ~KScopedSchedulerLock() = default; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h index 93c47f1b1..73314b45e 100644 --- a/src/core/hle/kernel/k_scheduler_lock.h +++ b/src/core/hle/kernel/k_scheduler_lock.h @@ -1,13 +1,15 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include <atomic> #include "common/assert.h" +#include "core/hle/kernel/k_interrupt_manager.h" #include "core/hle/kernel/k_spin_lock.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/physical_core.h" namespace Kernel { @@ -75,7 +77,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_scoped_lock.h b/src/core/hle/kernel/k_scoped_lock.h index 89a7ffe49..857e21156 100644 --- a/src/core/hle/kernel/k_scoped_lock.h +++ b/src/core/hle/kernel/k_scoped_lock.h @@ -1,9 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -// This file references various implementation details from Atmosphere, an open-source firmware for -// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_scoped_resource_reservation.h b/src/core/hle/kernel/k_scoped_resource_reservation.h index 07272075d..436bcf9fe 100644 --- a/src/core/hle/kernel/k_scoped_resource_reservation.h +++ b/src/core/hle/kernel/k_scoped_resource_reservation.h @@ -1,9 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -// This file references various implementation details from Atmosphere, an open-source firmware for -// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h index 2995c492d..76c095e69 100644 --- a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h +++ b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h @@ -1,9 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -// This file references various implementation details from Atmosphere, an open-source firmware for -// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp index 433fc98e1..e968f26ad 100644 --- a/src/core/hle/kernel/k_server_port.cpp +++ b/src/core/hle/kernel/k_server_port.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <tuple> #include "common/assert.h" @@ -62,6 +61,12 @@ void KServerPort::Destroy() { // Close our reference to our parent. parent->Close(); + + // Release host emulation members. + session_handler.reset(); + + // Ensure that the global list tracking server objects does not hold on to a reference. + kernel.UnregisterServerObject(this); } bool KServerPort::IsSignaled() const { diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h index 6302d5e61..fd4f4bd20 100644 --- a/src/core/hle/kernel/k_server_port.h +++ b/src/core/hle/kernel/k_server_port.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -30,11 +29,11 @@ public: /// Whether or not this server port has an HLE handler available. bool HasSessionRequestHandler() const { - return session_handler != nullptr; + return !session_handler.expired(); } /// Gets the HLE handler for this port. - SessionRequestHandlerPtr GetSessionRequestHandler() const { + SessionRequestHandlerWeakPtr GetSessionRequestHandler() const { return session_handler; } @@ -42,7 +41,7 @@ public: * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port * will inherit a reference to this handler. */ - void SetSessionHandler(SessionRequestHandlerPtr&& handler) { + void SetSessionHandler(SessionRequestHandlerWeakPtr&& handler) { session_handler = std::move(handler); } @@ -66,7 +65,7 @@ private: void CleanupSessions(); SessionList session_list; - SessionRequestHandlerPtr session_handler; + SessionRequestHandlerWeakPtr session_handler; KPort* parent{}; }; diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index 4d94eb9cf..802c646a6 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <tuple> #include <utility> @@ -27,10 +26,7 @@ namespace Kernel { KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {} -KServerSession::~KServerSession() { - // Ensure that the global list tracking server sessions does not hold on to a reference. - kernel.UnregisterServerSession(this); -} +KServerSession::~KServerSession() = default; void KServerSession::Initialize(KSession* parent_session_, std::string&& name_, std::shared_ptr<SessionRequestManager> manager_) { @@ -49,6 +45,12 @@ void KServerSession::Destroy() { parent->OnServerClosed(); parent->Close(); + + // Release host emulation members. + manager.reset(); + + // Ensure that the global list tracking server objects does not hold on to a reference. + kernel.UnregisterServerObject(this); } void KServerSession::OnClientClosed() { @@ -77,7 +79,7 @@ std::size_t KServerSession::NumDomainRequestHandlers() const { return manager->DomainHandlerCount(); } -ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) { +Result KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) { if (!context.HasDomainMessageHeader()) { return ResultSuccess; } @@ -95,10 +97,15 @@ ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& co "object_id {} is too big! This probably means a recent service call " "to {} needed to return a new interface!", object_id, name); - UNREACHABLE(); + ASSERT(false); return ResultSuccess; // Ignore error if asserts are off } - return manager->DomainHandler(object_id - 1)->HandleSyncRequest(*this, context); + if (auto strong_ptr = manager->DomainHandler(object_id - 1).lock()) { + return strong_ptr->HandleSyncRequest(*this, context); + } else { + ASSERT(false); + return ResultSuccess; + } case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id); @@ -116,7 +123,7 @@ ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& co return ResultSuccess; } -ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) { +Result KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) { u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))}; auto context = std::make_shared<HLERequestContext>(kernel, memory, this, thread); @@ -136,8 +143,8 @@ ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memor return ResultSuccess; } -ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) { - ResultCode result = ResultSuccess; +Result KServerSession::CompleteSyncRequest(HLERequestContext& context) { + Result result = ResultSuccess; // If the session has been converted to a domain, handle the domain request if (manager->HasSessionRequestHandler(context)) { @@ -166,8 +173,8 @@ ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) { return result; } -ResultCode KServerSession::HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory, - Core::Timing::CoreTiming& core_timing) { +Result KServerSession::HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory, + Core::Timing::CoreTiming& core_timing) { return QueueSyncRequest(thread, memory); } diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h index 5b76bf17c..6d0821945 100644 --- a/src/core/hle/kernel/k_server_session.h +++ b/src/core/hle/kernel/k_server_session.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -74,10 +73,10 @@ public: * @param memory Memory context to handle the sync request under. * @param core_timing Core timing context to schedule the request event under. * - * @returns ResultCode from the operation. + * @returns Result from the operation. */ - ResultCode HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory, - Core::Timing::CoreTiming& core_timing); + Result HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory, + Core::Timing::CoreTiming& core_timing); /// Adds a new domain request handler to the collection of request handlers within /// this ServerSession instance. @@ -104,14 +103,14 @@ public: private: /// Queues a sync request from the emulated application. - ResultCode QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory); + Result QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory); /// Completes a sync request from the emulated application. - ResultCode CompleteSyncRequest(HLERequestContext& context); + Result CompleteSyncRequest(HLERequestContext& context); /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an /// object handle. - ResultCode HandleDomainSyncRequest(Kernel::HLERequestContext& context); + Result HandleDomainSyncRequest(Kernel::HLERequestContext& context); /// This session's HLE request handlers std::shared_ptr<SessionRequestManager> manager; diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp index a64b56b9e..ee05aa282 100644 --- a/src/core/hle/kernel/k_session.cpp +++ b/src/core/hle/kernel/k_session.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_client_port.h" #include "core/hle/kernel/k_client_session.h" diff --git a/src/core/hle/kernel/k_session.h b/src/core/hle/kernel/k_session.h index 62c328a68..c6ead403b 100644 --- a/src/core/hle/kernel/k_session.h +++ b/src/core/hle/kernel/k_session.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp index 51d7538ca..8ff1545b6 100644 --- a/src/core/hle/kernel/k_shared_memory.cpp +++ b/src/core/hle/kernel/k_shared_memory.cpp @@ -1,6 +1,5 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2014 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "core/core.h" @@ -18,12 +17,10 @@ KSharedMemory::~KSharedMemory() { kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size); } -ResultCode KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, - KPageLinkedList&& page_list_, - Svc::MemoryPermission owner_permission_, - Svc::MemoryPermission user_permission_, - PAddr physical_address_, std::size_t size_, - std::string name_) { +Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, + KPageGroup&& page_list_, Svc::MemoryPermission owner_permission_, + Svc::MemoryPermission user_permission_, PAddr physical_address_, + std::size_t size_, std::string name_) { // Set members. owner_process = owner_process_; device_memory = &device_memory_; @@ -67,8 +64,8 @@ void KSharedMemory::Finalize() { KAutoObjectWithSlabHeapAndContainer<KSharedMemory, KAutoObjectWithList>::Finalize(); } -ResultCode KSharedMemory::Map(KProcess& target_process, VAddr address, std::size_t map_size, - Svc::MemoryPermission permissions) { +Result KSharedMemory::Map(KProcess& target_process, VAddr address, std::size_t map_size, + Svc::MemoryPermission permissions) { const u64 page_count{(map_size + PageSize - 1) / PageSize}; if (page_list.GetNumPages() != page_count) { @@ -86,7 +83,7 @@ ResultCode KSharedMemory::Map(KProcess& target_process, VAddr address, std::size ConvertToKMemoryPermission(permissions)); } -ResultCode KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) { +Result KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) { const u64 page_count{(unmap_size + PageSize - 1) / PageSize}; if (page_list.GetNumPages() != page_count) { diff --git a/src/core/hle/kernel/k_shared_memory.h b/src/core/hle/kernel/k_shared_memory.h index 81de36136..34cb98456 100644 --- a/src/core/hle/kernel/k_shared_memory.h +++ b/src/core/hle/kernel/k_shared_memory.h @@ -1,6 +1,5 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2014 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -9,7 +8,7 @@ #include "common/common_types.h" #include "core/device_memory.h" #include "core/hle/kernel/k_memory_block.h" -#include "core/hle/kernel/k_page_linked_list.h" +#include "core/hle/kernel/k_page_group.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/slab_helpers.h" #include "core/hle/result.h" @@ -26,10 +25,10 @@ public: explicit KSharedMemory(KernelCore& kernel_); ~KSharedMemory() override; - ResultCode Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, - KPageLinkedList&& page_list_, Svc::MemoryPermission owner_permission_, - Svc::MemoryPermission user_permission_, PAddr physical_address_, - std::size_t size_, std::string name_); + Result Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, + KPageGroup&& page_list_, Svc::MemoryPermission owner_permission_, + Svc::MemoryPermission user_permission_, PAddr physical_address_, + std::size_t size_, std::string name_); /** * Maps a shared memory block to an address in the target process' address space @@ -38,8 +37,8 @@ public: * @param map_size Size of the shared memory block to map * @param permissions Memory block map permissions (specified by SVC field) */ - ResultCode Map(KProcess& target_process, VAddr address, std::size_t map_size, - Svc::MemoryPermission permissions); + Result Map(KProcess& target_process, VAddr address, std::size_t map_size, + Svc::MemoryPermission permissions); /** * Unmaps a shared memory block from an address in the target process' address space @@ -47,7 +46,7 @@ public: * @param address Address in system memory to unmap shared memory block * @param unmap_size Size of the shared memory block to unmap */ - ResultCode Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size); + Result Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size); /** * Gets a pointer to the shared memory block @@ -77,7 +76,7 @@ public: private: Core::DeviceMemory* device_memory; KProcess* owner_process{}; - KPageLinkedList page_list; + KPageGroup page_list; Svc::MemoryPermission owner_permission{}; Svc::MemoryPermission user_permission{}; PAddr physical_address{}; diff --git a/src/core/hle/kernel/k_shared_memory_info.h b/src/core/hle/kernel/k_shared_memory_info.h index 20bc19f46..e43db8515 100644 --- a/src/core/hle/kernel/k_shared_memory_info.h +++ b/src/core/hle/kernel/k_shared_memory_info.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_slab_heap.h b/src/core/hle/kernel/k_slab_heap.h index 05c0bec9c..2b303537e 100644 --- a/src/core/hle/kernel/k_slab_heap.h +++ b/src/core/hle/kernel/k_slab_heap.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -16,39 +15,34 @@ class KernelCore; namespace impl { -class KSlabHeapImpl final { -public: +class KSlabHeapImpl { YUZU_NON_COPYABLE(KSlabHeapImpl); YUZU_NON_MOVEABLE(KSlabHeapImpl); +public: struct Node { Node* next{}; }; +public: constexpr KSlabHeapImpl() = default; - constexpr ~KSlabHeapImpl() = default; - void Initialize(std::size_t size) { - ASSERT(head == nullptr); - obj_size = size; - } - - constexpr std::size_t GetObjectSize() const { - return obj_size; + void Initialize() { + ASSERT(m_head == nullptr); } Node* GetHead() const { - return head; + return m_head; } void* Allocate() { - Node* ret = head.load(); + Node* ret = m_head.load(); do { if (ret == nullptr) { break; } - } while (!head.compare_exchange_weak(ret, ret->next)); + } while (!m_head.compare_exchange_weak(ret, ret->next)); return ret; } @@ -56,170 +50,157 @@ public: void Free(void* obj) { Node* node = static_cast<Node*>(obj); - Node* cur_head = head.load(); + Node* cur_head = m_head.load(); do { node->next = cur_head; - } while (!head.compare_exchange_weak(cur_head, node)); + } while (!m_head.compare_exchange_weak(cur_head, node)); } private: - std::atomic<Node*> head{}; - std::size_t obj_size{}; + std::atomic<Node*> m_head{}; }; } // namespace impl -class KSlabHeapBase { -public: +template <bool SupportDynamicExpansion> +class KSlabHeapBase : protected impl::KSlabHeapImpl { YUZU_NON_COPYABLE(KSlabHeapBase); YUZU_NON_MOVEABLE(KSlabHeapBase); - constexpr KSlabHeapBase() = default; - constexpr ~KSlabHeapBase() = default; +private: + size_t m_obj_size{}; + uintptr_t m_peak{}; + uintptr_t m_start{}; + uintptr_t m_end{}; - constexpr bool Contains(uintptr_t addr) const { - return start <= addr && addr < end; - } +private: + void UpdatePeakImpl(uintptr_t obj) { + static_assert(std::atomic_ref<uintptr_t>::is_always_lock_free); + std::atomic_ref<uintptr_t> peak_ref(m_peak); - constexpr std::size_t GetSlabHeapSize() const { - return (end - start) / GetObjectSize(); + const uintptr_t alloc_peak = obj + this->GetObjectSize(); + uintptr_t cur_peak = m_peak; + do { + if (alloc_peak <= cur_peak) { + break; + } + } while (!peak_ref.compare_exchange_strong(cur_peak, alloc_peak)); } - constexpr std::size_t GetObjectSize() const { - return impl.GetObjectSize(); - } +public: + constexpr KSlabHeapBase() = default; - constexpr uintptr_t GetSlabHeapAddress() const { - return start; + bool Contains(uintptr_t address) const { + return m_start <= address && address < m_end; } - std::size_t GetObjectIndexImpl(const void* obj) const { - return (reinterpret_cast<uintptr_t>(obj) - start) / GetObjectSize(); + void Initialize(size_t obj_size, void* memory, size_t memory_size) { + // Ensure we don't initialize a slab using null memory. + ASSERT(memory != nullptr); + + // Set our object size. + m_obj_size = obj_size; + + // Initialize the base allocator. + KSlabHeapImpl::Initialize(); + + // Set our tracking variables. + const size_t num_obj = (memory_size / obj_size); + m_start = reinterpret_cast<uintptr_t>(memory); + m_end = m_start + num_obj * obj_size; + m_peak = m_start; + + // Free the objects. + u8* cur = reinterpret_cast<u8*>(m_end); + + for (size_t i = 0; i < num_obj; i++) { + cur -= obj_size; + KSlabHeapImpl::Free(cur); + } } - std::size_t GetPeakIndex() const { - return GetObjectIndexImpl(reinterpret_cast<const void*>(peak)); + size_t GetSlabHeapSize() const { + return (m_end - m_start) / this->GetObjectSize(); } - void* AllocateImpl() { - return impl.Allocate(); + size_t GetObjectSize() const { + return m_obj_size; } - void FreeImpl(void* obj) { - // Don't allow freeing an object that wasn't allocated from this heap - ASSERT(Contains(reinterpret_cast<uintptr_t>(obj))); + void* Allocate() { + void* obj = KSlabHeapImpl::Allocate(); - impl.Free(obj); + return obj; } - void InitializeImpl(std::size_t obj_size, void* memory, std::size_t memory_size) { - // Ensure we don't initialize a slab using null memory - ASSERT(memory != nullptr); - - // Initialize the base allocator - impl.Initialize(obj_size); + void Free(void* obj) { + // Don't allow freeing an object that wasn't allocated from this heap. + const bool contained = this->Contains(reinterpret_cast<uintptr_t>(obj)); + ASSERT(contained); + KSlabHeapImpl::Free(obj); + } - // Set our tracking variables - const std::size_t num_obj = (memory_size / obj_size); - start = reinterpret_cast<uintptr_t>(memory); - end = start + num_obj * obj_size; - peak = start; + size_t GetObjectIndex(const void* obj) const { + if constexpr (SupportDynamicExpansion) { + if (!this->Contains(reinterpret_cast<uintptr_t>(obj))) { + return std::numeric_limits<size_t>::max(); + } + } - // Free the objects - u8* cur = reinterpret_cast<u8*>(end); + return (reinterpret_cast<uintptr_t>(obj) - m_start) / this->GetObjectSize(); + } - for (std::size_t i{}; i < num_obj; i++) { - cur -= obj_size; - impl.Free(cur); - } + size_t GetPeakIndex() const { + return this->GetObjectIndex(reinterpret_cast<const void*>(m_peak)); } -private: - using Impl = impl::KSlabHeapImpl; + uintptr_t GetSlabHeapAddress() const { + return m_start; + } - Impl impl; - uintptr_t peak{}; - uintptr_t start{}; - uintptr_t end{}; + size_t GetNumRemaining() const { + // Only calculate the number of remaining objects under debug configuration. + return 0; + } }; template <typename T> -class KSlabHeap final : public KSlabHeapBase { -public: - enum class AllocationType { - Host, - Guest, - }; +class KSlabHeap final : public KSlabHeapBase<false> { +private: + using BaseHeap = KSlabHeapBase<false>; - explicit constexpr KSlabHeap(AllocationType allocation_type_ = AllocationType::Host) - : KSlabHeapBase(), allocation_type{allocation_type_} {} +public: + constexpr KSlabHeap() = default; - void Initialize(void* memory, std::size_t memory_size) { - if (allocation_type == AllocationType::Guest) { - InitializeImpl(sizeof(T), memory, memory_size); - } + void Initialize(void* memory, size_t memory_size) { + BaseHeap::Initialize(sizeof(T), memory, memory_size); } T* Allocate() { - switch (allocation_type) { - case AllocationType::Host: - // Fallback for cases where we do not yet support allocating guest memory from the slab - // heap, such as for kernel memory regions. - return new T; - - case AllocationType::Guest: - T* obj = static_cast<T*>(AllocateImpl()); - if (obj != nullptr) { - new (obj) T(); - } - return obj; - } + T* obj = static_cast<T*>(BaseHeap::Allocate()); - UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type); - return nullptr; + if (obj != nullptr) [[likely]] { + std::construct_at(obj); + } + return obj; } - T* AllocateWithKernel(KernelCore& kernel) { - switch (allocation_type) { - case AllocationType::Host: - // Fallback for cases where we do not yet support allocating guest memory from the slab - // heap, such as for kernel memory regions. - return new T(kernel); + T* Allocate(KernelCore& kernel) { + T* obj = static_cast<T*>(BaseHeap::Allocate()); - case AllocationType::Guest: - T* obj = static_cast<T*>(AllocateImpl()); - if (obj != nullptr) { - new (obj) T(kernel); - } - return obj; + if (obj != nullptr) [[likely]] { + std::construct_at(obj, kernel); } - - UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type); - return nullptr; + return obj; } void Free(T* obj) { - switch (allocation_type) { - case AllocationType::Host: - // Fallback for cases where we do not yet support allocating guest memory from the slab - // heap, such as for kernel memory regions. - delete obj; - return; - - case AllocationType::Guest: - FreeImpl(obj); - return; - } - - UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type); + BaseHeap::Free(obj); } - constexpr std::size_t GetObjectIndex(const T* obj) const { - return GetObjectIndexImpl(obj); + size_t GetObjectIndex(const T* obj) const { + return BaseHeap::GetObjectIndex(obj); } - -private: - const AllocationType allocation_type; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_spin_lock.cpp b/src/core/hle/kernel/k_spin_lock.cpp index 4412aa4bb..6e16a1849 100644 --- a/src/core/hle/kernel/k_spin_lock.cpp +++ b/src/core/hle/kernel/k_spin_lock.cpp @@ -1,54 +1,20 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_spin_lock.h" -#if _MSC_VER -#include <intrin.h> -#if _M_AMD64 -#define __x86_64__ 1 -#endif -#if _M_ARM64 -#define __aarch64__ 1 -#endif -#else -#if __x86_64__ -#include <xmmintrin.h> -#endif -#endif - -namespace { - -void ThreadPause() { -#if __x86_64__ - _mm_pause(); -#elif __aarch64__ && _MSC_VER - __yield(); -#elif __aarch64__ - asm("yield"); -#endif -} - -} // namespace - namespace Kernel { void KSpinLock::Lock() { - while (lck.test_and_set(std::memory_order_acquire)) { - ThreadPause(); - } + lck.lock(); } void KSpinLock::Unlock() { - lck.clear(std::memory_order_release); + lck.unlock(); } bool KSpinLock::TryLock() { - if (lck.test_and_set(std::memory_order_acquire)) { - return false; - } - return true; + return lck.try_lock(); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_spin_lock.h b/src/core/hle/kernel/k_spin_lock.h index 4d87d006a..397a93d21 100644 --- a/src/core/hle/kernel/k_spin_lock.h +++ b/src/core/hle/kernel/k_spin_lock.h @@ -1,10 +1,9 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include <atomic> +#include <mutex> #include "core/hle/kernel/k_scoped_lock.h" @@ -25,7 +24,7 @@ public: [[nodiscard]] bool TryLock(); private: - std::atomic_flag lck = ATOMIC_FLAG_INIT; + std::mutex lck; }; // TODO(bunnei): Alias for now, in case we want to implement these accurately in the future. diff --git a/src/core/hle/kernel/k_synchronization_object.cpp b/src/core/hle/kernel/k_synchronization_object.cpp index e4c5eb74f..802dca046 100644 --- a/src/core/hle/kernel/k_synchronization_object.cpp +++ b/src/core/hle/kernel/k_synchronization_object.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "common/common_types.h" @@ -23,7 +22,7 @@ public: : KThreadQueueWithoutEndWait(kernel_), m_objects(o), m_nodes(n), m_count(c) {} void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object, - ResultCode wait_result) override { + Result wait_result) override { // Determine the sync index, and unlink all nodes. s32 sync_index = -1; for (auto i = 0; i < m_count; ++i) { @@ -46,8 +45,7 @@ public: KThreadQueue::EndWait(waiting_thread, wait_result); } - void CancelWait(KThread* waiting_thread, ResultCode wait_result, - bool cancel_timer_task) override { + void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // Remove all nodes from our list. for (auto i = 0; i < m_count; ++i) { m_objects[i]->UnlinkNode(std::addressof(m_nodes[i])); @@ -73,9 +71,9 @@ void KSynchronizationObject::Finalize() { KAutoObject::Finalize(); } -ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index, - KSynchronizationObject** objects, const s32 num_objects, - s64 timeout) { +Result KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index, + KSynchronizationObject** objects, const s32 num_objects, + s64 timeout) { // Allocate space on stack for thread nodes. std::vector<ThreadListNode> thread_nodes(num_objects); @@ -149,7 +147,7 @@ KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_) KSynchronizationObject::~KSynchronizationObject() = default; -void KSynchronizationObject::NotifyAvailable(ResultCode result) { +void KSynchronizationObject::NotifyAvailable(Result result) { KScopedSchedulerLock sl(kernel); // If we're not signaled, we've nothing to notify. diff --git a/src/core/hle/kernel/k_synchronization_object.h b/src/core/hle/kernel/k_synchronization_object.h index ec235437b..8d8122ab7 100644 --- a/src/core/hle/kernel/k_synchronization_object.h +++ b/src/core/hle/kernel/k_synchronization_object.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -25,9 +24,9 @@ public: KThread* thread{}; }; - [[nodiscard]] static ResultCode Wait(KernelCore& kernel, s32* out_index, - KSynchronizationObject** objects, const s32 num_objects, - s64 timeout); + [[nodiscard]] static Result Wait(KernelCore& kernel, s32* out_index, + KSynchronizationObject** objects, const s32 num_objects, + s64 timeout); void Finalize() override; @@ -73,7 +72,7 @@ protected: virtual void OnFinalizeSynchronizationObject() {} - void NotifyAvailable(ResultCode result); + void NotifyAvailable(Result result); void NotifyAvailable() { return this->NotifyAvailable(ResultSuccess); } diff --git a/src/core/hle/kernel/k_system_control.h b/src/core/hle/kernel/k_system_control.h index d755082c2..b8292e9d0 100644 --- a/src/core/hle/kernel/k_system_control.h +++ b/src/core/hle/kernel/k_system_control.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index de3ffe0c7..174afc80d 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <atomic> @@ -14,9 +13,7 @@ #include "common/common_types.h" #include "common/fiber.h" #include "common/logging/log.h" -#include "common/scope_exit.h" #include "common/settings.h" -#include "common/thread_queue_list.h" #include "core/core.h" #include "core/cpu_manager.h" #include "core/hardware_properties.h" @@ -33,7 +30,6 @@ #include "core/hle/kernel/k_worker_task_manager.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/svc_results.h" -#include "core/hle/kernel/time_manager.h" #include "core/hle/result.h" #include "core/memory.h" @@ -84,8 +80,7 @@ public: explicit ThreadQueueImplForKThreadSetProperty(KernelCore& kernel_, KThread::WaiterList* wl) : KThreadQueue(kernel_), m_wait_list(wl) {} - void CancelWait(KThread* waiting_thread, ResultCode wait_result, - bool cancel_timer_task) override { + void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override { // Remove the thread from the wait list. m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread)); @@ -103,8 +98,8 @@ KThread::KThread(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_}, activity_pause_lock{kernel_} {} KThread::~KThread() = default; -ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, s32 prio, - s32 virt_core, KProcess* owner, ThreadType type) { +Result KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, s32 prio, + s32 virt_core, KProcess* owner, ThreadType type) { // Assert parameters are valid. ASSERT((type == ThreadType::Main) || (type == ThreadType::Dummy) || (Svc::HighestThreadPriority <= prio && prio <= Svc::LowestThreadPriority)); @@ -137,7 +132,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s UNIMPLEMENTED(); break; default: - UNREACHABLE_MSG("KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type)); + ASSERT_MSG(false, "KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type)); break; } thread_type = type; @@ -202,6 +197,10 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s resource_limit_release_hint = false; cpu_time = 0; + // Set debug context. + stack_top = user_stack_top; + argument = arg; + // Clear our stack parameters. std::memset(static_cast<void*>(std::addressof(GetStackParameters())), 0, sizeof(StackParameters)); @@ -210,7 +209,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s if (owner != nullptr) { // Setup the TLS, if needed. if (type == ThreadType::User) { - tls_address = owner->CreateTLSRegion(); + R_TRY(owner->CreateThreadLocalRegion(std::addressof(tls_address))); } parent = owner; @@ -225,7 +224,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s // Setup the stack parameters. StackParameters& sp = GetStackParameters(); sp.cur_thread = this; - sp.disable_count = 0; + sp.disable_count = 1; SetInExceptionHandler(); // Set thread ID. @@ -245,46 +244,51 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s return ResultSuccess; } -ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg, - VAddr user_stack_top, s32 prio, s32 core, KProcess* owner, - ThreadType type, std::function<void(void*)>&& init_func, - void* init_func_parameter) { +Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg, + VAddr user_stack_top, s32 prio, s32 core, KProcess* owner, + ThreadType type, std::function<void()>&& init_func) { // Initialize the thread. R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type)); // Initialize emulation parameters. - thread->host_context = - std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter); + thread->host_context = std::make_shared<Common::Fiber>(std::move(init_func)); thread->is_single_core = !Settings::values.use_multi_core.GetValue(); return ResultSuccess; } -ResultCode KThread::InitializeDummyThread(KThread* thread) { - return thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy); +Result KThread::InitializeDummyThread(KThread* thread) { + // Initialize the thread. + R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy)); + + // Initialize emulation parameters. + thread->stack_parameters.disable_count = 0; + + return ResultSuccess; +} + +Result KThread::InitializeMainThread(Core::System& system, KThread* thread, s32 virt_core) { + return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main, + system.GetCpuManager().GetGuestActivateFunc()); } -ResultCode KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) { +Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) { return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main, - Core::CpuManager::GetIdleThreadStartFunc(), - system.GetCpuManager().GetStartFuncParamater()); + system.GetCpuManager().GetIdleThreadStartFunc()); } -ResultCode KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread, - KThreadFunction func, uintptr_t arg, - s32 virt_core) { +Result KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread, + KThreadFunction func, uintptr_t arg, s32 virt_core) { return InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, ThreadType::HighPriority, - Core::CpuManager::GetSuspendThreadStartFunc(), - system.GetCpuManager().GetStartFuncParamater()); + system.GetCpuManager().GetShutdownThreadStartFunc()); } -ResultCode KThread::InitializeUserThread(Core::System& system, KThread* thread, - KThreadFunction func, uintptr_t arg, VAddr user_stack_top, - s32 prio, s32 virt_core, KProcess* owner) { +Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThreadFunction func, + uintptr_t arg, VAddr user_stack_top, s32 prio, s32 virt_core, + KProcess* owner) { system.Kernel().GlobalSchedulerContext().AddThread(thread); return InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner, - ThreadType::User, Core::CpuManager::GetGuestThreadStartFunc(), - system.GetCpuManager().GetStartFuncParamater()); + ThreadType::User, system.GetCpuManager().GetGuestThreadFunc()); } void KThread::PostDestroy(uintptr_t arg) { @@ -305,7 +309,7 @@ void KThread::Finalize() { // If the thread has a local region, delete it. if (tls_address != 0) { - parent->FreeTLSRegion(tls_address); + ASSERT(parent->DeleteThreadLocalRegion(tls_address).IsSuccess()); } // Release any waiters. @@ -315,17 +319,26 @@ void KThread::Finalize() { auto it = waiter_list.begin(); while (it != waiter_list.end()) { - // Clear the lock owner - it->SetLockOwner(nullptr); + // Get the thread. + KThread* const waiter = std::addressof(*it); + + // The thread shouldn't be a kernel waiter. + ASSERT(!IsKernelAddressKey(waiter->GetAddressKey())); + + // Clear the lock owner. + waiter->SetLockOwner(nullptr); // Erase the waiter from our list. it = waiter_list.erase(it); // Cancel the thread's wait. - it->CancelWait(ResultInvalidState, true); + waiter->CancelWait(ResultInvalidState, true); } } + // Release host emulation members. + host_context.reset(); + // Perform inherited finalization. KSynchronizationObject::Finalize(); } @@ -379,7 +392,7 @@ void KThread::FinishTermination() { for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) { KThread* core_thread{}; do { - core_thread = kernel.Scheduler(i).GetCurrentThread(); + core_thread = kernel.Scheduler(i).GetSchedulerCurrentThread(); } while (core_thread == this); } } @@ -484,9 +497,7 @@ void KThread::Unpin() { // Resume any threads that began waiting on us while we were pinned. for (auto it = pinned_waiter_list.begin(); it != pinned_waiter_list.end(); ++it) { - if (it->GetState() == ThreadState::Waiting) { - it->SetState(ThreadState::Runnable); - } + it->EndWait(ResultSuccess); } } @@ -520,7 +531,7 @@ void KThread::ClearInterruptFlag() { memory.Write16(tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 0); } -ResultCode KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { +Result KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { KScopedSchedulerLock sl{kernel}; // Get the virtual mask. @@ -530,7 +541,7 @@ ResultCode KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { return ResultSuccess; } -ResultCode KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { +Result KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { KScopedSchedulerLock sl{kernel}; ASSERT(num_core_migration_disables >= 0); @@ -546,7 +557,7 @@ ResultCode KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_m return ResultSuccess; } -ResultCode KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) { +Result KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) { ASSERT(parent != nullptr); ASSERT(v_affinity_mask != 0); KScopedLightLock lk(activity_pause_lock); @@ -628,7 +639,7 @@ ResultCode KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) { s32 thread_core; for (thread_core = 0; thread_core < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++thread_core) { - if (kernel.Scheduler(thread_core).GetCurrentThread() == this) { + if (kernel.Scheduler(thread_core).GetSchedulerCurrentThread() == this) { thread_is_current = true; break; } @@ -723,10 +734,10 @@ 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.load(std::memory_order_relaxed); const auto new_state = static_cast<ThreadState>(this->GetSuspendFlags()) | (old_state & ThreadState::Mask); - thread_state = new_state; + thread_state.store(new_state, std::memory_order_relaxed); // Note the state change in scheduler. if (new_state != old_state) { @@ -738,14 +749,27 @@ void KThread::Continue() { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); // Clear our suspend flags in state. - const auto old_state = thread_state; - thread_state = old_state & ThreadState::Mask; + const ThreadState old_state = thread_state.load(std::memory_order_relaxed); + thread_state.store(old_state & ThreadState::Mask, std::memory_order_relaxed); // Note the state change in scheduler. KScheduler::OnThreadStateChanged(kernel, this, old_state); } -ResultCode KThread::SetActivity(Svc::ThreadActivity activity) { +void KThread::WaitUntilSuspended() { + // Make sure we have a suspend requested. + ASSERT(IsSuspendRequested()); + + // Loop until the thread is not executing on any core. + for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) { + KThread* core_thread{}; + do { + core_thread = kernel.Scheduler(i).GetSchedulerCurrentThread(); + } while (core_thread == this); + } +} + +Result KThread::SetActivity(Svc::ThreadActivity activity) { // Lock ourselves. KScopedLightLock lk(activity_pause_lock); @@ -806,7 +830,7 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) { // Check if the thread is currently running. // If it is, we'll need to retry. for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { - if (kernel.Scheduler(i).GetCurrentThread() == this) { + if (kernel.Scheduler(i).GetSchedulerCurrentThread() == this) { thread_is_current = true; break; } @@ -818,7 +842,7 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) { return ResultSuccess; } -ResultCode KThread::GetThreadContext3(std::vector<u8>& out) { +Result KThread::GetThreadContext3(std::vector<u8>& out) { // Lock ourselves. KScopedLightLock lk{activity_pause_lock}; @@ -868,6 +892,7 @@ void KThread::AddWaiterImpl(KThread* thread) { // Keep track of how many kernel waiters we have. if (IsKernelAddressKey(thread->GetAddressKey())) { ASSERT((num_kernel_waiters++) >= 0); + KScheduler::SetSchedulerUpdateNeeded(kernel); } // Insert the waiter. @@ -881,6 +906,7 @@ void KThread::RemoveWaiterImpl(KThread* thread) { // Keep track of how many kernel waiters we have. if (IsKernelAddressKey(thread->GetAddressKey())) { ASSERT((num_kernel_waiters--) > 0); + KScheduler::SetSchedulerUpdateNeeded(kernel); } // Remove the waiter. @@ -956,6 +982,7 @@ KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) { // Keep track of how many kernel waiters we have. if (IsKernelAddressKey(thread->GetAddressKey())) { ASSERT((num_kernel_waiters--) > 0); + KScheduler::SetSchedulerUpdateNeeded(kernel); } it = waiter_list.erase(it); @@ -983,7 +1010,7 @@ KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) { return next_lock_owner; } -ResultCode KThread::Run() { +Result KThread::Run() { while (true) { KScopedSchedulerLock lk{kernel}; @@ -1011,8 +1038,6 @@ ResultCode KThread::Run() { // Set our state and finish. SetState(ThreadState::Runnable); - DisableDispatch(); - return ResultSuccess; } } @@ -1044,9 +1069,11 @@ void KThread::Exit() { // Register the thread as a work task. KWorkerTaskManager::AddTask(kernel, KWorkerTaskManager::WorkerType::Exit, this); } + + UNREACHABLE_MSG("KThread::Exit() would return"); } -ResultCode KThread::Sleep(s64 timeout) { +Result KThread::Sleep(s64 timeout) { ASSERT(!kernel.GlobalSchedulerContext().IsLocked()); ASSERT(this == GetCurrentThreadPointer(kernel)); ASSERT(timeout > 0); @@ -1079,17 +1106,12 @@ void KThread::IfDummyThreadTryWait() { return; } - // Block until we can grab the lock. - KScopedSpinLock lk{dummy_wait_lock}; -} - -void KThread::IfDummyThreadBeginWait() { - if (!IsDummyThread()) { - return; - } + ASSERT(!kernel.IsPhantomModeForSingleCore()); - // Ensure the thread will block when IfDummyThreadTryWait is called. - dummy_wait_lock.Lock(); + // Block until we are no longer waiting. + std::unique_lock lk(dummy_wait_lock); + dummy_wait_cv.wait( + lk, [&] { return GetState() != ThreadState::Waiting || kernel.IsShuttingDown(); }); } void KThread::IfDummyThreadEndWait() { @@ -1097,8 +1119,8 @@ void KThread::IfDummyThreadEndWait() { return; } - // Ensure the thread will no longer block. - dummy_wait_lock.Unlock(); + // Wake up the waiting thread. + dummy_wait_cv.notify_one(); } void KThread::BeginWait(KThreadQueue* queue) { @@ -1107,12 +1129,9 @@ void KThread::BeginWait(KThreadQueue* queue) { // Set our wait queue. wait_queue = queue; - - // Special case for dummy threads to ensure they block. - IfDummyThreadBeginWait(); } -void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) { +void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, Result wait_result_) { // Lock the scheduler. KScopedSchedulerLock sl(kernel); @@ -1122,7 +1141,7 @@ void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCod } } -void KThread::EndWait(ResultCode wait_result_) { +void KThread::EndWait(Result wait_result_) { // Lock the scheduler. KScopedSchedulerLock sl(kernel); @@ -1141,7 +1160,7 @@ void KThread::EndWait(ResultCode wait_result_) { } } -void KThread::CancelWait(ResultCode wait_result_, bool cancel_timer_task) { +void KThread::CancelWait(Result wait_result_, bool cancel_timer_task) { // Lock the scheduler. KScopedSchedulerLock sl(kernel); @@ -1158,10 +1177,11 @@ void KThread::SetState(ThreadState state) { SetMutexWaitAddressForDebugging({}); SetWaitReasonForDebugging({}); - const ThreadState old_state = thread_state; - thread_state = - static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)); - if (thread_state != old_state) { + const ThreadState old_state = thread_state.load(std::memory_order_relaxed); + thread_state.store( + static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)), + std::memory_order_relaxed); + if (thread_state.load(std::memory_order_relaxed) != old_state) { KScheduler::OnThreadStateChanged(kernel, this, old_state); } } @@ -1170,6 +1190,10 @@ std::shared_ptr<Common::Fiber>& KThread::GetHostContext() { return host_context; } +void SetCurrentThread(KernelCore& kernel, KThread* thread) { + kernel.SetCurrentEmuThread(thread); +} + KThread* GetCurrentThreadPointer(KernelCore& kernel) { return kernel.GetCurrentEmuThread(); } @@ -1188,16 +1212,13 @@ KScopedDisableDispatch::~KScopedDisableDispatch() { return; } - // Skip the reschedule if single-core, as dispatch tracking is disabled here. - if (!Settings::values.use_multi_core.GetValue()) { - return; - } - if (GetCurrentThread(kernel).GetDisableDispatchCount() <= 1) { - auto scheduler = kernel.CurrentScheduler(); + auto* scheduler = kernel.CurrentScheduler(); - if (scheduler) { + if (scheduler && !kernel.IsPhantomModeForSingleCore()) { scheduler->RescheduleCurrentCore(); + } else { + KScheduler::RescheduleCurrentHLEThread(kernel); } } else { GetCurrentThread(kernel).EnableDispatch(); diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index d058db62c..9ee20208e 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -1,10 +1,12 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include <array> +#include <atomic> +#include <condition_variable> +#include <mutex> #include <span> #include <string> #include <utility> @@ -14,6 +16,7 @@ #include "common/common_types.h" #include "common/intrusive_red_black_tree.h" +#include "common/spin_lock.h" #include "core/arm/arm_interface.h" #include "core/hle/kernel/k_affinity_mask.h" #include "core/hle/kernel/k_light_lock.h" @@ -97,6 +100,13 @@ enum class ThreadWaitReasonForDebugging : u32 { Suspended, ///< Thread is waiting due to process suspension }; +enum class StepState : u32 { + NotStepping, ///< Thread is not currently stepping + StepPending, ///< Thread will step when next scheduled + StepPerformed, ///< Thread has stepped, waiting to be scheduled again +}; + +void SetCurrentThread(KernelCore& kernel, KThread* thread); [[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel); [[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel); [[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel); @@ -166,7 +176,7 @@ public: void SetBasePriority(s32 value); - [[nodiscard]] ResultCode Run(); + [[nodiscard]] Result Run(); void Exit(); @@ -198,6 +208,8 @@ public: void Continue(); + void WaitUntilSuspended(); + constexpr void SetSyncedIndex(s32 index) { synced_index = index; } @@ -206,11 +218,11 @@ public: return synced_index; } - constexpr void SetWaitResult(ResultCode wait_res) { + constexpr void SetWaitResult(Result wait_res) { wait_result = wait_res; } - [[nodiscard]] constexpr ResultCode GetWaitResult() const { + [[nodiscard]] constexpr Result GetWaitResult() const { return wait_result; } @@ -255,15 +267,23 @@ public: [[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext(); [[nodiscard]] ThreadState GetState() const { - return thread_state & ThreadState::Mask; + return thread_state.load(std::memory_order_relaxed) & ThreadState::Mask; } [[nodiscard]] ThreadState GetRawState() const { - return thread_state; + return thread_state.load(std::memory_order_relaxed); } void SetState(ThreadState state); + [[nodiscard]] StepState GetStepState() const { + return step_state; + } + + void SetStepState(StepState state) { + step_state = state; + } + [[nodiscard]] s64 GetLastScheduledTick() const { return last_scheduled_tick; } @@ -325,15 +345,15 @@ public: return physical_affinity_mask; } - [[nodiscard]] ResultCode GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask); + [[nodiscard]] Result GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask); - [[nodiscard]] ResultCode GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask); + [[nodiscard]] Result GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask); - [[nodiscard]] ResultCode SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask); + [[nodiscard]] Result SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask); - [[nodiscard]] ResultCode SetActivity(Svc::ThreadActivity activity); + [[nodiscard]] Result SetActivity(Svc::ThreadActivity activity); - [[nodiscard]] ResultCode Sleep(s64 timeout); + [[nodiscard]] Result Sleep(s64 timeout); [[nodiscard]] s64 GetYieldScheduleCount() const { return schedule_count; @@ -391,20 +411,22 @@ public: static void PostDestroy(uintptr_t arg); - [[nodiscard]] static ResultCode InitializeDummyThread(KThread* thread); + [[nodiscard]] static Result InitializeDummyThread(KThread* thread); + + [[nodiscard]] static Result InitializeMainThread(Core::System& system, KThread* thread, + s32 virt_core); - [[nodiscard]] static ResultCode InitializeIdleThread(Core::System& system, KThread* thread, - s32 virt_core); + [[nodiscard]] static Result InitializeIdleThread(Core::System& system, KThread* thread, + s32 virt_core); - [[nodiscard]] static ResultCode InitializeHighPriorityThread(Core::System& system, - KThread* thread, - KThreadFunction func, - uintptr_t arg, s32 virt_core); + [[nodiscard]] static Result InitializeHighPriorityThread(Core::System& system, KThread* thread, + KThreadFunction func, uintptr_t arg, + s32 virt_core); - [[nodiscard]] static ResultCode InitializeUserThread(Core::System& system, KThread* thread, - KThreadFunction func, uintptr_t arg, - VAddr user_stack_top, s32 prio, - s32 virt_core, KProcess* owner); + [[nodiscard]] static Result InitializeUserThread(Core::System& system, KThread* thread, + KThreadFunction func, uintptr_t arg, + VAddr user_stack_top, s32 prio, s32 virt_core, + KProcess* owner); public: struct StackParameters { @@ -461,39 +483,16 @@ public: return per_core_priority_queue_entry[core]; } - [[nodiscard]] bool IsKernelThread() const { - return GetActiveCore() == 3; - } - - [[nodiscard]] bool IsDispatchTrackingDisabled() const { - return is_single_core || IsKernelThread(); - } - [[nodiscard]] s32 GetDisableDispatchCount() const { - if (IsDispatchTrackingDisabled()) { - // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch. - return 1; - } - return this->GetStackParameters().disable_count; } void DisableDispatch() { - if (IsDispatchTrackingDisabled()) { - // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch. - return; - } - ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0); this->GetStackParameters().disable_count++; } void EnableDispatch() { - if (IsDispatchTrackingDisabled()) { - // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch. - return; - } - ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0); this->GetStackParameters().disable_count--; } @@ -590,7 +589,7 @@ public: void RemoveWaiter(KThread* thread); - [[nodiscard]] ResultCode GetThreadContext3(std::vector<u8>& out); + [[nodiscard]] Result GetThreadContext3(std::vector<u8>& out); [[nodiscard]] KThread* RemoveWaiterByKey(s32* out_num_waiters, VAddr key); @@ -616,9 +615,9 @@ public: } void BeginWait(KThreadQueue* queue); - void NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_); - void EndWait(ResultCode wait_result_); - void CancelWait(ResultCode wait_result_, bool cancel_timer_task); + void NotifyAvailable(KSynchronizationObject* signaled_object, Result wait_result_); + void EndWait(Result wait_result_); + void CancelWait(Result wait_result_, bool cancel_timer_task); [[nodiscard]] bool HasWaiters() const { return !waiter_list.empty(); @@ -641,9 +640,16 @@ public: // blocking as needed. void IfDummyThreadTryWait(); - void IfDummyThreadBeginWait(); void IfDummyThreadEndWait(); + [[nodiscard]] uintptr_t GetArgument() const { + return argument; + } + + [[nodiscard]] VAddr GetUserStackTop() const { + return stack_top; + } + private: static constexpr size_t PriorityInheritanceCountMax = 10; union SyncObjectBuffer { @@ -656,7 +662,7 @@ private: static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles)); struct ConditionVariableComparator { - struct LightCompareType { + struct RedBlackKeyType { u64 cv_key{}; s32 priority{}; @@ -672,8 +678,8 @@ private: template <typename T> requires( std::same_as<T, KThread> || - std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs, - const KThread& rhs) { + std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs, + const KThread& rhs) { const u64 l_key = lhs.GetConditionVariableKey(); const u64 r_key = rhs.GetConditionVariableKey(); @@ -697,14 +703,13 @@ private: void FinishTermination(); - [[nodiscard]] ResultCode Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, - s32 prio, s32 virt_core, KProcess* owner, ThreadType type); + [[nodiscard]] Result Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, + s32 prio, s32 virt_core, KProcess* owner, ThreadType type); - [[nodiscard]] static ResultCode InitializeThread(KThread* thread, KThreadFunction func, - uintptr_t arg, VAddr user_stack_top, s32 prio, - s32 core, KProcess* owner, ThreadType type, - std::function<void(void*)>&& init_func, - void* init_func_parameter); + [[nodiscard]] static Result InitializeThread(KThread* thread, KThreadFunction func, + uintptr_t arg, VAddr user_stack_top, s32 prio, + s32 core, KProcess* owner, ThreadType type, + std::function<void()>&& init_func); static void RestorePriority(KernelCore& kernel_ctx, KThread* thread); @@ -741,7 +746,7 @@ private: u32 suspend_request_flags{}; u32 suspend_allowed_flags{}; s32 synced_index{}; - ResultCode wait_result{ResultSuccess}; + Result wait_result{ResultSuccess}; s32 base_priority{}; s32 physical_ideal_core_id{}; s32 virtual_ideal_core_id{}; @@ -751,7 +756,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{}; @@ -761,18 +766,22 @@ private: s8 priority_inheritance_count{}; bool resource_limit_release_hint{}; StackParameters stack_parameters{}; - KSpinLock context_guard{}; - KSpinLock dummy_wait_lock{}; + Common::SpinLock context_guard{}; // For emulation std::shared_ptr<Common::Fiber> host_context{}; bool is_single_core{}; ThreadType thread_type{}; + StepState step_state{}; + std::mutex dummy_wait_lock; + std::condition_variable dummy_wait_cv; // For debugging std::vector<KSynchronizationObject*> wait_objects_for_debugging; VAddr mutex_wait_address_for_debugging{}; ThreadWaitReasonForDebugging wait_reason_for_debugging{}; + uintptr_t argument; + VAddr stack_top; public: using ConditionVariableThreadTreeType = ConditionVariableThreadTree; diff --git a/src/core/hle/kernel/k_thread_local_page.cpp b/src/core/hle/kernel/k_thread_local_page.cpp new file mode 100644 index 000000000..563560114 --- /dev/null +++ b/src/core/hle/kernel/k_thread_local_page.cpp @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/scope_exit.h" +#include "core/core.h" + +#include "core/hle/kernel/k_memory_block.h" +#include "core/hle/kernel/k_page_buffer.h" +#include "core/hle/kernel/k_page_table.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_thread_local_page.h" +#include "core/hle/kernel/kernel.h" + +namespace Kernel { + +Result KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) { + // Set that this process owns us. + m_owner = process; + m_kernel = &kernel; + + // Allocate a new page. + KPageBuffer* page_buf = KPageBuffer::Allocate(kernel); + R_UNLESS(page_buf != nullptr, ResultOutOfMemory); + auto page_buf_guard = SCOPE_GUARD({ KPageBuffer::Free(kernel, page_buf); }); + + // Map the address in. + const auto phys_addr = kernel.System().DeviceMemory().GetPhysicalAddr(page_buf); + R_TRY(m_owner->PageTable().MapPages(std::addressof(m_virt_addr), 1, PageSize, phys_addr, + KMemoryState::ThreadLocal, + KMemoryPermission::UserReadWrite)); + + // We succeeded. + page_buf_guard.Cancel(); + + return ResultSuccess; +} + +Result KThreadLocalPage::Finalize() { + // Get the physical address of the page. + const PAddr phys_addr = m_owner->PageTable().GetPhysicalAddr(m_virt_addr); + ASSERT(phys_addr); + + // Unmap the page. + R_TRY(m_owner->PageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal)); + + // Free the page. + KPageBuffer::Free(*m_kernel, KPageBuffer::FromPhysicalAddress(m_kernel->System(), phys_addr)); + + return ResultSuccess; +} + +VAddr KThreadLocalPage::Reserve() { + for (size_t i = 0; i < m_is_region_free.size(); i++) { + if (m_is_region_free[i]) { + m_is_region_free[i] = false; + return this->GetRegionAddress(i); + } + } + + return 0; +} + +void KThreadLocalPage::Release(VAddr addr) { + m_is_region_free[this->GetRegionIndex(addr)] = true; +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_thread_local_page.h b/src/core/hle/kernel/k_thread_local_page.h new file mode 100644 index 000000000..0a7f22680 --- /dev/null +++ b/src/core/hle/kernel/k_thread_local_page.h @@ -0,0 +1,110 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <algorithm> +#include <array> + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/common_types.h" +#include "common/intrusive_red_black_tree.h" +#include "core/hle/kernel/memory_types.h" +#include "core/hle/kernel/slab_helpers.h" +#include "core/hle/result.h" + +namespace Kernel { + +class KernelCore; +class KProcess; + +class KThreadLocalPage final : public Common::IntrusiveRedBlackTreeBaseNode<KThreadLocalPage>, + public KSlabAllocated<KThreadLocalPage> { +public: + static constexpr size_t RegionsPerPage = PageSize / Svc::ThreadLocalRegionSize; + static_assert(RegionsPerPage > 0); + +public: + constexpr explicit KThreadLocalPage(VAddr addr = {}) : m_virt_addr(addr) { + m_is_region_free.fill(true); + } + + constexpr VAddr GetAddress() const { + return m_virt_addr; + } + + Result Initialize(KernelCore& kernel, KProcess* process); + Result Finalize(); + + VAddr Reserve(); + void Release(VAddr addr); + + bool IsAllUsed() const { + return std::ranges::all_of(m_is_region_free.begin(), m_is_region_free.end(), + [](bool is_free) { return !is_free; }); + } + + bool IsAllFree() const { + return std::ranges::all_of(m_is_region_free.begin(), m_is_region_free.end(), + [](bool is_free) { return is_free; }); + } + + bool IsAnyUsed() const { + return !this->IsAllFree(); + } + + bool IsAnyFree() const { + return !this->IsAllUsed(); + } + +public: + using RedBlackKeyType = VAddr; + + static constexpr RedBlackKeyType GetRedBlackKey(const RedBlackKeyType& v) { + return v; + } + static constexpr RedBlackKeyType GetRedBlackKey(const KThreadLocalPage& v) { + return v.GetAddress(); + } + + template <typename T> + requires(std::same_as<T, KThreadLocalPage> || + std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs, + const KThreadLocalPage& + rhs) { + const VAddr lval = GetRedBlackKey(lhs); + const VAddr rval = GetRedBlackKey(rhs); + + if (lval < rval) { + return -1; + } else if (lval == rval) { + return 0; + } else { + return 1; + } + } + +private: + constexpr VAddr GetRegionAddress(size_t i) const { + return this->GetAddress() + i * Svc::ThreadLocalRegionSize; + } + + constexpr bool Contains(VAddr addr) const { + return this->GetAddress() <= addr && addr < this->GetAddress() + PageSize; + } + + constexpr size_t GetRegionIndex(VAddr addr) const { + ASSERT(Common::IsAligned(addr, Svc::ThreadLocalRegionSize)); + ASSERT(this->Contains(addr)); + return (addr - this->GetAddress()) / Svc::ThreadLocalRegionSize; + } + +private: + VAddr m_virt_addr{}; + KProcess* m_owner{}; + KernelCore* m_kernel{}; + std::array<bool, RegionsPerPage> m_is_region_free{}; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_thread_queue.cpp b/src/core/hle/kernel/k_thread_queue.cpp index d5248b547..9f4e081ba 100644 --- a/src/core/hle/kernel/k_thread_queue.cpp +++ b/src/core/hle/kernel/k_thread_queue.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_thread_queue.h" #include "core/hle/kernel/kernel.h" @@ -10,9 +9,9 @@ namespace Kernel { void KThreadQueue::NotifyAvailable([[maybe_unused]] KThread* waiting_thread, [[maybe_unused]] KSynchronizationObject* signaled_object, - [[maybe_unused]] ResultCode wait_result) {} + [[maybe_unused]] Result wait_result) {} -void KThreadQueue::EndWait(KThread* waiting_thread, ResultCode wait_result) { +void KThreadQueue::EndWait(KThread* waiting_thread, Result wait_result) { // Set the thread's wait result. waiting_thread->SetWaitResult(wait_result); @@ -26,8 +25,7 @@ void KThreadQueue::EndWait(KThread* waiting_thread, ResultCode wait_result) { kernel.TimeManager().UnscheduleTimeEvent(waiting_thread); } -void KThreadQueue::CancelWait(KThread* waiting_thread, ResultCode wait_result, - bool cancel_timer_task) { +void KThreadQueue::CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) { // Set the thread's wait result. waiting_thread->SetWaitResult(wait_result); @@ -44,6 +42,6 @@ void KThreadQueue::CancelWait(KThread* waiting_thread, ResultCode wait_result, } void KThreadQueueWithoutEndWait::EndWait([[maybe_unused]] KThread* waiting_thread, - [[maybe_unused]] ResultCode wait_result) {} + [[maybe_unused]] Result wait_result) {} } // namespace Kernel diff --git a/src/core/hle/kernel/k_thread_queue.h b/src/core/hle/kernel/k_thread_queue.h index ccb718e49..8d76ece81 100644 --- a/src/core/hle/kernel/k_thread_queue.h +++ b/src/core/hle/kernel/k_thread_queue.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -15,10 +14,9 @@ public: virtual ~KThreadQueue() = default; virtual void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object, - ResultCode wait_result); - virtual void EndWait(KThread* waiting_thread, ResultCode wait_result); - virtual void CancelWait(KThread* waiting_thread, ResultCode wait_result, - bool cancel_timer_task); + Result wait_result); + virtual void EndWait(KThread* waiting_thread, Result wait_result); + virtual void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task); private: KernelCore& kernel; @@ -29,7 +27,7 @@ class KThreadQueueWithoutEndWait : public KThreadQueue { public: explicit KThreadQueueWithoutEndWait(KernelCore& kernel_) : KThreadQueue(kernel_) {} - void EndWait(KThread* waiting_thread, ResultCode wait_result) override final; + void EndWait(KThread* waiting_thread, Result wait_result) override final; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_trace.h b/src/core/hle/kernel/k_trace.h index d3fed1888..d61af4830 100644 --- a/src/core/hle/kernel/k_trace.h +++ b/src/core/hle/kernel/k_trace.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp index 1732925c9..b0320eb73 100644 --- a/src/core/hle/kernel/k_transfer_memory.cpp +++ b/src/core/hle/kernel/k_transfer_memory.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_resource_limit.h" @@ -14,8 +13,8 @@ KTransferMemory::KTransferMemory(KernelCore& kernel_) KTransferMemory::~KTransferMemory() = default; -ResultCode KTransferMemory::Initialize(VAddr address_, std::size_t size_, - Svc::MemoryPermission owner_perm_) { +Result KTransferMemory::Initialize(VAddr address_, std::size_t size_, + Svc::MemoryPermission owner_perm_) { // Set members. owner = kernel.CurrentProcess(); diff --git a/src/core/hle/kernel/k_transfer_memory.h b/src/core/hle/kernel/k_transfer_memory.h index cb7521823..85d508ee7 100644 --- a/src/core/hle/kernel/k_transfer_memory.h +++ b/src/core/hle/kernel/k_transfer_memory.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,7 +7,7 @@ #include "core/hle/kernel/svc_types.h" #include "core/hle/result.h" -union ResultCode; +union Result; namespace Core::Memory { class Memory; @@ -27,7 +26,7 @@ public: explicit KTransferMemory(KernelCore& kernel_); ~KTransferMemory() override; - ResultCode Initialize(VAddr address_, std::size_t size_, Svc::MemoryPermission owner_perm_); + Result Initialize(VAddr address_, std::size_t size_, Svc::MemoryPermission owner_perm_); void Finalize() override; diff --git a/src/core/hle/kernel/k_worker_task.h b/src/core/hle/kernel/k_worker_task.h index b7794c6a8..ef591d831 100644 --- a/src/core/hle/kernel/k_worker_task.h +++ b/src/core/hle/kernel/k_worker_task.h @@ -1,6 +1,5 @@ -// Copyright 2022 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_worker_task_manager.cpp b/src/core/hle/kernel/k_worker_task_manager.cpp index 785e08111..04042bf8f 100644 --- a/src/core/hle/kernel/k_worker_task_manager.cpp +++ b/src/core/hle/kernel/k_worker_task_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2022 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "core/hle/kernel/k_process.h" @@ -24,7 +23,7 @@ void KWorkerTask::DoWorkerTask() { } } -KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "yuzu:KWorkerTaskManager") {} +KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "KWorkerTaskManager") {} void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) { ASSERT(type <= WorkerType::Count); diff --git a/src/core/hle/kernel/k_worker_task_manager.h b/src/core/hle/kernel/k_worker_task_manager.h index 43d1bfcec..f6618883e 100644 --- a/src/core/hle/kernel/k_worker_task_manager.h +++ b/src/core/hle/kernel/k_worker_task_manager.h @@ -1,6 +1,5 @@ -// Copyright 2022 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/k_writable_event.cpp b/src/core/hle/kernel/k_writable_event.cpp index bdb1db6d5..ff88c5acd 100644 --- a/src/core/hle/kernel/k_writable_event.cpp +++ b/src/core/hle/kernel/k_writable_event.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_readable_event.h" @@ -19,11 +18,11 @@ void KWritableEvent::Initialize(KEvent* parent_event_, std::string&& name_) { parent->GetReadableEvent().Open(); } -ResultCode KWritableEvent::Signal() { +Result KWritableEvent::Signal() { return parent->GetReadableEvent().Signal(); } -ResultCode KWritableEvent::Clear() { +Result KWritableEvent::Clear() { return parent->GetReadableEvent().Clear(); } diff --git a/src/core/hle/kernel/k_writable_event.h b/src/core/hle/kernel/k_writable_event.h index 858d982c4..3fd0c7d0a 100644 --- a/src/core/hle/kernel/k_writable_event.h +++ b/src/core/hle/kernel/k_writable_event.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -26,8 +25,8 @@ public: static void PostDestroy([[maybe_unused]] uintptr_t arg) {} void Initialize(KEvent* parent_, std::string&& name_); - ResultCode Signal(); - ResultCode Clear(); + Result Signal(); + Result Clear(); KEvent* GetParent() const { return parent; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 49c0714ed..9251f29ad 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> #include <atomic> @@ -18,13 +17,10 @@ #include "common/thread.h" #include "common/thread_worker.h" #include "core/arm/arm_interface.h" -#include "core/arm/cpu_interrupt_handler.h" #include "core/arm/exclusive_monitor.h" #include "core/core.h" #include "core/core_timing.h" -#include "core/core_timing_util.h" #include "core/cpu_manager.h" -#include "core/device_memory.h" #include "core/hardware_properties.h" #include "core/hle/kernel/init/init_slab_setup.h" #include "core/hle/kernel/k_client_port.h" @@ -35,7 +31,6 @@ #include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_shared_memory.h" -#include "core/hle/kernel/k_slab_heap.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_worker_task_manager.h" #include "core/hle/kernel/kernel.h" @@ -52,42 +47,41 @@ namespace Kernel { struct KernelCore::Impl { explicit Impl(Core::System& system_, KernelCore& kernel_) - : time_manager{system_}, object_list_container{kernel_}, - service_threads_manager{1, "yuzu:ServiceThreadsManager"}, system{system_} {} + : time_manager{system_}, + service_threads_manager{1, "ServiceThreadsManager"}, system{system_} {} void SetMulticore(bool is_multi) { is_multicore = is_multi; } void Initialize(KernelCore& kernel) { + global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel); global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel); global_handle_table->Initialize(KHandleTable::MaxTableSize); + default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread"); is_phantom_mode_for_singlecore = false; - InitializePhysicalCores(); - // Derive the initial memory layout from the emulated board Init::InitializeSlabResourceCounts(kernel); - KMemoryLayout memory_layout; - DeriveInitialMemoryLayout(memory_layout); - Init::InitializeSlabHeaps(system, memory_layout); + DeriveInitialMemoryLayout(); + Init::InitializeSlabHeaps(system, *memory_layout); // Initialize kernel memory and resources. - InitializeSystemResourceLimit(kernel, system.CoreTiming(), memory_layout); - InitializeMemoryLayout(memory_layout); - InitializePageSlab(); - InitializeSchedulers(); - InitializeSuspendThreads(); + InitializeSystemResourceLimit(kernel, system.CoreTiming()); + InitializeMemoryLayout(); + Init::InitializeKPageBufferSlabHeap(system); + InitializeShutdownThreads(); InitializePreemption(kernel); + InitializePhysicalCores(); RegisterHostThread(); } 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); } } @@ -98,39 +92,16 @@ struct KernelCore::Impl { process_list.clear(); - // Close all open server ports. - std::unordered_set<KServerPort*> server_ports_; - { - std::lock_guard lk(server_ports_lock); - server_ports_ = server_ports; - server_ports.clear(); - } - for (auto* server_port : server_ports_) { - server_port->Close(); - } - // Close all open server sessions. - std::unordered_set<KServerSession*> server_sessions_; - { - std::lock_guard lk(server_sessions_lock); - server_sessions_ = server_sessions; - server_sessions.clear(); - } - for (auto* server_session : server_sessions_) { - server_session->Close(); - } - - // Ensure that the object list container is finalized and properly shutdown. - object_list_container.Finalize(); - - // Ensures all service threads gracefully shutdown. - ClearServiceThreads(); + CloseServices(); next_object_id = 0; next_kernel_process_id = KProcess::InitialKIPIDMin; next_user_process_id = KProcess::ProcessIDMin; next_thread_id = 1; - cores.clear(); + for (auto& core : cores) { + core = nullptr; + } global_handle_table->Finalize(); global_handle_table.reset(); @@ -155,15 +126,15 @@ struct KernelCore::Impl { CleanupObject(font_shared_mem); CleanupObject(irs_shared_mem); CleanupObject(time_shared_mem); + CleanupObject(hidbus_shared_mem); CleanupObject(system_resource_limit); for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { - if (suspend_threads[core_id]) { - suspend_threads[core_id]->Close(); - suspend_threads[core_id] = nullptr; + if (shutdown_threads[core_id]) { + shutdown_threads[core_id]->Close(); + shutdown_threads[core_id] = nullptr; } - schedulers[core_id]->Finalize(); schedulers[core_id].reset(); } @@ -172,7 +143,7 @@ struct KernelCore::Impl { // Close kernel objects that were not freed on shutdown { - std::lock_guard lk(registered_in_use_objects_lock); + std::scoped_lock lk{registered_in_use_objects_lock}; if (registered_in_use_objects.size()) { for (auto& object : registered_in_use_objects) { object->Close(); @@ -183,48 +154,76 @@ 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; } // Track kernel objects that were not freed on shutdown { - std::lock_guard lk(registered_objects_lock); + std::scoped_lock lk{registered_objects_lock}; if (registered_objects.size()) { - LOG_WARNING(Kernel, "{} kernel objects were dangling on shutdown!", - registered_objects.size()); + LOG_DEBUG(Kernel, "{} kernel objects were dangling on shutdown!", + registered_objects.size()); registered_objects.clear(); } } + + // Ensure that the object list container is finalized and properly shutdown. + global_object_list_container->Finalize(); + global_object_list_container.reset(); + } + + void CloseServices() { + // Close all open server sessions and ports. + std::unordered_set<KAutoObject*> server_objects_; + { + std::scoped_lock lk(server_objects_lock); + server_objects_ = server_objects; + server_objects.clear(); + } + for (auto* server_object : server_objects_) { + server_object->Close(); + } + + // Ensures all service threads gracefully shutdown. + ClearServiceThreads(); } void InitializePhysicalCores() { exclusive_monitor = Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES); for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { - schedulers[i] = std::make_unique<Kernel::KScheduler>(system, i); - cores.emplace_back(i, system, *schedulers[i], interrupts); - } - } + const s32 core{static_cast<s32>(i)}; - void InitializeSchedulers() { - for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { - cores[i].Scheduler().Initialize(); + schedulers[i] = std::make_unique<Kernel::KScheduler>(system.Kernel()); + cores[i] = std::make_unique<Kernel::PhysicalCore>(i, system, *schedulers[i]); + + auto* main_thread{Kernel::KThread::Create(system.Kernel())}; + main_thread->SetName(fmt::format("MainThread:{}", core)); + main_thread->SetCurrentCore(core); + ASSERT(Kernel::KThread::InitializeMainThread(system, main_thread, core).IsSuccess()); + + auto* idle_thread{Kernel::KThread::Create(system.Kernel())}; + idle_thread->SetCurrentCore(core); + ASSERT(Kernel::KThread::InitializeIdleThread(system, idle_thread, core).IsSuccess()); + + schedulers[i]->Initialize(main_thread, idle_thread, core); } } // Creates the default system resource limit void InitializeSystemResourceLimit(KernelCore& kernel, - const Core::Timing::CoreTiming& core_timing, - const KMemoryLayout& memory_layout) { + const Core::Timing::CoreTiming& core_timing) { system_resource_limit = KResourceLimit::Create(system.Kernel()); system_resource_limit->Initialize(&core_timing); - const auto [total_size, kernel_size] = memory_layout.GetTotalAndKernelMemorySizes(); + const auto sizes{memory_layout->GetTotalAndKernelMemorySizes()}; + const auto total_size{sizes.first}; + const auto kernel_size{sizes.second}; // If setting the default system values fails, then something seriously wrong has occurred. ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size) @@ -240,37 +239,31 @@ struct KernelCore::Impl { constexpr u64 secure_applet_memory_size{4_MiB}; ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory, secure_applet_memory_size)); - - // This memory seems to be reserved on hardware, but is not reserved/used by yuzu. - // Likely Horizon OS reserved memory - // TODO(ameerj): Derive the memory rather than hardcode it. - constexpr u64 unknown_reserved_memory{0x2f896000}; - ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory, - unknown_reserved_memory)); } void InitializePreemption(KernelCore& kernel) { preemption_event = Core::Timing::CreateEvent( - "PreemptionCallback", [this, &kernel](std::uintptr_t, std::chrono::nanoseconds) { + "PreemptionCallback", + [this, &kernel](std::uintptr_t, s64 time, + std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { { KScopedSchedulerLock lock(kernel); global_scheduler_context->PreemptThreads(); } - const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)}; - system.CoreTiming().ScheduleEvent(time_interval, preemption_event); + return std::nullopt; }); const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)}; - system.CoreTiming().ScheduleEvent(time_interval, preemption_event); + system.CoreTiming().ScheduleLoopingEvent(time_interval, time_interval, preemption_event); } - void InitializeSuspendThreads() { + void InitializeShutdownThreads() { for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { - suspend_threads[core_id] = KThread::Create(system.Kernel()); - ASSERT(KThread::InitializeHighPriorityThread(system, suspend_threads[core_id], {}, {}, + shutdown_threads[core_id] = KThread::Create(system.Kernel()); + ASSERT(KThread::InitializeHighPriorityThread(system, shutdown_threads[core_id], {}, {}, core_id) .IsSuccess()); - suspend_threads[core_id]->SetName(fmt::format("SuspendThread:{}", core_id)); + shutdown_threads[core_id]->SetName(fmt::format("SuspendThread:{}", core_id)); } } @@ -300,15 +293,16 @@ struct KernelCore::Impl { // Gets the dummy KThread for the caller, allocating a new one if this is the first time KThread* GetHostDummyThread() { - auto make_thread = [this]() { - KThread* thread = KThread::Create(system.Kernel()); + auto initialize = [this](KThread* thread) { ASSERT(KThread::InitializeDummyThread(thread).IsSuccess()); thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); return thread; }; - thread_local KThread* saved_thread = make_thread(); - return saved_thread; + thread_local auto raw_thread = KThread(system.Kernel()); + thread_local auto thread = initialize(&raw_thread); + + return thread; } /// Registers a CPU core thread by allocating a host thread ID for it @@ -347,6 +341,8 @@ struct KernelCore::Impl { return is_shutting_down.load(std::memory_order_relaxed); } + static inline thread_local KThread* current_thread{nullptr}; + KThread* GetCurrentEmuThread() { // If we are shutting down the kernel, none of this is relevant anymore. if (IsShuttingDown()) { @@ -357,19 +353,26 @@ struct KernelCore::Impl { if (thread_id >= Core::Hardware::NUM_CPU_CORES) { return GetHostDummyThread(); } - return schedulers[thread_id]->GetCurrentThread(); + + return current_thread; + } + + void SetCurrentEmuThread(KThread* thread) { + current_thread = thread; } - void DeriveInitialMemoryLayout(KMemoryLayout& memory_layout) { + void DeriveInitialMemoryLayout() { + memory_layout = std::make_unique<KMemoryLayout>(); + // Insert the root region for the virtual memory tree, from which all other regions will // derive. - memory_layout.GetVirtualMemoryRegionTree().InsertDirectly( + memory_layout->GetVirtualMemoryRegionTree().InsertDirectly( KernelVirtualAddressSpaceBase, KernelVirtualAddressSpaceBase + KernelVirtualAddressSpaceSize - 1); // Insert the root region for the physical memory tree, from which all other regions will // derive. - memory_layout.GetPhysicalMemoryRegionTree().InsertDirectly( + memory_layout->GetPhysicalMemoryRegionTree().InsertDirectly( KernelPhysicalAddressSpaceBase, KernelPhysicalAddressSpaceBase + KernelPhysicalAddressSpaceSize - 1); @@ -386,7 +389,7 @@ struct KernelCore::Impl { if (!(kernel_region_start + KernelRegionSize - 1 <= KernelVirtualAddressSpaceLast)) { kernel_region_size = KernelVirtualAddressSpaceEnd - kernel_region_start; } - ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( kernel_region_start, kernel_region_size, KMemoryRegionType_Kernel)); // Setup the code region. @@ -395,11 +398,11 @@ struct KernelCore::Impl { Common::AlignDown(code_start_virt_addr, CodeRegionAlign); constexpr VAddr code_region_end = Common::AlignUp(code_end_virt_addr, CodeRegionAlign); constexpr size_t code_region_size = code_region_end - code_region_start; - ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( code_region_start, code_region_size, KMemoryRegionType_KernelCode)); // Setup board-specific device physical regions. - Init::SetupDevicePhysicalMemoryRegions(memory_layout); + Init::SetupDevicePhysicalMemoryRegions(*memory_layout); // Determine the amount of space needed for the misc region. size_t misc_region_needed_size; @@ -408,7 +411,7 @@ struct KernelCore::Impl { misc_region_needed_size = Core::Hardware::NUM_CPU_CORES * (3 * (PageSize + PageSize)); // Account for each auto-map device. - for (const auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { + for (const auto& region : memory_layout->GetPhysicalMemoryRegionTree()) { if (region.HasTypeAttribute(KMemoryRegionAttr_ShouldKernelMap)) { // Check that the region is valid. ASSERT(region.GetEndAddress() != 0); @@ -433,22 +436,22 @@ struct KernelCore::Impl { // Setup the misc region. const VAddr misc_region_start = - memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion( + memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion( misc_region_size, MiscRegionAlign, KMemoryRegionType_Kernel); - ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( misc_region_start, misc_region_size, KMemoryRegionType_KernelMisc)); // Setup the stack region. constexpr size_t StackRegionSize = 14_MiB; constexpr size_t StackRegionAlign = KernelAslrAlignment; const VAddr stack_region_start = - memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion( + memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion( StackRegionSize, StackRegionAlign, KMemoryRegionType_Kernel); - ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( stack_region_start, StackRegionSize, KMemoryRegionType_KernelStack)); // Determine the size of the resource region. - const size_t resource_region_size = memory_layout.GetResourceRegionSizeForInit(); + const size_t resource_region_size = memory_layout->GetResourceRegionSizeForInit(); // Determine the size of the slab region. const size_t slab_region_size = @@ -465,23 +468,23 @@ struct KernelCore::Impl { Common::AlignUp(code_end_phys_addr + slab_region_size, SlabRegionAlign) - Common::AlignDown(code_end_phys_addr, SlabRegionAlign); const VAddr slab_region_start = - memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion( + memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion( slab_region_needed_size, SlabRegionAlign, KMemoryRegionType_Kernel) + (code_end_phys_addr % SlabRegionAlign); - ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( slab_region_start, slab_region_size, KMemoryRegionType_KernelSlab)); // Setup the temp region. constexpr size_t TempRegionSize = 128_MiB; constexpr size_t TempRegionAlign = KernelAslrAlignment; const VAddr temp_region_start = - memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion( + memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion( TempRegionSize, TempRegionAlign, KMemoryRegionType_Kernel); - ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(temp_region_start, TempRegionSize, - KMemoryRegionType_KernelTemp)); + ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(temp_region_start, TempRegionSize, + KMemoryRegionType_KernelTemp)); // Automatically map in devices that have auto-map attributes. - for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { + for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) { // We only care about kernel regions. if (!region.IsDerivedFrom(KMemoryRegionType_Kernel)) { continue; @@ -508,21 +511,21 @@ struct KernelCore::Impl { const size_t map_size = Common::AlignUp(region.GetEndAddress(), PageSize) - map_phys_addr; const VAddr map_virt_addr = - memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard( + memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard( map_size, PageSize, KMemoryRegionType_KernelMisc, PageSize); - ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( map_virt_addr, map_size, KMemoryRegionType_KernelMiscMappedDevice)); region.SetPairAddress(map_virt_addr + region.GetAddress() - map_phys_addr); } - Init::SetupDramPhysicalMemoryRegions(memory_layout); + Init::SetupDramPhysicalMemoryRegions(*memory_layout); // Insert a physical region for the kernel code region. - ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert( code_start_phys_addr, code_region_size, KMemoryRegionType_DramKernelCode)); // Insert a physical region for the kernel slab region. - ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert( slab_start_phys_addr, slab_region_size, KMemoryRegionType_DramKernelSlab)); // Determine size available for kernel page table heaps, requiring > 8 MB. @@ -531,12 +534,12 @@ struct KernelCore::Impl { ASSERT(page_table_heap_size / 4_MiB > 2); // Insert a physical region for the kernel page table heap region - ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert( slab_end_phys_addr, page_table_heap_size, KMemoryRegionType_DramKernelPtHeap)); // All DRAM regions that we haven't tagged by this point will be mapped under the linear // mapping. Tag them. - for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { + for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) { if (region.GetType() == KMemoryRegionType_Dram) { // Check that the region is valid. ASSERT(region.GetEndAddress() != 0); @@ -548,7 +551,7 @@ struct KernelCore::Impl { // Get the linear region extents. const auto linear_extents = - memory_layout.GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( + memory_layout->GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( KMemoryRegionAttr_LinearMapped); ASSERT(linear_extents.GetEndAddress() != 0); @@ -560,7 +563,7 @@ struct KernelCore::Impl { Common::AlignUp(linear_extents.GetEndAddress(), LinearRegionAlign) - aligned_linear_phys_start; const VAddr linear_region_start = - memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard( + memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard( linear_region_size, LinearRegionAlign, KMemoryRegionType_None, LinearRegionAlign); const u64 linear_region_phys_to_virt_diff = linear_region_start - aligned_linear_phys_start; @@ -569,7 +572,7 @@ struct KernelCore::Impl { { PAddr cur_phys_addr = 0; u64 cur_size = 0; - for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { + for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) { if (!region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) { continue; } @@ -588,55 +591,49 @@ struct KernelCore::Impl { const VAddr region_virt_addr = region.GetAddress() + linear_region_phys_to_virt_diff; - ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( region_virt_addr, region.GetSize(), GetTypeForVirtualLinearMapping(region.GetType()))); region.SetPairAddress(region_virt_addr); KMemoryRegion* virt_region = - memory_layout.GetVirtualMemoryRegionTree().FindModifiable(region_virt_addr); + memory_layout->GetVirtualMemoryRegionTree().FindModifiable(region_virt_addr); ASSERT(virt_region != nullptr); virt_region->SetPairAddress(region.GetAddress()); } } // Insert regions for the initial page table region. - ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( + ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert( resource_end_phys_addr, KernelPageTableHeapSize, KMemoryRegionType_DramKernelInitPt)); - ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( + ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( resource_end_phys_addr + linear_region_phys_to_virt_diff, KernelPageTableHeapSize, KMemoryRegionType_VirtualDramKernelInitPt)); // All linear-mapped DRAM regions that we haven't tagged by this point will be allocated to // some pool partition. Tag them. - for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { + for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) { if (region.GetType() == (KMemoryRegionType_Dram | KMemoryRegionAttr_LinearMapped)) { region.SetType(KMemoryRegionType_DramPoolPartition); } } // Setup all other memory regions needed to arrange the pool partitions. - Init::SetupPoolPartitionMemoryRegions(memory_layout); + Init::SetupPoolPartitionMemoryRegions(*memory_layout); // Cache all linear regions in their own trees for faster access, later. - memory_layout.InitializeLinearMemoryRegionTrees(aligned_linear_phys_start, - linear_region_start); + memory_layout->InitializeLinearMemoryRegionTrees(aligned_linear_phys_start, + linear_region_start); } - void InitializeMemoryLayout(const KMemoryLayout& memory_layout) { - const auto system_pool = memory_layout.GetKernelSystemPoolRegionPhysicalExtents(); - const auto applet_pool = memory_layout.GetKernelAppletPoolRegionPhysicalExtents(); - const auto application_pool = memory_layout.GetKernelApplicationPoolRegionPhysicalExtents(); + void InitializeMemoryLayout() { + const auto system_pool = memory_layout->GetKernelSystemPoolRegionPhysicalExtents(); - // Initialize memory managers + // Initialize the memory manager. memory_manager = std::make_unique<KMemoryManager>(system); - memory_manager->InitializeManager(KMemoryManager::Pool::Application, - application_pool.GetAddress(), - application_pool.GetEndAddress()); - memory_manager->InitializeManager(KMemoryManager::Pool::Applet, applet_pool.GetAddress(), - applet_pool.GetEndAddress()); - memory_manager->InitializeManager(KMemoryManager::Pool::System, system_pool.GetAddress(), - system_pool.GetEndAddress()); + const auto& management_region = memory_layout->GetPoolManagementRegion(); + ASSERT(management_region.GetEndAddress() != 0); + memory_manager->Initialize(management_region.GetAddress(), management_region.GetSize()); // Setup memory regions for emulated processes // TODO(bunnei): These should not be hardcoded regions initialized within the kernel @@ -644,16 +641,20 @@ struct KernelCore::Impl { constexpr std::size_t font_size{0x1100000}; constexpr std::size_t irs_size{0x8000}; constexpr std::size_t time_size{0x1000}; + constexpr std::size_t hidbus_size{0x1000}; const PAddr hid_phys_addr{system_pool.GetAddress()}; const PAddr font_phys_addr{system_pool.GetAddress() + hid_size}; const PAddr irs_phys_addr{system_pool.GetAddress() + hid_size + font_size}; const PAddr time_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size}; + const PAddr hidbus_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size + + time_size}; hid_shared_mem = KSharedMemory::Create(system.Kernel()); font_shared_mem = KSharedMemory::Create(system.Kernel()); irs_shared_mem = KSharedMemory::Create(system.Kernel()); time_shared_mem = KSharedMemory::Create(system.Kernel()); + hidbus_shared_mem = KSharedMemory::Create(system.Kernel()); hid_shared_mem->Initialize(system.DeviceMemory(), nullptr, {hid_phys_addr, hid_size / PageSize}, @@ -671,22 +672,10 @@ struct KernelCore::Impl { {time_phys_addr, time_size / PageSize}, Svc::MemoryPermission::None, Svc::MemoryPermission::Read, time_phys_addr, time_size, "Time:SharedMemory"); - } - - void InitializePageSlab() { - // Allocate slab heaps - user_slab_heap_pages = - std::make_unique<KSlabHeap<Page>>(KSlabHeap<Page>::AllocationType::Guest); - - // TODO(ameerj): This should be derived, not hardcoded within the kernel - constexpr u64 user_slab_heap_size{0x3de000}; - // Reserve slab heaps - ASSERT( - system_resource_limit->Reserve(LimitableResource::PhysicalMemory, user_slab_heap_size)); - // Initialize slab heap - user_slab_heap_pages->Initialize( - system.DeviceMemory().GetPointer(Core::DramMemoryMap::SlabHeapBase), - user_slab_heap_size); + hidbus_shared_mem->Initialize(system.DeviceMemory(), nullptr, + {hidbus_phys_addr, hidbus_size / PageSize}, + Svc::MemoryPermission::None, Svc::MemoryPermission::Read, + hidbus_phys_addr, hidbus_size, "HidBus:SharedMemory"); } KClientPort* CreateNamedServicePort(std::string name) { @@ -697,13 +686,20 @@ struct KernelCore::Impl { } KClientPort* port = &search->second(system.ServiceManager(), system); - { - std::lock_guard lk(server_ports_lock); - server_ports.insert(&port->GetParent()->GetServerPort()); - } + RegisterServerObject(&port->GetParent()->GetServerPort()); return port; } + void RegisterServerObject(KAutoObject* server_object) { + std::scoped_lock lk(server_objects_lock); + server_objects.insert(server_object); + } + + void UnregisterServerObject(KAutoObject* server_object) { + std::scoped_lock lk(server_objects_lock); + server_objects.erase(server_object); + } + std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel, const std::string& name) { auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name); @@ -716,6 +712,12 @@ struct KernelCore::Impl { void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { if (auto strong_ptr = service_thread.lock()) { + if (strong_ptr == default_service_thread.lock()) { + // Nothing to do here, the service is using default_service_thread, which will be + // released on shutdown. + return; + } + service_threads_manager.QueueWork( [this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); }); } @@ -725,8 +727,7 @@ struct KernelCore::Impl { service_threads_manager.QueueWork([this]() { service_threads.clear(); }); } - std::mutex server_ports_lock; - std::mutex server_sessions_lock; + std::mutex server_objects_lock; std::mutex registered_objects_lock; std::mutex registered_in_use_objects_lock; @@ -737,7 +738,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; @@ -750,39 +751,41 @@ struct KernelCore::Impl { // stores all the objects in place. std::unique_ptr<KHandleTable> global_handle_table; - KAutoObjectWithListContainer object_list_container; + std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container; /// Map of named ports managed by the kernel, which can be retrieved using /// the ConnectToPort SVC. std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory; NamedPortTable named_ports; - std::unordered_set<KServerPort*> server_ports; - std::unordered_set<KServerSession*> server_sessions; + std::unordered_set<KAutoObject*> server_objects; std::unordered_set<KAutoObject*> registered_objects; std::unordered_set<KAutoObject*> registered_in_use_objects; std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; - std::vector<Kernel::PhysicalCore> cores; + std::array<std::unique_ptr<Kernel::PhysicalCore>, Core::Hardware::NUM_CPU_CORES> cores; // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others std::atomic<u32> next_host_thread_id{Core::Hardware::NUM_CPU_CORES}; // Kernel memory management std::unique_ptr<KMemoryManager> memory_manager; - std::unique_ptr<KSlabHeap<Page>> user_slab_heap_pages; // Shared memory for services Kernel::KSharedMemory* hid_shared_mem{}; Kernel::KSharedMemory* font_shared_mem{}; Kernel::KSharedMemory* irs_shared_mem{}; Kernel::KSharedMemory* time_shared_mem{}; + Kernel::KSharedMemory* hidbus_shared_mem{}; + + // Memory layout + std::unique_ptr<KMemoryLayout> memory_layout; // Threads used for services - std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads; + std::unordered_set<std::shared_ptr<ServiceThread>> service_threads; + std::weak_ptr<ServiceThread> default_service_thread; Common::ThreadWorker service_threads_manager; - std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads; - std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{}; + std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads; std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; bool is_multicore{}; @@ -818,6 +821,10 @@ void KernelCore::Shutdown() { impl->Shutdown(); } +void KernelCore::CloseServices() { + impl->CloseServices(); +} + const KResourceLimit* KernelCore::GetSystemResourceLimit() const { return impl->system_resource_limit; } @@ -867,11 +874,11 @@ const Kernel::KScheduler& KernelCore::Scheduler(std::size_t id) const { } Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) { - return impl->cores[id]; + return *impl->cores[id]; } const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const { - return impl->cores[id]; + return *impl->cores[id]; } size_t KernelCore::CurrentPhysicalCoreIndex() const { @@ -883,11 +890,11 @@ size_t KernelCore::CurrentPhysicalCoreIndex() const { } Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() { - return impl->cores[CurrentPhysicalCoreIndex()]; + return *impl->cores[CurrentPhysicalCoreIndex()]; } const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const { - return impl->cores[CurrentPhysicalCoreIndex()]; + return *impl->cores[CurrentPhysicalCoreIndex()]; } Kernel::KScheduler* KernelCore::CurrentScheduler() { @@ -899,15 +906,6 @@ Kernel::KScheduler* KernelCore::CurrentScheduler() { return impl->schedulers[core_id].get(); } -std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts() { - return impl->interrupts; -} - -const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts() - const { - return impl->interrupts; -} - Kernel::TimeManager& KernelCore::TimeManager() { return impl->time_manager; } @@ -925,25 +923,25 @@ const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const { } KAutoObjectWithListContainer& KernelCore::ObjectListContainer() { - return impl->object_list_container; + return *impl->global_object_list_container; } const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const { - return impl->object_list_container; + return *impl->global_object_list_container; } void KernelCore::InvalidateAllInstructionCaches() { for (auto& physical_core : impl->cores) { - physical_core.ArmInterface().ClearInstructionCache(); + physical_core->ArmInterface().ClearInstructionCache(); } } void KernelCore::InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size) { for (auto& physical_core : impl->cores) { - if (!physical_core.IsInitialized()) { + if (!physical_core->IsInitialized()) { continue; } - physical_core.ArmInterface().InvalidateCacheRange(addr, size); + physical_core->ArmInterface().InvalidateCacheRange(addr, size); } } @@ -959,33 +957,31 @@ KClientPort* KernelCore::CreateNamedServicePort(std::string name) { return impl->CreateNamedServicePort(std::move(name)); } -void KernelCore::RegisterServerSession(KServerSession* server_session) { - std::lock_guard lk(impl->server_sessions_lock); - impl->server_sessions.insert(server_session); +void KernelCore::RegisterServerObject(KAutoObject* server_object) { + impl->RegisterServerObject(server_object); } -void KernelCore::UnregisterServerSession(KServerSession* server_session) { - std::lock_guard lk(impl->server_sessions_lock); - impl->server_sessions.erase(server_session); +void KernelCore::UnregisterServerObject(KAutoObject* server_object) { + impl->UnregisterServerObject(server_object); } void KernelCore::RegisterKernelObject(KAutoObject* object) { - std::lock_guard lk(impl->registered_objects_lock); + std::scoped_lock lk{impl->registered_objects_lock}; impl->registered_objects.insert(object); } void KernelCore::UnregisterKernelObject(KAutoObject* object) { - std::lock_guard lk(impl->registered_objects_lock); + std::scoped_lock lk{impl->registered_objects_lock}; impl->registered_objects.erase(object); } void KernelCore::RegisterInUseObject(KAutoObject* object) { - std::lock_guard lk(impl->registered_in_use_objects_lock); + std::scoped_lock lk{impl->registered_in_use_objects_lock}; impl->registered_in_use_objects.insert(object); } void KernelCore::UnregisterInUseObject(KAutoObject* object) { - std::lock_guard lk(impl->registered_in_use_objects_lock); + std::scoped_lock lk{impl->registered_in_use_objects_lock}; impl->registered_in_use_objects.erase(object); } @@ -1033,6 +1029,10 @@ KThread* KernelCore::GetCurrentEmuThread() const { return impl->GetCurrentEmuThread(); } +void KernelCore::SetCurrentEmuThread(KThread* thread) { + impl->SetCurrentEmuThread(thread); +} + KMemoryManager& KernelCore::MemoryManager() { return *impl->memory_manager; } @@ -1041,14 +1041,6 @@ const KMemoryManager& KernelCore::MemoryManager() const { return *impl->memory_manager; } -KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() { - return *impl->user_slab_heap_pages; -} - -const KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() const { - return *impl->user_slab_heap_pages; -} - Kernel::KSharedMemory& KernelCore::GetHidSharedMem() { return *impl->hid_shared_mem; } @@ -1081,22 +1073,38 @@ const Kernel::KSharedMemory& KernelCore::GetTimeSharedMem() const { return *impl->time_shared_mem; } -void KernelCore::Suspend(bool in_suspention) { - const bool should_suspend = exception_exited || in_suspention; - { - KScopedSchedulerLock lock(*this); - const auto state = should_suspend ? ThreadState::Runnable : ThreadState::Waiting; - for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { - impl->suspend_threads[core_id]->SetState(state); - impl->suspend_threads[core_id]->SetWaitReasonForDebugging( - ThreadWaitReasonForDebugging::Suspended); - if (!should_suspend) { - impl->suspend_threads[core_id]->DisableDispatch(); +Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() { + return *impl->hidbus_shared_mem; +} + +const Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() const { + return *impl->hidbus_shared_mem; +} + +void KernelCore::Suspend(bool suspended) { + const bool should_suspend{exception_exited || suspended}; + const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable; + + for (auto* process : GetProcessList()) { + process->SetActivity(activity); + + if (should_suspend) { + // Wait for execution to stop + for (auto* thread : process->GetThreadList()) { + thread->WaitUntilSuspended(); } } } } +void KernelCore::ShutdownCores() { + KScopedSchedulerLock lk{*this}; + + for (auto* thread : impl->shutdown_threads) { + void(thread->Run()); + } +} + bool KernelCore::IsMulticore() const { return impl->is_multicore; } @@ -1122,6 +1130,10 @@ std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std:: return impl->CreateServiceThread(*this, name); } +std::weak_ptr<Kernel::ServiceThread> KernelCore::GetDefaultServiceThread() const { + return impl->default_service_thread; +} + void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { impl->ReleaseServiceThread(service_thread); } @@ -1142,6 +1154,10 @@ const KWorkerTaskManager& KernelCore::WorkerTaskManager() const { return impl->worker_task_manager; } +const KMemoryLayout& KernelCore::MemoryLayout() const { + return *impl->memory_layout; +} + bool KernelCore::IsPhantomModeForSingleCore() const { return impl->IsPhantomModeForSingleCore(); } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 0e04fc3bb..bcf016a97 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -10,15 +9,12 @@ #include <string> #include <unordered_map> #include <vector> -#include "core/arm/cpu_interrupt_handler.h" #include "core/hardware_properties.h" #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_slab_heap.h" -#include "core/hle/kernel/memory_types.h" #include "core/hle/kernel/svc_common.h" namespace Core { -class CPUInterruptHandler; class ExclusiveMonitor; class System; } // namespace Core @@ -41,7 +37,9 @@ class KClientSession; class KEvent; class KHandleTable; class KLinkedListNode; +class KMemoryLayout; class KMemoryManager; +class KPageBuffer; class KPort; class KProcess; class KResourceLimit; @@ -51,6 +49,7 @@ class KSession; class KSharedMemory; class KSharedMemoryInfo; class KThread; +class KThreadLocalPage; class KTransferMemory; class KWorkerTaskManager; class KWritableEvent; @@ -108,6 +107,9 @@ public: /// Clears all resources in use by the kernel instance. void Shutdown(); + /// Close all active services in use by the kernel instance. + void CloseServices(); + /// Retrieves a shared pointer to the system resource limit instance. const KResourceLimit* GetSystemResourceLimit() const; @@ -179,10 +181,6 @@ public: const KAutoObjectWithListContainer& ObjectListContainer() const; - std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts(); - - const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts() const; - void InvalidateAllInstructionCaches(); void InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size); @@ -193,13 +191,13 @@ public: /// Opens a port to a service previously registered with RegisterNamedService. KClientPort* CreateNamedServicePort(std::string name); - /// Registers a server session with the gobal emulation state, to be freed on shutdown. This is - /// necessary because we do not emulate processes for HLE sessions. - void RegisterServerSession(KServerSession* server_session); + /// Registers a server session or port with the gobal emulation state, to be freed on shutdown. + /// This is necessary because we do not emulate processes for HLE sessions and ports. + void RegisterServerObject(KAutoObject* server_object); - /// Unregisters a server session previously registered with RegisterServerSession when it was - /// destroyed during the current emulation session. - void UnregisterServerSession(KServerSession* server_session); + /// Unregisters a server session or port previously registered with RegisterServerSession when + /// it was destroyed during the current emulation session. + void UnregisterServerObject(KAutoObject* server_object); /// Registers all kernel objects with the global emulation state, this is purely for tracking /// leaks after emulation has been shutdown. @@ -223,6 +221,9 @@ public: /// Gets the current host_thread/guest_thread pointer. KThread* GetCurrentEmuThread() const; + /// Sets the current guest_thread pointer. + void SetCurrentEmuThread(KThread* thread); + /// Gets the current host_thread handle. u32 GetCurrentHostThreadID() const; @@ -238,12 +239,6 @@ public: /// Gets the virtual memory manager for the kernel. const KMemoryManager& MemoryManager() const; - /// Gets the slab heap allocated for user space pages. - KSlabHeap<Page>& GetUserSlabHeapPages(); - - /// Gets the slab heap allocated for user space pages. - const KSlabHeap<Page>& GetUserSlabHeapPages() const; - /// Gets the shared memory object for HID services. Kernel::KSharedMemory& GetHidSharedMem(); @@ -268,12 +263,21 @@ public: /// Gets the shared memory object for Time services. const Kernel::KSharedMemory& GetTimeSharedMem() const; - /// Suspend/unsuspend the OS. - void Suspend(bool in_suspention); + /// Gets the shared memory object for HIDBus services. + Kernel::KSharedMemory& GetHidBusSharedMem(); - /// Exceptional exit the OS. + /// Gets the shared memory object for HIDBus services. + const Kernel::KSharedMemory& GetHidBusSharedMem() const; + + /// Suspend/unsuspend all processes. + void Suspend(bool suspend); + + /// Exceptional exit all processes. void ExceptionalExit(); + /// Notify emulated CPU cores to shut down. + void ShutdownCores(); + bool IsMulticore() const; bool IsShuttingDown() const; @@ -283,9 +287,11 @@ public: void ExitSVCProfile(); /** - * Creates an HLE service thread, which are used to execute service routines asynchronously. - * While these are allocated per ServerSession, these need to be owned and managed outside - * of ServerSession to avoid a circular dependency. + * Creates a host thread to execute HLE service requests, which are used to execute service + * routines asynchronously. While these are allocated per ServerSession, these need to be owned + * and managed outside of ServerSession to avoid a circular dependency. In general, most + * services can just use the default service thread, and not need their own host service thread. + * See GetDefaultServiceThread. * @param name String name for the ServerSession creating this thread, used for debug * purposes. * @returns The a weak pointer newly created service thread. @@ -293,6 +299,14 @@ public: std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name); /** + * Gets the default host service thread, which executes HLE service requests. Unless service + * requests need to block on the host, the default service thread should be used in favor of + * creating a new service thread. + * @returns The a weak pointer for the default service thread. + */ + std::weak_ptr<Kernel::ServiceThread> GetDefaultServiceThread() const; + + /** * Releases a HLE service thread, instructing KernelCore to free it. This should be called when * the ServerSession associated with the thread is destroyed. * @param service_thread Service thread to release. @@ -335,6 +349,10 @@ public: return slab_heap_container->writeable_event; } else if constexpr (std::is_same_v<T, KCodeMemory>) { return slab_heap_container->code_memory; + } else if constexpr (std::is_same_v<T, KPageBuffer>) { + return slab_heap_container->page_buffer; + } else if constexpr (std::is_same_v<T, KThreadLocalPage>) { + return slab_heap_container->thread_local_page; } } @@ -350,6 +368,9 @@ public: /// Gets the current worker task manager, used for dispatching KThread/KProcess tasks. const KWorkerTaskManager& WorkerTaskManager() const; + /// Gets the memory layout. + const KMemoryLayout& MemoryLayout() const; + private: friend class KProcess; friend class KThread; @@ -393,6 +414,8 @@ private: KSlabHeap<KTransferMemory> transfer_memory; KSlabHeap<KWritableEvent> writeable_event; KSlabHeap<KCodeMemory> code_memory; + KSlabHeap<KPageBuffer> page_buffer; + KSlabHeap<KThreadLocalPage> thread_local_page; }; std::unique_ptr<SlabHeapContainer> slab_heap_container; diff --git a/src/core/hle/kernel/memory_types.h b/src/core/hle/kernel/memory_types.h index d458f0eca..3975507bd 100644 --- a/src/core/hle/kernel/memory_types.h +++ b/src/core/hle/kernel/memory_types.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index 7477668e4..d4375962f 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp @@ -1,9 +1,6 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later -#include "common/spin_lock.h" -#include "core/arm/cpu_interrupt_handler.h" #include "core/arm/dynarmic/arm_dynarmic_32.h" #include "core/arm/dynarmic/arm_dynarmic_64.h" #include "core/core.h" @@ -13,16 +10,14 @@ namespace Kernel { -PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_, - Core::CPUInterrupts& interrupts_) - : core_index{core_index_}, system{system_}, scheduler{scheduler_}, - interrupts{interrupts_}, guard{std::make_unique<Common::SpinLock>()} { +PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_) + : core_index{core_index_}, system{system_}, scheduler{scheduler_} { #ifdef ARCHITECTURE_x86_64 // TODO(bunnei): Initialization relies on a core being available. We may later replace this with // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager. auto& kernel = system.Kernel(); arm_interface = std::make_unique<Core::ARM_Dynarmic_64>( - system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index); + system, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index); #else #error Platform not supported yet. #endif @@ -36,7 +31,7 @@ void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) { if (!is_64_bit) { // We already initialized a 64-bit core, replace with a 32-bit one. arm_interface = std::make_unique<Core::ARM_Dynarmic_32>( - system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index); + system, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index); } #else #error Platform not supported yet. @@ -45,26 +40,30 @@ void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) { void PhysicalCore::Run() { arm_interface->Run(); + arm_interface->ClearExclusiveState(); } void PhysicalCore::Idle() { - interrupts[core_index].AwaitInterrupt(); + std::unique_lock lk{guard}; + on_interrupt.wait(lk, [this] { return is_interrupted; }); } bool PhysicalCore::IsInterrupted() const { - return interrupts[core_index].IsInterrupted(); + return is_interrupted; } void PhysicalCore::Interrupt() { - guard->lock(); - interrupts[core_index].SetInterrupt(true); - guard->unlock(); + std::unique_lock lk{guard}; + is_interrupted = true; + arm_interface->SignalInterrupt(); + on_interrupt.notify_all(); } void PhysicalCore::ClearInterrupt() { - guard->lock(); - interrupts[core_index].SetInterrupt(false); - guard->unlock(); + std::unique_lock lk{guard}; + is_interrupted = false; + arm_interface->ClearInterrupt(); + on_interrupt.notify_all(); } } // namespace Kernel diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h index 16a032e89..2fc8d4be2 100644 --- a/src/core/hle/kernel/physical_core.h +++ b/src/core/hle/kernel/physical_core.h @@ -1,24 +1,19 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include <cstddef> #include <memory> +#include <mutex> #include "core/arm/arm_interface.h" -namespace Common { -class SpinLock; -} - namespace Kernel { class KScheduler; } // namespace Kernel namespace Core { -class CPUInterruptHandler; class ExclusiveMonitor; class System; } // namespace Core @@ -27,15 +22,11 @@ namespace Kernel { class PhysicalCore { public: - PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_, - Core::CPUInterrupts& interrupts_); + PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_); ~PhysicalCore(); - PhysicalCore(const PhysicalCore&) = delete; - PhysicalCore& operator=(const PhysicalCore&) = delete; - - PhysicalCore(PhysicalCore&&) = default; - PhysicalCore& operator=(PhysicalCore&&) = delete; + YUZU_NON_COPYABLE(PhysicalCore); + YUZU_NON_MOVEABLE(PhysicalCore); /// Initialize the core for the specified parameters. void Initialize(bool is_64_bit); @@ -90,9 +81,11 @@ private: const std::size_t core_index; Core::System& system; Kernel::KScheduler& scheduler; - Core::CPUInterrupts& interrupts; - std::unique_ptr<Common::SpinLock> guard; + + std::mutex guard; + std::condition_variable on_interrupt; std::unique_ptr<Core::ARM_Interface> arm_interface; + bool is_interrupted; }; } // namespace Kernel diff --git a/src/core/hle/kernel/physical_memory.h b/src/core/hle/kernel/physical_memory.h index 7a0266780..253fa4563 100644 --- a/src/core/hle/kernel/physical_memory.h +++ b/src/core/hle/kernel/physical_memory.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp index 31a0867d3..773319ad8 100644 --- a/src/core/hle/kernel/process_capability.cpp +++ b/src/core/hle/kernel/process_capability.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <bit> @@ -69,9 +68,9 @@ u32 GetFlagBitOffset(CapabilityType type) { } // Anonymous namespace -ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities, - std::size_t num_capabilities, - KPageTable& page_table) { +Result ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities, + std::size_t num_capabilities, + KPageTable& page_table) { Clear(); // Allow all cores and priorities. @@ -82,9 +81,9 @@ ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabiliti return ParseCapabilities(capabilities, num_capabilities, page_table); } -ResultCode ProcessCapabilities::InitializeForUserProcess(const u32* capabilities, - std::size_t num_capabilities, - KPageTable& page_table) { +Result ProcessCapabilities::InitializeForUserProcess(const u32* capabilities, + std::size_t num_capabilities, + KPageTable& page_table) { Clear(); return ParseCapabilities(capabilities, num_capabilities, page_table); @@ -108,9 +107,8 @@ void ProcessCapabilities::InitializeForMetadatalessProcess() { can_force_debug = true; } -ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities, - std::size_t num_capabilities, - KPageTable& page_table) { +Result ProcessCapabilities::ParseCapabilities(const u32* capabilities, std::size_t num_capabilities, + KPageTable& page_table) { u32 set_flags = 0; u32 set_svc_bits = 0; @@ -156,8 +154,8 @@ ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities, return ResultSuccess; } -ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, - u32 flag, KPageTable& page_table) { +Result ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag, + KPageTable& page_table) { const auto type = GetCapabilityType(flag); if (type == CapabilityType::Unset) { @@ -225,7 +223,7 @@ void ProcessCapabilities::Clear() { can_force_debug = false; } -ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) { +Result ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) { if (priority_mask != 0 || core_mask != 0) { LOG_ERROR(Kernel, "Core or priority mask are not zero! priority_mask={}, core_mask={}", priority_mask, core_mask); @@ -267,7 +265,7 @@ ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) { return ResultSuccess; } -ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) { +Result ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) { const u32 index = flags >> 29; const u32 svc_bit = 1U << index; @@ -291,23 +289,23 @@ ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) return ResultSuccess; } -ResultCode ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags, - KPageTable& page_table) { +Result ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags, + KPageTable& page_table) { // TODO(Lioncache): Implement once the memory manager can handle this. return ResultSuccess; } -ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, KPageTable& page_table) { +Result ProcessCapabilities::HandleMapIOFlags(u32 flags, KPageTable& page_table) { // TODO(Lioncache): Implement once the memory manager can handle this. return ResultSuccess; } -ResultCode ProcessCapabilities::HandleMapRegionFlags(u32 flags, KPageTable& page_table) { +Result ProcessCapabilities::HandleMapRegionFlags(u32 flags, KPageTable& page_table) { // TODO(Lioncache): Implement once the memory manager can handle this. return ResultSuccess; } -ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) { +Result ProcessCapabilities::HandleInterruptFlags(u32 flags) { constexpr u32 interrupt_ignore_value = 0x3FF; const u32 interrupt0 = (flags >> 12) & 0x3FF; const u32 interrupt1 = (flags >> 22) & 0x3FF; @@ -334,7 +332,7 @@ ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) { return ResultSuccess; } -ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) { +Result ProcessCapabilities::HandleProgramTypeFlags(u32 flags) { const u32 reserved = flags >> 17; if (reserved != 0) { LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved); @@ -345,7 +343,7 @@ ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) { return ResultSuccess; } -ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) { +Result ProcessCapabilities::HandleKernelVersionFlags(u32 flags) { // Yes, the internal member variable is checked in the actual kernel here. // This might look odd for options that are only allowed to be initialized // just once, however the kernel has a separate initialization function for @@ -365,7 +363,7 @@ ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) { return ResultSuccess; } -ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) { +Result ProcessCapabilities::HandleHandleTableFlags(u32 flags) { const u32 reserved = flags >> 26; if (reserved != 0) { LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved); @@ -376,7 +374,7 @@ ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) { return ResultSuccess; } -ResultCode ProcessCapabilities::HandleDebugFlags(u32 flags) { +Result ProcessCapabilities::HandleDebugFlags(u32 flags) { const u32 reserved = flags >> 19; if (reserved != 0) { LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved); diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h index a9b44325b..ff05dc5ff 100644 --- a/src/core/hle/kernel/process_capability.h +++ b/src/core/hle/kernel/process_capability.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,7 +7,7 @@ #include "common/common_types.h" -union ResultCode; +union Result; namespace Kernel { @@ -87,8 +86,8 @@ public: /// @returns ResultSuccess if this capabilities instance was able to be initialized, /// otherwise, an error code upon failure. /// - ResultCode InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities, - KPageTable& page_table); + Result InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities, + KPageTable& page_table); /// Initializes this process capabilities instance for a userland process. /// @@ -100,8 +99,8 @@ public: /// @returns ResultSuccess if this capabilities instance was able to be initialized, /// otherwise, an error code upon failure. /// - ResultCode InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities, - KPageTable& page_table); + Result InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities, + KPageTable& page_table); /// Initializes this process capabilities instance for a process that does not /// have any metadata to parse. @@ -186,8 +185,8 @@ private: /// /// @return ResultSuccess if no errors occur, otherwise an error code. /// - ResultCode ParseCapabilities(const u32* capabilities, std::size_t num_capabilities, - KPageTable& page_table); + Result ParseCapabilities(const u32* capabilities, std::size_t num_capabilities, + KPageTable& page_table); /// Attempts to parse a capability descriptor that is only represented by a /// single flag set. @@ -201,8 +200,8 @@ private: /// /// @return ResultSuccess if no errors occurred, otherwise an error code. /// - ResultCode ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag, - KPageTable& page_table); + Result ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag, + KPageTable& page_table); /// Clears the internal state of this process capability instance. Necessary, /// to have a sane starting point due to us allowing running executables without @@ -220,34 +219,34 @@ private: void Clear(); /// Handles flags related to the priority and core number capability flags. - ResultCode HandlePriorityCoreNumFlags(u32 flags); + Result HandlePriorityCoreNumFlags(u32 flags); /// Handles flags related to determining the allowable SVC mask. - ResultCode HandleSyscallFlags(u32& set_svc_bits, u32 flags); + Result HandleSyscallFlags(u32& set_svc_bits, u32 flags); /// Handles flags related to mapping physical memory pages. - ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, KPageTable& page_table); + Result HandleMapPhysicalFlags(u32 flags, u32 size_flags, KPageTable& page_table); /// Handles flags related to mapping IO pages. - ResultCode HandleMapIOFlags(u32 flags, KPageTable& page_table); + Result HandleMapIOFlags(u32 flags, KPageTable& page_table); /// Handles flags related to mapping physical memory regions. - ResultCode HandleMapRegionFlags(u32 flags, KPageTable& page_table); + Result HandleMapRegionFlags(u32 flags, KPageTable& page_table); /// Handles flags related to the interrupt capability flags. - ResultCode HandleInterruptFlags(u32 flags); + Result HandleInterruptFlags(u32 flags); /// Handles flags related to the program type. - ResultCode HandleProgramTypeFlags(u32 flags); + Result HandleProgramTypeFlags(u32 flags); /// Handles flags related to the handle table size. - ResultCode HandleHandleTableFlags(u32 flags); + Result HandleHandleTableFlags(u32 flags); /// Handles flags related to the kernel version capability flags. - ResultCode HandleKernelVersionFlags(u32 flags); + Result HandleKernelVersionFlags(u32 flags); /// Handles flags related to debug-specific capabilities. - ResultCode HandleDebugFlags(u32 flags); + Result HandleDebugFlags(u32 flags); SyscallCapabilities svc_capabilities; InterruptCapabilities interrupt_capabilities; diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp index 4eb3a5988..d23d76706 100644 --- a/src/core/hle/kernel/service_thread.cpp +++ b/src/core/hle/kernel/service_thread.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <condition_variable> #include <functional> @@ -37,7 +36,7 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std : service_name{name} { for (std::size_t i = 0; i < num_threads; ++i) { threads.emplace_back([this, &kernel](std::stop_token stop_token) { - Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str()); + Common::SetCurrentThreadName(std::string{service_name}.c_str()); // Wait for first request before trying to acquire a render context { @@ -49,12 +48,9 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std return; } + // Allocate a dummy guest thread for this host thread. kernel.RegisterHostThread(); - // Ensure the dummy thread allocated for this host thread is closed on exit. - auto* dummy_thread = kernel.GetCurrentEmuThread(); - SCOPE_EXIT({ dummy_thread->Close(); }); - while (true) { std::function<void()> task; diff --git a/src/core/hle/kernel/service_thread.h b/src/core/hle/kernel/service_thread.h index 6a7fd7c56..c5896f2bd 100644 --- a/src/core/hle/kernel/service_thread.h +++ b/src/core/hle/kernel/service_thread.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/slab_helpers.h b/src/core/hle/kernel/slab_helpers.h index f1c11256e..299a981a8 100644 --- a/src/core/hle/kernel/slab_helpers.h +++ b/src/core/hle/kernel/slab_helpers.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -59,7 +58,7 @@ class KAutoObjectWithSlabHeapAndContainer : public Base { private: static Derived* Allocate(KernelCore& kernel) { - return kernel.SlabHeap<Derived>().AllocateWithKernel(kernel); + return kernel.SlabHeap<Derived>().Allocate(kernel); } static void Free(KernelCore& kernel, Derived* obj) { diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 9387373c1..27e5a805d 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <cinttypes> @@ -16,6 +15,7 @@ #include "common/scope_exit.h" #include "core/core.h" #include "core/core_timing.h" +#include "core/debugger/debugger.h" #include "core/hle/kernel/k_client_port.h" #include "core/hle/kernel/k_client_session.h" #include "core/hle/kernel/k_code_memory.h" @@ -58,8 +58,8 @@ constexpr bool IsValidAddressRange(VAddr address, u64 size) { // Helper function that performs the common sanity checks for svcMapMemory // and svcUnmapMemory. This is doable, as both functions perform their sanitizing // in the same order. -ResultCode MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr, VAddr src_addr, - u64 size) { +Result MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr, VAddr src_addr, + u64 size) { if (!Common::Is4KBAligned(dst_addr)) { LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr); return ResultInvalidAddress; @@ -135,7 +135,7 @@ enum class ResourceLimitValueType { } // Anonymous namespace /// Set the process heap to a given Size. It can both extend and shrink the heap. -static ResultCode SetHeapSize(Core::System& system, VAddr* out_address, u64 size) { +static Result SetHeapSize(Core::System& system, VAddr* out_address, u64 size) { LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size); // Validate size. @@ -148,9 +148,9 @@ static ResultCode SetHeapSize(Core::System& system, VAddr* out_address, u64 size return ResultSuccess; } -static ResultCode SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) { +static Result SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) { VAddr temp_heap_addr{}; - const ResultCode result{SetHeapSize(system, &temp_heap_addr, heap_size)}; + const Result result{SetHeapSize(system, &temp_heap_addr, heap_size)}; *heap_addr = static_cast<u32>(temp_heap_addr); return result; } @@ -166,8 +166,8 @@ constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) { } } -static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 size, - MemoryPermission perm) { +static Result SetMemoryPermission(Core::System& system, VAddr address, u64 size, + MemoryPermission perm) { LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X", address, size, perm); @@ -188,8 +188,8 @@ static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 s return page_table.SetMemoryPermission(address, size, perm); } -static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, - u32 attr) { +static Result SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, + u32 attr) { LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, size, mask, attr); @@ -213,19 +213,19 @@ static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 si return page_table.SetMemoryAttribute(address, size, mask, attr); } -static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask, - u32 attr) { +static Result SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask, + u32 attr) { return SetMemoryAttribute(system, address, size, mask, attr); } /// Maps a memory range into a different range. -static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { +static Result MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, src_addr, size); auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; - if (const ResultCode result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; + if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; result.IsError()) { return result; } @@ -233,18 +233,18 @@ static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr return page_table.MapMemory(dst_addr, src_addr, size); } -static ResultCode MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { +static Result MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { return MapMemory(system, dst_addr, src_addr, size); } /// Unmaps a region that was previously mapped with svcMapMemory -static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { +static Result UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, src_addr, size); auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; - if (const ResultCode result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; + if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; result.IsError()) { return result; } @@ -252,12 +252,12 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad return page_table.UnmapMemory(dst_addr, src_addr, size); } -static ResultCode UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { +static Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { return UnmapMemory(system, dst_addr, src_addr, size); } /// Connect to an OS service given the port name, returns the handle to the port to out -static ResultCode ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) { +static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) { auto& memory = system.Memory(); if (!memory.IsValidVirtualAddress(port_name_address)) { LOG_ERROR(Kernel_SVC, @@ -307,14 +307,14 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out, VAddr po return ResultSuccess; } -static ResultCode ConnectToNamedPort32(Core::System& system, Handle* out_handle, - u32 port_name_address) { +static Result ConnectToNamedPort32(Core::System& system, Handle* out_handle, + u32 port_name_address) { return ConnectToNamedPort(system, out_handle, port_name_address); } /// Makes a blocking IPC call to an OS service. -static ResultCode SendSyncRequest(Core::System& system, Handle handle) { +static Result SendSyncRequest(Core::System& system, Handle handle) { auto& kernel = system.Kernel(); // Create the wait queue. @@ -327,7 +327,6 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) { LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); - auto thread = kernel.CurrentScheduler()->GetCurrentThread(); { KScopedSchedulerLock lock(kernel); @@ -337,15 +336,15 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) { session->SendSyncRequest(&GetCurrentThread(kernel), system.Memory(), system.CoreTiming()); } - return thread->GetWaitResult(); + return GetCurrentThread(kernel).GetWaitResult(); } -static ResultCode SendSyncRequest32(Core::System& system, Handle handle) { +static Result SendSyncRequest32(Core::System& system, Handle handle) { return SendSyncRequest(system, handle); } /// Get the ID for the specified thread. -static ResultCode GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) { +static Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) { // Get the thread from its handle. KScopedAutoObject thread = system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); @@ -356,10 +355,10 @@ static ResultCode GetThreadId(Core::System& system, u64* out_thread_id, Handle t return ResultSuccess; } -static ResultCode GetThreadId32(Core::System& system, u32* out_thread_id_low, - u32* out_thread_id_high, Handle thread_handle) { +static Result GetThreadId32(Core::System& system, u32* out_thread_id_low, u32* out_thread_id_high, + Handle thread_handle) { u64 out_thread_id{}; - const ResultCode result{GetThreadId(system, &out_thread_id, thread_handle)}; + const Result result{GetThreadId(system, &out_thread_id, thread_handle)}; *out_thread_id_low = static_cast<u32>(out_thread_id >> 32); *out_thread_id_high = static_cast<u32>(out_thread_id & std::numeric_limits<u32>::max()); @@ -368,7 +367,7 @@ static ResultCode GetThreadId32(Core::System& system, u32* out_thread_id_low, } /// Gets the ID of the specified process or a specified thread's owning process. -static ResultCode GetProcessId(Core::System& system, u64* out_process_id, Handle handle) { +static Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle) { LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle); // Get the object from the handle table. @@ -399,8 +398,8 @@ static ResultCode GetProcessId(Core::System& system, u64* out_process_id, Handle return ResultSuccess; } -static ResultCode GetProcessId32(Core::System& system, u32* out_process_id_low, - u32* out_process_id_high, Handle handle) { +static Result GetProcessId32(Core::System& system, u32* out_process_id_low, + u32* out_process_id_high, Handle handle) { u64 out_process_id{}; const auto result = GetProcessId(system, &out_process_id, handle); *out_process_id_low = static_cast<u32>(out_process_id); @@ -409,8 +408,8 @@ static ResultCode GetProcessId32(Core::System& system, u32* out_process_id_low, } /// Wait for the given handles to synchronize, timeout after the specified nanoseconds -static ResultCode WaitSynchronization(Core::System& system, s32* index, VAddr handles_address, - s32 num_handles, s64 nano_seconds) { +static Result WaitSynchronization(Core::System& system, s32* index, VAddr handles_address, + s32 num_handles, s64 nano_seconds) { LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, num_handles={}, nano_seconds={}", handles_address, num_handles, nano_seconds); @@ -445,14 +444,14 @@ static ResultCode WaitSynchronization(Core::System& system, s32* index, VAddr ha nano_seconds); } -static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address, - s32 num_handles, u32 timeout_high, s32* index) { +static Result WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address, + s32 num_handles, u32 timeout_high, s32* index) { const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)}; return WaitSynchronization(system, index, handles_address, num_handles, nano_seconds); } /// Resumes a thread waiting on WaitSynchronization -static ResultCode CancelSynchronization(Core::System& system, Handle handle) { +static Result CancelSynchronization(Core::System& system, Handle handle) { LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle); // Get the thread from its handle. @@ -465,13 +464,12 @@ static ResultCode CancelSynchronization(Core::System& system, Handle handle) { return ResultSuccess; } -static ResultCode CancelSynchronization32(Core::System& system, Handle handle) { +static Result CancelSynchronization32(Core::System& system, Handle handle) { return CancelSynchronization(system, handle); } /// Attempts to locks a mutex -static ResultCode ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, - u32 tag) { +static Result ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, u32 tag) { LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}", thread_handle, address, tag); @@ -489,13 +487,12 @@ static ResultCode ArbitrateLock(Core::System& system, Handle thread_handle, VAdd return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag); } -static ResultCode ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, - u32 tag) { +static Result ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, u32 tag) { return ArbitrateLock(system, thread_handle, address, tag); } /// Unlock a mutex -static ResultCode ArbitrateUnlock(Core::System& system, VAddr address) { +static Result ArbitrateUnlock(Core::System& system, VAddr address) { LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address); // Validate the input address. @@ -513,7 +510,7 @@ static ResultCode ArbitrateUnlock(Core::System& system, VAddr address) { return system.Kernel().CurrentProcess()->SignalToAddress(address); } -static ResultCode ArbitrateUnlock32(Core::System& system, u32 address) { +static Result ArbitrateUnlock32(Core::System& system, u32 address) { return ArbitrateUnlock(system, address); } @@ -624,10 +621,16 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { handle_debug_buffer(info1, info2); - auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); + auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); const auto thread_processor_id = current_thread->GetActiveCore(); system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace(); } + + if (system.DebuggerEnabled()) { + auto* thread = system.Kernel().GetCurrentEmuThread(); + system.GetDebugger().NotifyThreadStopped(thread); + thread->RequestSuspend(Kernel::SuspendType::Debug); + } } static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) { @@ -645,9 +648,13 @@ static void OutputDebugString(Core::System& system, VAddr address, u64 len) { LOG_DEBUG(Debug_Emulated, "{}", str); } +static void OutputDebugString32(Core::System& system, u32 address, u32 len) { + OutputDebugString(system, address, len); +} + /// Gets system/memory information for the current process -static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle, - u64 info_sub_id) { +static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle, + u64 info_sub_id) { LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, info_sub_id, handle); @@ -682,6 +689,9 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle // 6.0.0+ TotalPhysicalMemoryAvailableWithoutSystemResource = 21, TotalPhysicalMemoryUsedWithoutSystemResource = 22, + + // Homebrew only + MesosphereCurrentProcess = 65001, }; const auto info_id_type = static_cast<GetInfoType>(info_id); @@ -874,10 +884,10 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle const auto& core_timing = system.CoreTiming(); const auto& scheduler = *system.Kernel().CurrentScheduler(); - const auto* const current_thread = scheduler.GetCurrentThread(); + const auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); const bool same_thread = current_thread == thread.GetPointerUnsafe(); - const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks(); + const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTime(); u64 out_ticks = 0; if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { const u64 thread_ticks = current_thread->GetCpuTime(); @@ -896,7 +906,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle // Verify the requested core is valid. const bool core_valid = - (info_sub_id == static_cast<u64>(-1ULL)) || + (info_sub_id == 0xFFFFFFFFFFFFFFFF) || (info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex())); R_UNLESS(core_valid, ResultInvalidCombination); @@ -904,18 +914,39 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime(); return ResultSuccess; } + case GetInfoType::MesosphereCurrentProcess: { + // Verify the input handle is invalid. + R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); + + // Verify the sub-type is valid. + R_UNLESS(info_sub_id == 0, ResultInvalidCombination); + + // Get the handle table. + KProcess* current_process = system.Kernel().CurrentProcess(); + KHandleTable& handle_table = current_process->GetHandleTable(); + + // Get a new handle for the current process. + Handle tmp; + R_TRY(handle_table.Add(&tmp, current_process)); + + // Set the output. + *result = tmp; + + // We succeeded. + return ResultSuccess; + } default: LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id); return ResultInvalidEnumValue; } } -static ResultCode GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low, - u32 info_id, u32 handle, u32 sub_id_high) { +static Result GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low, + u32 info_id, u32 handle, u32 sub_id_high) { const u64 sub_id{u64{sub_id_low} | (u64{sub_id_high} << 32)}; u64 res_value{}; - const ResultCode result{GetInfo(system, &res_value, info_id, handle, sub_id)}; + const Result result{GetInfo(system, &res_value, info_id, handle, sub_id)}; *result_high = static_cast<u32>(res_value >> 32); *result_low = static_cast<u32>(res_value & std::numeric_limits<u32>::max()); @@ -923,7 +954,7 @@ static ResultCode GetInfo32(Core::System& system, u32* result_low, u32* result_h } /// Maps memory at a desired address -static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { +static Result MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); if (!Common::Is4KBAligned(addr)) { @@ -971,12 +1002,12 @@ static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) return page_table.MapPhysicalMemory(addr, size); } -static ResultCode MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { +static Result MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { return MapPhysicalMemory(system, addr, size); } /// Unmaps memory previously mapped via MapPhysicalMemory -static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { +static Result UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); if (!Common::Is4KBAligned(addr)) { @@ -1024,13 +1055,13 @@ static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size return page_table.UnmapPhysicalMemory(addr, size); } -static ResultCode UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { +static Result UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { return UnmapPhysicalMemory(system, addr, size); } /// Sets the thread activity -static ResultCode SetThreadActivity(Core::System& system, Handle thread_handle, - ThreadActivity thread_activity) { +static Result SetThreadActivity(Core::System& system, Handle thread_handle, + ThreadActivity thread_activity) { LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle, thread_activity); @@ -1055,13 +1086,13 @@ static ResultCode SetThreadActivity(Core::System& system, Handle thread_handle, return ResultSuccess; } -static ResultCode SetThreadActivity32(Core::System& system, Handle thread_handle, - Svc::ThreadActivity thread_activity) { +static Result SetThreadActivity32(Core::System& system, Handle thread_handle, + Svc::ThreadActivity thread_activity) { return SetThreadActivity(system, thread_handle, thread_activity); } /// Gets the thread context -static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) { +static Result GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) { LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context, thread_handle); @@ -1093,7 +1124,7 @@ static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Hand if (thread->GetRawState() != ThreadState::Runnable) { bool current = false; for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { - if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetCurrentThread()) { + if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetSchedulerCurrentThread()) { current = true; break; } @@ -1118,12 +1149,12 @@ static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Hand return ResultSuccess; } -static ResultCode GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) { +static Result GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) { return GetThreadContext(system, out_context, thread_handle); } /// Gets the priority for the specified thread -static ResultCode GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) { +static Result GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) { LOG_TRACE(Kernel_SVC, "called"); // Get the thread from its handle. @@ -1136,12 +1167,12 @@ static ResultCode GetThreadPriority(Core::System& system, u32* out_priority, Han return ResultSuccess; } -static ResultCode GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) { +static Result GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) { return GetThreadPriority(system, out_priority, handle); } /// Sets the priority for the specified thread -static ResultCode SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority) { +static Result SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority) { // Get the current process. KProcess& process = *system.Kernel().CurrentProcess(); @@ -1159,7 +1190,7 @@ static ResultCode SetThreadPriority(Core::System& system, Handle thread_handle, return ResultSuccess; } -static ResultCode SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority) { +static Result SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority) { return SetThreadPriority(system, thread_handle, priority); } @@ -1219,8 +1250,8 @@ constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(Svc::MemoryPermission p } // Anonymous namespace -static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, - u64 size, Svc::MemoryPermission map_perm) { +static Result MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size, + Svc::MemoryPermission map_perm) { LOG_TRACE(Kernel_SVC, "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", shmem_handle, address, size, map_perm); @@ -1260,13 +1291,13 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAd return ResultSuccess; } -static ResultCode MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, - u32 size, Svc::MemoryPermission map_perm) { +static Result MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size, + Svc::MemoryPermission map_perm) { return MapSharedMemory(system, shmem_handle, address, size, map_perm); } -static ResultCode UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, - u64 size) { +static Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, + u64 size) { // Validate the address/size. R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); @@ -1293,13 +1324,13 @@ static ResultCode UnmapSharedMemory(Core::System& system, Handle shmem_handle, V return ResultSuccess; } -static ResultCode UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, - u32 size) { +static Result UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, + u32 size) { return UnmapSharedMemory(system, shmem_handle, address, size); } -static ResultCode SetProcessMemoryPermission(Core::System& system, Handle process_handle, - VAddr address, u64 size, Svc::MemoryPermission perm) { +static Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, VAddr address, + u64 size, Svc::MemoryPermission perm) { LOG_TRACE(Kernel_SVC, "called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", process_handle, address, size, perm); @@ -1328,8 +1359,8 @@ static ResultCode SetProcessMemoryPermission(Core::System& system, Handle proces return page_table.SetProcessMemoryPermission(address, size, perm); } -static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, - VAddr src_address, u64 size) { +static Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, + VAddr src_address, u64 size) { LOG_TRACE(Kernel_SVC, "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}", dst_address, process_handle, src_address, size); @@ -1358,8 +1389,11 @@ static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Hand ResultInvalidMemoryRegion); // Create a new page group. - KMemoryInfo kBlockInfo = dst_pt.QueryInfo(dst_address); - KPageLinkedList pg(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages()); + KPageGroup pg; + R_TRY(src_pt.MakeAndOpenPageGroup( + std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess, + KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None, + KMemoryAttribute::All, KMemoryAttribute::None)); // Map the group. R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode, @@ -1368,8 +1402,8 @@ static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Hand return ResultSuccess; } -static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, - VAddr src_address, u64 size) { +static Result UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, + VAddr src_address, u64 size) { LOG_TRACE(Kernel_SVC, "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}", dst_address, process_handle, src_address, size); @@ -1403,9 +1437,9 @@ static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Ha return ResultSuccess; } -static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) { - LOG_TRACE(Kernel_SVC, "called, handle_out=0x{:X}, address=0x{:X}, size=0x{:X}", - static_cast<void*>(out), address, size); +static Result CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) { + LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size); + // Get kernel instance. auto& kernel = system.Kernel(); @@ -1438,8 +1472,12 @@ static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr addr return ResultSuccess; } -static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation, - VAddr address, size_t size, Svc::MemoryPermission perm) { +static Result CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size) { + return CreateCodeMemory(system, out, address, size); +} + +static Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation, + VAddr address, size_t size, Svc::MemoryPermission perm) { LOG_TRACE(Kernel_SVC, "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, " @@ -1517,9 +1555,13 @@ static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_han return ResultSuccess; } -static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, - VAddr page_info_address, Handle process_handle, - VAddr address) { +static Result ControlCodeMemory32(Core::System& system, Handle code_memory_handle, u32 operation, + u64 address, u64 size, Svc::MemoryPermission perm) { + return ControlCodeMemory(system, code_memory_handle, operation, address, size, perm); +} + +static Result QueryProcessMemory(Core::System& system, VAddr memory_info_address, + VAddr page_info_address, Handle process_handle, VAddr address) { LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address); const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); @@ -1547,8 +1589,8 @@ static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_add return ResultSuccess; } -static ResultCode QueryMemory(Core::System& system, VAddr memory_info_address, - VAddr page_info_address, VAddr query_address) { +static Result QueryMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address, + VAddr query_address) { LOG_TRACE(Kernel_SVC, "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, " "query_address=0x{:016X}", @@ -1558,13 +1600,13 @@ static ResultCode QueryMemory(Core::System& system, VAddr memory_info_address, query_address); } -static ResultCode QueryMemory32(Core::System& system, u32 memory_info_address, - u32 page_info_address, u32 query_address) { +static Result QueryMemory32(Core::System& system, u32 memory_info_address, u32 page_info_address, + u32 query_address) { return QueryMemory(system, memory_info_address, page_info_address, query_address); } -static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, - u64 src_address, u64 size) { +static Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, + u64 src_address, u64 size) { LOG_DEBUG(Kernel_SVC, "called. process_handle=0x{:08X}, dst_address=0x{:016X}, " "src_address=0x{:016X}, size=0x{:016X}", @@ -1631,8 +1673,8 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand return page_table.MapCodeMemory(dst_address, src_address, size); } -static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle, - u64 dst_address, u64 src_address, u64 size) { +static Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, + u64 src_address, u64 size) { LOG_DEBUG(Kernel_SVC, "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, " "size=0x{:016X}", @@ -1650,7 +1692,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha return ResultInvalidAddress; } - if (size == 0 || Common::Is4KBAligned(size)) { + if (size == 0 || !Common::Is4KBAligned(size)) { LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size); return ResultInvalidSize; } @@ -1696,17 +1738,19 @@ 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 static void ExitProcess(Core::System& system) { auto* current_process = system.Kernel().CurrentProcess(); - UNIMPLEMENTED(); LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, "Process has already exited"); + + system.Exit(); } static void ExitProcess32(Core::System& system) { @@ -1722,8 +1766,8 @@ constexpr bool IsValidVirtualCoreId(int32_t core_id) { } // Anonymous namespace /// Creates a new thread -static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, - VAddr stack_bottom, u32 priority, s32 core_id) { +static Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, + VAddr stack_bottom, u32 priority, s32 core_id) { LOG_DEBUG(Kernel_SVC, "called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, " "priority=0x{:08X}, core_id=0x{:08X}", @@ -1794,13 +1838,13 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e return ResultSuccess; } -static ResultCode CreateThread32(Core::System& system, Handle* out_handle, u32 priority, - u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) { +static Result CreateThread32(Core::System& system, Handle* out_handle, u32 priority, + u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) { return CreateThread(system, out_handle, entry_point, arg, stack_top, priority, processor_id); } /// Starts the thread for the provided handle -static ResultCode StartThread(Core::System& system, Handle thread_handle) { +static Result StartThread(Core::System& system, Handle thread_handle) { LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle); // Get the thread from its handle. @@ -1818,7 +1862,7 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) { return ResultSuccess; } -static ResultCode StartThread32(Core::System& system, Handle thread_handle) { +static Result StartThread32(Core::System& system, Handle thread_handle) { return StartThread(system, thread_handle); } @@ -1826,7 +1870,7 @@ static ResultCode StartThread32(Core::System& system, Handle thread_handle) { static void ExitThread(Core::System& system) { LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); - auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); + auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); system.GlobalSchedulerContext().RemoveThread(current_thread); current_thread->Exit(); system.Kernel().UnregisterInUseObject(current_thread); @@ -1859,7 +1903,7 @@ static void SleepThread(Core::System& system, s64 nanoseconds) { KScheduler::YieldToAnyThread(kernel); } else { // Nintendo does nothing at all if an otherwise invalid value is passed. - UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); + ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds); } } @@ -1869,8 +1913,8 @@ static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanosec } /// Wait process wide key atomic -static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, - u32 tag, s64 timeout_ns) { +static Result WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, u32 tag, + s64 timeout_ns) { LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address, cv_key, tag, timeout_ns); @@ -1905,8 +1949,8 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr address, address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout); } -static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag, - u32 timeout_ns_low, u32 timeout_ns_high) { +static Result WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag, + u32 timeout_ns_low, u32 timeout_ns_high) { const auto timeout_ns = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32)); return WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns); } @@ -1951,8 +1995,8 @@ constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) { } // namespace // Wait for an address (via Address Arbiter) -static ResultCode WaitForAddress(Core::System& system, VAddr address, Svc::ArbitrationType arb_type, - s32 value, s64 timeout_ns) { +static Result WaitForAddress(Core::System& system, VAddr address, Svc::ArbitrationType arb_type, + s32 value, s64 timeout_ns) { LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}", address, arb_type, value, timeout_ns); @@ -1989,15 +2033,15 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, Svc::Arbit return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout); } -static ResultCode WaitForAddress32(Core::System& system, u32 address, Svc::ArbitrationType arb_type, - s32 value, u32 timeout_ns_low, u32 timeout_ns_high) { +static Result WaitForAddress32(Core::System& system, u32 address, Svc::ArbitrationType arb_type, + s32 value, u32 timeout_ns_low, u32 timeout_ns_high) { const auto timeout = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32)); return WaitForAddress(system, address, arb_type, value, timeout); } // Signals to an address (via Address Arbiter) -static ResultCode SignalToAddress(Core::System& system, VAddr address, Svc::SignalType signal_type, - s32 value, s32 count) { +static Result SignalToAddress(Core::System& system, VAddr address, Svc::SignalType signal_type, + s32 value, s32 count) { LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}", address, signal_type, value, count); @@ -2038,8 +2082,8 @@ static void SynchronizePreemptionState(Core::System& system) { } } -static ResultCode SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type, - s32 value, s32 count) { +static Result SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type, + s32 value, s32 count) { return SignalToAddress(system, address, signal_type, value, count); } @@ -2077,7 +2121,7 @@ static void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high) } /// Close a handle -static ResultCode CloseHandle(Core::System& system, Handle handle) { +static Result CloseHandle(Core::System& system, Handle handle) { LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); // Remove the handle. @@ -2087,12 +2131,12 @@ static ResultCode CloseHandle(Core::System& system, Handle handle) { return ResultSuccess; } -static ResultCode CloseHandle32(Core::System& system, Handle handle) { +static Result CloseHandle32(Core::System& system, Handle handle) { return CloseHandle(system, handle); } /// Clears the signaled state of an event or process. -static ResultCode ResetSignal(Core::System& system, Handle handle) { +static Result ResetSignal(Core::System& system, Handle handle) { LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle); // Get the current handle table. @@ -2119,7 +2163,7 @@ static ResultCode ResetSignal(Core::System& system, Handle handle) { return ResultInvalidHandle; } -static ResultCode ResetSignal32(Core::System& system, Handle handle) { +static Result ResetSignal32(Core::System& system, Handle handle) { return ResetSignal(system, handle); } @@ -2139,8 +2183,8 @@ constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) { } // Anonymous namespace /// Creates a TransferMemory object -static ResultCode CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size, - MemoryPermission map_perm) { +static Result CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size, + MemoryPermission map_perm) { auto& kernel = system.Kernel(); // Validate the size. @@ -2186,13 +2230,13 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* out, VAddr return ResultSuccess; } -static ResultCode CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size, - MemoryPermission map_perm) { +static Result CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size, + MemoryPermission map_perm) { return CreateTransferMemory(system, out, address, size, map_perm); } -static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id, - u64* out_affinity_mask) { +static Result GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id, + u64* out_affinity_mask) { LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); // Get the thread from its handle. @@ -2206,8 +2250,8 @@ static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, return ResultSuccess; } -static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id, - u32* out_affinity_mask_low, u32* out_affinity_mask_high) { +static Result GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id, + u32* out_affinity_mask_low, u32* out_affinity_mask_high) { u64 out_affinity_mask{}; const auto result = GetThreadCoreMask(system, thread_handle, out_core_id, &out_affinity_mask); *out_affinity_mask_high = static_cast<u32>(out_affinity_mask >> 32); @@ -2215,8 +2259,8 @@ static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle return result; } -static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id, - u64 affinity_mask) { +static Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id, + u64 affinity_mask) { // Determine the core id/affinity mask. if (core_id == IdealCoreUseProcessValue) { core_id = system.Kernel().CurrentProcess()->GetIdealCoreId(); @@ -2247,13 +2291,13 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, return ResultSuccess; } -static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id, - u32 affinity_mask_low, u32 affinity_mask_high) { +static Result SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id, + u32 affinity_mask_low, u32 affinity_mask_high) { const auto affinity_mask = u64{affinity_mask_low} | (u64{affinity_mask_high} << 32); return SetThreadCoreMask(system, thread_handle, core_id, affinity_mask); } -static ResultCode SignalEvent(Core::System& system, Handle event_handle) { +static Result SignalEvent(Core::System& system, Handle event_handle) { LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle); // Get the current handle table. @@ -2266,11 +2310,11 @@ static ResultCode SignalEvent(Core::System& system, Handle event_handle) { return writable_event->Signal(); } -static ResultCode SignalEvent32(Core::System& system, Handle event_handle) { +static Result SignalEvent32(Core::System& system, Handle event_handle) { return SignalEvent(system, event_handle); } -static ResultCode ClearEvent(Core::System& system, Handle event_handle) { +static Result ClearEvent(Core::System& system, Handle event_handle) { LOG_TRACE(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle); // Get the current handle table. @@ -2297,11 +2341,11 @@ static ResultCode ClearEvent(Core::System& system, Handle event_handle) { return ResultInvalidHandle; } -static ResultCode ClearEvent32(Core::System& system, Handle event_handle) { +static Result ClearEvent32(Core::System& system, Handle event_handle) { return ClearEvent(system, event_handle); } -static ResultCode CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) { +static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) { LOG_DEBUG(Kernel_SVC, "called"); // Get the kernel reference and handle table. @@ -2318,7 +2362,7 @@ static ResultCode CreateEvent(Core::System& system, Handle* out_write, Handle* o R_UNLESS(event != nullptr, ResultOutOfResource); // Initialize the event. - event->Initialize("CreateEvent"); + event->Initialize("CreateEvent", kernel.CurrentProcess()); // Commit the thread reservation. event_reservation.Commit(); @@ -2346,11 +2390,11 @@ static ResultCode CreateEvent(Core::System& system, Handle* out_write, Handle* o return ResultSuccess; } -static ResultCode CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read) { +static Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read) { return CreateEvent(system, out_write, out_read); } -static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { +static Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); // This function currently only allows retrieving a process' status. @@ -2376,7 +2420,7 @@ static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_ return ResultSuccess; } -static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) { +static Result CreateResourceLimit(Core::System& system, Handle* out_handle) { LOG_DEBUG(Kernel_SVC, "called"); // Create a new resource limit. @@ -2399,9 +2443,8 @@ static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) return ResultSuccess; } -static ResultCode GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value, - Handle resource_limit_handle, - LimitableResource which) { +static Result GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value, + Handle resource_limit_handle, LimitableResource which) { LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle, which); @@ -2420,9 +2463,8 @@ static ResultCode GetResourceLimitLimitValue(Core::System& system, u64* out_limi return ResultSuccess; } -static ResultCode GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value, - Handle resource_limit_handle, - LimitableResource which) { +static Result GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value, + Handle resource_limit_handle, LimitableResource which) { LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle, which); @@ -2441,8 +2483,8 @@ static ResultCode GetResourceLimitCurrentValue(Core::System& system, u64* out_cu return ResultSuccess; } -static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle, - LimitableResource which, u64 limit_value) { +static Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle, + LimitableResource which, u64 limit_value) { LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}, limit_value={}", resource_limit_handle, which, limit_value); @@ -2461,8 +2503,8 @@ static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resour return ResultSuccess; } -static ResultCode GetProcessList(Core::System& system, u32* out_num_processes, - VAddr out_process_ids, u32 out_process_ids_size) { +static Result GetProcessList(Core::System& system, u32* out_num_processes, VAddr out_process_ids, + u32 out_process_ids_size) { LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}", out_process_ids, out_process_ids_size); @@ -2498,8 +2540,8 @@ static ResultCode GetProcessList(Core::System& system, u32* out_num_processes, return ResultSuccess; } -static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids, - u32 out_thread_ids_size, Handle debug_handle) { +static Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids, + u32 out_thread_ids_size, Handle debug_handle) { // TODO: Handle this case when debug events are supported. UNIMPLEMENTED_IF(debug_handle != InvalidHandle); @@ -2513,7 +2555,7 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd return ResultOutOfRange; } - const auto* const current_process = system.Kernel().CurrentProcess(); + auto* const current_process = system.Kernel().CurrentProcess(); const auto total_copy_size = out_thread_ids_size * sizeof(u64); if (out_thread_ids_size > 0 && @@ -2538,9 +2580,9 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd return ResultSuccess; } -static ResultCode FlushProcessDataCache32([[maybe_unused]] Core::System& system, - [[maybe_unused]] Handle handle, - [[maybe_unused]] u32 address, [[maybe_unused]] u32 size) { +static Result FlushProcessDataCache32([[maybe_unused]] Core::System& system, + [[maybe_unused]] Handle handle, [[maybe_unused]] u32 address, + [[maybe_unused]] u32 size) { // Note(Blinkhawk): For emulation purposes of the data cache this is mostly a no-op, // as all emulation is done in the same cache level in host architecture, thus data cache // does not need flushing. @@ -2559,9 +2601,9 @@ struct FunctionDef { } // namespace static const FunctionDef SVC_Table_32[] = { - {0x00, nullptr, "Unknown"}, + {0x00, nullptr, "Unknown0"}, {0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"}, - {0x02, nullptr, "Unknown"}, + {0x02, nullptr, "SetMemoryPermission32"}, {0x03, SvcWrap32<SetMemoryAttribute32>, "SetMemoryAttribute32"}, {0x04, SvcWrap32<MapMemory32>, "MapMemory32"}, {0x05, SvcWrap32<UnmapMemory32>, "UnmapMemory32"}, @@ -2591,97 +2633,97 @@ static const FunctionDef SVC_Table_32[] = { {0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"}, {0x1e, SvcWrap32<GetSystemTick32>, "GetSystemTick32"}, {0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"}, - {0x20, nullptr, "Unknown"}, + {0x20, nullptr, "SendSyncRequestLight32"}, {0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"}, {0x22, nullptr, "SendSyncRequestWithUserBuffer32"}, - {0x23, nullptr, "Unknown"}, + {0x23, nullptr, "SendAsyncRequestWithUserBuffer32"}, {0x24, SvcWrap32<GetProcessId32>, "GetProcessId32"}, {0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"}, {0x26, SvcWrap32<Break32>, "Break32"}, - {0x27, nullptr, "OutputDebugString32"}, - {0x28, nullptr, "Unknown"}, + {0x27, SvcWrap32<OutputDebugString32>, "OutputDebugString32"}, + {0x28, nullptr, "ReturnFromException32"}, {0x29, SvcWrap32<GetInfo32>, "GetInfo32"}, - {0x2a, nullptr, "Unknown"}, - {0x2b, nullptr, "Unknown"}, + {0x2a, nullptr, "FlushEntireDataCache32"}, + {0x2b, nullptr, "FlushDataCache32"}, {0x2c, SvcWrap32<MapPhysicalMemory32>, "MapPhysicalMemory32"}, {0x2d, SvcWrap32<UnmapPhysicalMemory32>, "UnmapPhysicalMemory32"}, - {0x2e, nullptr, "Unknown"}, - {0x2f, nullptr, "Unknown"}, - {0x30, nullptr, "Unknown"}, - {0x31, nullptr, "Unknown"}, + {0x2e, nullptr, "GetDebugFutureThreadInfo32"}, + {0x2f, nullptr, "GetLastThreadInfo32"}, + {0x30, nullptr, "GetResourceLimitLimitValue32"}, + {0x31, nullptr, "GetResourceLimitCurrentValue32"}, {0x32, SvcWrap32<SetThreadActivity32>, "SetThreadActivity32"}, {0x33, SvcWrap32<GetThreadContext32>, "GetThreadContext32"}, {0x34, SvcWrap32<WaitForAddress32>, "WaitForAddress32"}, {0x35, SvcWrap32<SignalToAddress32>, "SignalToAddress32"}, {0x36, SvcWrap32<SynchronizePreemptionState>, "SynchronizePreemptionState32"}, - {0x37, nullptr, "Unknown"}, - {0x38, nullptr, "Unknown"}, - {0x39, nullptr, "Unknown"}, - {0x3a, nullptr, "Unknown"}, - {0x3b, nullptr, "Unknown"}, - {0x3c, nullptr, "Unknown"}, - {0x3d, nullptr, "Unknown"}, - {0x3e, nullptr, "Unknown"}, - {0x3f, nullptr, "Unknown"}, + {0x37, nullptr, "GetResourceLimitPeakValue32"}, + {0x38, nullptr, "Unknown38"}, + {0x39, nullptr, "CreateIoPool32"}, + {0x3a, nullptr, "CreateIoRegion32"}, + {0x3b, nullptr, "Unknown3b"}, + {0x3c, nullptr, "KernelDebug32"}, + {0x3d, nullptr, "ChangeKernelTraceState32"}, + {0x3e, nullptr, "Unknown3e"}, + {0x3f, nullptr, "Unknown3f"}, {0x40, nullptr, "CreateSession32"}, {0x41, nullptr, "AcceptSession32"}, - {0x42, nullptr, "Unknown"}, + {0x42, nullptr, "ReplyAndReceiveLight32"}, {0x43, nullptr, "ReplyAndReceive32"}, - {0x44, nullptr, "Unknown"}, + {0x44, nullptr, "ReplyAndReceiveWithUserBuffer32"}, {0x45, SvcWrap32<CreateEvent32>, "CreateEvent32"}, - {0x46, nullptr, "Unknown"}, - {0x47, nullptr, "Unknown"}, - {0x48, nullptr, "Unknown"}, - {0x49, nullptr, "Unknown"}, - {0x4a, nullptr, "Unknown"}, - {0x4b, nullptr, "Unknown"}, - {0x4c, nullptr, "Unknown"}, - {0x4d, nullptr, "Unknown"}, - {0x4e, nullptr, "Unknown"}, - {0x4f, nullptr, "Unknown"}, - {0x50, nullptr, "Unknown"}, - {0x51, nullptr, "Unknown"}, - {0x52, nullptr, "Unknown"}, - {0x53, nullptr, "Unknown"}, - {0x54, nullptr, "Unknown"}, - {0x55, nullptr, "Unknown"}, - {0x56, nullptr, "Unknown"}, - {0x57, nullptr, "Unknown"}, - {0x58, nullptr, "Unknown"}, - {0x59, nullptr, "Unknown"}, - {0x5a, nullptr, "Unknown"}, - {0x5b, nullptr, "Unknown"}, - {0x5c, nullptr, "Unknown"}, - {0x5d, nullptr, "Unknown"}, - {0x5e, nullptr, "Unknown"}, + {0x46, nullptr, "MapIoRegion32"}, + {0x47, nullptr, "UnmapIoRegion32"}, + {0x48, nullptr, "MapPhysicalMemoryUnsafe32"}, + {0x49, nullptr, "UnmapPhysicalMemoryUnsafe32"}, + {0x4a, nullptr, "SetUnsafeLimit32"}, + {0x4b, SvcWrap32<CreateCodeMemory32>, "CreateCodeMemory32"}, + {0x4c, SvcWrap32<ControlCodeMemory32>, "ControlCodeMemory32"}, + {0x4d, nullptr, "SleepSystem32"}, + {0x4e, nullptr, "ReadWriteRegister32"}, + {0x4f, nullptr, "SetProcessActivity32"}, + {0x50, nullptr, "CreateSharedMemory32"}, + {0x51, nullptr, "MapTransferMemory32"}, + {0x52, nullptr, "UnmapTransferMemory32"}, + {0x53, nullptr, "CreateInterruptEvent32"}, + {0x54, nullptr, "QueryPhysicalAddress32"}, + {0x55, nullptr, "QueryIoMapping32"}, + {0x56, nullptr, "CreateDeviceAddressSpace32"}, + {0x57, nullptr, "AttachDeviceAddressSpace32"}, + {0x58, nullptr, "DetachDeviceAddressSpace32"}, + {0x59, nullptr, "MapDeviceAddressSpaceByForce32"}, + {0x5a, nullptr, "MapDeviceAddressSpaceAligned32"}, + {0x5b, nullptr, "MapDeviceAddressSpace32"}, + {0x5c, nullptr, "UnmapDeviceAddressSpace32"}, + {0x5d, nullptr, "InvalidateProcessDataCache32"}, + {0x5e, nullptr, "StoreProcessDataCache32"}, {0x5F, SvcWrap32<FlushProcessDataCache32>, "FlushProcessDataCache32"}, - {0x60, nullptr, "Unknown"}, - {0x61, nullptr, "Unknown"}, - {0x62, nullptr, "Unknown"}, - {0x63, nullptr, "Unknown"}, - {0x64, nullptr, "Unknown"}, + {0x60, nullptr, "StoreProcessDataCache32"}, + {0x61, nullptr, "BreakDebugProcess32"}, + {0x62, nullptr, "TerminateDebugProcess32"}, + {0x63, nullptr, "GetDebugEvent32"}, + {0x64, nullptr, "ContinueDebugEvent32"}, {0x65, nullptr, "GetProcessList32"}, - {0x66, nullptr, "Unknown"}, - {0x67, nullptr, "Unknown"}, - {0x68, nullptr, "Unknown"}, - {0x69, nullptr, "Unknown"}, - {0x6A, nullptr, "Unknown"}, - {0x6B, nullptr, "Unknown"}, - {0x6C, nullptr, "Unknown"}, - {0x6D, nullptr, "Unknown"}, - {0x6E, nullptr, "Unknown"}, + {0x66, nullptr, "GetThreadList"}, + {0x67, nullptr, "GetDebugThreadContext32"}, + {0x68, nullptr, "SetDebugThreadContext32"}, + {0x69, nullptr, "QueryDebugProcessMemory32"}, + {0x6A, nullptr, "ReadDebugProcessMemory32"}, + {0x6B, nullptr, "WriteDebugProcessMemory32"}, + {0x6C, nullptr, "SetHardwareBreakPoint32"}, + {0x6D, nullptr, "GetDebugThreadParam32"}, + {0x6E, nullptr, "Unknown6E"}, {0x6f, nullptr, "GetSystemInfo32"}, {0x70, nullptr, "CreatePort32"}, {0x71, nullptr, "ManageNamedPort32"}, {0x72, nullptr, "ConnectToPort32"}, {0x73, nullptr, "SetProcessMemoryPermission32"}, - {0x74, nullptr, "Unknown"}, - {0x75, nullptr, "Unknown"}, - {0x76, nullptr, "Unknown"}, + {0x74, nullptr, "MapProcessMemory32"}, + {0x75, nullptr, "UnmapProcessMemory32"}, + {0x76, nullptr, "QueryProcessMemory32"}, {0x77, nullptr, "MapProcessCodeMemory32"}, {0x78, nullptr, "UnmapProcessCodeMemory32"}, - {0x79, nullptr, "Unknown"}, - {0x7A, nullptr, "Unknown"}, + {0x79, nullptr, "CreateProcess32"}, + {0x7A, nullptr, "StartProcess32"}, {0x7B, nullptr, "TerminateProcess32"}, {0x7C, nullptr, "GetProcessInfo32"}, {0x7D, nullptr, "CreateResourceLimit32"}, @@ -2754,7 +2796,7 @@ static const FunctionDef SVC_Table_32[] = { }; static const FunctionDef SVC_Table_64[] = { - {0x00, nullptr, "Unknown"}, + {0x00, nullptr, "Unknown0"}, {0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"}, {0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"}, {0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"}, @@ -2809,23 +2851,23 @@ static const FunctionDef SVC_Table_64[] = { {0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"}, {0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"}, {0x36, SvcWrap64<SynchronizePreemptionState>, "SynchronizePreemptionState"}, - {0x37, nullptr, "Unknown"}, - {0x38, nullptr, "Unknown"}, - {0x39, nullptr, "Unknown"}, - {0x3A, nullptr, "Unknown"}, - {0x3B, nullptr, "Unknown"}, + {0x37, nullptr, "GetResourceLimitPeakValue"}, + {0x38, nullptr, "Unknown38"}, + {0x39, nullptr, "CreateIoPool"}, + {0x3A, nullptr, "CreateIoRegion"}, + {0x3B, nullptr, "Unknown3B"}, {0x3C, SvcWrap64<KernelDebug>, "KernelDebug"}, {0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"}, - {0x3E, nullptr, "Unknown"}, - {0x3F, nullptr, "Unknown"}, + {0x3E, nullptr, "Unknown3e"}, + {0x3F, nullptr, "Unknown3f"}, {0x40, nullptr, "CreateSession"}, {0x41, nullptr, "AcceptSession"}, {0x42, nullptr, "ReplyAndReceiveLight"}, {0x43, nullptr, "ReplyAndReceive"}, {0x44, nullptr, "ReplyAndReceiveWithUserBuffer"}, {0x45, SvcWrap64<CreateEvent>, "CreateEvent"}, - {0x46, nullptr, "Unknown"}, - {0x47, nullptr, "Unknown"}, + {0x46, nullptr, "MapIoRegion"}, + {0x47, nullptr, "UnmapIoRegion"}, {0x48, nullptr, "MapPhysicalMemoryUnsafe"}, {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"}, {0x4A, nullptr, "SetUnsafeLimit"}, @@ -2864,7 +2906,7 @@ static const FunctionDef SVC_Table_64[] = { {0x6B, nullptr, "WriteDebugProcessMemory"}, {0x6C, nullptr, "SetHardwareBreakPoint"}, {0x6D, nullptr, "GetDebugThreadParam"}, - {0x6E, nullptr, "Unknown"}, + {0x6E, nullptr, "Unknown6E"}, {0x6F, nullptr, "GetSystemInfo"}, {0x70, nullptr, "CreatePort"}, {0x71, nullptr, "ManageNamedPort"}, @@ -2965,11 +3007,10 @@ static const FunctionDef* GetSVCInfo64(u32 func_num) { } void Call(Core::System& system, u32 immediate) { - system.ExitDynarmicProfile(); auto& kernel = system.Kernel(); kernel.EnterSVCProfile(); - auto* thread = kernel.CurrentScheduler()->GetCurrentThread(); + auto* thread = GetCurrentThreadPointer(kernel); thread->SetIsCallingSvc(); const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate) @@ -2985,13 +3026,6 @@ void Call(Core::System& system, u32 immediate) { } kernel.ExitSVCProfile(); - - if (!thread->IsCallingSvc()) { - auto* host_context = thread->GetHostContext().get(); - host_context->Rewind(); - } - - system.EnterDynarmicProfile(); } } // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h index 46e64277e..13f061b83 100644 --- a/src/core/hle/kernel/svc.h +++ b/src/core/hle/kernel/svc.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/svc_common.h b/src/core/hle/kernel/svc_common.h index 25de6e437..95750c3eb 100644 --- a/src/core/hle/kernel/svc_common.h +++ b/src/core/hle/kernel/svc_common.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/kernel/svc_results.h b/src/core/hle/kernel/svc_results.h index 53a940723..f27cade33 100644 --- a/src/core/hle/kernel/svc_results.h +++ b/src/core/hle/kernel/svc_results.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -10,34 +9,34 @@ namespace Kernel { // Confirmed Switch kernel error codes -constexpr ResultCode ResultOutOfSessions{ErrorModule::Kernel, 7}; -constexpr ResultCode ResultInvalidArgument{ErrorModule::Kernel, 14}; -constexpr ResultCode ResultNoSynchronizationObject{ErrorModule::Kernel, 57}; -constexpr ResultCode ResultTerminationRequested{ErrorModule::Kernel, 59}; -constexpr ResultCode ResultInvalidSize{ErrorModule::Kernel, 101}; -constexpr ResultCode ResultInvalidAddress{ErrorModule::Kernel, 102}; -constexpr ResultCode ResultOutOfResource{ErrorModule::Kernel, 103}; -constexpr ResultCode ResultOutOfMemory{ErrorModule::Kernel, 104}; -constexpr ResultCode ResultOutOfHandles{ErrorModule::Kernel, 105}; -constexpr ResultCode ResultInvalidCurrentMemory{ErrorModule::Kernel, 106}; -constexpr ResultCode ResultInvalidNewMemoryPermission{ErrorModule::Kernel, 108}; -constexpr ResultCode ResultInvalidMemoryRegion{ErrorModule::Kernel, 110}; -constexpr ResultCode ResultInvalidPriority{ErrorModule::Kernel, 112}; -constexpr ResultCode ResultInvalidCoreId{ErrorModule::Kernel, 113}; -constexpr ResultCode ResultInvalidHandle{ErrorModule::Kernel, 114}; -constexpr ResultCode ResultInvalidPointer{ErrorModule::Kernel, 115}; -constexpr ResultCode ResultInvalidCombination{ErrorModule::Kernel, 116}; -constexpr ResultCode ResultTimedOut{ErrorModule::Kernel, 117}; -constexpr ResultCode ResultCancelled{ErrorModule::Kernel, 118}; -constexpr ResultCode ResultOutOfRange{ErrorModule::Kernel, 119}; -constexpr ResultCode ResultInvalidEnumValue{ErrorModule::Kernel, 120}; -constexpr ResultCode ResultNotFound{ErrorModule::Kernel, 121}; -constexpr ResultCode ResultBusy{ErrorModule::Kernel, 122}; -constexpr ResultCode ResultSessionClosed{ErrorModule::Kernel, 123}; -constexpr ResultCode ResultInvalidState{ErrorModule::Kernel, 125}; -constexpr ResultCode ResultReservedUsed{ErrorModule::Kernel, 126}; -constexpr ResultCode ResultPortClosed{ErrorModule::Kernel, 131}; -constexpr ResultCode ResultLimitReached{ErrorModule::Kernel, 132}; -constexpr ResultCode ResultInvalidId{ErrorModule::Kernel, 519}; +constexpr Result ResultOutOfSessions{ErrorModule::Kernel, 7}; +constexpr Result ResultInvalidArgument{ErrorModule::Kernel, 14}; +constexpr Result ResultNoSynchronizationObject{ErrorModule::Kernel, 57}; +constexpr Result ResultTerminationRequested{ErrorModule::Kernel, 59}; +constexpr Result ResultInvalidSize{ErrorModule::Kernel, 101}; +constexpr Result ResultInvalidAddress{ErrorModule::Kernel, 102}; +constexpr Result ResultOutOfResource{ErrorModule::Kernel, 103}; +constexpr Result ResultOutOfMemory{ErrorModule::Kernel, 104}; +constexpr Result ResultOutOfHandles{ErrorModule::Kernel, 105}; +constexpr Result ResultInvalidCurrentMemory{ErrorModule::Kernel, 106}; +constexpr Result ResultInvalidNewMemoryPermission{ErrorModule::Kernel, 108}; +constexpr Result ResultInvalidMemoryRegion{ErrorModule::Kernel, 110}; +constexpr Result ResultInvalidPriority{ErrorModule::Kernel, 112}; +constexpr Result ResultInvalidCoreId{ErrorModule::Kernel, 113}; +constexpr Result ResultInvalidHandle{ErrorModule::Kernel, 114}; +constexpr Result ResultInvalidPointer{ErrorModule::Kernel, 115}; +constexpr Result ResultInvalidCombination{ErrorModule::Kernel, 116}; +constexpr Result ResultTimedOut{ErrorModule::Kernel, 117}; +constexpr Result ResultCancelled{ErrorModule::Kernel, 118}; +constexpr Result ResultOutOfRange{ErrorModule::Kernel, 119}; +constexpr Result ResultInvalidEnumValue{ErrorModule::Kernel, 120}; +constexpr Result ResultNotFound{ErrorModule::Kernel, 121}; +constexpr Result ResultBusy{ErrorModule::Kernel, 122}; +constexpr Result ResultSessionClosed{ErrorModule::Kernel, 123}; +constexpr Result ResultInvalidState{ErrorModule::Kernel, 125}; +constexpr Result ResultReservedUsed{ErrorModule::Kernel, 126}; +constexpr Result ResultPortClosed{ErrorModule::Kernel, 131}; +constexpr Result ResultLimitReached{ErrorModule::Kernel, 132}; +constexpr Result ResultInvalidId{ErrorModule::Kernel, 519}; } // namespace Kernel diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index 365e22e4e..79e15183a 100644 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -96,4 +95,6 @@ constexpr inline s32 IdealCoreNoUpdate = -3; constexpr inline s32 LowestThreadPriority = 63; constexpr inline s32 HighestThreadPriority = 0; +constexpr inline size_t ThreadLocalRegionSize = 0x200; + } // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index a60adfcab..4bc49087e 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -34,24 +33,24 @@ static inline void FuncReturn32(Core::System& system, u32 result) { } //////////////////////////////////////////////////////////////////////////////////////////////////// -// Function wrappers that return type ResultCode +// Function wrappers that return type Result -template <ResultCode func(Core::System&, u64)> +template <Result func(Core::System&, u64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, Param(system, 0)).raw); } -template <ResultCode func(Core::System&, u64, u64)> +template <Result func(Core::System&, u64, u64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, Param(system, 0), Param(system, 1)).raw); } -template <ResultCode func(Core::System&, u32)> +template <Result func(Core::System&, u32)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw); } -template <ResultCode func(Core::System&, u32, u32)> +template <Result func(Core::System&, u32, u32)> void SvcWrap64(Core::System& system) { FuncReturn( system, @@ -59,14 +58,14 @@ void SvcWrap64(Core::System& system) { } // Used by SetThreadActivity -template <ResultCode func(Core::System&, Handle, Svc::ThreadActivity)> +template <Result func(Core::System&, Handle, Svc::ThreadActivity)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), static_cast<Svc::ThreadActivity>(Param(system, 1))) .raw); } -template <ResultCode func(Core::System&, u32, u64, u64, u64)> +template <Result func(Core::System&, u32, u64, u64, u64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2), Param(system, 3)) @@ -74,7 +73,7 @@ void SvcWrap64(Core::System& system) { } // Used by MapProcessMemory and UnmapProcessMemory -template <ResultCode func(Core::System&, u64, u32, u64, u64)> +template <Result func(Core::System&, u64, u32, u64, u64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)), Param(system, 2), Param(system, 3)) @@ -82,7 +81,7 @@ void SvcWrap64(Core::System& system) { } // Used by ControlCodeMemory -template <ResultCode func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)> +template <Result func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1)), Param(system, 2), Param(system, 3), @@ -90,7 +89,7 @@ void SvcWrap64(Core::System& system) { .raw); } -template <ResultCode func(Core::System&, u32*)> +template <Result func(Core::System&, u32*)> void SvcWrap64(Core::System& system) { u32 param = 0; const u32 retval = func(system, ¶m).raw; @@ -98,7 +97,7 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -template <ResultCode func(Core::System&, u32*, u32)> +template <Result func(Core::System&, u32*, u32)> void SvcWrap64(Core::System& system) { u32 param_1 = 0; const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1))).raw; @@ -106,7 +105,7 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -template <ResultCode func(Core::System&, u32*, u32*)> +template <Result func(Core::System&, u32*, u32*)> void SvcWrap64(Core::System& system) { u32 param_1 = 0; u32 param_2 = 0; @@ -119,7 +118,7 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -template <ResultCode func(Core::System&, u32*, u64)> +template <Result func(Core::System&, u32*, u64)> void SvcWrap64(Core::System& system) { u32 param_1 = 0; const u32 retval = func(system, ¶m_1, Param(system, 1)).raw; @@ -127,7 +126,7 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -template <ResultCode func(Core::System&, u32*, u64, u32)> +template <Result func(Core::System&, u32*, u64, u32)> void SvcWrap64(Core::System& system) { u32 param_1 = 0; const u32 retval = @@ -137,7 +136,7 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -template <ResultCode func(Core::System&, u64*, u32)> +template <Result func(Core::System&, u64*, u32)> void SvcWrap64(Core::System& system) { u64 param_1 = 0; const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1))).raw; @@ -146,12 +145,12 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -template <ResultCode func(Core::System&, u64, u32)> +template <Result func(Core::System&, u64, u32)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1))).raw); } -template <ResultCode func(Core::System&, u64*, u64)> +template <Result func(Core::System&, u64*, u64)> void SvcWrap64(Core::System& system) { u64 param_1 = 0; const u32 retval = func(system, ¶m_1, Param(system, 1)).raw; @@ -160,7 +159,7 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -template <ResultCode func(Core::System&, u64*, u32, u32)> +template <Result func(Core::System&, u64*, u32, u32)> void SvcWrap64(Core::System& system) { u64 param_1 = 0; const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1)), @@ -172,7 +171,7 @@ void SvcWrap64(Core::System& system) { } // Used by GetResourceLimitLimitValue. -template <ResultCode func(Core::System&, u64*, Handle, LimitableResource)> +template <Result func(Core::System&, u64*, Handle, LimitableResource)> void SvcWrap64(Core::System& system) { u64 param_1 = 0; const u32 retval = func(system, ¶m_1, static_cast<Handle>(Param(system, 1)), @@ -183,13 +182,13 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -template <ResultCode func(Core::System&, u32, u64)> +template <Result func(Core::System&, u32, u64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1)).raw); } // Used by SetResourceLimitLimitValue -template <ResultCode func(Core::System&, Handle, LimitableResource, u64)> +template <Result func(Core::System&, Handle, LimitableResource, u64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)), static_cast<LimitableResource>(Param(system, 1)), Param(system, 2)) @@ -197,7 +196,7 @@ void SvcWrap64(Core::System& system) { } // Used by SetThreadCoreMask -template <ResultCode func(Core::System&, Handle, s32, u64)> +template <Result func(Core::System&, Handle, s32, u64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), static_cast<s32>(Param(system, 1)), Param(system, 2)) @@ -205,44 +204,44 @@ void SvcWrap64(Core::System& system) { } // Used by GetThreadCoreMask -template <ResultCode func(Core::System&, Handle, s32*, u64*)> +template <Result func(Core::System&, Handle, s32*, u64*)> void SvcWrap64(Core::System& system) { s32 param_1 = 0; u64 param_2 = 0; - const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), ¶m_1, ¶m_2); + const Result retval = func(system, static_cast<u32>(Param(system, 2)), ¶m_1, ¶m_2); system.CurrentArmInterface().SetReg(1, param_1); system.CurrentArmInterface().SetReg(2, param_2); FuncReturn(system, retval.raw); } -template <ResultCode func(Core::System&, u64, u64, u32, u32)> +template <Result func(Core::System&, u64, u64, u32, u32)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3))) .raw); } -template <ResultCode func(Core::System&, u64, u64, u32, u64)> +template <Result func(Core::System&, u64, u64, u32, u64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2)), Param(system, 3)) .raw); } -template <ResultCode func(Core::System&, u32, u64, u32)> +template <Result func(Core::System&, u32, u64, u32)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), static_cast<u32>(Param(system, 2))) .raw); } -template <ResultCode func(Core::System&, u64, u64, u64)> +template <Result func(Core::System&, u64, u64, u64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, Param(system, 0), Param(system, 1), Param(system, 2)).raw); } -template <ResultCode func(Core::System&, u64, u64, u32)> +template <Result func(Core::System&, u64, u64, u32)> void SvcWrap64(Core::System& system) { FuncReturn( system, @@ -250,7 +249,7 @@ void SvcWrap64(Core::System& system) { } // Used by SetMemoryPermission -template <ResultCode func(Core::System&, u64, u64, Svc::MemoryPermission)> +template <Result func(Core::System&, u64, u64, Svc::MemoryPermission)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, Param(system, 0), Param(system, 1), static_cast<Svc::MemoryPermission>(Param(system, 2))) @@ -258,14 +257,14 @@ void SvcWrap64(Core::System& system) { } // Used by MapSharedMemory -template <ResultCode func(Core::System&, Handle, u64, u64, Svc::MemoryPermission)> +template <Result func(Core::System&, Handle, u64, u64, Svc::MemoryPermission)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)), Param(system, 1), Param(system, 2), static_cast<Svc::MemoryPermission>(Param(system, 3))) .raw); } -template <ResultCode func(Core::System&, u32, u64, u64)> +template <Result func(Core::System&, u32, u64, u64)> void SvcWrap64(Core::System& system) { FuncReturn( system, @@ -273,7 +272,7 @@ void SvcWrap64(Core::System& system) { } // Used by WaitSynchronization -template <ResultCode func(Core::System&, s32*, u64, s32, s64)> +template <Result func(Core::System&, s32*, u64, s32, s64)> void SvcWrap64(Core::System& system) { s32 param_1 = 0; const u32 retval = func(system, ¶m_1, Param(system, 1), static_cast<s32>(Param(system, 2)), @@ -284,7 +283,7 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -template <ResultCode func(Core::System&, u64, u64, u32, s64)> +template <Result func(Core::System&, u64, u64, u32, s64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2)), static_cast<s64>(Param(system, 3))) @@ -292,7 +291,7 @@ void SvcWrap64(Core::System& system) { } // Used by GetInfo -template <ResultCode func(Core::System&, u64*, u64, Handle, u64)> +template <Result func(Core::System&, u64*, u64, Handle, u64)> void SvcWrap64(Core::System& system) { u64 param_1 = 0; const u32 retval = func(system, ¶m_1, Param(system, 1), @@ -303,7 +302,7 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -template <ResultCode func(Core::System&, u32*, u64, u64, u64, u32, s32)> +template <Result func(Core::System&, u32*, u64, u64, u64, u32, s32)> void SvcWrap64(Core::System& system) { u32 param_1 = 0; const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2), Param(system, 3), @@ -315,7 +314,7 @@ void SvcWrap64(Core::System& system) { } // Used by CreateTransferMemory -template <ResultCode func(Core::System&, Handle*, u64, u64, Svc::MemoryPermission)> +template <Result func(Core::System&, Handle*, u64, u64, Svc::MemoryPermission)> void SvcWrap64(Core::System& system) { u32 param_1 = 0; const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2), @@ -327,7 +326,7 @@ void SvcWrap64(Core::System& system) { } // Used by CreateCodeMemory -template <ResultCode func(Core::System&, Handle*, u64, u64)> +template <Result func(Core::System&, Handle*, u64, u64)> void SvcWrap64(Core::System& system) { u32 param_1 = 0; const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2)).raw; @@ -336,7 +335,7 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -template <ResultCode func(Core::System&, Handle*, u64, u32, u32)> +template <Result func(Core::System&, Handle*, u64, u32, u32)> void SvcWrap64(Core::System& system) { u32 param_1 = 0; const u32 retval = func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2)), @@ -348,7 +347,7 @@ void SvcWrap64(Core::System& system) { } // Used by WaitForAddress -template <ResultCode func(Core::System&, u64, Svc::ArbitrationType, s32, s64)> +template <Result func(Core::System&, u64, Svc::ArbitrationType, s32, s64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, Param(system, 0), static_cast<Svc::ArbitrationType>(Param(system, 1)), @@ -357,7 +356,7 @@ void SvcWrap64(Core::System& system) { } // Used by SignalToAddress -template <ResultCode func(Core::System&, u64, Svc::SignalType, s32, s32)> +template <Result func(Core::System&, u64, Svc::SignalType, s32, s32)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, Param(system, 0), static_cast<Svc::SignalType>(Param(system, 1)), @@ -426,7 +425,7 @@ void SvcWrap64(Core::System& system) { } // Used by QueryMemory32, ArbitrateLock32 -template <ResultCode func(Core::System&, u32, u32, u32)> +template <Result func(Core::System&, u32, u32, u32)> void SvcWrap32(Core::System& system) { FuncReturn32(system, func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2)).raw); @@ -457,7 +456,7 @@ void SvcWrap32(Core::System& system) { } // Used by CreateThread32 -template <ResultCode func(Core::System&, Handle*, u32, u32, u32, u32, s32)> +template <Result func(Core::System&, Handle*, u32, u32, u32, u32, s32)> void SvcWrap32(Core::System& system) { Handle param_1 = 0; @@ -470,7 +469,7 @@ void SvcWrap32(Core::System& system) { } // Used by GetInfo32 -template <ResultCode func(Core::System&, u32*, u32*, u32, u32, u32, u32)> +template <Result func(Core::System&, u32*, u32*, u32, u32, u32, u32)> void SvcWrap32(Core::System& system) { u32 param_1 = 0; u32 param_2 = 0; @@ -485,7 +484,7 @@ void SvcWrap32(Core::System& system) { } // Used by GetThreadPriority32, ConnectToNamedPort32 -template <ResultCode func(Core::System&, u32*, u32)> +template <Result func(Core::System&, u32*, u32)> void SvcWrap32(Core::System& system) { u32 param_1 = 0; const u32 retval = func(system, ¶m_1, Param32(system, 1)).raw; @@ -494,7 +493,7 @@ void SvcWrap32(Core::System& system) { } // Used by GetThreadId32 -template <ResultCode func(Core::System&, u32*, u32*, u32)> +template <Result func(Core::System&, u32*, u32*, u32)> void SvcWrap32(Core::System& system) { u32 param_1 = 0; u32 param_2 = 0; @@ -517,7 +516,7 @@ void SvcWrap32(Core::System& system) { } // Used by CreateEvent32 -template <ResultCode func(Core::System&, Handle*, Handle*)> +template <Result func(Core::System&, Handle*, Handle*)> void SvcWrap32(Core::System& system) { Handle param_1 = 0; Handle param_2 = 0; @@ -529,7 +528,7 @@ void SvcWrap32(Core::System& system) { } // Used by GetThreadId32 -template <ResultCode func(Core::System&, Handle, u32*, u32*, u32*)> +template <Result func(Core::System&, Handle, u32*, u32*, u32*)> void SvcWrap32(Core::System& system) { u32 param_1 = 0; u32 param_2 = 0; @@ -543,7 +542,7 @@ void SvcWrap32(Core::System& system) { } // Used by GetThreadCoreMask32 -template <ResultCode func(Core::System&, Handle, s32*, u32*, u32*)> +template <Result func(Core::System&, Handle, s32*, u32*, u32*)> void SvcWrap32(Core::System& system) { s32 param_1 = 0; u32 param_2 = 0; @@ -563,7 +562,7 @@ void SvcWrap32(Core::System& system) { } // Used by SetThreadActivity32 -template <ResultCode func(Core::System&, Handle, Svc::ThreadActivity)> +template <Result func(Core::System&, Handle, Svc::ThreadActivity)> void SvcWrap32(Core::System& system) { const u32 retval = func(system, static_cast<Handle>(Param(system, 0)), static_cast<Svc::ThreadActivity>(Param(system, 1))) @@ -572,7 +571,7 @@ void SvcWrap32(Core::System& system) { } // Used by SetThreadPriority32 -template <ResultCode func(Core::System&, Handle, u32)> +template <Result func(Core::System&, Handle, u32)> void SvcWrap32(Core::System& system) { const u32 retval = func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw; @@ -580,7 +579,7 @@ void SvcWrap32(Core::System& system) { } // Used by SetMemoryAttribute32 -template <ResultCode func(Core::System&, Handle, u32, u32, u32)> +template <Result func(Core::System&, Handle, u32, u32, u32)> void SvcWrap32(Core::System& system) { const u32 retval = func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1)), @@ -590,7 +589,7 @@ void SvcWrap32(Core::System& system) { } // Used by MapSharedMemory32 -template <ResultCode func(Core::System&, Handle, u32, u32, Svc::MemoryPermission)> +template <Result func(Core::System&, Handle, u32, u32, Svc::MemoryPermission)> void SvcWrap32(Core::System& system) { const u32 retval = func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1)), static_cast<u32>(Param(system, 2)), @@ -600,7 +599,7 @@ void SvcWrap32(Core::System& system) { } // Used by SetThreadCoreMask32 -template <ResultCode func(Core::System&, Handle, s32, u32, u32)> +template <Result func(Core::System&, Handle, s32, u32, u32)> void SvcWrap32(Core::System& system) { const u32 retval = func(system, static_cast<Handle>(Param(system, 0)), static_cast<s32>(Param(system, 1)), @@ -610,7 +609,7 @@ void SvcWrap32(Core::System& system) { } // Used by WaitProcessWideKeyAtomic32 -template <ResultCode func(Core::System&, u32, u32, Handle, u32, u32)> +template <Result func(Core::System&, u32, u32, Handle, u32, u32)> void SvcWrap32(Core::System& system) { const u32 retval = func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1)), @@ -621,7 +620,7 @@ void SvcWrap32(Core::System& system) { } // Used by WaitForAddress32 -template <ResultCode func(Core::System&, u32, Svc::ArbitrationType, s32, u32, u32)> +template <Result func(Core::System&, u32, Svc::ArbitrationType, s32, u32, u32)> void SvcWrap32(Core::System& system) { const u32 retval = func(system, static_cast<u32>(Param(system, 0)), static_cast<Svc::ArbitrationType>(Param(system, 1)), @@ -632,7 +631,7 @@ void SvcWrap32(Core::System& system) { } // Used by SignalToAddress32 -template <ResultCode func(Core::System&, u32, Svc::SignalType, s32, s32)> +template <Result func(Core::System&, u32, Svc::SignalType, s32, s32)> void SvcWrap32(Core::System& system) { const u32 retval = func(system, static_cast<u32>(Param(system, 0)), static_cast<Svc::SignalType>(Param(system, 1)), @@ -642,13 +641,13 @@ void SvcWrap32(Core::System& system) { } // Used by SendSyncRequest32, ArbitrateUnlock32 -template <ResultCode func(Core::System&, u32)> +template <Result func(Core::System&, u32)> void SvcWrap32(Core::System& system) { FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw); } // Used by CreateTransferMemory32 -template <ResultCode func(Core::System&, Handle*, u32, u32, Svc::MemoryPermission)> +template <Result func(Core::System&, Handle*, u32, u32, Svc::MemoryPermission)> void SvcWrap32(Core::System& system) { Handle handle = 0; const u32 retval = func(system, &handle, Param32(system, 1), Param32(system, 2), @@ -659,7 +658,7 @@ void SvcWrap32(Core::System& system) { } // Used by WaitSynchronization32 -template <ResultCode func(Core::System&, u32, u32, s32, u32, s32*)> +template <Result func(Core::System&, u32, u32, s32, u32, s32*)> void SvcWrap32(Core::System& system) { s32 param_1 = 0; const u32 retval = func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2), @@ -669,4 +668,26 @@ void SvcWrap32(Core::System& system) { FuncReturn(system, retval); } +// Used by CreateCodeMemory32 +template <Result func(Core::System&, Handle*, u32, u32)> +void SvcWrap32(Core::System& system) { + Handle handle = 0; + + const u32 retval = func(system, &handle, Param32(system, 1), Param32(system, 2)).raw; + + system.CurrentArmInterface().SetReg(1, handle); + FuncReturn(system, retval); +} + +// Used by ControlCodeMemory32 +template <Result func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)> +void SvcWrap32(Core::System& system) { + const u32 retval = + func(system, Param32(system, 0), Param32(system, 1), Param(system, 2), Param(system, 4), + static_cast<Svc::MemoryPermission>(Param32(system, 6))) + .raw; + + FuncReturn(system, retval); +} + } // namespace Kernel diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index aa985d820..5ee72c432 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "core/core.h" @@ -12,19 +11,21 @@ namespace Kernel { TimeManager::TimeManager(Core::System& system_) : system{system_} { - time_manager_event_type = - Core::Timing::CreateEvent("Kernel::TimeManagerCallback", - [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) { - KThread* thread = reinterpret_cast<KThread*>(thread_handle); - { - KScopedSchedulerLock sl(system.Kernel()); - thread->OnTimer(); - } - }); + time_manager_event_type = Core::Timing::CreateEvent( + "Kernel::TimeManagerCallback", + [this](std::uintptr_t thread_handle, s64 time, + std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { + KThread* thread = reinterpret_cast<KThread*>(thread_handle); + { + KScopedSchedulerLock sl(system.Kernel()); + thread->OnTimer(); + } + return std::nullopt; + }); } void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; if (nanoseconds > 0) { ASSERT(thread); ASSERT(thread->GetState() != ThreadState::Runnable); @@ -35,7 +36,7 @@ void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) { } void TimeManager::UnscheduleTimeEvent(KThread* thread) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; system.CoreTiming().UnscheduleEvent(time_manager_event_type, reinterpret_cast<uintptr_t>(thread)); } diff --git a/src/core/hle/kernel/time_manager.h b/src/core/hle/kernel/time_manager.h index b1fa26e8c..94d16b3b4 100644 --- a/src/core/hle/kernel/time_manager.h +++ b/src/core/hle/kernel/time_manager.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 3807b9aa8..47a1b829b 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -1,6 +1,5 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2014 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -112,15 +111,16 @@ enum class ErrorModule : u32 { }; /// Encapsulates a Horizon OS error code, allowing it to be separated into its constituent fields. -union ResultCode { +union Result { u32 raw; BitField<0, 9, ErrorModule> module; BitField<9, 13, u32> description; - constexpr explicit ResultCode(u32 raw_) : raw(raw_) {} + Result() = default; + constexpr explicit Result(u32 raw_) : raw(raw_) {} - constexpr ResultCode(ErrorModule module_, u32 description_) + constexpr Result(ErrorModule module_, u32 description_) : raw(module.FormatValue(module_) | description.FormatValue(description_)) {} [[nodiscard]] constexpr bool IsSuccess() const { @@ -131,19 +131,20 @@ union ResultCode { return !IsSuccess(); } }; +static_assert(std::is_trivial_v<Result>); -[[nodiscard]] constexpr bool operator==(const ResultCode& a, const ResultCode& b) { +[[nodiscard]] constexpr bool operator==(const Result& a, const Result& b) { return a.raw == b.raw; } -[[nodiscard]] constexpr bool operator!=(const ResultCode& a, const ResultCode& b) { +[[nodiscard]] constexpr bool operator!=(const Result& a, const Result& b) { return !operator==(a, b); } // Convenience functions for creating some common kinds of errors: -/// The default success `ResultCode`. -constexpr ResultCode ResultSuccess(0); +/// The default success `Result`. +constexpr Result ResultSuccess(0); /** * Placeholder result code used for unknown error codes. @@ -151,10 +152,52 @@ constexpr ResultCode ResultSuccess(0); * @note This should only be used when a particular error code * is not known yet. */ -constexpr ResultCode ResultUnknown(UINT32_MAX); +constexpr Result ResultUnknown(UINT32_MAX); /** - * This is an optional value type. It holds a `ResultCode` and, if that code is ResultSuccess, it + * A ResultRange defines an inclusive range of error descriptions within an error module. + * This can be used to check whether the description of a given Result falls within the range. + * The conversion function returns a Result with its description set to description_start. + * + * An example of how it could be used: + * \code + * constexpr ResultRange ResultCommonError{ErrorModule::Common, 0, 9999}; + * + * Result Example(int value) { + * const Result result = OtherExample(value); + * + * // This will only evaluate to true if result.module is ErrorModule::Common and + * // result.description is in between 0 and 9999 inclusive. + * if (ResultCommonError.Includes(result)) { + * // This returns Result{ErrorModule::Common, 0}; + * return ResultCommonError; + * } + * + * return ResultSuccess; + * } + * \endcode + */ +class ResultRange { +public: + consteval ResultRange(ErrorModule module, u32 description_start, u32 description_end_) + : code{module, description_start}, description_end{description_end_} {} + + [[nodiscard]] constexpr operator Result() const { + return code; + } + + [[nodiscard]] constexpr bool Includes(Result other) const { + return code.module == other.module && code.description <= other.description && + other.description <= description_end; + } + +private: + Result code; + u32 description_end; +}; + +/** + * This is an optional value type. It holds a `Result` and, if that code is ResultSuccess, it * also holds a result of type `T`. If the code is an error code (not ResultSuccess), then trying * to access the inner value with operator* is undefined behavior and will assert with Unwrap(). * Users of this class must be cognizant to check the status of the ResultVal with operator bool(), @@ -165,7 +208,7 @@ constexpr ResultCode ResultUnknown(UINT32_MAX); * ResultVal<int> Frobnicate(float strength) { * if (strength < 0.f || strength > 1.0f) { * // Can't frobnicate too weakly or too strongly - * return ResultCode{ErrorModule::Common, 1}; + * return Result{ErrorModule::Common, 1}; * } else { * // Frobnicated! Give caller a cookie * return 42; @@ -188,7 +231,9 @@ class ResultVal { public: constexpr ResultVal() : expected{} {} - constexpr ResultVal(ResultCode code) : expected{Common::Unexpected(code)} {} + constexpr ResultVal(Result code) : expected{Common::Unexpected(code)} {} + + constexpr ResultVal(ResultRange range) : expected{Common::Unexpected(range)} {} template <typename U> constexpr ResultVal(U&& val) : expected{std::forward<U>(val)} {} @@ -208,7 +253,7 @@ public: return expected.has_value(); } - [[nodiscard]] constexpr ResultCode Code() const { + [[nodiscard]] constexpr Result Code() const { return expected.has_value() ? ResultSuccess : expected.error(); } @@ -275,8 +320,8 @@ public: } private: - // TODO: Replace this with std::expected once it is standardized in the STL. - Common::Expected<T, ResultCode> expected; + // TODO (Morph): Replace this with C++23 std::expected. + Common::Expected<T, Result> expected; }; /** @@ -293,7 +338,7 @@ private: target = std::move(*CONCAT2(check_result_L, __LINE__)) /** - * Analogous to CASCADE_RESULT, but for a bare ResultCode. The code will be propagated if + * Analogous to CASCADE_RESULT, but for a bare Result. The code will be propagated if * non-success, or discarded otherwise. */ #define CASCADE_CODE(source) \ diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index e34ef5a78..bb838e285 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <array> @@ -16,7 +15,6 @@ #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/kernel.h" #include "core/hle/service/acc/acc.h" #include "core/hle/service/acc/acc_aa.h" #include "core/hle/service/acc/acc_su.h" @@ -30,11 +28,11 @@ namespace Service::Account { -constexpr ResultCode ERR_INVALID_USER_ID{ErrorModule::Account, 20}; -constexpr ResultCode ERR_INVALID_APPLICATION_ID{ErrorModule::Account, 22}; -constexpr ResultCode ERR_INVALID_BUFFER{ErrorModule::Account, 30}; -constexpr ResultCode ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 31}; -constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100}; +constexpr Result ERR_INVALID_USER_ID{ErrorModule::Account, 20}; +constexpr Result ERR_INVALID_APPLICATION_ID{ErrorModule::Account, 22}; +constexpr Result ERR_INVALID_BUFFER{ErrorModule::Account, 30}; +constexpr Result ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 31}; +constexpr Result ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100}; // Thumbnails are hard coded to be at least this size constexpr std::size_t THUMBNAIL_SIZE = 0x24000; @@ -292,7 +290,7 @@ protected: void Get(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString()); ProfileBase profile_base{}; - ProfileData data{}; + UserData data{}; if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { ctx.WriteBuffer(data); IPC::ResponseBuilder rb{ctx, 16}; @@ -375,18 +373,18 @@ protected: reinterpret_cast<const char*>(base.username.data()), base.username.size()), base.timestamp, base.user_uuid.RawString()); - if (user_data.size() < sizeof(ProfileData)) { - LOG_ERROR(Service_ACC, "ProfileData buffer too small!"); + if (user_data.size() < sizeof(UserData)) { + LOG_ERROR(Service_ACC, "UserData buffer too small!"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERR_INVALID_BUFFER); return; } - ProfileData data; - std::memcpy(&data, user_data.data(), sizeof(ProfileData)); + UserData data; + std::memcpy(&data, user_data.data(), sizeof(UserData)); if (!profile_manager.SetProfileBaseAndData(user_id, base, data)) { - LOG_ERROR(Service_ACC, "Failed to update profile data and base!"); + LOG_ERROR(Service_ACC, "Failed to update user data and base!"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERR_FAILED_SAVE_DATA); return; @@ -408,15 +406,15 @@ protected: reinterpret_cast<const char*>(base.username.data()), base.username.size()), base.timestamp, base.user_uuid.RawString()); - if (user_data.size() < sizeof(ProfileData)) { - LOG_ERROR(Service_ACC, "ProfileData buffer too small!"); + if (user_data.size() < sizeof(UserData)) { + LOG_ERROR(Service_ACC, "UserData buffer too small!"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERR_INVALID_BUFFER); return; } - ProfileData data; - std::memcpy(&data, user_data.data(), sizeof(ProfileData)); + UserData data; + std::memcpy(&data, user_data.data(), sizeof(UserData)); Common::FS::IOFile image(GetImagePath(user_id), Common::FS::FileAccessMode::Write, Common::FS::FileType::BinaryFile); @@ -507,7 +505,7 @@ protected: void Cancel() override {} - ResultCode GetResult() const override { + Result GetResult() const override { return ResultSuccess; } }; @@ -536,7 +534,7 @@ public: private: void CheckAvailability(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_ACC, "(STUBBED) called"); + LOG_DEBUG(Service_ACC, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); rb.Push(false); // TODO: Check when this is supposed to return true and when not @@ -749,7 +747,7 @@ void Module::Interface::InitializeApplicationInfoRestricted(Kernel::HLERequestCo rb.Push(InitializeApplicationInfoBase()); } -ResultCode Module::Interface::InitializeApplicationInfoBase() { +Result Module::Interface::InitializeApplicationInfoBase() { if (application_info) { LOG_ERROR(Service_ACC, "Application already initialized"); return ERR_ACCOUNTINFO_ALREADY_INITIALIZED; diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index f7e9bc4f8..1621e7c0a 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -42,7 +41,7 @@ public: void StoreSaveDataThumbnailSystem(Kernel::HLERequestContext& ctx); private: - ResultCode InitializeApplicationInfoBase(); + Result InitializeApplicationInfoBase(); void StoreSaveDataThumbnail(Kernel::HLERequestContext& ctx, const Common::UUID& uuid, const u64 tid); diff --git a/src/core/hle/service/acc/acc_aa.cpp b/src/core/hle/service/acc/acc_aa.cpp index e498fb64d..90ed0f519 100644 --- a/src/core/hle/service/acc/acc_aa.cpp +++ b/src/core/hle/service/acc/acc_aa.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/acc/acc_aa.h" diff --git a/src/core/hle/service/acc/acc_aa.h b/src/core/hle/service/acc/acc_aa.h index d1be20ff3..623daeaef 100644 --- a/src/core/hle/service/acc/acc_aa.h +++ b/src/core/hle/service/acc/acc_aa.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index f4034d591..b6bfd6155 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/acc/acc_su.h" diff --git a/src/core/hle/service/acc/acc_su.h b/src/core/hle/service/acc/acc_su.h index 132a126b4..8daef38b8 100644 --- a/src/core/hle/service/acc/acc_su.h +++ b/src/core/hle/service/acc/acc_su.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index df77c58f0..65023b8c2 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/acc/acc_u0.h" diff --git a/src/core/hle/service/acc/acc_u0.h b/src/core/hle/service/acc/acc_u0.h index 4c2600b67..35cd4b492 100644 --- a/src/core/hle/service/acc/acc_u0.h +++ b/src/core/hle/service/acc/acc_u0.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp index 991921984..92f704c2f 100644 --- a/src/core/hle/service/acc/acc_u1.cpp +++ b/src/core/hle/service/acc/acc_u1.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/acc/acc_u1.h" diff --git a/src/core/hle/service/acc/acc_u1.h b/src/core/hle/service/acc/acc_u1.h index 2d478324a..e711d3925 100644 --- a/src/core/hle/service/acc/acc_u1.h +++ b/src/core/hle/service/acc/acc_u1.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/acc/async_context.cpp b/src/core/hle/service/acc/async_context.cpp index a49dfdec7..c85b2e43a 100644 --- a/src/core/hle/service/acc/async_context.cpp +++ b/src/core/hle/service/acc/async_context.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" #include "core/hle/ipc_helpers.h" diff --git a/src/core/hle/service/acc/async_context.h b/src/core/hle/service/acc/async_context.h index cc3a0a9fe..26332d241 100644 --- a/src/core/hle/service/acc/async_context.h +++ b/src/core/hle/service/acc/async_context.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -27,7 +26,7 @@ public: protected: virtual bool IsComplete() const = 0; virtual void Cancel() = 0; - virtual ResultCode GetResult() const = 0; + virtual Result GetResult() const = 0; void MarkComplete(); diff --git a/src/core/hle/service/acc/errors.h b/src/core/hle/service/acc/errors.h index 1f0577239..e9c16b951 100644 --- a/src/core/hle/service/acc/errors.h +++ b/src/core/hle/service/acc/errors.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,7 +7,7 @@ namespace Service::Account { -constexpr ResultCode ERR_ACCOUNTINFO_BAD_APPLICATION{ErrorModule::Account, 22}; -constexpr ResultCode ERR_ACCOUNTINFO_ALREADY_INITIALIZED{ErrorModule::Account, 41}; +constexpr Result ERR_ACCOUNTINFO_BAD_APPLICATION{ErrorModule::Account, 22}; +constexpr Result ERR_ACCOUNTINFO_ALREADY_INITIALIZED{ErrorModule::Account, 41}; } // namespace Service::Account diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index fba847142..a58da4d5f 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include <random> @@ -23,7 +22,7 @@ struct UserRaw { UUID uuid2{}; u64 timestamp{}; ProfileUsername username{}; - ProfileData extra_data{}; + UserData extra_data{}; }; static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size."); @@ -34,9 +33,9 @@ struct ProfileDataRaw { static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size."); // TODO(ogniK): Get actual error codes -constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, u32(-1)); -constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, u32(-2)); -constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20); +constexpr Result ERROR_TOO_MANY_USERS(ErrorModule::Account, u32(-1)); +constexpr Result ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, u32(-2)); +constexpr Result ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20); constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "system/save/8000000000000010/su/avators"; @@ -88,7 +87,7 @@ bool ProfileManager::RemoveProfileAtIndex(std::size_t index) { } /// Helper function to register a user to the system -ResultCode ProfileManager::AddUser(const ProfileInfo& user) { +Result ProfileManager::AddUser(const ProfileInfo& user) { if (!AddToProfiles(user)) { return ERROR_TOO_MANY_USERS; } @@ -97,7 +96,7 @@ ResultCode ProfileManager::AddUser(const ProfileInfo& user) { /// Create a new user on the system. If the uuid of the user already exists, the user is not /// created. -ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& username) { +Result ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& username) { if (user_count == MAX_USERS) { return ERROR_TOO_MANY_USERS; } @@ -124,7 +123,7 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& usern /// Creates a new user on the system. This function allows a much simpler method of registration /// specifically by allowing an std::string for the username. This is required specifically since /// we're loading a string straight from the config -ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) { +Result ProfileManager::CreateNewUser(UUID uuid, const std::string& username) { ProfileUsername username_output{}; if (username.size() > username_output.size()) { @@ -264,7 +263,7 @@ UUID ProfileManager::GetLastOpenedUser() const { /// Return the users profile base and the unknown arbitary data. bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, - ProfileData& data) const { + UserData& data) const { if (GetProfileBase(index, profile)) { data = profiles[*index].data; return true; @@ -273,15 +272,14 @@ bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, Pro } /// Return the users profile base and the unknown arbitary data. -bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, - ProfileData& data) const { +bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, UserData& data) const { const auto idx = GetUserIndex(uuid); return GetProfileBaseAndData(idx, profile, data); } /// Return the users profile base and the unknown arbitary data. bool ProfileManager::GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, - ProfileData& data) const { + UserData& data) const { return GetProfileBaseAndData(user.user_uuid, profile, data); } @@ -319,7 +317,7 @@ bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) { } bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new, - const ProfileData& data_new) { + const UserData& data_new) { const auto index = GetUserIndex(uuid); if (index.has_value() && SetProfileBase(uuid, profile_new)) { profiles[*index].data = data_new; diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index 17347f7ef..135f7d0d5 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h @@ -1,12 +1,12 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include <array> #include <optional> +#include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" #include "common/uuid.h" @@ -22,7 +22,7 @@ using UserIDArray = std::array<Common::UUID, MAX_USERS>; /// Contains extra data related to a user. /// TODO: RE this structure -struct ProfileData { +struct UserData { INSERT_PADDING_WORDS_NOINIT(1); u32 icon_id; u8 bg_color_id; @@ -30,7 +30,7 @@ struct ProfileData { INSERT_PADDING_BYTES_NOINIT(0x10); INSERT_PADDING_BYTES_NOINIT(0x60); }; -static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect size"); +static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); /// This holds general information about a users profile. This is where we store all the information /// based on a specific user @@ -38,7 +38,7 @@ struct ProfileInfo { Common::UUID user_uuid{}; ProfileUsername username{}; u64 creation_time{}; - ProfileData data{}; // TODO(ognik): Work out what this is + UserData data{}; // TODO(ognik): Work out what this is bool is_open{}; }; @@ -64,9 +64,9 @@ public: ProfileManager(); ~ProfileManager(); - ResultCode AddUser(const ProfileInfo& user); - ResultCode CreateNewUser(Common::UUID uuid, const ProfileUsername& username); - ResultCode CreateNewUser(Common::UUID uuid, const std::string& username); + Result AddUser(const ProfileInfo& user); + Result CreateNewUser(Common::UUID uuid, const ProfileUsername& username); + Result CreateNewUser(Common::UUID uuid, const std::string& username); std::optional<Common::UUID> GetUser(std::size_t index) const; std::optional<std::size_t> GetUserIndex(const Common::UUID& uuid) const; std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; @@ -74,10 +74,9 @@ public: bool GetProfileBase(Common::UUID uuid, ProfileBase& profile) const; bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, - ProfileData& data) const; - bool GetProfileBaseAndData(Common::UUID uuid, ProfileBase& profile, ProfileData& data) const; - bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, - ProfileData& data) const; + UserData& data) const; + bool GetProfileBaseAndData(Common::UUID uuid, ProfileBase& profile, UserData& data) const; + bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, UserData& data) const; std::size_t GetUserCount() const; std::size_t GetOpenUserCount() const; bool UserExists(Common::UUID uuid) const; @@ -93,7 +92,7 @@ public: bool RemoveUser(Common::UUID uuid); bool SetProfileBase(Common::UUID uuid, const ProfileBase& profile_new); bool SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new, - const ProfileData& data_new); + const UserData& data_new); private: void ParseUserSaveFile(); diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 773dc9f29..6fb7e198e 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -1,12 +1,10 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <array> #include <cinttypes> #include <cstring> -#include "audio_core/audio_renderer.h" #include "common/settings.h" #include "core/core.h" #include "core/file_sys/control_metadata.h" @@ -41,9 +39,9 @@ namespace Service::AM { -constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 2}; -constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 3}; -constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 503}; +constexpr Result ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 2}; +constexpr Result ERR_NO_MESSAGES{ErrorModule::AM, 3}; +constexpr Result ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 503}; enum class LaunchParameterKind : u32 { ApplicationSpecific = 1, @@ -239,6 +237,7 @@ IDebugFunctions::IDebugFunctions(Core::System& system_) {130, nullptr, "FriendInvitationSetApplicationParameter"}, {131, nullptr, "FriendInvitationClearApplicationParameter"}, {132, nullptr, "FriendInvitationPushApplicationParameter"}, + {140, nullptr, "RestrictPowerOperationForSecureLaunchModeForDebug"}, {900, nullptr, "GetGrcProcessLaunchedSystemEvent"}, }; // clang-format on @@ -286,7 +285,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv {62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"}, {63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"}, {64, nullptr, "SetInputDetectionSourceSet"}, - {65, nullptr, "ReportUserIsActive"}, + {65, &ISelfController::ReportUserIsActive, "ReportUserIsActive"}, {66, nullptr, "GetCurrentIlluminance"}, {67, nullptr, "IsIlluminanceAvailable"}, {68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"}, @@ -366,7 +365,7 @@ void ISelfController::LeaveFatalSection(Kernel::HLERequestContext& ctx) { // Entry and exit of fatal sections must be balanced. if (num_fatal_sections_entered == 0) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode{ErrorModule::AM, 512}); + rb.Push(Result{ErrorModule::AM, 512}); return; } @@ -518,6 +517,13 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c rb.Push<u32>(idle_time_detection_extension); } +void ISelfController::ReportUserIsActive(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void ISelfController::SetAutoSleepDisabled(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; is_auto_sleep_disabled = rp.Pop<bool>(); @@ -618,7 +624,7 @@ void AppletMessageQueue::PushMessage(AppletMessage msg) { AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() { if (messages.empty()) { on_new_message->GetWritableEvent().Clear(); - return AppletMessage::NoMessage; + return AppletMessage::None; } auto msg = messages.front(); messages.pop(); @@ -633,7 +639,11 @@ std::size_t AppletMessageQueue::GetMessageCount() const { } void AppletMessageQueue::RequestExit() { - PushMessage(AppletMessage::ExitRequested); + PushMessage(AppletMessage::Exit); +} + +void AppletMessageQueue::RequestResume() { + PushMessage(AppletMessage::Resume); } void AppletMessageQueue::FocusStateChanged() { @@ -687,7 +697,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, {66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"}, {67, nullptr, "CancelCpuBoostMode"}, {68, nullptr, "GetBuiltInDisplayType"}, - {80, nullptr, "PerformSystemButtonPressingIfInFocus"}, + {80, &ICommonStateGetter::PerformSystemButtonPressingIfInFocus, "PerformSystemButtonPressingIfInFocus"}, {90, nullptr, "SetPerformanceConfigurationChangedNotification"}, {91, nullptr, "GetCurrentPerformanceConfiguration"}, {100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"}, @@ -732,7 +742,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { const auto message = msg_queue->PopMessage(); IPC::ResponseBuilder rb{ctx, 3}; - if (message == AppletMessageQueue::AppletMessage::NoMessage) { + if (message == AppletMessageQueue::AppletMessage::None) { LOG_ERROR(Service_AM, "Message queue is empty"); rb.Push(ERR_NO_MESSAGES); rb.PushEnum<AppletMessageQueue::AppletMessage>(message); @@ -744,7 +754,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { } void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_DEBUG(Service_AM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); @@ -827,6 +837,16 @@ void ICommonStateGetter::SetCpuBoostMode(Kernel::HLERequestContext& ctx) { apm_sys->SetCpuBoostMode(ctx); } +void ICommonStateGetter::PerformSystemButtonPressingIfInFocus(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto system_button{rp.PopEnum<SystemButtonType>()}; + + LOG_WARNING(Service_AM, "(STUBBED) called, system_button={}", system_button); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled( Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) called"); @@ -980,7 +1000,7 @@ private: LOG_DEBUG(Service_AM, "called"); IPC::RequestParser rp{ctx}; - applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>()); + applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>().lock()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -1007,7 +1027,7 @@ private: LOG_DEBUG(Service_AM, "called"); IPC::RequestParser rp{ctx}; - applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>()); + applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>().lock()); ASSERT(applet->IsInitialized()); applet->ExecuteInteractive(); @@ -1301,6 +1321,8 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"}, {34, nullptr, "SelectApplicationLicense"}, {35, nullptr, "GetDeviceSaveDataSizeMax"}, + {36, nullptr, "GetLimitedApplicationLicense"}, + {37, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"}, {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"}, {60, nullptr, "SetMediaPlaybackStateForApplication"}, @@ -1337,7 +1359,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) {200, nullptr, "GetLastApplicationExitReason"}, {500, nullptr, "StartContinuousRecordingFlushForDebug"}, {1000, nullptr, "CreateMovieMaker"}, - {1001, nullptr, "PrepareForJit"}, + {1001, &IApplicationFunctions::PrepareForJit, "PrepareForJit"}, }; // clang-format on @@ -1787,6 +1809,13 @@ void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(Kernel::HLERe rb.PushCopyObjects(health_warning_disappeared_system_event->GetReadableEvent()); } +void IApplicationFunctions::PrepareForJit(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, Core::System& system) { auto message_queue = std::make_shared<AppletMessageQueue>(system); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 2a578aea5..bb75c6281 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -22,6 +21,7 @@ class NVFlinger; namespace Service::AM { +// This is nn::settings::Language enum SystemLanguage { Japanese = 0, English = 1, // en-US @@ -41,16 +41,44 @@ enum SystemLanguage { // 4.0.0+ SimplifiedChinese = 15, TraditionalChinese = 16, + // 10.1.0+ + BrazilianPortuguese = 17, }; class AppletMessageQueue { public: + // This is nn::am::AppletMessage enum class AppletMessage : u32 { - NoMessage = 0, - ExitRequested = 4, + None = 0, + ChangeIntoForeground = 1, + ChangeIntoBackground = 2, + Exit = 4, + ApplicationExited = 6, FocusStateChanged = 15, + Resume = 16, + DetectShortPressingHomeButton = 20, + DetectLongPressingHomeButton = 21, + DetectShortPressingPowerButton = 22, + DetectMiddlePressingPowerButton = 23, + DetectLongPressingPowerButton = 24, + RequestToPrepareSleep = 25, + FinishedSleepSequence = 26, + SleepRequiredByHighTemperature = 27, + SleepRequiredByLowBattery = 28, + AutoPowerDown = 29, OperationModeChanged = 30, PerformanceModeChanged = 31, + DetectReceivingCecSystemStandby = 32, + SdCardRemoved = 33, + LaunchApplicationRequested = 50, + RequestToDisplay = 51, + ShowApplicationLogo = 55, + HideApplicationLogo = 56, + ForceHideApplicationLogo = 57, + FloatingApplicationDetected = 60, + DetectShortPressingCaptureButton = 90, + AlbumScreenShotTaken = 92, + AlbumRecordingSaved = 93, }; explicit AppletMessageQueue(Core::System& system); @@ -62,6 +90,7 @@ public: AppletMessage PopMessage(); std::size_t GetMessageCount() const; void RequestExit(); + void RequestResume(); void FocusStateChanged(); void OperationModeChanged(); @@ -146,6 +175,7 @@ private: void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx); void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); + void ReportUserIsActive(Kernel::HLERequestContext& ctx); void SetAutoSleepDisabled(Kernel::HLERequestContext& ctx); void IsAutoSleepDisabled(Kernel::HLERequestContext& ctx); void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); @@ -179,16 +209,31 @@ public: ~ICommonStateGetter() override; private: + // This is nn::oe::FocusState enum class FocusState : u8 { InFocus = 1, NotInFocus = 2, + Background = 3, }; + // This is nn::oe::OperationMode enum class OperationMode : u8 { Handheld = 0, Docked = 1, }; + // This is nn::am::service::SystemButtonType + enum class SystemButtonType { + None, + HomeButtonShortPressing, + HomeButtonLongPressing, + PowerButtonShortPressing, + PowerButtonLongPressing, + ShutdownSystem, + CaptureButtonShortPressing, + CaptureButtonLongPressing, + }; + void GetEventHandle(Kernel::HLERequestContext& ctx); void ReceiveMessage(Kernel::HLERequestContext& ctx); void GetCurrentFocusState(Kernel::HLERequestContext& ctx); @@ -203,6 +248,7 @@ private: void EndVrModeEx(Kernel::HLERequestContext& ctx); void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx); void SetCpuBoostMode(Kernel::HLERequestContext& ctx); + void PerformSystemButtonPressingIfInFocus(Kernel::HLERequestContext& ctx); void SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(Kernel::HLERequestContext& ctx); std::shared_ptr<AppletMessageQueue> msg_queue; @@ -304,6 +350,7 @@ private: void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx); void GetNotificationStorageChannelEvent(Kernel::HLERequestContext& ctx); void GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx); + void PrepareForJit(Kernel::HLERequestContext& ctx); KernelHelpers::ServiceContext service_context; diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp index 0ec4fd4ca..d7719da35 100644 --- a/src/core/hle/service/am/applet_ae.cpp +++ b/src/core/hle/service/am/applet_ae.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/core.h" diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h index f89f65649..2147976a6 100644 --- a/src/core/hle/service/am/applet_ae.h +++ b/src/core/hle/service/am/applet_ae.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp index b8859f4e6..00fc4202c 100644 --- a/src/core/hle/service/am/applet_oe.cpp +++ b/src/core/hle/service/am/applet_oe.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h index 64b874ead..8fea249f1 100644 --- a/src/core/hle/service/am/applet_oe.h +++ b/src/core/hle/service/am/applet_oe.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/am/applets/applet_controller.cpp b/src/core/hle/service/am/applets/applet_controller.cpp index d073f2210..b418031de 100644 --- a/src/core/hle/service/am/applets/applet_controller.cpp +++ b/src/core/hle/service/am/applets/applet_controller.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <cstring> @@ -21,9 +20,9 @@ namespace Service::AM::Applets { // This error code (0x183ACA) is thrown when the applet fails to initialize. -[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3101{ErrorModule::HID, 3101}; +[[maybe_unused]] constexpr Result ERR_CONTROLLER_APPLET_3101{ErrorModule::HID, 3101}; // This error code (0x183CCA) is thrown when the u32 result in ControllerSupportResultInfo is 2. -[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3102{ErrorModule::HID, 3102}; +[[maybe_unused]] constexpr Result ERR_CONTROLLER_APPLET_3102{ErrorModule::HID, 3102}; static Core::Frontend::ControllerParameters ConvertToFrontendParameters( ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text, @@ -174,12 +173,12 @@ bool Controller::TransactionComplete() const { return complete; } -ResultCode Controller::GetStatus() const { +Result Controller::GetStatus() const { return status; } void Controller::ExecuteInteractive() { - UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet."); + ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet."); } void Controller::Execute() { diff --git a/src/core/hle/service/am/applets/applet_controller.h b/src/core/hle/service/am/applets/applet_controller.h index 1a832505e..1f9adec65 100644 --- a/src/core/hle/service/am/applets/applet_controller.h +++ b/src/core/hle/service/am/applets/applet_controller.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -127,7 +126,7 @@ public: void Initialize() override; bool TransactionComplete() const override; - ResultCode GetStatus() const override; + Result GetStatus() const override; void ExecuteInteractive() override; void Execute() override; @@ -144,7 +143,7 @@ private: ControllerUpdateFirmwareArg controller_update_arg; ControllerKeyRemappingArg controller_key_remapping_arg; bool complete{false}; - ResultCode status{ResultSuccess}; + Result status{ResultSuccess}; bool is_single_mode{false}; std::vector<u8> out_data; }; diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp index a06c2b872..fcf34bf7e 100644 --- a/src/core/hle/service/am/applets/applet_error.cpp +++ b/src/core/hle/service/am/applets/applet_error.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> #include <cstring> @@ -26,15 +25,15 @@ struct ErrorCode { }; } - static constexpr ErrorCode FromResultCode(ResultCode result) { + static constexpr ErrorCode FromResult(Result result) { return { .error_category{2000 + static_cast<u32>(result.module.Value())}, .error_number{result.description.Value()}, }; } - constexpr ResultCode ToResultCode() const { - return ResultCode{static_cast<ErrorModule>(error_category - 2000), error_number}; + constexpr Result ToResult() const { + return Result{static_cast<ErrorModule>(error_category - 2000), error_number}; } }; static_assert(sizeof(ErrorCode) == 0x8, "ErrorCode has incorrect size."); @@ -98,8 +97,8 @@ void CopyArgumentData(const std::vector<u8>& data, T& variable) { std::memcpy(&variable, data.data(), sizeof(T)); } -ResultCode Decode64BitError(u64 error) { - return ErrorCode::FromU64(error).ToResultCode(); +Result Decode64BitError(u64 error) { + return ErrorCode::FromU64(error).ToResult(); } } // Anonymous namespace @@ -128,16 +127,16 @@ void Error::Initialize() { if (args->error.use_64bit_error_code) { error_code = Decode64BitError(args->error.error_code_64); } else { - error_code = ResultCode(args->error.error_code_32); + error_code = Result(args->error.error_code_32); } break; case ErrorAppletMode::ShowSystemError: CopyArgumentData(data, args->system_error); - error_code = ResultCode(Decode64BitError(args->system_error.error_code_64)); + error_code = Result(Decode64BitError(args->system_error.error_code_64)); break; case ErrorAppletMode::ShowApplicationError: CopyArgumentData(data, args->application_error); - error_code = ResultCode(args->application_error.error_code); + error_code = Result(args->application_error.error_code); break; case ErrorAppletMode::ShowErrorRecord: CopyArgumentData(data, args->error_record); @@ -152,12 +151,12 @@ bool Error::TransactionComplete() const { return complete; } -ResultCode Error::GetStatus() const { +Result Error::GetStatus() const { return ResultSuccess; } void Error::ExecuteInteractive() { - UNREACHABLE_MSG("Unexpected interactive applet data!"); + ASSERT_MSG(false, "Unexpected interactive applet data!"); } void Error::Execute() { diff --git a/src/core/hle/service/am/applets/applet_error.h b/src/core/hle/service/am/applets/applet_error.h index 8aa9046a5..d78d6f1d1 100644 --- a/src/core/hle/service/am/applets/applet_error.h +++ b/src/core/hle/service/am/applets/applet_error.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -32,7 +31,7 @@ public: void Initialize() override; bool TransactionComplete() const override; - ResultCode GetStatus() const override; + Result GetStatus() const override; void ExecuteInteractive() override; void Execute() override; @@ -42,7 +41,7 @@ private: union ErrorArguments; const Core::Frontend::ErrorApplet& frontend; - ResultCode error_code = ResultSuccess; + Result error_code = ResultSuccess; ErrorAppletMode mode = ErrorAppletMode::ShowError; std::unique_ptr<ErrorArguments> args; diff --git a/src/core/hle/service/am/applets/applet_general_backend.cpp b/src/core/hle/service/am/applets/applet_general_backend.cpp index 2c6e9d83c..c34ef08b3 100644 --- a/src/core/hle/service/am/applets/applet_general_backend.cpp +++ b/src/core/hle/service/am/applets/applet_general_backend.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "common/hex_util.h" @@ -14,7 +13,7 @@ namespace Service::AM::Applets { -constexpr ResultCode ERROR_INVALID_PIN{ErrorModule::PCTL, 221}; +constexpr Result ERROR_INVALID_PIN{ErrorModule::PCTL, 221}; static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) { std::shared_ptr<IStorage> storage = broker.PopNormalDataToApplet(); @@ -72,12 +71,12 @@ bool Auth::TransactionComplete() const { return complete; } -ResultCode Auth::GetStatus() const { +Result Auth::GetStatus() const { return successful ? ResultSuccess : ERROR_INVALID_PIN; } void Auth::ExecuteInteractive() { - UNREACHABLE_MSG("Unexpected interactive applet data."); + ASSERT_MSG(false, "Unexpected interactive applet data."); } void Auth::Execute() { @@ -137,7 +136,7 @@ void Auth::AuthFinished(bool is_successful) { successful = is_successful; struct Return { - ResultCode result_code; + Result result_code; }; static_assert(sizeof(Return) == 0x4, "Return (AuthApplet) has incorrect size."); @@ -171,12 +170,12 @@ bool PhotoViewer::TransactionComplete() const { return complete; } -ResultCode PhotoViewer::GetStatus() const { +Result PhotoViewer::GetStatus() const { return ResultSuccess; } void PhotoViewer::ExecuteInteractive() { - UNREACHABLE_MSG("Unexpected interactive applet data."); + ASSERT_MSG(false, "Unexpected interactive applet data."); } void PhotoViewer::Execute() { @@ -224,7 +223,7 @@ bool StubApplet::TransactionComplete() const { return true; } -ResultCode StubApplet::GetStatus() const { +Result StubApplet::GetStatus() const { LOG_WARNING(Service_AM, "called (STUBBED)"); return ResultSuccess; } diff --git a/src/core/hle/service/am/applets/applet_general_backend.h b/src/core/hle/service/am/applets/applet_general_backend.h index 7496ded88..a9f2535a2 100644 --- a/src/core/hle/service/am/applets/applet_general_backend.h +++ b/src/core/hle/service/am/applets/applet_general_backend.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -26,7 +25,7 @@ public: void Initialize() override; bool TransactionComplete() const override; - ResultCode GetStatus() const override; + Result GetStatus() const override; void ExecuteInteractive() override; void Execute() override; @@ -57,7 +56,7 @@ public: void Initialize() override; bool TransactionComplete() const override; - ResultCode GetStatus() const override; + Result GetStatus() const override; void ExecuteInteractive() override; void Execute() override; @@ -78,7 +77,7 @@ public: void Initialize() override; bool TransactionComplete() const override; - ResultCode GetStatus() const override; + Result GetStatus() const override; void ExecuteInteractive() override; void Execute() override; diff --git a/src/core/hle/service/am/applets/applet_mii_edit.cpp b/src/core/hle/service/am/applets/applet_mii_edit.cpp new file mode 100644 index 000000000..ae80ef506 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_mii_edit.cpp @@ -0,0 +1,138 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/core.h" +#include "core/frontend/applets/mii_edit.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/applet_mii_edit.h" +#include "core/hle/service/mii/mii_manager.h" + +namespace Service::AM::Applets { + +MiiEdit::MiiEdit(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::MiiEditApplet& frontend_) + : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} + +MiiEdit::~MiiEdit() = default; + +void MiiEdit::Initialize() { + // Note: MiiEdit is not initialized with common arguments. + // Instead, it is initialized by an AppletInput storage with size 0x100 bytes. + // Do NOT call Applet::Initialize() here. + + const auto storage = broker.PopNormalDataToApplet(); + ASSERT(storage != nullptr); + + const auto applet_input_data = storage->GetData(); + ASSERT(applet_input_data.size() >= sizeof(MiiEditAppletInputCommon)); + + std::memcpy(&applet_input_common, applet_input_data.data(), sizeof(MiiEditAppletInputCommon)); + + LOG_INFO(Service_AM, + "Initializing MiiEdit Applet with MiiEditAppletVersion={} and MiiEditAppletMode={}", + applet_input_common.version, applet_input_common.applet_mode); + + switch (applet_input_common.version) { + case MiiEditAppletVersion::Version3: + ASSERT(applet_input_data.size() == + sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV3)); + std::memcpy(&applet_input_v3, applet_input_data.data() + sizeof(MiiEditAppletInputCommon), + sizeof(MiiEditAppletInputV3)); + break; + case MiiEditAppletVersion::Version4: + ASSERT(applet_input_data.size() == + sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV4)); + std::memcpy(&applet_input_v4, applet_input_data.data() + sizeof(MiiEditAppletInputCommon), + sizeof(MiiEditAppletInputV4)); + break; + default: + UNIMPLEMENTED_MSG("Unknown MiiEditAppletVersion={} with size={}", + applet_input_common.version, applet_input_data.size()); + ASSERT(applet_input_data.size() >= + sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV4)); + std::memcpy(&applet_input_v4, applet_input_data.data() + sizeof(MiiEditAppletInputCommon), + sizeof(MiiEditAppletInputV4)); + break; + } +} + +bool MiiEdit::TransactionComplete() const { + return is_complete; +} + +Result MiiEdit::GetStatus() const { + return ResultSuccess; +} + +void MiiEdit::ExecuteInteractive() { + ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet."); +} + +void MiiEdit::Execute() { + if (is_complete) { + return; + } + + // This is a default stub for each of the MiiEdit applet modes. + switch (applet_input_common.applet_mode) { + case MiiEditAppletMode::ShowMiiEdit: + case MiiEditAppletMode::AppendMii: + case MiiEditAppletMode::AppendMiiImage: + case MiiEditAppletMode::UpdateMiiImage: + MiiEditOutput(MiiEditResult::Success, 0); + break; + case MiiEditAppletMode::CreateMii: + case MiiEditAppletMode::EditMii: { + Service::Mii::MiiManager mii_manager; + + const MiiEditCharInfo char_info{ + .mii_info{applet_input_common.applet_mode == MiiEditAppletMode::EditMii + ? applet_input_v4.char_info.mii_info + : mii_manager.BuildDefault(0)}, + }; + + MiiEditOutputForCharInfoEditing(MiiEditResult::Success, char_info); + break; + } + default: + UNIMPLEMENTED_MSG("Unknown MiiEditAppletMode={}", applet_input_common.applet_mode); + + MiiEditOutput(MiiEditResult::Success, 0); + break; + } +} + +void MiiEdit::MiiEditOutput(MiiEditResult result, s32 index) { + const MiiEditAppletOutput applet_output{ + .result{result}, + .index{index}, + }; + + std::vector<u8> out_data(sizeof(MiiEditAppletOutput)); + std::memcpy(out_data.data(), &applet_output, sizeof(MiiEditAppletOutput)); + + is_complete = true; + + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data))); + broker.SignalStateChanged(); +} + +void MiiEdit::MiiEditOutputForCharInfoEditing(MiiEditResult result, + const MiiEditCharInfo& char_info) { + const MiiEditAppletOutputForCharInfoEditing applet_output{ + .result{result}, + .char_info{char_info}, + }; + + std::vector<u8> out_data(sizeof(MiiEditAppletOutputForCharInfoEditing)); + std::memcpy(out_data.data(), &applet_output, sizeof(MiiEditAppletOutputForCharInfoEditing)); + + is_complete = true; + + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data))); + broker.SignalStateChanged(); +} + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_mii_edit.h b/src/core/hle/service/am/applets/applet_mii_edit.h new file mode 100644 index 000000000..d18dd3cf5 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_mii_edit.h @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/result.h" +#include "core/hle/service/am/applets/applet_mii_edit_types.h" +#include "core/hle/service/am/applets/applets.h" + +namespace Core { +class System; +} // namespace Core + +namespace Service::AM::Applets { + +class MiiEdit final : public Applet { +public: + explicit MiiEdit(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::MiiEditApplet& frontend_); + ~MiiEdit() override; + + void Initialize() override; + + bool TransactionComplete() const override; + Result GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + + void MiiEditOutput(MiiEditResult result, s32 index); + + void MiiEditOutputForCharInfoEditing(MiiEditResult result, const MiiEditCharInfo& char_info); + +private: + const Core::Frontend::MiiEditApplet& frontend; + Core::System& system; + + MiiEditAppletInputCommon applet_input_common{}; + MiiEditAppletInputV3 applet_input_v3{}; + MiiEditAppletInputV4 applet_input_v4{}; + + bool is_complete{false}; +}; + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_mii_edit_types.h b/src/core/hle/service/am/applets/applet_mii_edit_types.h new file mode 100644 index 000000000..4705d019f --- /dev/null +++ b/src/core/hle/service/am/applets/applet_mii_edit_types.h @@ -0,0 +1,82 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/service/mii/types.h" + +namespace Service::AM::Applets { + +enum class MiiEditAppletVersion : s32 { + Version3 = 0x3, // 1.0.0 - 10.1.1 + Version4 = 0x4, // 10.2.0+ +}; + +// This is nn::mii::AppletMode +enum class MiiEditAppletMode : u32 { + ShowMiiEdit = 0, + AppendMii = 1, + AppendMiiImage = 2, + UpdateMiiImage = 3, + CreateMii = 4, + EditMii = 5, +}; + +enum class MiiEditResult : u32 { + Success, + Cancel, +}; + +struct MiiEditCharInfo { + Service::Mii::CharInfo mii_info{}; +}; +static_assert(sizeof(MiiEditCharInfo) == 0x58, "MiiEditCharInfo has incorrect size."); + +struct MiiEditAppletInputCommon { + MiiEditAppletVersion version{}; + MiiEditAppletMode applet_mode{}; +}; +static_assert(sizeof(MiiEditAppletInputCommon) == 0x8, + "MiiEditAppletInputCommon has incorrect size."); + +struct MiiEditAppletInputV3 { + u32 special_mii_key_code{}; + std::array<Common::UUID, 8> valid_uuids{}; + Common::UUID used_uuid{}; + INSERT_PADDING_BYTES(0x64); +}; +static_assert(sizeof(MiiEditAppletInputV3) == 0x100 - sizeof(MiiEditAppletInputCommon), + "MiiEditAppletInputV3 has incorrect size."); + +struct MiiEditAppletInputV4 { + u32 special_mii_key_code{}; + MiiEditCharInfo char_info{}; + INSERT_PADDING_BYTES(0x28); + Common::UUID used_uuid{}; + INSERT_PADDING_BYTES(0x64); +}; +static_assert(sizeof(MiiEditAppletInputV4) == 0x100 - sizeof(MiiEditAppletInputCommon), + "MiiEditAppletInputV4 has incorrect size."); + +// This is nn::mii::AppletOutput +struct MiiEditAppletOutput { + MiiEditResult result{}; + s32 index{}; + INSERT_PADDING_BYTES(0x18); +}; +static_assert(sizeof(MiiEditAppletOutput) == 0x20, "MiiEditAppletOutput has incorrect size."); + +// This is nn::mii::AppletOutputForCharInfoEditing +struct MiiEditAppletOutputForCharInfoEditing { + MiiEditResult result{}; + MiiEditCharInfo char_info{}; + INSERT_PADDING_BYTES(0x24); +}; +static_assert(sizeof(MiiEditAppletOutputForCharInfoEditing) == 0x80, + "MiiEditAppletOutputForCharInfoEditing has incorrect size."); + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_profile_select.cpp b/src/core/hle/service/am/applets/applet_profile_select.cpp index 82500e121..c738db028 100644 --- a/src/core/hle/service/am/applets/applet_profile_select.cpp +++ b/src/core/hle/service/am/applets/applet_profile_select.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> @@ -13,7 +12,7 @@ namespace Service::AM::Applets { -constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1}; +constexpr Result ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1}; ProfileSelect::ProfileSelect(Core::System& system_, LibraryAppletMode applet_mode_, const Core::Frontend::ProfileSelectApplet& frontend_) @@ -40,12 +39,12 @@ bool ProfileSelect::TransactionComplete() const { return complete; } -ResultCode ProfileSelect::GetStatus() const { +Result ProfileSelect::GetStatus() const { return status; } void ProfileSelect::ExecuteInteractive() { - UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet."); + ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet."); } void ProfileSelect::Execute() { diff --git a/src/core/hle/service/am/applets/applet_profile_select.h b/src/core/hle/service/am/applets/applet_profile_select.h index 852e1e0c0..b77f1d205 100644 --- a/src/core/hle/service/am/applets/applet_profile_select.h +++ b/src/core/hle/service/am/applets/applet_profile_select.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -40,7 +39,7 @@ public: void Initialize() override; bool TransactionComplete() const override; - ResultCode GetStatus() const override; + Result GetStatus() const override; void ExecuteInteractive() override; void Execute() override; @@ -51,7 +50,7 @@ private: UserSelectionConfig config; bool complete = false; - ResultCode status = ResultSuccess; + Result status = ResultSuccess; std::vector<u8> final_data; Core::System& system; }; diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.cpp b/src/core/hle/service/am/applets/applet_software_keyboard.cpp index f38f53f69..c18236045 100644 --- a/src/core/hle/service/am/applets/applet_software_keyboard.cpp +++ b/src/core/hle/service/am/applets/applet_software_keyboard.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/string_util.h" #include "core/core.h" @@ -72,7 +71,7 @@ void SoftwareKeyboard::Initialize() { InitializeBackground(applet_mode); break; default: - UNREACHABLE_MSG("Invalid LibraryAppletMode={}", applet_mode); + ASSERT_MSG(false, "Invalid LibraryAppletMode={}", applet_mode); break; } } @@ -81,7 +80,7 @@ bool SoftwareKeyboard::TransactionComplete() const { return complete; } -ResultCode SoftwareKeyboard::GetStatus() const { +Result SoftwareKeyboard::GetStatus() const { return status; } @@ -226,7 +225,7 @@ void SoftwareKeyboard::InitializeForeground() { ASSERT(work_buffer_storage != nullptr); if (swkbd_config_common.initial_string_length == 0) { - InitializeFrontendKeyboard(); + InitializeFrontendNormalKeyboard(); return; } @@ -243,7 +242,7 @@ void SoftwareKeyboard::InitializeForeground() { LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text)); - InitializeFrontendKeyboard(); + InitializeFrontendNormalKeyboard(); } void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) { @@ -480,129 +479,179 @@ void SoftwareKeyboard::ChangeState(SwkbdState state) { ReplyDefault(); } -void SoftwareKeyboard::InitializeFrontendKeyboard() { - if (is_background) { - const auto& appear_arg = swkbd_calc_arg.appear_arg; - - std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - appear_arg.ok_text.data(), appear_arg.ok_text.size()); - - const u32 max_text_length = - appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH - ? appear_arg.max_text_length - : DEFAULT_MAX_TEXT_LENGTH; - - const u32 min_text_length = - appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; - - const s32 initial_cursor_position = - current_cursor_position > 0 ? current_cursor_position : 0; - - const auto text_draw_type = - max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; - - Core::Frontend::KeyboardInitializeParameters initialize_parameters{ - .ok_text{std::move(ok_text)}, - .header_text{}, - .sub_text{}, - .guide_text{}, - .initial_text{current_text}, - .max_text_length{max_text_length}, - .min_text_length{min_text_length}, - .initial_cursor_position{initial_cursor_position}, - .type{appear_arg.type}, - .password_mode{SwkbdPasswordMode::Disabled}, - .text_draw_type{text_draw_type}, - .key_disable_flags{appear_arg.key_disable_flags}, - .use_blur_background{false}, - .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, - .enable_return_button{appear_arg.enable_return_button}, - .disable_cancel_button{appear_arg.disable_cancel_button}, - }; - - frontend.InitializeKeyboard( - true, std::move(initialize_parameters), {}, - [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) { - SubmitTextInline(reply_type, submitted_text, cursor_position); - }); - } else { - std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size()); - - std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size()); - - std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size()); - - std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size()); - - const u32 max_text_length = - swkbd_config_common.max_text_length > 0 && - swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH - ? swkbd_config_common.max_text_length - : DEFAULT_MAX_TEXT_LENGTH; - - const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length - ? swkbd_config_common.min_text_length - : 0; - - const s32 initial_cursor_position = [this] { - switch (swkbd_config_common.initial_cursor_position) { - case SwkbdInitialCursorPosition::Start: - default: - return 0; - case SwkbdInitialCursorPosition::End: - return static_cast<s32>(initial_text.size()); - } - }(); - - const auto text_draw_type = [this, max_text_length] { - switch (swkbd_config_common.text_draw_type) { - case SwkbdTextDrawType::Line: - default: - return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; - case SwkbdTextDrawType::Box: - case SwkbdTextDrawType::DownloadCode: - return swkbd_config_common.text_draw_type; - } - }(); - - const auto enable_return_button = text_draw_type == SwkbdTextDrawType::Box - ? swkbd_config_common.enable_return_button - : false; - - const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227 - ? swkbd_config_new.disable_cancel_button - : false; - - Core::Frontend::KeyboardInitializeParameters initialize_parameters{ - .ok_text{std::move(ok_text)}, - .header_text{std::move(header_text)}, - .sub_text{std::move(sub_text)}, - .guide_text{std::move(guide_text)}, - .initial_text{initial_text}, - .max_text_length{max_text_length}, - .min_text_length{min_text_length}, - .initial_cursor_position{initial_cursor_position}, - .type{swkbd_config_common.type}, - .password_mode{swkbd_config_common.password_mode}, - .text_draw_type{text_draw_type}, - .key_disable_flags{swkbd_config_common.key_disable_flags}, - .use_blur_background{swkbd_config_common.use_blur_background}, - .enable_backspace_button{true}, - .enable_return_button{enable_return_button}, - .disable_cancel_button{disable_cancel_button}, - }; - - frontend.InitializeKeyboard( - false, std::move(initialize_parameters), - [this](SwkbdResult result, std::u16string submitted_text, bool confirmed) { - SubmitTextNormal(result, submitted_text, confirmed); - }, - {}); - } +void SoftwareKeyboard::InitializeFrontendNormalKeyboard() { + std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size()); + + std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size()); + + std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size()); + + std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size()); + + const u32 max_text_length = + swkbd_config_common.max_text_length > 0 && + swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? swkbd_config_common.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length + ? swkbd_config_common.min_text_length + : 0; + + const s32 initial_cursor_position = [this] { + switch (swkbd_config_common.initial_cursor_position) { + case SwkbdInitialCursorPosition::Start: + default: + return 0; + case SwkbdInitialCursorPosition::End: + return static_cast<s32>(initial_text.size()); + } + }(); + + const auto text_draw_type = [this, max_text_length] { + switch (swkbd_config_common.text_draw_type) { + case SwkbdTextDrawType::Line: + default: + return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; + case SwkbdTextDrawType::Box: + case SwkbdTextDrawType::DownloadCode: + return swkbd_config_common.text_draw_type; + } + }(); + + const auto enable_return_button = + text_draw_type == SwkbdTextDrawType::Box ? swkbd_config_common.enable_return_button : false; + + const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227 + ? swkbd_config_new.disable_cancel_button + : false; + + Core::Frontend::KeyboardInitializeParameters initialize_parameters{ + .ok_text{std::move(ok_text)}, + .header_text{std::move(header_text)}, + .sub_text{std::move(sub_text)}, + .guide_text{std::move(guide_text)}, + .initial_text{initial_text}, + .left_optional_symbol_key{swkbd_config_common.left_optional_symbol_key}, + .right_optional_symbol_key{swkbd_config_common.right_optional_symbol_key}, + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .initial_cursor_position{initial_cursor_position}, + .type{swkbd_config_common.type}, + .password_mode{swkbd_config_common.password_mode}, + .text_draw_type{text_draw_type}, + .key_disable_flags{swkbd_config_common.key_disable_flags}, + .use_blur_background{swkbd_config_common.use_blur_background}, + .enable_backspace_button{true}, + .enable_return_button{enable_return_button}, + .disable_cancel_button{disable_cancel_button}, + }; + + frontend.InitializeKeyboard( + false, std::move(initialize_parameters), + [this](SwkbdResult result, std::u16string submitted_text, bool confirmed) { + SubmitTextNormal(result, submitted_text, confirmed); + }, + {}); +} + +void SoftwareKeyboard::InitializeFrontendInlineKeyboard( + Core::Frontend::KeyboardInitializeParameters initialize_parameters) { + frontend.InitializeKeyboard( + true, std::move(initialize_parameters), {}, + [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) { + SubmitTextInline(reply_type, submitted_text, cursor_position); + }); +} + +void SoftwareKeyboard::InitializeFrontendInlineKeyboardOld() { + const auto& appear_arg = swkbd_calc_arg_old.appear_arg; + + std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + appear_arg.ok_text.data(), appear_arg.ok_text.size()); + + const u32 max_text_length = + appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? appear_arg.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = + appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; + + const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0; + + const auto text_draw_type = + max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; + + Core::Frontend::KeyboardInitializeParameters initialize_parameters{ + .ok_text{std::move(ok_text)}, + .header_text{}, + .sub_text{}, + .guide_text{}, + .initial_text{current_text}, + .left_optional_symbol_key{appear_arg.left_optional_symbol_key}, + .right_optional_symbol_key{appear_arg.right_optional_symbol_key}, + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .initial_cursor_position{initial_cursor_position}, + .type{appear_arg.type}, + .password_mode{SwkbdPasswordMode::Disabled}, + .text_draw_type{text_draw_type}, + .key_disable_flags{appear_arg.key_disable_flags}, + .use_blur_background{false}, + .enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button}, + .enable_return_button{appear_arg.enable_return_button}, + .disable_cancel_button{appear_arg.disable_cancel_button}, + }; + + InitializeFrontendInlineKeyboard(std::move(initialize_parameters)); +} + +void SoftwareKeyboard::InitializeFrontendInlineKeyboardNew() { + const auto& appear_arg = swkbd_calc_arg_new.appear_arg; + + std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + appear_arg.ok_text.data(), appear_arg.ok_text.size()); + + const u32 max_text_length = + appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? appear_arg.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = + appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; + + const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0; + + const auto text_draw_type = + max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; + + Core::Frontend::KeyboardInitializeParameters initialize_parameters{ + .ok_text{std::move(ok_text)}, + .header_text{}, + .sub_text{}, + .guide_text{}, + .initial_text{current_text}, + .left_optional_symbol_key{appear_arg.left_optional_symbol_key}, + .right_optional_symbol_key{appear_arg.right_optional_symbol_key}, + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .initial_cursor_position{initial_cursor_position}, + .type{appear_arg.type}, + .password_mode{SwkbdPasswordMode::Disabled}, + .text_draw_type{text_draw_type}, + .key_disable_flags{appear_arg.key_disable_flags}, + .use_blur_background{false}, + .enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button}, + .enable_return_button{appear_arg.enable_return_button}, + .disable_cancel_button{appear_arg.disable_cancel_button}, + }; + + InitializeFrontendInlineKeyboard(std::move(initialize_parameters)); } void SoftwareKeyboard::ShowNormalKeyboard() { @@ -614,14 +663,21 @@ void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_resul frontend.ShowTextCheckDialog(text_check_result, std::move(text_check_message)); } -void SoftwareKeyboard::ShowInlineKeyboard() { +void SoftwareKeyboard::ShowInlineKeyboard( + Core::Frontend::InlineAppearParameters appear_parameters) { + frontend.ShowInlineKeyboard(std::move(appear_parameters)); + + ChangeState(SwkbdState::InitializedIsShown); +} + +void SoftwareKeyboard::ShowInlineKeyboardOld() { if (swkbd_state != SwkbdState::InitializedIsHidden) { return; } ChangeState(SwkbdState::InitializedIsAppearing); - const auto& appear_arg = swkbd_calc_arg.appear_arg; + const auto& appear_arg = swkbd_calc_arg_old.appear_arg; const u32 max_text_length = appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH @@ -634,21 +690,54 @@ void SoftwareKeyboard::ShowInlineKeyboard() { Core::Frontend::InlineAppearParameters appear_parameters{ .max_text_length{max_text_length}, .min_text_length{min_text_length}, - .key_top_scale_x{swkbd_calc_arg.key_top_scale_x}, - .key_top_scale_y{swkbd_calc_arg.key_top_scale_y}, - .key_top_translate_x{swkbd_calc_arg.key_top_translate_x}, - .key_top_translate_y{swkbd_calc_arg.key_top_translate_y}, + .key_top_scale_x{swkbd_calc_arg_old.key_top_scale_x}, + .key_top_scale_y{swkbd_calc_arg_old.key_top_scale_y}, + .key_top_translate_x{swkbd_calc_arg_old.key_top_translate_x}, + .key_top_translate_y{swkbd_calc_arg_old.key_top_translate_y}, .type{appear_arg.type}, .key_disable_flags{appear_arg.key_disable_flags}, - .key_top_as_floating{swkbd_calc_arg.key_top_as_floating}, - .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, + .key_top_as_floating{swkbd_calc_arg_old.key_top_as_floating}, + .enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button}, .enable_return_button{appear_arg.enable_return_button}, .disable_cancel_button{appear_arg.disable_cancel_button}, }; - frontend.ShowInlineKeyboard(std::move(appear_parameters)); + ShowInlineKeyboard(std::move(appear_parameters)); +} - ChangeState(SwkbdState::InitializedIsShown); +void SoftwareKeyboard::ShowInlineKeyboardNew() { + if (swkbd_state != SwkbdState::InitializedIsHidden) { + return; + } + + ChangeState(SwkbdState::InitializedIsAppearing); + + const auto& appear_arg = swkbd_calc_arg_new.appear_arg; + + const u32 max_text_length = + appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? appear_arg.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = + appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; + + Core::Frontend::InlineAppearParameters appear_parameters{ + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .key_top_scale_x{swkbd_calc_arg_new.key_top_scale_x}, + .key_top_scale_y{swkbd_calc_arg_new.key_top_scale_y}, + .key_top_translate_x{swkbd_calc_arg_new.key_top_translate_x}, + .key_top_translate_y{swkbd_calc_arg_new.key_top_translate_y}, + .type{appear_arg.type}, + .key_disable_flags{appear_arg.key_disable_flags}, + .key_top_as_floating{swkbd_calc_arg_new.key_top_as_floating}, + .enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button}, + .enable_return_button{appear_arg.enable_return_button}, + .disable_cancel_button{appear_arg.disable_cancel_button}, + }; + + ShowInlineKeyboard(std::move(appear_parameters)); } void SoftwareKeyboard::HideInlineKeyboard() { @@ -693,6 +782,8 @@ void SoftwareKeyboard::RequestFinalize(const std::vector<u8>& request_data) { void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector<u8>& request_data) { LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented."); + + ReplyReleasedUserWordInfo(); } void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_data) { @@ -702,53 +793,135 @@ void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_dat void SoftwareKeyboard::RequestCalc(const std::vector<u8>& request_data) { LOG_DEBUG(Service_AM, "Processing Request: Calc"); - ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArg)); + ASSERT(request_data.size() >= sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon)); + + std::memcpy(&swkbd_calc_arg_common, request_data.data() + sizeof(SwkbdRequestCommand), + sizeof(SwkbdCalcArgCommon)); + + switch (swkbd_calc_arg_common.calc_arg_size) { + case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld): + ASSERT(request_data.size() == + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld)); + std::memcpy(&swkbd_calc_arg_old, + request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon), + sizeof(SwkbdCalcArgOld)); + RequestCalcOld(); + break; + case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew): + ASSERT(request_data.size() == + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew)); + std::memcpy(&swkbd_calc_arg_new, + request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon), + sizeof(SwkbdCalcArgNew)); + RequestCalcNew(); + break; + default: + UNIMPLEMENTED_MSG("Unknown SwkbdCalcArg size={}", swkbd_calc_arg_common.calc_arg_size); + ASSERT(request_data.size() >= + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew)); + std::memcpy(&swkbd_calc_arg_new, + request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon), + sizeof(SwkbdCalcArgNew)); + RequestCalcNew(); + break; + } +} + +void SoftwareKeyboard::RequestCalcOld() { + if (swkbd_calc_arg_common.flags.set_input_text) { + current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_calc_arg_old.input_text.data(), swkbd_calc_arg_old.input_text.size()); + } + + if (swkbd_calc_arg_common.flags.set_cursor_position) { + current_cursor_position = swkbd_calc_arg_old.cursor_position; + } + + if (swkbd_calc_arg_common.flags.set_utf8_mode) { + inline_use_utf8 = swkbd_calc_arg_old.utf8_mode; + } - std::memcpy(&swkbd_calc_arg, request_data.data() + sizeof(SwkbdRequestCommand), - sizeof(SwkbdCalcArg)); + if (swkbd_state <= SwkbdState::InitializedIsHidden && + swkbd_calc_arg_common.flags.unset_customize_dic) { + ReplyUnsetCustomizeDic(); + } + + if (swkbd_state <= SwkbdState::InitializedIsHidden && + swkbd_calc_arg_common.flags.unset_user_word_info) { + ReplyReleasedUserWordInfo(); + } + + if (swkbd_state == SwkbdState::NotInitialized && + swkbd_calc_arg_common.flags.set_initialize_arg) { + InitializeFrontendInlineKeyboardOld(); + + ChangeState(SwkbdState::InitializedIsHidden); + + ReplyFinishedInitialize(); + } + + if (!swkbd_calc_arg_common.flags.set_initialize_arg && + (swkbd_calc_arg_common.flags.set_input_text || + swkbd_calc_arg_common.flags.set_cursor_position)) { + InlineTextChanged(); + } + + if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) { + ShowInlineKeyboardOld(); + return; + } + + if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) { + HideInlineKeyboard(); + return; + } +} - if (swkbd_calc_arg.flags.set_input_text) { +void SoftwareKeyboard::RequestCalcNew() { + if (swkbd_calc_arg_common.flags.set_input_text) { current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_calc_arg.input_text.data(), swkbd_calc_arg.input_text.size()); + swkbd_calc_arg_new.input_text.data(), swkbd_calc_arg_new.input_text.size()); } - if (swkbd_calc_arg.flags.set_cursor_position) { - current_cursor_position = swkbd_calc_arg.cursor_position; + if (swkbd_calc_arg_common.flags.set_cursor_position) { + current_cursor_position = swkbd_calc_arg_new.cursor_position; } - if (swkbd_calc_arg.flags.set_utf8_mode) { - inline_use_utf8 = swkbd_calc_arg.utf8_mode; + if (swkbd_calc_arg_common.flags.set_utf8_mode) { + inline_use_utf8 = swkbd_calc_arg_new.utf8_mode; } if (swkbd_state <= SwkbdState::InitializedIsHidden && - swkbd_calc_arg.flags.unset_customize_dic) { + swkbd_calc_arg_common.flags.unset_customize_dic) { ReplyUnsetCustomizeDic(); } if (swkbd_state <= SwkbdState::InitializedIsHidden && - swkbd_calc_arg.flags.unset_user_word_info) { + swkbd_calc_arg_common.flags.unset_user_word_info) { ReplyReleasedUserWordInfo(); } - if (swkbd_state == SwkbdState::NotInitialized && swkbd_calc_arg.flags.set_initialize_arg) { - InitializeFrontendKeyboard(); + if (swkbd_state == SwkbdState::NotInitialized && + swkbd_calc_arg_common.flags.set_initialize_arg) { + InitializeFrontendInlineKeyboardNew(); ChangeState(SwkbdState::InitializedIsHidden); ReplyFinishedInitialize(); } - if (!swkbd_calc_arg.flags.set_initialize_arg && - (swkbd_calc_arg.flags.set_input_text || swkbd_calc_arg.flags.set_cursor_position)) { + if (!swkbd_calc_arg_common.flags.set_initialize_arg && + (swkbd_calc_arg_common.flags.set_input_text || + swkbd_calc_arg_common.flags.set_cursor_position)) { InlineTextChanged(); } - if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg.flags.appear) { - ShowInlineKeyboard(); + if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) { + ShowInlineKeyboardNew(); return; } - if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg.flags.disappear) { + if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) { HideInlineKeyboard(); return; } diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.h b/src/core/hle/service/am/applets/applet_software_keyboard.h index a0fddd965..b01b31c98 100644 --- a/src/core/hle/service/am/applets/applet_software_keyboard.h +++ b/src/core/hle/service/am/applets/applet_software_keyboard.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -13,6 +12,11 @@ namespace Core { class System; } +namespace Core::Frontend { +struct KeyboardInitializeParameters; +struct InlineAppearParameters; +} // namespace Core::Frontend + namespace Service::AM::Applets { class SoftwareKeyboard final : public Applet { @@ -24,7 +28,7 @@ public: void Initialize() override; bool TransactionComplete() const override; - ResultCode GetStatus() const override; + Result GetStatus() const override; void ExecuteInteractive() override; void Execute() override; @@ -78,13 +82,22 @@ private: void ChangeState(SwkbdState state); /** - * Signals the frontend to initialize the software keyboard with common parameters. - * This initializes either the normal software keyboard or the inline software keyboard - * depending on the state of is_background. + * Signals the frontend to initialize the normal software keyboard with common parameters. * Note that this does not cause the keyboard to appear. - * Use the respective Show*Keyboard() functions to cause the respective keyboards to appear. + * Use the ShowNormalKeyboard() functions to cause the keyboard to appear. */ - void InitializeFrontendKeyboard(); + void InitializeFrontendNormalKeyboard(); + + /** + * Signals the frontend to initialize the inline software keyboard with common parameters. + * Note that this does not cause the keyboard to appear. + * Use the ShowInlineKeyboard() to cause the keyboard to appear. + */ + void InitializeFrontendInlineKeyboard( + Core::Frontend::KeyboardInitializeParameters initialize_parameters); + + void InitializeFrontendInlineKeyboardOld(); + void InitializeFrontendInlineKeyboardNew(); /// Signals the frontend to show the normal software keyboard. void ShowNormalKeyboard(); @@ -94,7 +107,10 @@ private: std::u16string text_check_message); /// Signals the frontend to show the inline software keyboard. - void ShowInlineKeyboard(); + void ShowInlineKeyboard(Core::Frontend::InlineAppearParameters appear_parameters); + + void ShowInlineKeyboardOld(); + void ShowInlineKeyboardNew(); /// Signals the frontend to hide the inline software keyboard. void HideInlineKeyboard(); @@ -111,6 +127,8 @@ private: void RequestSetUserWordInfo(const std::vector<u8>& request_data); void RequestSetCustomizeDic(const std::vector<u8>& request_data); void RequestCalc(const std::vector<u8>& request_data); + void RequestCalcOld(); + void RequestCalcNew(); void RequestSetCustomizedDictionaries(const std::vector<u8>& request_data); void RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data); void RequestSetChangedStringV2Flag(const std::vector<u8>& request_data); @@ -149,7 +167,9 @@ private: SwkbdState swkbd_state{SwkbdState::NotInitialized}; SwkbdInitializeArg swkbd_initialize_arg; - SwkbdCalcArg swkbd_calc_arg; + SwkbdCalcArgCommon swkbd_calc_arg_common; + SwkbdCalcArgOld swkbd_calc_arg_old; + SwkbdCalcArgNew swkbd_calc_arg_new; bool use_changed_string_v2{false}; bool use_moved_cursor_v2{false}; bool inline_use_utf8{false}; @@ -160,7 +180,7 @@ private: bool is_background{false}; bool complete{false}; - ResultCode status{ResultSuccess}; + Result status{ResultSuccess}; }; } // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_software_keyboard_types.h b/src/core/hle/service/am/applets/applet_software_keyboard_types.h index 21aa8e800..1f696900e 100644 --- a/src/core/hle/service/am/applets/applet_software_keyboard_types.h +++ b/src/core/hle/service/am/applets/applet_software_keyboard_types.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -10,6 +9,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" +#include "common/uuid.h" namespace Service::AM::Applets { @@ -216,7 +216,7 @@ struct SwkbdInitializeArg { }; static_assert(sizeof(SwkbdInitializeArg) == 0x8, "SwkbdInitializeArg has incorrect size."); -struct SwkbdAppearArg { +struct SwkbdAppearArgOld { SwkbdType type{}; std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{}; char16_t left_optional_symbol_key{}; @@ -229,19 +229,76 @@ struct SwkbdAppearArg { bool enable_return_button{}; INSERT_PADDING_BYTES(3); u32 flags{}; - INSERT_PADDING_WORDS(6); + bool is_use_save_data{}; + INSERT_PADDING_BYTES(7); + Common::UUID user_id{}; }; -static_assert(sizeof(SwkbdAppearArg) == 0x48, "SwkbdAppearArg has incorrect size."); +static_assert(sizeof(SwkbdAppearArgOld) == 0x48, "SwkbdAppearArg has incorrect size."); -struct SwkbdCalcArg { +struct SwkbdAppearArgNew { + SwkbdType type{}; + std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{}; + char16_t left_optional_symbol_key{}; + char16_t right_optional_symbol_key{}; + bool use_prediction{}; + bool disable_cancel_button{}; + SwkbdKeyDisableFlags key_disable_flags{}; + u32 max_text_length{}; + u32 min_text_length{}; + bool enable_return_button{}; + INSERT_PADDING_BYTES(3); + u32 flags{}; + bool is_use_save_data{}; + INSERT_PADDING_BYTES(7); + Common::UUID user_id{}; + u64 start_sampling_number{}; + INSERT_PADDING_WORDS(8); +}; +static_assert(sizeof(SwkbdAppearArgNew) == 0x70, "SwkbdAppearArg has incorrect size."); + +struct SwkbdCalcArgCommon { u32 unknown{}; u16 calc_arg_size{}; INSERT_PADDING_BYTES(2); SwkbdCalcArgFlags flags{}; SwkbdInitializeArg initialize_arg{}; +}; +static_assert(sizeof(SwkbdCalcArgCommon) == 0x18, "SwkbdCalcArgCommon has incorrect size."); + +struct SwkbdCalcArgOld { + f32 volume{}; + s32 cursor_position{}; + SwkbdAppearArgOld appear_arg{}; + std::array<char16_t, 0x1FA> input_text{}; + bool utf8_mode{}; + INSERT_PADDING_BYTES(1); + bool enable_backspace_button{}; + INSERT_PADDING_BYTES(3); + bool key_top_as_floating{}; + bool footer_scalable{}; + bool alpha_enabled_in_input_mode{}; + u8 input_mode_fade_type{}; + bool disable_touch{}; + bool disable_hardware_keyboard{}; + INSERT_PADDING_BYTES(8); + f32 key_top_scale_x{}; + f32 key_top_scale_y{}; + f32 key_top_translate_x{}; + f32 key_top_translate_y{}; + f32 key_top_bg_alpha{}; + f32 footer_bg_alpha{}; + f32 balloon_scale{}; + INSERT_PADDING_WORDS(4); + u8 se_group{}; + INSERT_PADDING_BYTES(3); +}; +static_assert(sizeof(SwkbdCalcArgOld) == 0x4A0 - sizeof(SwkbdCalcArgCommon), + "SwkbdCalcArgOld has incorrect size."); + +struct SwkbdCalcArgNew { + SwkbdAppearArgNew appear_arg{}; f32 volume{}; s32 cursor_position{}; - SwkbdAppearArg appear_arg{}; std::array<char16_t, 0x1FA> input_text{}; bool utf8_mode{}; INSERT_PADDING_BYTES(1); @@ -264,8 +321,10 @@ struct SwkbdCalcArg { INSERT_PADDING_WORDS(4); u8 se_group{}; INSERT_PADDING_BYTES(3); + INSERT_PADDING_WORDS(8); }; -static_assert(sizeof(SwkbdCalcArg) == 0x4A0, "SwkbdCalcArg has incorrect size."); +static_assert(sizeof(SwkbdCalcArgNew) == 0x4E8 - sizeof(SwkbdCalcArgCommon), + "SwkbdCalcArgNew has incorrect size."); struct SwkbdChangedStringArg { u32 text_length{}; diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp index bb5cb61be..14aa6f69e 100644 --- a/src/core/hle/service/am/applets/applet_web_browser.cpp +++ b/src/core/hle/service/am/applets/applet_web_browser.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "common/fs/file.h" @@ -22,7 +21,7 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/applet_web_browser.h" #include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/ns/pl_u.h" +#include "core/hle/service/ns/iplatform_service_manager.h" #include "core/loader/loader.h" namespace Service::AM::Applets { @@ -280,7 +279,7 @@ void WebBrowser::Initialize() { InitializeLobby(); break; default: - UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind); + ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind); break; } } @@ -289,7 +288,7 @@ bool WebBrowser::TransactionComplete() const { return complete; } -ResultCode WebBrowser::GetStatus() const { +Result WebBrowser::GetStatus() const { return status; } @@ -321,7 +320,7 @@ void WebBrowser::Execute() { ExecuteLobby(); break; default: - UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind); + ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind); WebBrowserExit(WebExitReason::EndButtonPressed); break; } @@ -446,6 +445,14 @@ void WebBrowser::ExecuteLogin() { } void WebBrowser::ExecuteOffline() { + // TODO (Morph): This is a hack for WebSession foreground web applets such as those used by + // Super Mario 3D All-Stars. + // TODO (Morph): Implement WebSession. + if (applet_mode == LibraryAppletMode::AllForegroundInitiallyHidden) { + LOG_WARNING(Service_AM, "WebSession is not implemented"); + return; + } + const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document)); if (!Common::FS::Exists(main_url)) { diff --git a/src/core/hle/service/am/applets/applet_web_browser.h b/src/core/hle/service/am/applets/applet_web_browser.h index b3364ee06..fd727fac8 100644 --- a/src/core/hle/service/am/applets/applet_web_browser.h +++ b/src/core/hle/service/am/applets/applet_web_browser.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -33,7 +32,7 @@ public: void Initialize() override; bool TransactionComplete() const override; - ResultCode GetStatus() const override; + Result GetStatus() const override; void ExecuteInteractive() override; void Execute() override; @@ -67,7 +66,7 @@ private: const Core::Frontend::WebBrowserApplet& frontend; bool complete{false}; - ResultCode status{ResultSuccess}; + Result status{ResultSuccess}; WebAppletVersion web_applet_version{}; WebArgHeader web_arg_header{}; diff --git a/src/core/hle/service/am/applets/applet_web_browser_types.h b/src/core/hle/service/am/applets/applet_web_browser_types.h index 419c2bf79..c522c5c1a 100644 --- a/src/core/hle/service/am/applets/applet_web_browser_types.h +++ b/src/core/hle/service/am/applets/applet_web_browser_types.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index 134ac1ee2..b5b8e4cad 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> @@ -9,6 +8,7 @@ #include "core/frontend/applets/controller.h" #include "core/frontend/applets/error.h" #include "core/frontend/applets/general_frontend.h" +#include "core/frontend/applets/mii_edit.h" #include "core/frontend/applets/profile_select.h" #include "core/frontend/applets/software_keyboard.h" #include "core/frontend/applets/web_browser.h" @@ -19,6 +19,7 @@ #include "core/hle/service/am/applets/applet_controller.h" #include "core/hle/service/am/applets/applet_error.h" #include "core/hle/service/am/applets/applet_general_backend.h" +#include "core/hle/service/am/applets/applet_mii_edit.h" #include "core/hle/service/am/applets/applet_profile_select.h" #include "core/hle/service/am/applets/applet_software_keyboard.h" #include "core/hle/service/am/applets/applet_web_browser.h" @@ -171,11 +172,12 @@ void Applet::Initialize() { AppletFrontendSet::AppletFrontendSet() = default; AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, + MiiEdit mii_edit_, ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_, ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_, WebBrowser web_browser_) : controller{std::move(controller_applet)}, error{std::move(error_applet)}, - parental_controls{std::move(parental_controls_applet)}, + mii_edit{std::move(mii_edit_)}, parental_controls{std::move(parental_controls_applet)}, photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)}, software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {} @@ -202,6 +204,10 @@ void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { frontend.error = std::move(set.error); } + if (set.mii_edit != nullptr) { + frontend.mii_edit = std::move(set.mii_edit); + } + if (set.parental_controls != nullptr) { frontend.parental_controls = std::move(set.parental_controls); } @@ -238,6 +244,10 @@ void AppletManager::SetDefaultAppletsIfMissing() { frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>(); } + if (frontend.mii_edit == nullptr) { + frontend.mii_edit = std::make_unique<Core::Frontend::DefaultMiiEditApplet>(); + } + if (frontend.parental_controls == nullptr) { frontend.parental_controls = std::make_unique<Core::Frontend::DefaultParentalControlsApplet>(); @@ -277,6 +287,8 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, LibraryAppletMode return std::make_shared<ProfileSelect>(system, mode, *frontend.profile_select); case AppletId::SoftwareKeyboard: return std::make_shared<SoftwareKeyboard>(system, mode, *frontend.software_keyboard); + case AppletId::MiiEdit: + return std::make_shared<MiiEdit>(system, mode, *frontend.mii_edit); case AppletId::Web: case AppletId::Shop: case AppletId::OfflineWeb: diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index 15eeb4ee1..e78a57657 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -10,7 +9,7 @@ #include "common/swap.h" #include "core/hle/service/kernel_helpers.h" -union ResultCode; +union Result; namespace Core { class System; @@ -20,6 +19,7 @@ namespace Core::Frontend { class ControllerApplet; class ECommerceApplet; class ErrorApplet; +class MiiEditApplet; class ParentalControlsApplet; class PhotoViewerApplet; class ProfileSelectApplet; @@ -138,7 +138,7 @@ public: virtual void Initialize(); virtual bool TransactionComplete() const = 0; - virtual ResultCode GetStatus() const = 0; + virtual Result GetStatus() const = 0; virtual void ExecuteInteractive() = 0; virtual void Execute() = 0; @@ -178,6 +178,7 @@ protected: struct AppletFrontendSet { using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>; using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>; + using MiiEdit = std::unique_ptr<Core::Frontend::MiiEditApplet>; using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>; using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>; using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>; @@ -186,9 +187,9 @@ struct AppletFrontendSet { AppletFrontendSet(); AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, - ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_, - ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_, - WebBrowser web_browser_); + MiiEdit mii_edit_, ParentalControlsApplet parental_controls_applet, + PhotoViewer photo_viewer_, ProfileSelect profile_select_, + SoftwareKeyboard software_keyboard_, WebBrowser web_browser_); ~AppletFrontendSet(); AppletFrontendSet(const AppletFrontendSet&) = delete; @@ -199,6 +200,7 @@ struct AppletFrontendSet { ControllerApplet controller; ErrorApplet error; + MiiEdit mii_edit; ParentalControlsApplet parental_controls; PhotoViewer photo_viewer; ProfileSelect profile_select; diff --git a/src/core/hle/service/am/idle.cpp b/src/core/hle/service/am/idle.cpp index 6196773d5..603515284 100644 --- a/src/core/hle/service/am/idle.cpp +++ b/src/core/hle/service/am/idle.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/am/idle.h" diff --git a/src/core/hle/service/am/idle.h b/src/core/hle/service/am/idle.h index e290c30b1..15b31f67e 100644 --- a/src/core/hle/service/am/idle.h +++ b/src/core/hle/service/am/idle.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/am/omm.cpp b/src/core/hle/service/am/omm.cpp index 6da9b9f58..66824e495 100644 --- a/src/core/hle/service/am/omm.cpp +++ b/src/core/hle/service/am/omm.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/am/omm.h" diff --git a/src/core/hle/service/am/omm.h b/src/core/hle/service/am/omm.h index 3766150fe..73d0c82d5 100644 --- a/src/core/hle/service/am/omm.h +++ b/src/core/hle/service/am/omm.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/am/spsm.cpp b/src/core/hle/service/am/spsm.cpp index 95218d9ee..ec581e32b 100644 --- a/src/core/hle/service/am/spsm.cpp +++ b/src/core/hle/service/am/spsm.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/am/spsm.h" diff --git a/src/core/hle/service/am/spsm.h b/src/core/hle/service/am/spsm.h index 04bbf9e68..922f8863e 100644 --- a/src/core/hle/service/am/spsm.h +++ b/src/core/hle/service/am/spsm.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/am/tcap.cpp b/src/core/hle/service/am/tcap.cpp index 4d0971c03..818420e22 100644 --- a/src/core/hle/service/am/tcap.cpp +++ b/src/core/hle/service/am/tcap.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/am/tcap.h" diff --git a/src/core/hle/service/am/tcap.h b/src/core/hle/service/am/tcap.h index e9578f16e..6b2148c29 100644 --- a/src/core/hle/service/am/tcap.h +++ b/src/core/hle/service/am/tcap.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 3c83717b5..368ccd52f 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <numeric> diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h index 4b5f7c5f2..6c1ce601a 100644 --- a/src/core/hle/service/aoc/aoc_u.h +++ b/src/core/hle/service/aoc/aoc_u.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/apm/apm.cpp b/src/core/hle/service/apm/apm.cpp index 243ea15b8..8a338d9b1 100644 --- a/src/core/hle/service/apm/apm.cpp +++ b/src/core/hle/service/apm/apm.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" #include "core/hle/service/apm/apm.h" diff --git a/src/core/hle/service/apm/apm.h b/src/core/hle/service/apm/apm.h index 691fe6c16..0fecc766a 100644 --- a/src/core/hle/service/apm/apm.h +++ b/src/core/hle/service/apm/apm.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/apm/apm_controller.cpp b/src/core/hle/service/apm/apm_controller.cpp index 98839fe97..d6de84066 100644 --- a/src/core/hle/service/apm/apm_controller.cpp +++ b/src/core/hle/service/apm/apm_controller.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <array> @@ -17,8 +16,8 @@ constexpr auto DEFAULT_PERFORMANCE_CONFIGURATION = PerformanceConfiguration::Con Controller::Controller(Core::Timing::CoreTiming& core_timing_) : core_timing{core_timing_}, configs{ - {PerformanceMode::Handheld, DEFAULT_PERFORMANCE_CONFIGURATION}, - {PerformanceMode::Docked, DEFAULT_PERFORMANCE_CONFIGURATION}, + {PerformanceMode::Normal, DEFAULT_PERFORMANCE_CONFIGURATION}, + {PerformanceMode::Boost, DEFAULT_PERFORMANCE_CONFIGURATION}, } {} Controller::~Controller() = default; @@ -63,13 +62,13 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) { PerformanceConfiguration::Config15, }}; - SetPerformanceConfiguration(PerformanceMode::Docked, + SetPerformanceConfiguration(PerformanceMode::Boost, BOOST_MODE_TO_CONFIG_MAP.at(static_cast<u32>(mode))); } PerformanceMode Controller::GetCurrentPerformanceMode() const { - return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Docked - : PerformanceMode::Handheld; + return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Boost + : PerformanceMode::Normal; } PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) { @@ -81,7 +80,7 @@ PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(Performa } void Controller::SetClockSpeed(u32 mhz) { - LOG_INFO(Service_APM, "called, mhz={:08X}", mhz); + LOG_DEBUG(Service_APM, "called, mhz={:08X}", mhz); // TODO(DarkLordZach): Actually signal core_timing to change clock speed. // TODO(Rodrigo): Remove [[maybe_unused]] when core_timing is used. } diff --git a/src/core/hle/service/apm/apm_controller.h b/src/core/hle/service/apm/apm_controller.h index 8d48e0104..3357b7762 100644 --- a/src/core/hle/service/apm/apm_controller.h +++ b/src/core/hle/service/apm/apm_controller.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -32,15 +31,18 @@ enum class PerformanceConfiguration : u32 { Config16 = 0x9222000C, }; +// This is nn::oe::CpuBoostMode enum class CpuBoostMode : u32 { - Disabled = 0, - Full = 1, // CPU + GPU -> Config 13, 14, 15, or 16 - Partial = 2, // GPU Only -> Config 15 or 16 + Normal = 0, // Boost mode disabled + FastLoad = 1, // CPU + GPU -> Config 13, 14, 15, or 16 + Partial = 2, // GPU Only -> Config 15 or 16 }; -enum class PerformanceMode : u8 { - Handheld = 0, - Docked = 1, +// This is nn::oe::PerformanceMode +enum class PerformanceMode : s32 { + Invalid = -1, + Normal = 0, + Boost = 1, }; // Class to manage the state and change of the emulated system performance. diff --git a/src/core/hle/service/apm/apm_interface.cpp b/src/core/hle/service/apm/apm_interface.cpp index 6163e3294..041fc16bd 100644 --- a/src/core/hle/service/apm/apm_interface.cpp +++ b/src/core/hle/service/apm/apm_interface.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" diff --git a/src/core/hle/service/apm/apm_interface.h b/src/core/hle/service/apm/apm_interface.h index 063ad5308..0740fd4ba 100644 --- a/src/core/hle/service/apm/apm_interface.h +++ b/src/core/hle/service/apm/apm_interface.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp index 260fd0e0e..4a2ae5f88 100644 --- a/src/core/hle/service/audio/audctl.cpp +++ b/src/core/hle/service/audio/audctl.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" diff --git a/src/core/hle/service/audio/audctl.h b/src/core/hle/service/audio/audctl.h index 15f6c77a0..a27ff6cfe 100644 --- a/src/core/hle/service/audio/audctl.h +++ b/src/core/hle/service/audio/audctl.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/audio/auddbg.cpp b/src/core/hle/service/audio/auddbg.cpp index 6264e4bda..5541af300 100644 --- a/src/core/hle/service/audio/auddbg.cpp +++ b/src/core/hle/service/audio/auddbg.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/audio/auddbg.h" diff --git a/src/core/hle/service/audio/auddbg.h b/src/core/hle/service/audio/auddbg.h index d1653eedd..8f26be5dc 100644 --- a/src/core/hle/service/audio/auddbg.h +++ b/src/core/hle/service/audio/auddbg.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/audio/audin_a.cpp b/src/core/hle/service/audio/audin_a.cpp index 10acaad19..98f4a6048 100644 --- a/src/core/hle/service/audio/audin_a.cpp +++ b/src/core/hle/service/audio/audin_a.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/audio/audin_a.h" diff --git a/src/core/hle/service/audio/audin_a.h b/src/core/hle/service/audio/audin_a.h index 15120a4b6..19a927de5 100644 --- a/src/core/hle/service/audio/audin_a.h +++ b/src/core/hle/service/audio/audin_a.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index 34cc659ed..48a9a73a0 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp @@ -1,69 +1,211 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#include "audio_core/in/audio_in_system.h" +#include "audio_core/renderer/audio_device.h" +#include "common/common_funcs.h" #include "common/logging/log.h" +#include "common/string_util.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_event.h" #include "core/hle/service/audio/audin_u.h" namespace Service::Audio { +using namespace AudioCore::AudioIn; -IAudioIn::IAudioIn(Core::System& system_) - : ServiceFramework{system_, "IAudioIn"}, service_context{system_, "IAudioIn"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "GetAudioInState"}, - {1, &IAudioIn::Start, "Start"}, - {2, nullptr, "Stop"}, - {3, nullptr, "AppendAudioInBuffer"}, - {4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"}, - {5, nullptr, "GetReleasedAudioInBuffer"}, - {6, nullptr, "ContainsAudioInBuffer"}, - {7, nullptr, "AppendUacInBuffer"}, - {8, &IAudioIn::AppendAudioInBufferAuto, "AppendAudioInBufferAuto"}, - {9, nullptr, "GetReleasedAudioInBuffersAuto"}, - {10, nullptr, "AppendUacInBufferAuto"}, - {11, nullptr, "GetAudioInBufferCount"}, - {12, nullptr, "SetDeviceGain"}, - {13, nullptr, "GetDeviceGain"}, - {14, nullptr, "FlushAudioInBuffers"}, - }; - // clang-format on +class IAudioIn final : public ServiceFramework<IAudioIn> { +public: + explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id, + std::string& device_name, const AudioInParameter& in_params, u32 handle, + u64 applet_resource_user_id) + : ServiceFramework{system_, "IAudioIn"}, + service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")}, + impl{std::make_shared<In>(system_, manager, event, session_id)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IAudioIn::GetAudioInState, "GetAudioInState"}, + {1, &IAudioIn::Start, "Start"}, + {2, &IAudioIn::Stop, "Stop"}, + {3, &IAudioIn::AppendAudioInBuffer, "AppendAudioInBuffer"}, + {4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"}, + {5, &IAudioIn::GetReleasedAudioInBuffer, "GetReleasedAudioInBuffer"}, + {6, &IAudioIn::ContainsAudioInBuffer, "ContainsAudioInBuffer"}, + {7, &IAudioIn::AppendAudioInBuffer, "AppendUacInBuffer"}, + {8, &IAudioIn::AppendAudioInBuffer, "AppendAudioInBufferAuto"}, + {9, &IAudioIn::GetReleasedAudioInBuffer, "GetReleasedAudioInBuffersAuto"}, + {10, &IAudioIn::AppendAudioInBuffer, "AppendUacInBufferAuto"}, + {11, &IAudioIn::GetAudioInBufferCount, "GetAudioInBufferCount"}, + {12, &IAudioIn::SetDeviceGain, "SetDeviceGain"}, + {13, &IAudioIn::GetDeviceGain, "GetDeviceGain"}, + {14, &IAudioIn::FlushAudioInBuffers, "FlushAudioInBuffers"}, + }; + // clang-format on - RegisterHandlers(functions); + RegisterHandlers(functions); - buffer_event = service_context.CreateEvent("IAudioIn:BufferEvent"); -} + if (impl->GetSystem() + .Initialize(device_name, in_params, handle, applet_resource_user_id) + .IsError()) { + LOG_ERROR(Service_Audio, "Failed to initialize the AudioIn System!"); + } + } -IAudioIn::~IAudioIn() { - service_context.CloseEvent(buffer_event); -} + ~IAudioIn() override { + impl->Free(); + service_context.CloseEvent(event); + } -void IAudioIn::Start(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + [[nodiscard]] std::shared_ptr<In> GetImpl() { + return impl; + } - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} +private: + void GetAudioInState(Kernel::HLERequestContext& ctx) { + const auto state = static_cast<u32>(impl->GetState()); -void IAudioIn::RegisterBufferEvent(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "called. State={}", state); - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(buffer_event->GetReadableEvent()); -} + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(state); + } -void IAudioIn::AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + void Start(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} + auto result = impl->StartSystem(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + } + + void Stop(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + + auto result = impl->StopSystem(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + } + + void AppendAudioInBuffer(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + u64 tag = rp.PopRaw<u64>(); -AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} { + const auto in_buffer_size{ctx.GetReadBufferSize()}; + if (in_buffer_size < sizeof(AudioInBuffer)) { + LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioInBuffer!"); + } + + const auto& in_buffer = ctx.ReadBuffer(); + AudioInBuffer buffer{}; + std::memcpy(&buffer, in_buffer.data(), sizeof(AudioInBuffer)); + + [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()}; + LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag); + + auto result = impl->AppendBuffer(buffer, tag); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + } + + void RegisterBufferEvent(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + + auto& buffer_event = impl->GetBufferEvent(); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(buffer_event); + } + + void GetReleasedAudioInBuffer(Kernel::HLERequestContext& ctx) { + auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64); + std::vector<u64> released_buffers(write_buffer_size, 0); + + auto count = impl->GetReleasedBuffers(released_buffers); + + [[maybe_unused]] std::string tags{}; + for (u32 i = 0; i < count; i++) { + tags += fmt::format("{:08X}, ", released_buffers[i]); + } + [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()}; + LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count, + tags); + + ctx.WriteBuffer(released_buffers); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(count); + } + + void ContainsAudioInBuffer(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const u64 tag{rp.Pop<u64>()}; + const auto buffer_queued{impl->ContainsAudioBuffer(tag)}; + + LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", tag, buffer_queued); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(buffer_queued); + } + + void GetAudioInBufferCount(Kernel::HLERequestContext& ctx) { + const auto buffer_count = impl->GetBufferCount(); + + LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count); + + IPC::ResponseBuilder rb{ctx, 3}; + + rb.Push(ResultSuccess); + rb.Push(buffer_count); + } + + void SetDeviceGain(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto volume{rp.Pop<f32>()}; + LOG_DEBUG(Service_Audio, "called. Gain {}", volume); + + impl->SetVolume(volume); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void GetDeviceGain(Kernel::HLERequestContext& ctx) { + auto volume{impl->GetVolume()}; + + LOG_DEBUG(Service_Audio, "called. Gain {}", volume); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(volume); + } + + void FlushAudioInBuffers(Kernel::HLERequestContext& ctx) { + bool flushed{impl->FlushAudioInBuffers()}; + + LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", flushed); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(flushed); + } + + KernelHelpers::ServiceContext service_context; + Kernel::KEvent* event; + std::shared_ptr<AudioCore::AudioIn::In> impl; +}; + +AudInU::AudInU(Core::System& system_) + : ServiceFramework{system_, "audin:u", ServiceThreadType::CreateNew}, + service_context{system_, "AudInU"}, impl{std::make_unique<AudioCore::AudioIn::Manager>( + system_)} { // clang-format off static const FunctionInfo functions[] = { {0, &AudInU::ListAudioIns, "ListAudioIns"}, @@ -81,59 +223,152 @@ AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} { AudInU::~AudInU() = default; void AudInU::ListAudioIns(Kernel::HLERequestContext& ctx) { + using namespace AudioCore::AudioRenderer; + LOG_DEBUG(Service_Audio, "called"); - const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioInDeviceName); - const std::size_t device_count = std::min(count, audio_device_names.size()); - std::vector<AudioInDeviceName> device_names; - device_names.reserve(device_count); + const auto write_count = + static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName)); + std::vector<AudioDevice::AudioDeviceName> device_names{}; - for (std::size_t i = 0; i < device_count; i++) { - const auto& device_name = audio_device_names[i]; - auto& entry = device_names.emplace_back(); - device_name.copy(entry.data(), device_name.size()); + u32 out_count{0}; + if (write_count > 0) { + out_count = impl->GetDeviceNames(device_names, write_count, false); + ctx.WriteBuffer(device_names); } - ctx.WriteBuffer(device_names); - IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(device_names.size())); + rb.Push(out_count); } void AudInU::ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx) { + using namespace AudioCore::AudioRenderer; + LOG_DEBUG(Service_Audio, "called"); - constexpr u32 device_count = 0; - // Since we don't actually use any other audio input devices, we return 0 devices. Filtered - // device listing just omits the default input device + const auto write_count = + static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName)); + std::vector<AudioDevice::AudioDeviceName> device_names{}; + + u32 out_count{0}; + if (write_count > 0) { + out_count = impl->GetDeviceNames(device_names, write_count, true); + ctx.WriteBuffer(device_names); + } IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(device_count)); + rb.Push(out_count); } -void AudInU::OpenInOutImpl(Kernel::HLERequestContext& ctx) { - AudInOutParams params{}; - params.channel_count = 2; - params.sample_format = SampleFormat::PCM16; - params.sample_rate = 48000; - params.state = State::Started; +void AudInU::OpenAudioIn(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto in_params{rp.PopRaw<AudioInParameter>()}; + auto applet_resource_user_id{rp.PopRaw<u64>()}; + const auto device_name_data{ctx.ReadBuffer()}; + auto device_name = Common::StringFromBuffer(device_name_data); + auto handle{ctx.GetCopyHandle(0)}; + + std::scoped_lock l{impl->mutex}; + auto link{impl->LinkToManager()}; + if (link.IsError()) { + LOG_ERROR(Service_Audio, "Failed to link Audio In to Audio Manager"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(link); + return; + } + + size_t new_session_id{}; + auto result{impl->AcquireSessionId(new_session_id)}; + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + + LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id, + impl->num_free_sessions); + + auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, + in_params, handle, applet_resource_user_id); + impl->sessions[new_session_id] = audio_in->GetImpl(); + impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id; + + auto& out_system = impl->sessions[new_session_id]->GetSystem(); + AudioInParameterInternal out_params{.sample_rate = out_system.GetSampleRate(), + .channel_count = out_system.GetChannelCount(), + .sample_format = + static_cast<u32>(out_system.GetSampleFormat()), + .state = static_cast<u32>(out_system.GetState())}; IPC::ResponseBuilder rb{ctx, 6, 0, 1}; - rb.Push(ResultSuccess); - rb.PushRaw<AudInOutParams>(params); - rb.PushIpcInterface<IAudioIn>(system); -} -void AudInU::OpenAudioIn(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); - OpenInOutImpl(ctx); + std::string out_name{out_system.GetName()}; + ctx.WriteBuffer(out_name); + + rb.Push(ResultSuccess); + rb.PushRaw<AudioInParameterInternal>(out_params); + rb.PushIpcInterface<IAudioIn>(audio_in); } void AudInU::OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); - OpenInOutImpl(ctx); + IPC::RequestParser rp{ctx}; + auto protocol_specified{rp.PopRaw<u64>()}; + auto in_params{rp.PopRaw<AudioInParameter>()}; + auto applet_resource_user_id{rp.PopRaw<u64>()}; + const auto device_name_data{ctx.ReadBuffer()}; + auto device_name = Common::StringFromBuffer(device_name_data); + auto handle{ctx.GetCopyHandle(0)}; + + std::scoped_lock l{impl->mutex}; + auto link{impl->LinkToManager()}; + if (link.IsError()) { + LOG_ERROR(Service_Audio, "Failed to link Audio In to Audio Manager"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(link); + return; + } + + size_t new_session_id{}; + auto result{impl->AcquireSessionId(new_session_id)}; + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + + LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id, + impl->num_free_sessions); + + auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, + in_params, handle, applet_resource_user_id); + impl->sessions[new_session_id] = audio_in->GetImpl(); + impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id; + + auto& out_system = impl->sessions[new_session_id]->GetSystem(); + AudioInParameterInternal out_params{.sample_rate = out_system.GetSampleRate(), + .channel_count = out_system.GetChannelCount(), + .sample_format = + static_cast<u32>(out_system.GetSampleFormat()), + .state = static_cast<u32>(out_system.GetState())}; + + IPC::ResponseBuilder rb{ctx, 6, 0, 1}; + + std::string out_name{out_system.GetName()}; + if (protocol_specified == 0) { + if (out_system.IsUac()) { + out_name = "UacIn"; + } else { + out_name = "DeviceIn"; + } + } + + ctx.WriteBuffer(out_name); + + rb.Push(ResultSuccess); + rb.PushRaw<AudioInParameterInternal>(out_params); + rb.PushIpcInterface<IAudioIn>(audio_in); } } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audin_u.h b/src/core/hle/service/audio/audin_u.h index bf3418613..b45fda78a 100644 --- a/src/core/hle/service/audio/audin_u.h +++ b/src/core/hle/service/audio/audin_u.h @@ -1,9 +1,10 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include "audio_core/audio_in_manager.h" +#include "audio_core/in/audio_in.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" @@ -15,22 +16,12 @@ namespace Kernel { class HLERequestContext; } -namespace Service::Audio { - -class IAudioIn final : public ServiceFramework<IAudioIn> { -public: - explicit IAudioIn(Core::System& system_); - ~IAudioIn() override; - -private: - void Start(Kernel::HLERequestContext& ctx); - void RegisterBufferEvent(Kernel::HLERequestContext& ctx); - void AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx); - - KernelHelpers::ServiceContext service_context; +namespace AudioCore::AudioOut { +class Manager; +class In; +} // namespace AudioCore::AudioOut - Kernel::KEvent* buffer_event; -}; +namespace Service::Audio { class AudInU final : public ServiceFramework<AudInU> { public: @@ -38,33 +29,14 @@ public: ~AudInU() override; private: - enum class SampleFormat : u32_le { - PCM16 = 2, - }; - - enum class State : u32_le { - Started = 0, - Stopped = 1, - }; - - struct AudInOutParams { - u32_le sample_rate{}; - u32_le channel_count{}; - SampleFormat sample_format{}; - State state{}; - }; - static_assert(sizeof(AudInOutParams) == 0x10, "AudInOutParams is an invalid size"); - - using AudioInDeviceName = std::array<char, 256>; - static constexpr std::array<std::string_view, 1> audio_device_names{{ - "BuiltInHeadset", - }}; - void ListAudioIns(Kernel::HLERequestContext& ctx); void ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx); void OpenInOutImpl(Kernel::HLERequestContext& ctx); void OpenAudioIn(Kernel::HLERequestContext& ctx); void OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx); + + KernelHelpers::ServiceContext service_context; + std::unique_ptr<AudioCore::AudioIn::Manager> impl; }; } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp index b3f24f9bb..97da71dfa 100644 --- a/src/core/hle/service/audio/audio.cpp +++ b/src/core/hle/service/audio/audio.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/audio/audctl.h" #include "core/hle/service/audio/auddbg.h" diff --git a/src/core/hle/service/audio/audio.h b/src/core/hle/service/audio/audio.h index b6d13912e..bbb2214e4 100644 --- a/src/core/hle/service/audio/audio.h +++ b/src/core/hle/service/audio/audio.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/audio/audout_a.cpp b/src/core/hle/service/audio/audout_a.cpp index 3ee522b50..5ecb99236 100644 --- a/src/core/hle/service/audio/audout_a.cpp +++ b/src/core/hle/service/audio/audout_a.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/audio/audout_a.h" diff --git a/src/core/hle/service/audio/audout_a.h b/src/core/hle/service/audio/audout_a.h index 2043dfb77..f641cffeb 100644 --- a/src/core/hle/service/audio/audout_a.h +++ b/src/core/hle/service/audio/audout_a.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index affa7971c..49c092301 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -1,60 +1,47 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> #include <cstring> #include <vector> -#include "audio_core/audio_out.h" -#include "audio_core/codec.h" +#include "audio_core/out/audio_out_system.h" +#include "audio_core/renderer/audio_device.h" #include "common/common_funcs.h" #include "common/logging/log.h" +#include "common/string_util.h" #include "common/swap.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_event.h" #include "core/hle/service/audio/audout_u.h" #include "core/hle/service/audio/errors.h" -#include "core/hle/service/kernel_helpers.h" #include "core/memory.h" namespace Service::Audio { - -constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}}; -constexpr int DefaultSampleRate{48000}; - -struct AudoutParams { - s32_le sample_rate; - u16_le channel_count; - INSERT_PADDING_BYTES_NOINIT(2); -}; -static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size"); - -enum class AudioState : u32 { - Started, - Stopped, -}; +using namespace AudioCore::AudioOut; class IAudioOut final : public ServiceFramework<IAudioOut> { public: - explicit IAudioOut(Core::System& system_, AudoutParams audio_params_, - AudioCore::AudioOut& audio_core_, std::string&& device_name_, - std::string&& unique_name) - : ServiceFramework{system_, "IAudioOut"}, audio_core{audio_core_}, - device_name{std::move(device_name_)}, audio_params{audio_params_}, - main_memory{system.Memory()}, service_context{system_, "IAudioOut"} { + explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, + size_t session_id, std::string& device_name, + const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) + : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew}, + service_context{system_, "IAudioOut"}, event{service_context.CreateEvent( + "AudioOutEvent")}, + impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} { + // clang-format off static const FunctionInfo functions[] = { {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, - {1, &IAudioOut::StartAudioOut, "Start"}, - {2, &IAudioOut::StopAudioOut, "Stop"}, - {3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"}, + {1, &IAudioOut::Start, "Start"}, + {2, &IAudioOut::Stop, "Stop"}, + {3, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBuffer"}, {4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"}, - {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffers"}, + {5, &IAudioOut::GetReleasedAudioOutBuffers, "GetReleasedAudioOutBuffers"}, {6, &IAudioOut::ContainsAudioOutBuffer, "ContainsAudioOutBuffer"}, - {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"}, - {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"}, + {7, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBufferAuto"}, + {8, &IAudioOut::GetReleasedAudioOutBuffers, "GetReleasedAudioOutBuffersAuto"}, {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"}, {10, &IAudioOut::GetAudioOutPlayedSampleCount, "GetAudioOutPlayedSampleCount"}, {11, &IAudioOut::FlushAudioOutBuffers, "FlushAudioOutBuffers"}, @@ -64,241 +51,262 @@ public: // clang-format on RegisterHandlers(functions); - // This is the event handle used to check if the audio buffer was released - buffer_event = service_context.CreateEvent("IAudioOutBufferReleased"); - - stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate, - audio_params.channel_count, std::move(unique_name), [this] { - const auto guard = LockService(); - buffer_event->GetWritableEvent().Signal(); - }); + if (impl->GetSystem() + .Initialize(device_name, in_params, handle, applet_resource_user_id) + .IsError()) { + LOG_ERROR(Service_Audio, "Failed to initialize the AudioOut System!"); + } } ~IAudioOut() override { - service_context.CloseEvent(buffer_event); + impl->Free(); + service_context.CloseEvent(event); } -private: - struct AudioBuffer { - u64_le next; - u64_le buffer; - u64_le buffer_capacity; - u64_le buffer_size; - u64_le offset; - }; - static_assert(sizeof(AudioBuffer) == 0x28, "AudioBuffer is an invalid size"); + [[nodiscard]] std::shared_ptr<AudioCore::AudioOut::Out> GetImpl() { + return impl; + } +private: void GetAudioOutState(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const auto state = static_cast<u32>(impl->GetState()); + + LOG_DEBUG(Service_Audio, "called. State={}", state); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(stream->IsPlaying() ? AudioState::Started : AudioState::Stopped)); + rb.Push(state); } - void StartAudioOut(Kernel::HLERequestContext& ctx) { + void Start(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); - if (stream->IsPlaying()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_OPERATION_FAILED); - return; - } - - audio_core.StartStream(stream); + auto result = impl->StartSystem(); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } - void StopAudioOut(Kernel::HLERequestContext& ctx) { + void Stop(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); - if (stream->IsPlaying()) { - audio_core.StopStream(stream); - } + auto result = impl->StopSystem(); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } - void RegisterBufferEvent(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(buffer_event->GetReadableEvent()); - } - - void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "(STUBBED) called {}", ctx.Description()); + void AppendAudioOutBuffer(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; + u64 tag = rp.PopRaw<u64>(); - const auto& input_buffer{ctx.ReadBuffer()}; - ASSERT_MSG(input_buffer.size() == sizeof(AudioBuffer), - "AudioBuffer input is an invalid size!"); - AudioBuffer audio_buffer{}; - std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer)); - const u64 tag{rp.Pop<u64>()}; + const auto in_buffer_size{ctx.GetReadBufferSize()}; + if (in_buffer_size < sizeof(AudioOutBuffer)) { + LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioOutBuffer!"); + } - std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16)); - main_memory.ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size); + const auto& in_buffer = ctx.ReadBuffer(); + AudioOutBuffer buffer{}; + std::memcpy(&buffer, in_buffer.data(), sizeof(AudioOutBuffer)); - if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_BUFFER_COUNT_EXCEEDED); - return; - } + [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()}; + LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag); + + auto result = impl->AppendBuffer(buffer, tag); IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + } + + void RegisterBufferEvent(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + + auto& buffer_event = impl->GetBufferEvent(); + + IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); + rb.PushCopyObjects(buffer_event); } - void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called {}", ctx.Description()); + void GetReleasedAudioOutBuffers(Kernel::HLERequestContext& ctx) { + auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64); + std::vector<u64> released_buffers(write_buffer_size, 0); - const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)}; - const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)}; + auto count = impl->GetReleasedBuffers(released_buffers); - std::vector<u64> tags{released_buffers}; - tags.resize(max_count); - ctx.WriteBuffer(tags); + [[maybe_unused]] std::string tags{}; + for (u32 i = 0; i < count; i++) { + tags += fmt::format("{:08X}, ", released_buffers[i]); + } + [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()}; + LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count, + tags); + ctx.WriteBuffer(released_buffers); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push<u32>(static_cast<u32>(released_buffers.size())); + rb.Push(count); } void ContainsAudioOutBuffer(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); - IPC::RequestParser rp{ctx}; + const u64 tag{rp.Pop<u64>()}; + const auto buffer_queued{impl->ContainsAudioBuffer(tag)}; + + LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", tag, buffer_queued); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(stream->ContainsBuffer(tag)); + rb.Push(buffer_queued); } void GetAudioOutBufferCount(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const auto buffer_count = impl->GetBufferCount(); + + LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count); IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(stream->GetQueueSize())); + rb.Push(buffer_count); } void GetAudioOutPlayedSampleCount(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const auto samples_played = impl->GetPlayedSampleCount(); + + LOG_DEBUG(Service_Audio, "called. Played samples={}", samples_played); IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); - rb.Push(stream->GetPlayedSampleCount()); + rb.Push(samples_played); } void FlushAudioOutBuffers(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + bool flushed{impl->FlushAudioOutBuffers()}; + + LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", flushed); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(stream->Flush()); + rb.Push(flushed); } void SetAudioOutVolume(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const float volume = rp.Pop<float>(); - LOG_DEBUG(Service_Audio, "called, volume={}", volume); + const auto volume = rp.Pop<f32>(); + + LOG_DEBUG(Service_Audio, "called. Volume={}", volume); - stream->SetVolume(volume); + impl->SetVolume(volume); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void GetAudioOutVolume(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const auto volume = impl->GetVolume(); + + LOG_DEBUG(Service_Audio, "called. Volume={}", volume); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(stream->GetVolume()); + rb.Push(volume); } - AudioCore::AudioOut& audio_core; - AudioCore::StreamPtr stream; - std::string device_name; - - [[maybe_unused]] AudoutParams audio_params{}; - - Core::Memory::Memory& main_memory; - KernelHelpers::ServiceContext service_context; - - /// This is the event handle used to check if the audio buffer was released - Kernel::KEvent* buffer_event; + Kernel::KEvent* event; + std::shared_ptr<AudioCore::AudioOut::Out> impl; }; -AudOutU::AudOutU(Core::System& system_) : ServiceFramework{system_, "audout:u"} { +AudOutU::AudOutU(Core::System& system_) + : ServiceFramework{system_, "audout:u", ServiceThreadType::CreateNew}, + service_context{system_, "AudOutU"}, impl{std::make_unique<AudioCore::AudioOut::Manager>( + system_)} { // clang-format off static const FunctionInfo functions[] = { - {0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"}, - {1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"}, - {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"}, - {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"}, + {0, &AudOutU::ListAudioOuts, "ListAudioOuts"}, + {1, &AudOutU::OpenAudioOut, "OpenAudioOut"}, + {2, &AudOutU::ListAudioOuts, "ListAudioOutsAuto"}, + {3, &AudOutU::OpenAudioOut, "OpenAudioOutAuto"}, }; // clang-format on RegisterHandlers(functions); - audio_core = std::make_unique<AudioCore::AudioOut>(); } AudOutU::~AudOutU() = default; -void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); +void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) { + using namespace AudioCore::AudioRenderer; - ctx.WriteBuffer(DefaultDevice); + std::scoped_lock l{impl->mutex}; + + const auto write_count = + static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName)); + std::vector<AudioDevice::AudioDeviceName> device_names{}; + if (write_count > 0) { + device_names.emplace_back("DeviceOut"); + LOG_DEBUG(Service_Audio, "called. \nName=DeviceOut"); + } else { + LOG_DEBUG(Service_Audio, "called. Empty buffer passed in."); + } + + ctx.WriteBuffer(device_names); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push<u32>(1); // Amount of audio devices + rb.Push<u32>(static_cast<u32>(device_names.size())); } -void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); - +void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto in_params{rp.PopRaw<AudioOutParameter>()}; + auto applet_resource_user_id{rp.PopRaw<u64>()}; const auto device_name_data{ctx.ReadBuffer()}; - std::string device_name; - if (device_name_data[0] != '\0') { - device_name.assign(device_name_data.begin(), device_name_data.end()); - } else { - device_name.assign(DefaultDevice.begin(), DefaultDevice.end()); - } - ctx.WriteBuffer(device_name); + auto device_name = Common::StringFromBuffer(device_name_data); + auto handle{ctx.GetCopyHandle(0)}; - IPC::RequestParser rp{ctx}; - auto params{rp.PopRaw<AudoutParams>()}; - if (params.channel_count <= 2) { - // Mono does not exist for audout - params.channel_count = 2; - } else { - params.channel_count = 6; + auto link{impl->LinkToManager()}; + if (link.IsError()) { + LOG_ERROR(Service_Audio, "Failed to link Audio Out to Audio Manager"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(link); + return; } - if (!params.sample_rate) { - params.sample_rate = DefaultSampleRate; + + size_t new_session_id{}; + auto result{impl->AcquireSessionId(new_session_id)}; + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; } - std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())}; - auto audio_out_interface = std::make_shared<IAudioOut>( - system, params, *audio_core, std::move(device_name), std::move(unique_name)); + LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id, + impl->num_free_sessions); + + auto audio_out = std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name, + in_params, handle, applet_resource_user_id); + + impl->sessions[new_session_id] = audio_out->GetImpl(); + impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id; + + auto& out_system = impl->sessions[new_session_id]->GetSystem(); + AudioOutParameterInternal out_params{.sample_rate = out_system.GetSampleRate(), + .channel_count = out_system.GetChannelCount(), + .sample_format = + static_cast<u32>(out_system.GetSampleFormat()), + .state = static_cast<u32>(out_system.GetState())}; IPC::ResponseBuilder rb{ctx, 6, 0, 1}; - rb.Push(ResultSuccess); - rb.Push<u32>(DefaultSampleRate); - rb.Push<u32>(params.channel_count); - rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16)); - rb.Push<u32>(static_cast<u32>(AudioState::Stopped)); - rb.PushIpcInterface<IAudioOut>(audio_out_interface); - audio_out_interfaces.push_back(std::move(audio_out_interface)); + ctx.WriteBuffer(out_system.GetName()); + + rb.Push(ResultSuccess); + rb.PushRaw<AudioOutParameterInternal>(out_params); + rb.PushIpcInterface<IAudioOut>(audio_out); } } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h index f7ae2f2bf..fdc0ee754 100644 --- a/src/core/hle/service/audio/audout_u.h +++ b/src/core/hle/service/audio/audout_u.h @@ -1,16 +1,13 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include <vector> +#include "audio_core/audio_out_manager.h" +#include "audio_core/out/audio_out.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" -namespace AudioCore { -class AudioOut; -} - namespace Core { class System; } @@ -19,6 +16,11 @@ namespace Kernel { class HLERequestContext; } +namespace AudioCore::AudioOut { +class Manager; +class Out; +} // namespace AudioCore::AudioOut + namespace Service::Audio { class IAudioOut; @@ -29,11 +31,11 @@ public: ~AudOutU() override; private: - void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); - void OpenAudioOutImpl(Kernel::HLERequestContext& ctx); + void ListAudioOuts(Kernel::HLERequestContext& ctx); + void OpenAudioOut(Kernel::HLERequestContext& ctx); - std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces; - std::unique_ptr<AudioCore::AudioOut> audio_core; + KernelHelpers::ServiceContext service_context; + std::unique_ptr<AudioCore::AudioOut::Manager> impl; }; } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audrec_a.cpp b/src/core/hle/service/audio/audrec_a.cpp index 70fc17ae2..fa82e9ac7 100644 --- a/src/core/hle/service/audio/audrec_a.cpp +++ b/src/core/hle/service/audio/audrec_a.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/audio/audrec_a.h" diff --git a/src/core/hle/service/audio/audrec_a.h b/src/core/hle/service/audio/audrec_a.h index 2cce90b1d..9edf89f6c 100644 --- a/src/core/hle/service/audio/audrec_a.h +++ b/src/core/hle/service/audio/audrec_a.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/audio/audrec_u.cpp b/src/core/hle/service/audio/audrec_u.cpp index 74a65ccff..bc55cec17 100644 --- a/src/core/hle/service/audio/audrec_u.cpp +++ b/src/core/hle/service/audio/audrec_u.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/audio/audrec_u.h" diff --git a/src/core/hle/service/audio/audrec_u.h b/src/core/hle/service/audio/audrec_u.h index f79d49e5c..8b4817884 100644 --- a/src/core/hle/service/audio/audrec_u.h +++ b/src/core/hle/service/audio/audrec_u.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/audio/audren_a.cpp b/src/core/hle/service/audio/audren_a.cpp index cf8c34a15..e775ac3bf 100644 --- a/src/core/hle/service/audio/audren_a.cpp +++ b/src/core/hle/service/audio/audren_a.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/audio/audren_a.h" diff --git a/src/core/hle/service/audio/audren_a.h b/src/core/hle/service/audio/audren_a.h index 5d0a626ad..9e08b4245 100644 --- a/src/core/hle/service/audio/audren_a.h +++ b/src/core/hle/service/audio/audren_a.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index f45e5cecc..6fb07c37d 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -1,11 +1,15 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> #include <memory> -#include "audio_core/audio_renderer.h" +#include "audio_core/audio_core.h" +#include "audio_core/common/audio_renderer_parameter.h" +#include "audio_core/common/feature_support.h" +#include "audio_core/renderer/audio_device.h" +#include "audio_core/renderer/audio_renderer.h" +#include "audio_core/renderer/voice/voice_info.h" #include "common/alignment.h" #include "common/bit_util.h" #include "common/common_funcs.h" @@ -14,90 +18,127 @@ #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/service/audio/audren_u.h" #include "core/hle/service/audio/errors.h" +#include "core/memory.h" + +using namespace AudioCore::AudioRenderer; namespace Service::Audio { class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { public: - explicit IAudioRenderer(Core::System& system_, - const AudioCommon::AudioRendererParameter& audren_params, - const std::size_t instance_number) - : ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"} { + explicit IAudioRenderer(Core::System& system_, Manager& manager_, + AudioCore::AudioRendererParameterInternal& params, + Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, + u32 process_handle, u64 applet_resource_user_id, s32 session_id) + : ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew}, + service_context{system_, "IAudioRenderer"}, rendered_event{service_context.CreateEvent( + "IAudioRendererEvent")}, + manager{manager_}, impl{std::make_unique<Renderer>(system_, manager, rendered_event)} { // clang-format off static const FunctionInfo functions[] = { {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, {1, &IAudioRenderer::GetSampleCount, "GetSampleCount"}, {2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"}, {3, &IAudioRenderer::GetState, "GetState"}, - {4, &IAudioRenderer::RequestUpdateImpl, "RequestUpdate"}, + {4, &IAudioRenderer::RequestUpdate, "RequestUpdate"}, {5, &IAudioRenderer::Start, "Start"}, {6, &IAudioRenderer::Stop, "Stop"}, {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"}, {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"}, - {10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"}, - {11, &IAudioRenderer::ExecuteAudioRendererRendering, "ExecuteAudioRendererRendering"}, + {10, &IAudioRenderer::RequestUpdate, "RequestUpdateAuto"}, + {11, nullptr, "ExecuteAudioRendererRendering"}, }; // clang-format on RegisterHandlers(functions); - system_event = service_context.CreateEvent("IAudioRenderer:SystemEvent"); - renderer = std::make_unique<AudioCore::AudioRenderer>( - system.CoreTiming(), system.Memory(), audren_params, - [this]() { - const auto guard = LockService(); - system_event->GetWritableEvent().Signal(); - }, - instance_number); + impl->Initialize(params, transfer_memory, transfer_memory_size, process_handle, + applet_resource_user_id, session_id); } ~IAudioRenderer() override { - service_context.CloseEvent(system_event); + impl->Finalize(); + service_context.CloseEvent(rendered_event); } private: void GetSampleRate(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const auto sample_rate{impl->GetSystem().GetSampleRate()}; + + LOG_DEBUG(Service_Audio, "called. Sample rate {}", sample_rate); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push<u32>(renderer->GetSampleRate()); + rb.Push(sample_rate); } void GetSampleCount(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const auto sample_count{impl->GetSystem().GetSampleCount()}; + + LOG_DEBUG(Service_Audio, "called. Sample count {}", sample_count); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push<u32>(renderer->GetSampleCount()); + rb.Push(sample_count); } void GetState(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const u32 state{!impl->GetSystem().IsActive()}; + + LOG_DEBUG(Service_Audio, "called, state {}", state); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push<u32>(static_cast<u32>(renderer->GetStreamState())); + rb.Push(state); } void GetMixBufferCount(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); + const auto buffer_count{impl->GetSystem().GetMixBufferCount()}; + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push<u32>(renderer->GetMixBufferCount()); + rb.Push(buffer_count); } - void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "(STUBBED) called"); + void RequestUpdate(Kernel::HLERequestContext& ctx) { + LOG_TRACE(Service_Audio, "called"); + + std::vector<u8> input{ctx.ReadBuffer(0)}; + + // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for + // checking size 0. Performance size is 0 for most games. + + std::vector<u8> output{}; + std::vector<u8> performance{}; + auto is_buffer_b{ctx.BufferDescriptorB()[0].Size() != 0}; + if (is_buffer_b) { + const auto buffersB{ctx.BufferDescriptorB()}; + output.resize(buffersB[0].Size(), 0); + performance.resize(buffersB[1].Size(), 0); + } else { + const auto buffersC{ctx.BufferDescriptorC()}; + output.resize(buffersC[0].Size(), 0); + performance.resize(buffersC[1].Size(), 0); + } - std::vector<u8> output_params(ctx.GetWriteBufferSize(), 0); - auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params); + auto result = impl->RequestUpdate(input, performance, output); if (result.IsSuccess()) { - ctx.WriteBuffer(output_params); + if (is_buffer_b) { + ctx.WriteBufferB(output.data(), output.size(), 0); + ctx.WriteBufferB(performance.data(), performance.size(), 1); + } else { + ctx.WriteBufferC(output.data(), output.size(), 0); + ctx.WriteBufferC(performance.data(), performance.size(), 1); + } + } else { + LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description); } IPC::ResponseBuilder rb{ctx, 2}; @@ -105,38 +146,45 @@ private: } void Start(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "called"); - const auto result = renderer->Start(); + impl->Start(); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + rb.Push(ResultSuccess); } void Stop(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "called"); - const auto result = renderer->Stop(); + impl->Stop(); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + rb.Push(ResultSuccess); } void QuerySystemEvent(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "called"); + + if (impl->GetSystem().GetExecutionMode() == AudioCore::ExecutionMode::Manual) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_NOT_SUPPORTED); + return; + } IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(system_event->GetReadableEvent()); + rb.PushCopyObjects(rendered_event->GetReadableEvent()); } void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + IPC::RequestParser rp{ctx}; - rendering_time_limit_percent = rp.Pop<u32>(); - LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}", - rendering_time_limit_percent); + auto limit = rp.PopRaw<u32>(); - ASSERT(rendering_time_limit_percent <= 100); + auto& system_ = impl->GetSystem(); + system_.SetRenderingTimeLimit(limit); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -145,34 +193,34 @@ private: void GetRenderingTimeLimit(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); + auto& system_ = impl->GetSystem(); + auto time = system_.GetRenderingTimeLimit(); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(rendering_time_limit_percent); + rb.Push(time); } void ExecuteAudioRendererRendering(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); - - // This service command currently only reports an unsupported operation - // error code, or aborts. Given that, we just always return an error - // code in this case. - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_SUPPORTED); } KernelHelpers::ServiceContext service_context; - - Kernel::KEvent* system_event; - std::unique_ptr<AudioCore::AudioRenderer> renderer; - u32 rendering_time_limit_percent = 100; + Kernel::KEvent* rendered_event; + Manager& manager; + std::unique_ptr<Renderer> impl; }; class IAudioDevice final : public ServiceFramework<IAudioDevice> { + public: - explicit IAudioDevice(Core::System& system_, Kernel::KEvent* buffer_event_, u32_le revision_) - : ServiceFramework{system_, "IAudioDevice"}, buffer_event{buffer_event_}, revision{ - revision_} { + explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision, + u32 device_num) + : ServiceFramework{system_, "IAudioDevice", ServiceThreadType::CreateNew}, + service_context{system_, "IAudioDevice"}, impl{std::make_unique<AudioDevice>( + system_, applet_resource_user_id, + revision)}, + event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} { static const FunctionInfo functions[] = { {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"}, {1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"}, @@ -186,54 +234,45 @@ public: {10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"}, {11, &IAudioDevice::QueryAudioDeviceInputEvent, "QueryAudioDeviceInputEvent"}, {12, &IAudioDevice::QueryAudioDeviceOutputEvent, "QueryAudioDeviceOutputEvent"}, - {13, nullptr, "GetActiveAudioOutputDeviceName"}, - {14, nullptr, "ListAudioOutputDeviceName"}, + {13, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioOutputDeviceName"}, + {14, &IAudioDevice::ListAudioOutputDeviceName, "ListAudioOutputDeviceName"}, }; RegisterHandlers(functions); + + event->GetWritableEvent().Signal(); } -private: - using AudioDeviceName = std::array<char, 256>; - static constexpr std::array<std::string_view, 4> audio_device_names{{ - "AudioStereoJackOutput", - "AudioBuiltInSpeakerOutput", - "AudioTvOutput", - "AudioUsbDeviceOutput", - }}; - enum class DeviceType { - AHUBHeadphones, - AHUBSpeakers, - HDA, - USBOutput, - }; + ~IAudioDevice() override { + service_context.CloseEvent(event); + } +private: void ListAudioDeviceName(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName); - const bool usb_output_supported = - IsFeatureSupported(AudioFeatures::AudioUSBDeviceOutput, revision); - const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioDeviceName); + std::vector<AudioDevice::AudioDeviceName> out_names{}; - std::vector<AudioDeviceName> name_buffer; - name_buffer.reserve(audio_device_names.size()); + const u32 out_count = impl->ListAudioDeviceName(out_names, in_count); - for (std::size_t i = 0; i < count && i < audio_device_names.size(); i++) { - const auto type = static_cast<DeviceType>(i); - - if (!usb_output_supported && type == DeviceType::USBOutput) { - continue; + std::string out{}; + for (u32 i = 0; i < out_count; i++) { + std::string a{}; + u32 j = 0; + while (out_names[i].name[j] != '\0') { + a += out_names[i].name[j]; + j++; } - - const auto& device_name = audio_device_names[i]; - auto& entry = name_buffer.emplace_back(); - device_name.copy(entry.data(), device_name.size()); + out += "\n\t" + a; } - ctx.WriteBuffer(name_buffer); + LOG_DEBUG(Service_Audio, "called.\nNames={}", out); IPC::ResponseBuilder rb{ctx, 3}; + + ctx.WriteBuffer(out_names); + rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(name_buffer.size())); + rb.Push(out_count); } void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) { @@ -243,7 +282,11 @@ private: const auto device_name_buffer = ctx.ReadBuffer(); const std::string name = Common::StringFromBuffer(device_name_buffer); - LOG_WARNING(Service_Audio, "(STUBBED) called. name={}, volume={}", name, volume); + LOG_DEBUG(Service_Audio, "called. name={}, volume={}", name, volume); + + if (name == "AudioTvOutput") { + impl->SetDeviceVolumes(volume); + } IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -253,53 +296,60 @@ private: const auto device_name_buffer = ctx.ReadBuffer(); const std::string name = Common::StringFromBuffer(device_name_buffer); - LOG_WARNING(Service_Audio, "(STUBBED) called. name={}", name); + LOG_DEBUG(Service_Audio, "called. Name={}", name); + + f32 volume{1.0f}; + if (name == "AudioTvOutput") { + volume = impl->GetDeviceVolume(name); + } IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(1.0f); + rb.Push(volume); } void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + const auto write_size = ctx.GetWriteBufferSize() / sizeof(char); + std::string out_name{"AudioTvOutput"}; - // Currently set to always be TV audio output. - const auto& device_name = audio_device_names[2]; + LOG_DEBUG(Service_Audio, "(STUBBED) called. Name={}", out_name); - AudioDeviceName out_device_name{}; - device_name.copy(out_device_name.data(), device_name.size()); + out_name.resize(write_size); - ctx.WriteBuffer(out_device_name); + ctx.WriteBuffer(out_name); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "(STUBBED) called"); - buffer_event->GetWritableEvent().Signal(); + event->GetWritableEvent().Signal(); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(buffer_event->GetReadableEvent()); + rb.PushCopyObjects(event->GetReadableEvent()); } void GetActiveChannelCount(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + const auto& sink{system.AudioCore().GetOutputSink()}; + u32 channel_count{sink.GetDeviceChannels()}; + + LOG_DEBUG(Service_Audio, "(STUBBED) called. Channels={}", channel_count); IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); - rb.Push<u32>(2); + rb.Push<u32>(channel_count); } - // Should be similar to QueryAudioDeviceOutputEvent void QueryAudioDeviceInputEvent(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(buffer_event->GetReadableEvent()); + rb.PushCopyObjects(event->GetReadableEvent()); } void QueryAudioDeviceOutputEvent(Kernel::HLERequestContext& ctx) { @@ -307,402 +357,167 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(buffer_event->GetReadableEvent()); + rb.PushCopyObjects(event->GetReadableEvent()); } - Kernel::KEvent* buffer_event; - u32_le revision = 0; + void ListAudioOutputDeviceName(Kernel::HLERequestContext& ctx) { + const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName); + + std::vector<AudioDevice::AudioDeviceName> out_names{}; + + const u32 out_count = impl->ListAudioOutputDeviceName(out_names, in_count); + + std::string out{}; + for (u32 i = 0; i < out_count; i++) { + std::string a{}; + u32 j = 0; + while (out_names[i].name[j] != '\0') { + a += out_names[i].name[j]; + j++; + } + out += "\n\t" + a; + } + + LOG_DEBUG(Service_Audio, "called.\nNames={}", out); + + IPC::ResponseBuilder rb{ctx, 3}; + + ctx.WriteBuffer(out_names); + + rb.Push(ResultSuccess); + rb.Push(out_count); + } + + KernelHelpers::ServiceContext service_context; + std::unique_ptr<AudioDevice> impl; + Kernel::KEvent* event; }; AudRenU::AudRenU(Core::System& system_) - : ServiceFramework{system_, "audren:u"}, service_context{system_, "audren:u"} { + : ServiceFramework{system_, "audren:u", ServiceThreadType::CreateNew}, + service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} { // clang-format off static const FunctionInfo functions[] = { {0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"}, - {1, &AudRenU::GetAudioRendererWorkBufferSize, "GetWorkBufferSize"}, + {1, &AudRenU::GetWorkBufferSize, "GetWorkBufferSize"}, {2, &AudRenU::GetAudioDeviceService, "GetAudioDeviceService"}, - {3, &AudRenU::OpenAudioRendererForManualExecution, "OpenAudioRendererForManualExecution"}, + {3, nullptr, "OpenAudioRendererForManualExecution"}, {4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo, "GetAudioDeviceServiceWithRevisionInfo"}, }; // clang-format on RegisterHandlers(functions); - - buffer_event = service_context.CreateEvent("IAudioOutBufferReleasedEvent"); } -AudRenU::~AudRenU() { - service_context.CloseEvent(buffer_event); -} +AudRenU::~AudRenU() = default; void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); - - OpenAudioRendererImpl(ctx); -} - -static u64 CalculateNumPerformanceEntries(const AudioCommon::AudioRendererParameter& params) { - // +1 represents the final mix. - return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count + - 1; -} - -void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); - - // Several calculations below align the sizes being calculated - // onto a 64 byte boundary. - static constexpr u64 buffer_alignment_size = 64; - - // Some calculations that calculate portions of the buffer - // that will contain information, on the other hand, align - // the result of some of their calcularions on a 16 byte boundary. - static constexpr u64 info_field_alignment_size = 16; - - // Maximum detail entries that may exist at one time for performance - // frame statistics. - static constexpr u64 max_perf_detail_entries = 100; - - // Size of the data structure representing the bulk of the voice-related state. - static constexpr u64 voice_state_size_bytes = 0x100; - - // Size of the upsampler manager data structure - constexpr u64 upsampler_manager_size = 0x48; - - // Calculates the part of the size that relates to mix buffers. - const auto calculate_mix_buffer_sizes = [](const AudioCommon::AudioRendererParameter& params) { - // As of 8.0.0 this is the maximum on voice channels. - constexpr u64 max_voice_channels = 6; - - // The service expects the sample_count member of the parameters to either be - // a value of 160 or 240, so the maximum sample count is assumed in order - // to adequately handle all values at runtime. - constexpr u64 default_max_sample_count = 240; - - const u64 total_mix_buffers = params.mix_buffer_count + max_voice_channels; - - u64 size = 0; - size += total_mix_buffers * (sizeof(s32) * params.sample_count); - size += total_mix_buffers * (sizeof(s32) * default_max_sample_count); - size += u64{params.submix_count} + params.sink_count; - size = Common::AlignUp(size, buffer_alignment_size); - size += Common::AlignUp(params.unknown_30, buffer_alignment_size); - size += Common::AlignUp(sizeof(s32) * params.mix_buffer_count, buffer_alignment_size); - return size; - }; - - // Calculates the portion of the size related to the mix data (and the sorting thereof). - const auto calculate_mix_info_size = [](const AudioCommon::AudioRendererParameter& params) { - // The size of the mixing info data structure. - constexpr u64 mix_info_size = 0x940; - - // Consists of total submixes with the final mix included. - const u64 total_mix_count = u64{params.submix_count} + 1; - - // The total number of effects that may be available to the audio renderer at any time. - constexpr u64 max_effects = 256; - - // Calculates the part of the size related to the audio node state. - // This will only be used if the audio revision supports the splitter. - const auto calculate_node_state_size = [](std::size_t num_nodes) { - // Internally within a nodestate, it appears to use a data structure - // similar to a std::bitset<64> twice. - constexpr u64 bit_size = Common::BitSize<u64>(); - constexpr u64 num_bitsets = 2; - - // Node state instances have three states internally for performing - // depth-first searches of nodes. Initialized, Found, and Done Sorting. - constexpr u64 num_states = 3; - - u64 size = 0; - size += (num_nodes * num_nodes) * sizeof(s32); - size += num_states * (num_nodes * sizeof(s32)); - size += num_bitsets * (Common::AlignUp(num_nodes, bit_size) / Common::BitSize<u8>()); - return size; - }; - - // Calculates the part of the size related to the adjacency (aka edge) matrix. - const auto calculate_edge_matrix_size = [](std::size_t num_nodes) { - return (num_nodes * num_nodes) * sizeof(s32); - }; - - u64 size = 0; - size += Common::AlignUp(sizeof(void*) * total_mix_count, info_field_alignment_size); - size += Common::AlignUp(mix_info_size * total_mix_count, info_field_alignment_size); - size += Common::AlignUp(sizeof(s32) * max_effects * params.submix_count, - info_field_alignment_size); - - if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { - size += Common::AlignUp(calculate_node_state_size(total_mix_count) + - calculate_edge_matrix_size(total_mix_count), - info_field_alignment_size); - } - - return size; - }; - - // Calculates the part of the size related to voice channel info. - const auto calculate_voice_info_size = [](const AudioCommon::AudioRendererParameter& params) { - constexpr u64 voice_info_size = 0x220; - constexpr u64 voice_resource_size = 0xD0; - - u64 size = 0; - size += Common::AlignUp(sizeof(void*) * params.voice_count, info_field_alignment_size); - size += Common::AlignUp(voice_info_size * params.voice_count, info_field_alignment_size); - size += - Common::AlignUp(voice_resource_size * params.voice_count, info_field_alignment_size); - size += - Common::AlignUp(voice_state_size_bytes * params.voice_count, info_field_alignment_size); - return size; - }; - - // Calculates the part of the size related to memory pools. - const auto calculate_memory_pools_size = [](const AudioCommon::AudioRendererParameter& params) { - const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count); - const u64 memory_pool_info_size = 0x20; - return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size); - }; - - // Calculates the part of the size related to the splitter context. - const auto calculate_splitter_context_size = - [](const AudioCommon::AudioRendererParameter& params) -> u64 { - if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { - return 0; - } - - constexpr u64 splitter_info_size = 0x20; - constexpr u64 splitter_destination_data_size = 0xE0; - - u64 size = 0; - size += params.num_splitter_send_channels; - size += - Common::AlignUp(splitter_info_size * params.splitter_count, info_field_alignment_size); - size += Common::AlignUp(splitter_destination_data_size * params.num_splitter_send_channels, - info_field_alignment_size); - - return size; - }; - - // Calculates the part of the size related to the upsampler info. - const auto calculate_upsampler_info_size = - [](const AudioCommon::AudioRendererParameter& params) { - constexpr u64 upsampler_info_size = 0x280; - // Yes, using the buffer size over info alignment size is intentional here. - return Common::AlignUp(upsampler_info_size * - (u64{params.submix_count} + params.sink_count), - buffer_alignment_size); - }; - - // Calculates the part of the size related to effect info. - const auto calculate_effect_info_size = [](const AudioCommon::AudioRendererParameter& params) { - constexpr u64 effect_info_size = 0x2B0; - return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size); - }; - - // Calculates the part of the size related to audio sink info. - const auto calculate_sink_info_size = [](const AudioCommon::AudioRendererParameter& params) { - const u64 sink_info_size = 0x170; - return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size); - }; - - // Calculates the part of the size related to voice state info. - const auto calculate_voice_state_size = [](const AudioCommon::AudioRendererParameter& params) { - const u64 voice_state_size = 0x100; - const u64 additional_size = buffer_alignment_size - 1; - return Common::AlignUp(voice_state_size * params.voice_count + additional_size, - info_field_alignment_size); - }; - - // Calculates the part of the size related to performance statistics. - const auto calculate_perf_size = [](const AudioCommon::AudioRendererParameter& params) { - // Extra size value appended to the end of the calculation. - constexpr u64 appended = 128; - - // Whether or not we assume the newer version of performance metrics data structures. - const bool is_v2 = - IsFeatureSupported(AudioFeatures::PerformanceMetricsVersion2, params.revision); - - // Data structure sizes - constexpr u64 perf_statistics_size = 0x0C; - const u64 header_size = is_v2 ? 0x30 : 0x18; - const u64 entry_size = is_v2 ? 0x18 : 0x10; - const u64 detail_size = is_v2 ? 0x18 : 0x10; - - const u64 entry_count = CalculateNumPerformanceEntries(params); - const u64 size_per_frame = - header_size + (entry_size * entry_count) + (detail_size * max_perf_detail_entries); - - u64 size = 0; - size += Common::AlignUp(size_per_frame * params.performance_frame_count + 1, - buffer_alignment_size); - size += Common::AlignUp(perf_statistics_size, buffer_alignment_size); - size += appended; - return size; - }; - - // Calculates the part of the size that relates to the audio command buffer. - const auto calculate_command_buffer_size = - [](const AudioCommon::AudioRendererParameter& params) { - constexpr u64 alignment = (buffer_alignment_size - 1) * 2; - - if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) { - constexpr u64 command_buffer_size = 0x18000; - - return command_buffer_size + alignment; - } - - // When the variadic command buffer is supported, this means - // the command generator for the audio renderer can issue commands - // that are (as one would expect), variable in size. So what we need to do - // is determine the maximum possible size for a few command data structures - // then multiply them by the amount of present commands indicated by the given - // respective audio parameters. - - constexpr u64 max_biquad_filters = 2; - constexpr u64 max_mix_buffers = 24; - - constexpr u64 biquad_filter_command_size = 0x2C; - - constexpr u64 depop_mix_command_size = 0x24; - constexpr u64 depop_setup_command_size = 0x50; - - constexpr u64 effect_command_max_size = 0x540; - - constexpr u64 mix_command_size = 0x1C; - constexpr u64 mix_ramp_command_size = 0x24; - constexpr u64 mix_ramp_grouped_command_size = 0x13C; - - constexpr u64 perf_command_size = 0x28; - - constexpr u64 sink_command_size = 0x130; - - constexpr u64 submix_command_max_size = - depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers; - - constexpr u64 volume_command_size = 0x1C; - constexpr u64 volume_ramp_command_size = 0x20; - - constexpr u64 voice_biquad_filter_command_size = - biquad_filter_command_size * max_biquad_filters; - constexpr u64 voice_data_command_size = 0x9C; - const u64 voice_command_max_size = - (params.splitter_count * depop_setup_command_size) + - (voice_data_command_size + voice_biquad_filter_command_size + - volume_ramp_command_size + mix_ramp_grouped_command_size); + IPC::RequestParser rp{ctx}; - // Now calculate the individual elements that comprise the size and add them together. - const u64 effect_commands_size = params.effect_count * effect_command_max_size; + AudioCore::AudioRendererParameterInternal params; + rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params); + auto transfer_memory_handle = ctx.GetCopyHandle(0); + auto process_handle = ctx.GetCopyHandle(1); + auto transfer_memory_size = rp.Pop<u64>(); + auto applet_resource_user_id = rp.Pop<u64>(); - const u64 final_mix_commands_size = - depop_mix_command_size + volume_command_size * max_mix_buffers; + if (impl->GetSessionCount() + 1 > AudioCore::MaxRendererSessions) { + LOG_ERROR(Service_Audio, "Too many AudioRenderer sessions open!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_MAXIMUM_SESSIONS_REACHED); + return; + } - const u64 perf_commands_size = - perf_command_size * - (CalculateNumPerformanceEntries(params) + max_perf_detail_entries); + const auto& handle_table{system.CurrentProcess()->GetHandleTable()}; + auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)}; + auto transfer_memory{ + process->GetHandleTable().GetObject<Kernel::KTransferMemory>(transfer_memory_handle)}; - const u64 sink_commands_size = params.sink_count * sink_command_size; + const auto session_id{impl->GetSessionId()}; + if (session_id == -1) { + LOG_ERROR(Service_Audio, "Tried to open a session that's already in use!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_MAXIMUM_SESSIONS_REACHED); + return; + } - const u64 splitter_commands_size = - params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size; + LOG_DEBUG(Service_Audio, "Opened new AudioRenderer session {} sessions open {}", session_id, + impl->GetSessionCount()); - const u64 submix_commands_size = params.submix_count * submix_command_max_size; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IAudioRenderer>(system, *impl, params, transfer_memory.GetPointerUnsafe(), + transfer_memory_size, process_handle, + applet_resource_user_id, session_id); +} - const u64 voice_commands_size = params.voice_count * voice_command_max_size; - - return effect_commands_size + final_mix_commands_size + perf_commands_size + - sink_commands_size + splitter_commands_size + submix_commands_size + - voice_commands_size + alignment; - }; +void AudRenU::GetWorkBufferSize(Kernel::HLERequestContext& ctx) { + AudioCore::AudioRendererParameterInternal params; IPC::RequestParser rp{ctx}; - const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>(); - - u64 size = 0; - size += calculate_mix_buffer_sizes(params); - size += calculate_mix_info_size(params); - size += calculate_voice_info_size(params); - size += upsampler_manager_size; - size += calculate_memory_pools_size(params); - size += calculate_splitter_context_size(params); - - size = Common::AlignUp(size, buffer_alignment_size); - - size += calculate_upsampler_info_size(params); - size += calculate_effect_info_size(params); - size += calculate_sink_info_size(params); - size += calculate_voice_state_size(params); - size += calculate_perf_size(params); - size += calculate_command_buffer_size(params); - - // finally, 4KB page align the size, and we're done. - size = Common::AlignUp(size, 4096); + rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params); + + u64 size{0}; + auto result = impl->GetWorkBufferSize(params, size); + + std::string output_info{}; + output_info += fmt::format("\tRevision {}", AudioCore::GetRevisionNum(params.revision)); + output_info += + fmt::format("\n\tSample Rate {}, Sample Count {}", params.sample_rate, params.sample_count); + output_info += fmt::format("\n\tExecution Mode {}, Voice Drop Enabled {}", + static_cast<u32>(params.execution_mode), params.voice_drop_enabled); + output_info += fmt::format( + "\n\tSizes: Effects {:04X}, Mixes {:04X}, Sinks {:04X}, Submixes {:04X}, Splitter Infos " + "{:04X}, Splitter Destinations {:04X}, Voices {:04X}, Performance Frames {:04X} External " + "Context {:04X}", + params.effects, params.mixes, params.sinks, params.sub_mixes, params.splitter_infos, + params.splitter_destinations, params.voices, params.perf_frames, + params.external_context_size); + + LOG_DEBUG(Service_Audio, "called.\nInput params:\n{}\nOutput params:\n\tWorkbuffer size {:08X}", + output_info, size); IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); + rb.Push(result); rb.Push<u64>(size); - - LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", size); } void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const u64 aruid = rp.Pop<u64>(); - LOG_DEBUG(Service_Audio, "called. aruid={:016X}", aruid); + const auto applet_resource_user_id = rp.Pop<u64>(); + + LOG_DEBUG(Service_Audio, "called. Applet resource id {}", applet_resource_user_id); - // Revisionless variant of GetAudioDeviceServiceWithRevisionInfo that - // always assumes the initial release revision (REV1). IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); - rb.PushIpcInterface<IAudioDevice>(system, buffer_event, Common::MakeMagic('R', 'E', 'V', '1')); + rb.PushIpcInterface<IAudioDevice>(system, applet_resource_user_id, + ::Common::MakeMagic('R', 'E', 'V', '1'), num_audio_devices++); } void AudRenU::OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); - - OpenAudioRendererImpl(ctx); } void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) { struct Parameters { u32 revision; - u64 aruid; + u64 applet_resource_user_id; }; IPC::RequestParser rp{ctx}; - const auto [revision, aruid] = rp.PopRaw<Parameters>(); - LOG_DEBUG(Service_Audio, "called. revision={:08X}, aruid={:016X}", revision, aruid); + const auto [revision, applet_resource_user_id] = rp.PopRaw<Parameters>(); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IAudioDevice>(system, buffer_event, revision); -} + LOG_DEBUG(Service_Audio, "called. Revision {} Applet resource id {}", + AudioCore::GetRevisionNum(revision), applet_resource_user_id); -void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>(); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface<IAudioRenderer>(system, params, audren_instance_count++); -} - -bool IsFeatureSupported(AudioFeatures feature, u32_le revision) { - // Byte swap - const u32_be version_num = revision - Common::MakeMagic('R', 'E', 'V', '0'); - - switch (feature) { - case AudioFeatures::AudioUSBDeviceOutput: - return version_num >= 4U; - case AudioFeatures::Splitter: - return version_num >= 2U; - case AudioFeatures::PerformanceMetricsVersion2: - case AudioFeatures::VariadicCommandBuffer: - return version_num >= 5U; - default: - return false; - } + rb.PushIpcInterface<IAudioDevice>(system, applet_resource_user_id, revision, + num_audio_devices++); } } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h index 5922b4b27..4384a9b3c 100644 --- a/src/core/hle/service/audio/audren_u.h +++ b/src/core/hle/service/audio/audren_u.h @@ -1,9 +1,9 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include "audio_core/audio_render_manager.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" @@ -16,6 +16,7 @@ class HLERequestContext; } namespace Service::Audio { +class IAudioRenderer; class AudRenU final : public ServiceFramework<AudRenU> { public: @@ -24,28 +25,14 @@ public: private: void OpenAudioRenderer(Kernel::HLERequestContext& ctx); - void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx); + void GetWorkBufferSize(Kernel::HLERequestContext& ctx); void GetAudioDeviceService(Kernel::HLERequestContext& ctx); void OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx); void GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx); - void OpenAudioRendererImpl(Kernel::HLERequestContext& ctx); - KernelHelpers::ServiceContext service_context; - - std::size_t audren_instance_count = 0; - Kernel::KEvent* buffer_event; + std::unique_ptr<AudioCore::AudioRenderer::Manager> impl; + u32 num_audio_devices{0}; }; -// Describes a particular audio feature that may be supported in a particular revision. -enum class AudioFeatures : u32 { - AudioUSBDeviceOutput, - Splitter, - PerformanceMetricsVersion2, - VariadicCommandBuffer, -}; - -// Tests if a particular audio feature is supported with a given audio revision. -bool IsFeatureSupported(AudioFeatures feature, u32_le revision); - } // namespace Service::Audio diff --git a/src/core/hle/service/audio/codecctl.cpp b/src/core/hle/service/audio/codecctl.cpp index 42961d908..81b956d7e 100644 --- a/src/core/hle/service/audio/codecctl.cpp +++ b/src/core/hle/service/audio/codecctl.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/audio/codecctl.h" diff --git a/src/core/hle/service/audio/codecctl.h b/src/core/hle/service/audio/codecctl.h index 58e53259e..34da98212 100644 --- a/src/core/hle/service/audio/codecctl.h +++ b/src/core/hle/service/audio/codecctl.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/audio/errors.h b/src/core/hle/service/audio/errors.h index 6f8c09bcf..d706978cb 100644 --- a/src/core/hle/service/audio/errors.h +++ b/src/core/hle/service/audio/errors.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,8 +7,17 @@ namespace Service::Audio { -constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::Audio, 2}; -constexpr ResultCode ERR_BUFFER_COUNT_EXCEEDED{ErrorModule::Audio, 8}; -constexpr ResultCode ERR_NOT_SUPPORTED{ErrorModule::Audio, 513}; +constexpr Result ERR_INVALID_DEVICE_NAME{ErrorModule::Audio, 1}; +constexpr Result ERR_OPERATION_FAILED{ErrorModule::Audio, 2}; +constexpr Result ERR_INVALID_SAMPLE_RATE{ErrorModule::Audio, 3}; +constexpr Result ERR_INSUFFICIENT_BUFFER_SIZE{ErrorModule::Audio, 4}; +constexpr Result ERR_MAXIMUM_SESSIONS_REACHED{ErrorModule::Audio, 5}; +constexpr Result ERR_BUFFER_COUNT_EXCEEDED{ErrorModule::Audio, 8}; +constexpr Result ERR_INVALID_CHANNEL_COUNT{ErrorModule::Audio, 10}; +constexpr Result ERR_INVALID_UPDATE_DATA{ErrorModule::Audio, 41}; +constexpr Result ERR_POOL_MAPPING_FAILED{ErrorModule::Audio, 42}; +constexpr Result ERR_NOT_SUPPORTED{ErrorModule::Audio, 513}; +constexpr Result ERR_INVALID_PROCESS_HANDLE{ErrorModule::Audio, 1536}; +constexpr Result ERR_INVALID_REVISION{ErrorModule::Audio, 1537}; } // namespace Service::Audio diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index 981b6c996..8bafc3a98 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <chrono> #include <cstring> @@ -256,6 +255,32 @@ void HwOpus::GetWorkBufferSizeEx(Kernel::HLERequestContext& ctx) { GetWorkBufferSize(ctx); } +void HwOpus::GetWorkBufferSizeForMultiStreamEx(Kernel::HLERequestContext& ctx) { + OpusMultiStreamParametersEx param; + std::memcpy(¶m, ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); + + const auto sample_rate = param.sample_rate; + const auto channel_count = param.channel_count; + const auto number_streams = param.number_streams; + const auto number_stereo_streams = param.number_stereo_streams; + + LOG_DEBUG( + Audio, + "called with sample_rate={}, channel_count={}, number_streams={}, number_stereo_streams={}", + sample_rate, channel_count, number_streams, number_stereo_streams); + + ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || + sample_rate == 12000 || sample_rate == 8000, + "Invalid sample rate"); + + const u32 worker_buffer_sz = + static_cast<u32>(opus_multistream_decoder_get_size(number_streams, number_stereo_streams)); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u32>(worker_buffer_sz); +} + void HwOpus::OpenHardwareOpusDecoder(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto sample_rate = rp.Pop<u32>(); @@ -299,7 +324,7 @@ void HwOpus::OpenHardwareOpusDecoderEx(Kernel::HLERequestContext& ctx) { const auto sample_rate = rp.Pop<u32>(); const auto channel_count = rp.Pop<u32>(); - LOG_CRITICAL(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count); + LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count); ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || sample_rate == 12000 || sample_rate == 8000, @@ -336,7 +361,7 @@ HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} { {4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"}, {5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"}, {6, nullptr, "OpenHardwareOpusDecoderForMultiStreamEx"}, - {7, nullptr, "GetWorkBufferSizeForMultiStreamEx"}, + {7, &HwOpus::GetWorkBufferSizeForMultiStreamEx, "GetWorkBufferSizeForMultiStreamEx"}, }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/audio/hwopus.h b/src/core/hle/service/audio/hwopus.h index b74824ff3..e6092e290 100644 --- a/src/core/hle/service/audio/hwopus.h +++ b/src/core/hle/service/audio/hwopus.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -12,6 +11,16 @@ class System; namespace Service::Audio { +struct OpusMultiStreamParametersEx { + u32 sample_rate; + u32 channel_count; + u32 number_streams; + u32 number_stereo_streams; + u32 use_large_frame_size; + u32 padding; + std::array<u32, 64> channel_mappings; +}; + class HwOpus final : public ServiceFramework<HwOpus> { public: explicit HwOpus(Core::System& system_); @@ -22,6 +31,7 @@ private: void OpenHardwareOpusDecoderEx(Kernel::HLERequestContext& ctx); void GetWorkBufferSize(Kernel::HLERequestContext& ctx); void GetWorkBufferSizeEx(Kernel::HLERequestContext& ctx); + void GetWorkBufferSizeForMultiStreamEx(Kernel::HLERequestContext& ctx); }; } // namespace Service::Audio diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp index ee49edbb9..cd0b405ff 100644 --- a/src/core/hle/service/bcat/backend/backend.cpp +++ b/src/core/hle/service/bcat/backend/backend.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/hex_util.h" #include "common/logging/log.h" @@ -75,7 +74,7 @@ void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) { SignalUpdate(); } -void ProgressServiceBackend::FinishDownload(ResultCode result) { +void ProgressServiceBackend::FinishDownload(Result result) { impl.total_downloaded_bytes = impl.total_bytes; impl.status = DeliveryCacheProgressImpl::Status::Done; impl.result = result; diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h index 63833c927..205ed0702 100644 --- a/src/core/hle/service/bcat/backend/backend.h +++ b/src/core/hle/service/bcat/backend/backend.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -50,7 +49,7 @@ struct DeliveryCacheProgressImpl { }; Status status; - ResultCode result = ResultSuccess; + Result result = ResultSuccess; DirectoryName current_directory; FileName current_file; s64 current_downloaded_bytes; ///< Bytes downloaded on current file. @@ -91,7 +90,7 @@ public: void CommitDirectory(std::string_view dir_name); // Notifies the application that the operation completed with result code result. - void FinishDownload(ResultCode result); + void FinishDownload(Result result); private: explicit ProgressServiceBackend(Core::System& system, std::string_view event_name); diff --git a/src/core/hle/service/bcat/bcat.cpp b/src/core/hle/service/bcat/bcat.cpp index 5a95707de..d0ac17324 100644 --- a/src/core/hle/service/bcat/bcat.cpp +++ b/src/core/hle/service/bcat/bcat.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/bcat/bcat.h" diff --git a/src/core/hle/service/bcat/bcat.h b/src/core/hle/service/bcat/bcat.h index 1eba477da..db9d3c8c5 100644 --- a/src/core/hle/service/bcat/bcat.h +++ b/src/core/hle/service/bcat/bcat.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/bcat/bcat_module.cpp b/src/core/hle/service/bcat/bcat_module.cpp index 500e7e52d..bc08ac487 100644 --- a/src/core/hle/service/bcat/bcat_module.cpp +++ b/src/core/hle/service/bcat/bcat_module.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cctype> #include <mbedtls/md5.h> @@ -19,15 +18,15 @@ namespace Service::BCAT { -constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::BCAT, 1}; -constexpr ResultCode ERROR_FAILED_OPEN_ENTITY{ErrorModule::BCAT, 2}; -constexpr ResultCode ERROR_ENTITY_ALREADY_OPEN{ErrorModule::BCAT, 6}; -constexpr ResultCode ERROR_NO_OPEN_ENTITY{ErrorModule::BCAT, 7}; +constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::BCAT, 1}; +constexpr Result ERROR_FAILED_OPEN_ENTITY{ErrorModule::BCAT, 2}; +constexpr Result ERROR_ENTITY_ALREADY_OPEN{ErrorModule::BCAT, 6}; +constexpr Result ERROR_NO_OPEN_ENTITY{ErrorModule::BCAT, 7}; // The command to clear the delivery cache just calls fs IFileSystem DeleteFile on all of the files // and if any of them have a non-zero result it just forwards that result. This is the FS error code // for permission denied, which is the closest approximation of this scenario. -constexpr ResultCode ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400}; +constexpr Result ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400}; using BCATDigest = std::array<u8, 0x10>; @@ -141,8 +140,8 @@ public: {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"}, {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"}, {30100, &IBcatService::SetPassphrase, "SetPassphrase"}, - {30101, nullptr, "Unknown"}, - {30102, nullptr, "Unknown2"}, + {30101, nullptr, "Unknown30101"}, + {30102, nullptr, "Unknown30102"}, {30200, nullptr, "RegisterBackgroundDeliveryTask"}, {30201, nullptr, "UnregisterBackgroundDeliveryTask"}, {30202, nullptr, "BlockDeliveryTask"}, diff --git a/src/core/hle/service/bcat/bcat_module.h b/src/core/hle/service/bcat/bcat_module.h index 738731c06..b2fcf9bfb 100644 --- a/src/core/hle/service/bcat/bcat_module.h +++ b/src/core/hle/service/bcat/bcat_module.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/bpc/bpc.cpp b/src/core/hle/service/bpc/bpc.cpp index 78e01d8d8..466163538 100644 --- a/src/core/hle/service/bpc/bpc.cpp +++ b/src/core/hle/service/bpc/bpc.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/bpc/bpc.h b/src/core/hle/service/bpc/bpc.h index 6ec25aa9b..8adc2f962 100644 --- a/src/core/hle/service/bpc/bpc.h +++ b/src/core/hle/service/bpc/bpc.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp index 0787f43f4..ec7e5320c 100644 --- a/src/core/hle/service/btdrv/btdrv.cpp +++ b/src/core/hle/service/btdrv/btdrv.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/core.h" @@ -182,6 +181,11 @@ public: {147, nullptr, "RegisterAudioControlNotification"}, {148, nullptr, "SendAudioControlPassthroughCommand"}, {149, nullptr, "SendAudioControlSetAbsoluteVolumeCommand"}, + {150, nullptr, "AcquireAudioSinkVolumeLocallyChangedEvent"}, + {151, nullptr, "AcquireAudioSinkVolumeUpdateRequestCompletedEvent"}, + {152, nullptr, "GetAudioSinkVolume"}, + {153, nullptr, "RequestUpdateAudioSinkVolume"}, + {154, nullptr, "IsAudioSinkVolumeSupported"}, {256, nullptr, "IsManufacturingMode"}, {257, nullptr, "EmulateBluetoothCrash"}, {258, nullptr, "GetBleChannelMap"}, diff --git a/src/core/hle/service/btdrv/btdrv.h b/src/core/hle/service/btdrv/btdrv.h index 191410dbc..9cbe2926f 100644 --- a/src/core/hle/service/btdrv/btdrv.h +++ b/src/core/hle/service/btdrv/btdrv.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index cc268d877..eebf85e03 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> @@ -215,8 +214,12 @@ public: {76, nullptr, "Unknown76"}, {100, nullptr, "Unknown100"}, {101, nullptr, "Unknown101"}, - {110, nullptr, "Unknown102"}, - {111, nullptr, "Unknown103"}, + {110, nullptr, "Unknown110"}, + {111, nullptr, "Unknown111"}, + {112, nullptr, "Unknown112"}, + {113, nullptr, "Unknown113"}, + {114, nullptr, "Unknown114"}, + {115, nullptr, "Unknown115"}, }; // clang-format on diff --git a/src/core/hle/service/btm/btm.h b/src/core/hle/service/btm/btm.h index c6b878043..9dcda1848 100644 --- a/src/core/hle/service/btm/btm.h +++ b/src/core/hle/service/btm/btm.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp index 5b7fe8e9b..13940a8c9 100644 --- a/src/core/hle/service/caps/caps.cpp +++ b/src/core/hle/service/caps/caps.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/caps/caps.h" #include "core/hle/service/caps/caps_a.h" diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h index 7254055e6..3e89c82cb 100644 --- a/src/core/hle/service/caps/caps.h +++ b/src/core/hle/service/caps/caps.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp index 6220e9f77..44267b284 100644 --- a/src/core/hle/service/caps/caps_a.cpp +++ b/src/core/hle/service/caps/caps_a.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/caps/caps_a.h" diff --git a/src/core/hle/service/caps/caps_a.h b/src/core/hle/service/caps/caps_a.h index 389cc6dbe..319c173d8 100644 --- a/src/core/hle/service/caps/caps_a.h +++ b/src/core/hle/service/caps/caps_a.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/caps/caps_c.cpp b/src/core/hle/service/caps/caps_c.cpp index a9ad5c8b1..725a2e3a7 100644 --- a/src/core/hle/service/caps/caps_c.cpp +++ b/src/core/hle/service/caps/caps_c.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" diff --git a/src/core/hle/service/caps/caps_c.h b/src/core/hle/service/caps/caps_c.h index c6d1dfdce..983a4212d 100644 --- a/src/core/hle/service/caps/caps_c.h +++ b/src/core/hle/service/caps/caps_c.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/caps/caps_sc.cpp b/src/core/hle/service/caps/caps_sc.cpp index d91e18e80..395b13da7 100644 --- a/src/core/hle/service/caps/caps_sc.cpp +++ b/src/core/hle/service/caps/caps_sc.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/caps/caps_sc.h" diff --git a/src/core/hle/service/caps/caps_sc.h b/src/core/hle/service/caps/caps_sc.h index e79a33ee5..e5600f6d7 100644 --- a/src/core/hle/service/caps/caps_sc.h +++ b/src/core/hle/service/caps/caps_sc.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/caps/caps_ss.cpp b/src/core/hle/service/caps/caps_ss.cpp index 33a976ddf..62b9edd41 100644 --- a/src/core/hle/service/caps/caps_ss.cpp +++ b/src/core/hle/service/caps/caps_ss.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/caps/caps_ss.h" diff --git a/src/core/hle/service/caps/caps_ss.h b/src/core/hle/service/caps/caps_ss.h index 1816f7885..718ade485 100644 --- a/src/core/hle/service/caps/caps_ss.h +++ b/src/core/hle/service/caps/caps_ss.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/caps/caps_su.cpp b/src/core/hle/service/caps/caps_su.cpp index 45b705950..fcb496756 100644 --- a/src/core/hle/service/caps/caps_su.cpp +++ b/src/core/hle/service/caps/caps_su.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" diff --git a/src/core/hle/service/caps/caps_su.h b/src/core/hle/service/caps/caps_su.h index b366fdb13..c9a1d507b 100644 --- a/src/core/hle/service/caps/caps_su.h +++ b/src/core/hle/service/caps/caps_su.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp index 8f8ee2bb4..5fbba8673 100644 --- a/src/core/hle/service/caps/caps_u.cpp +++ b/src/core/hle/service/caps/caps_u.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" diff --git a/src/core/hle/service/caps/caps_u.h b/src/core/hle/service/caps/caps_u.h index e7e0d8775..c3d4b9cea 100644 --- a/src/core/hle/service/caps/caps_u.h +++ b/src/core/hle/service/caps/caps_u.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/erpt/erpt.cpp b/src/core/hle/service/erpt/erpt.cpp index c767926a4..923c0022a 100644 --- a/src/core/hle/service/erpt/erpt.cpp +++ b/src/core/hle/service/erpt/erpt.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/erpt/erpt.h b/src/core/hle/service/erpt/erpt.h index 8cd5c081f..507d626ec 100644 --- a/src/core/hle/service/erpt/erpt.h +++ b/src/core/hle/service/erpt/erpt.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index f6184acc9..ff9b0427c 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/crypto/key_manager.h" #include "core/hle/ipc_helpers.h" @@ -9,8 +8,8 @@ namespace Service::ES { -constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::ETicket, 2}; -constexpr ResultCode ERROR_INVALID_RIGHTS_ID{ErrorModule::ETicket, 3}; +constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::ETicket, 2}; +constexpr Result ERROR_INVALID_RIGHTS_ID{ErrorModule::ETicket, 3}; class ETicket final : public ServiceFramework<ETicket> { public: diff --git a/src/core/hle/service/es/es.h b/src/core/hle/service/es/es.h index 2a7b27d12..530563550 100644 --- a/src/core/hle/service/es/es.h +++ b/src/core/hle/service/es/es.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/eupld/eupld.cpp b/src/core/hle/service/eupld/eupld.cpp index 2d650b1b7..d1553ace0 100644 --- a/src/core/hle/service/eupld/eupld.cpp +++ b/src/core/hle/service/eupld/eupld.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/eupld/eupld.h b/src/core/hle/service/eupld/eupld.h index 539993a9d..5de8219be 100644 --- a/src/core/hle/service/eupld/eupld.h +++ b/src/core/hle/service/eupld/eupld.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp index f84506af0..27675615b 100644 --- a/src/core/hle/service/fatal/fatal.cpp +++ b/src/core/hle/service/fatal/fatal.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> #include <cstring> @@ -63,8 +62,7 @@ enum class FatalType : u32 { ErrorScreen = 2, }; -static void GenerateErrorReport(Core::System& system, ResultCode error_code, - const FatalInfo& info) { +static void GenerateErrorReport(Core::System& system, Result error_code, const FatalInfo& info) { const auto title_id = system.GetCurrentProcessProgramID(); std::string crash_report = fmt::format( "Yuzu {}-{} crash report\n" @@ -107,7 +105,7 @@ static void GenerateErrorReport(Core::System& system, ResultCode error_code, info.backtrace_size, info.ArchAsString(), info.unk10); } -static void ThrowFatalError(Core::System& system, ResultCode error_code, FatalType fatal_type, +static void ThrowFatalError(Core::System& system, Result error_code, FatalType fatal_type, const FatalInfo& info) { LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code 0x{:X}", fatal_type, error_code.raw); @@ -130,7 +128,7 @@ static void ThrowFatalError(Core::System& system, ResultCode error_code, FatalTy void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) { LOG_ERROR(Service_Fatal, "called"); IPC::RequestParser rp{ctx}; - const auto error_code = rp.Pop<ResultCode>(); + const auto error_code = rp.Pop<Result>(); ThrowFatalError(system, error_code, FatalType::ErrorScreen, {}); IPC::ResponseBuilder rb{ctx, 2}; @@ -140,7 +138,7 @@ void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) { void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) { LOG_ERROR(Service_Fatal, "called"); IPC::RequestParser rp(ctx); - const auto error_code = rp.Pop<ResultCode>(); + const auto error_code = rp.Pop<Result>(); const auto fatal_type = rp.PopEnum<FatalType>(); ThrowFatalError(system, error_code, fatal_type, @@ -152,7 +150,7 @@ void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) { void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) { LOG_ERROR(Service_Fatal, "called"); IPC::RequestParser rp(ctx); - const auto error_code = rp.Pop<ResultCode>(); + const auto error_code = rp.Pop<Result>(); const auto fatal_type = rp.PopEnum<FatalType>(); const auto fatal_info = ctx.ReadBuffer(); FatalInfo info{}; diff --git a/src/core/hle/service/fatal/fatal.h b/src/core/hle/service/fatal/fatal.h index 2095bf89f..a7a310f7b 100644 --- a/src/core/hle/service/fatal/fatal.h +++ b/src/core/hle/service/fatal/fatal.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/fatal/fatal_p.cpp b/src/core/hle/service/fatal/fatal_p.cpp index 8672b85dc..4a81bb5e2 100644 --- a/src/core/hle/service/fatal/fatal_p.cpp +++ b/src/core/hle/service/fatal/fatal_p.cpp @@ -1,13 +1,18 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/fatal/fatal_p.h" namespace Service::Fatal { Fatal_P::Fatal_P(std::shared_ptr<Module> module_, Core::System& system_) - : Interface(std::move(module_), system_, "fatal:p") {} + : Interface(std::move(module_), system_, "fatal:p") { + static const FunctionInfo functions[] = { + {0, nullptr, "GetFatalEvent"}, + {10, nullptr, "GetFatalContext"}, + }; + RegisterHandlers(functions); +} Fatal_P::~Fatal_P() = default; diff --git a/src/core/hle/service/fatal/fatal_p.h b/src/core/hle/service/fatal/fatal_p.h index ffa5b7b98..f74336835 100644 --- a/src/core/hle/service/fatal/fatal_p.h +++ b/src/core/hle/service/fatal/fatal_p.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/fatal/fatal_u.cpp b/src/core/hle/service/fatal/fatal_u.cpp index 82993938a..3739711fc 100644 --- a/src/core/hle/service/fatal/fatal_u.cpp +++ b/src/core/hle/service/fatal/fatal_u.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/fatal/fatal_u.h" diff --git a/src/core/hle/service/fatal/fatal_u.h b/src/core/hle/service/fatal/fatal_u.h index 0b58c9112..65fbe2696 100644 --- a/src/core/hle/service/fatal/fatal_u.h +++ b/src/core/hle/service/fatal/fatal_u.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/fgm/fgm.cpp b/src/core/hle/service/fgm/fgm.cpp index d7a638f96..7e9fb0385 100644 --- a/src/core/hle/service/fgm/fgm.cpp +++ b/src/core/hle/service/fgm/fgm.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/fgm/fgm.h b/src/core/hle/service/fgm/fgm.h index 75978f2ed..077e48812 100644 --- a/src/core/hle/service/fgm/fgm.h +++ b/src/core/hle/service/fgm/fgm.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 3703ca4c6..11c604a0f 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <utility> @@ -50,7 +49,7 @@ std::string VfsDirectoryServiceWrapper::GetName() const { return backing->GetName(); } -ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const { +Result VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const { std::string path(Common::FS::SanitizePath(path_)); auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); if (dir == nullptr) { @@ -74,7 +73,7 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 return ResultSuccess; } -ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const { +Result VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const { std::string path(Common::FS::SanitizePath(path_)); if (path.empty()) { // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but... @@ -93,7 +92,7 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) cons return ResultSuccess; } -ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const { +Result VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const { std::string path(Common::FS::SanitizePath(path_)); // NOTE: This is inaccurate behavior. CreateDirectory is not recursive. @@ -117,7 +116,7 @@ ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) return ResultSuccess; } -ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const { +Result VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const { std::string path(Common::FS::SanitizePath(path_)); auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); if (!dir->DeleteSubdirectory(Common::FS::GetFilename(path))) { @@ -127,7 +126,7 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) return ResultSuccess; } -ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const { +Result VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const { std::string path(Common::FS::SanitizePath(path_)); auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); if (!dir->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path))) { @@ -137,7 +136,7 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::str return ResultSuccess; } -ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const { +Result VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const { const std::string sanitized_path(Common::FS::SanitizePath(path)); auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(sanitized_path)); @@ -149,8 +148,8 @@ ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::stri return ResultSuccess; } -ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_, - const std::string& dest_path_) const { +Result VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_, + const std::string& dest_path_) const { std::string src_path(Common::FS::SanitizePath(src_path_)); std::string dest_path(Common::FS::SanitizePath(dest_path_)); auto src = backing->GetFileRelative(src_path); @@ -174,7 +173,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_, ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found."); ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(), - "Could not write all of the bytes but everything else has succeded."); + "Could not write all of the bytes but everything else has succeeded."); if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) { // TODO(DarkLordZach): Find a better error code for this @@ -184,8 +183,8 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_, return ResultSuccess; } -ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_, - const std::string& dest_path_) const { +Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_, + const std::string& dest_path_) const { std::string src_path(Common::FS::SanitizePath(src_path_)); std::string dest_path(Common::FS::SanitizePath(dest_path_)); auto src = GetDirectoryRelativeWrapped(backing, src_path); @@ -274,28 +273,27 @@ FileSystemController::FileSystemController(Core::System& system_) : system{syste FileSystemController::~FileSystemController() = default; -ResultCode FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) { +Result FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) { romfs_factory = std::move(factory); LOG_DEBUG(Service_FS, "Registered RomFS"); return ResultSuccess; } -ResultCode FileSystemController::RegisterSaveData( - std::unique_ptr<FileSys::SaveDataFactory>&& factory) { +Result FileSystemController::RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory) { ASSERT_MSG(save_data_factory == nullptr, "Tried to register a second save data"); save_data_factory = std::move(factory); LOG_DEBUG(Service_FS, "Registered save data"); return ResultSuccess; } -ResultCode FileSystemController::RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) { +Result FileSystemController::RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) { ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC"); sdmc_factory = std::move(factory); LOG_DEBUG(Service_FS, "Registered SDMC"); return ResultSuccess; } -ResultCode FileSystemController::RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) { +Result FileSystemController::RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) { ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS"); bis_factory = std::move(factory); LOG_DEBUG(Service_FS, "Registered BIS"); diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index b155e0811..5b27de9fa 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -59,10 +58,10 @@ public: explicit FileSystemController(Core::System& system_); ~FileSystemController(); - ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory); - ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory); - ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory); - ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); + Result RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory); + Result RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory); + Result RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory); + Result RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); void SetPackedUpdate(FileSys::VirtualFile update_raw); ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() const; @@ -142,7 +141,7 @@ private: void InstallInterfaces(Core::System& system); -// A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of +// A class that wraps a VfsDirectory with methods that return ResultVal and Result instead of // pointers and booleans. This makes using a VfsDirectory with switch services much easier and // avoids repetitive code. class VfsDirectoryServiceWrapper { @@ -161,35 +160,35 @@ public: * @param size The size of the new file, filled with zeroes * @return Result of the operation */ - ResultCode CreateFile(const std::string& path, u64 size) const; + Result CreateFile(const std::string& path, u64 size) const; /** * Delete a file specified by its path * @param path Path relative to the archive * @return Result of the operation */ - ResultCode DeleteFile(const std::string& path) const; + Result DeleteFile(const std::string& path) const; /** * Create a directory specified by its path * @param path Path relative to the archive * @return Result of the operation */ - ResultCode CreateDirectory(const std::string& path) const; + Result CreateDirectory(const std::string& path) const; /** * Delete a directory specified by its path * @param path Path relative to the archive * @return Result of the operation */ - ResultCode DeleteDirectory(const std::string& path) const; + Result DeleteDirectory(const std::string& path) const; /** * Delete a directory specified by its path and anything under it * @param path Path relative to the archive * @return Result of the operation */ - ResultCode DeleteDirectoryRecursively(const std::string& path) const; + Result DeleteDirectoryRecursively(const std::string& path) const; /** * Cleans the specified directory. This is similar to DeleteDirectoryRecursively, @@ -201,7 +200,7 @@ public: * * @return Result of the operation. */ - ResultCode CleanDirectoryRecursively(const std::string& path) const; + Result CleanDirectoryRecursively(const std::string& path) const; /** * Rename a File specified by its path @@ -209,7 +208,7 @@ public: * @param dest_path Destination path relative to the archive * @return Result of the operation */ - ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const; + Result RenameFile(const std::string& src_path, const std::string& dest_path) const; /** * Rename a Directory specified by its path @@ -217,7 +216,7 @@ public: * @param dest_path Destination path relative to the archive * @return Result of the operation */ - ResultCode RenameDirectory(const std::string& src_path, const std::string& dest_path) const; + Result RenameDirectory(const std::string& src_path, const std::string& dest_path) const; /** * Open a file specified by its path, using the specified mode diff --git a/src/core/hle/service/filesystem/fsp_ldr.cpp b/src/core/hle/service/filesystem/fsp_ldr.cpp index f112ae9d0..1e3366e71 100644 --- a/src/core/hle/service/filesystem/fsp_ldr.cpp +++ b/src/core/hle/service/filesystem/fsp_ldr.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/filesystem/fsp_ldr.h" diff --git a/src/core/hle/service/filesystem/fsp_ldr.h b/src/core/hle/service/filesystem/fsp_ldr.h index d6432a0e1..358739a87 100644 --- a/src/core/hle/service/filesystem/fsp_ldr.h +++ b/src/core/hle/service/filesystem/fsp_ldr.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/filesystem/fsp_pr.cpp b/src/core/hle/service/filesystem/fsp_pr.cpp index 9b7f7d861..4ffc31977 100644 --- a/src/core/hle/service/filesystem/fsp_pr.cpp +++ b/src/core/hle/service/filesystem/fsp_pr.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/filesystem/fsp_pr.h" diff --git a/src/core/hle/service/filesystem/fsp_pr.h b/src/core/hle/service/filesystem/fsp_pr.h index 9e622518c..bd4e0a730 100644 --- a/src/core/hle/service/filesystem/fsp_pr.h +++ b/src/core/hle/service/filesystem/fsp_pr.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index b087e7bba..e23eae36a 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cinttypes> #include <cstring> @@ -58,7 +57,8 @@ enum class FileSystemType : u8 { class IStorage final : public ServiceFramework<IStorage> { public: explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_) - : ServiceFramework{system_, "IStorage"}, backend(std::move(backend_)) { + : ServiceFramework{system_, "IStorage", ServiceThreadType::CreateNew}, + backend(std::move(backend_)) { static const FunctionInfo functions[] = { {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, @@ -116,7 +116,8 @@ private: class IFile final : public ServiceFramework<IFile> { public: explicit IFile(Core::System& system_, FileSys::VirtualFile backend_) - : ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) { + : ServiceFramework{system_, "IFile", ServiceThreadType::CreateNew}, + backend(std::move(backend_)) { static const FunctionInfo functions[] = { {0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"}, @@ -245,14 +246,16 @@ static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vec entries.reserve(entries.size() + new_data.size()); for (const auto& new_entry : new_data) { - entries.emplace_back(new_entry->GetName(), type, new_entry->GetSize()); + entries.emplace_back(new_entry->GetName(), type, + type == FileSys::EntryType::Directory ? 0 : new_entry->GetSize()); } } class IDirectory final : public ServiceFramework<IDirectory> { public: explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_) - : ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) { + : ServiceFramework{system_, "IDirectory", ServiceThreadType::CreateNew}, + backend(std::move(backend_)) { static const FunctionInfo functions[] = { {0, &IDirectory::Read, "Read"}, {1, &IDirectory::GetEntryCount, "GetEntryCount"}, @@ -308,8 +311,8 @@ private: class IFileSystem final : public ServiceFramework<IFileSystem> { public: explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_) - : ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move( - size_)} { + : ServiceFramework{system_, "IFileSystem", ServiceThreadType::CreateNew}, + backend{std::move(backend_)}, size{std::move(size_)} { static const FunctionInfo functions[] = { {0, &IFileSystem::CreateFile, "CreateFile"}, {1, &IFileSystem::DeleteFile, "DeleteFile"}, @@ -897,7 +900,7 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) { case FileSys::SaveDataSpaceId::TemporaryStorage: case FileSys::SaveDataSpaceId::ProperSystem: case FileSys::SaveDataSpaceId::SafeMode: - UNREACHABLE(); + ASSERT(false); } auto filesystem = std::make_shared<IFileSystem>(system, std::move(dir.Unwrap()), diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index 556708284..36f552e05 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/friend/errors.h b/src/core/hle/service/friend/errors.h index b3996e275..ff525d865 100644 --- a/src/core/hle/service/friend/errors.h +++ b/src/core/hle/service/friend/errors.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,5 +7,5 @@ namespace Service::Friend { -constexpr ResultCode ERR_NO_NOTIFICATIONS{ErrorModule::Account, 15}; +constexpr Result ERR_NO_NOTIFICATIONS{ErrorModule::Account, 15}; } diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index 79cd3acbb..e0db787fc 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <queue> #include "common/logging/log.h" diff --git a/src/core/hle/service/friend/friend.h b/src/core/hle/service/friend/friend.h index 8be3321db..444da8b35 100644 --- a/src/core/hle/service/friend/friend.h +++ b/src/core/hle/service/friend/friend.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/friend/friend_interface.cpp b/src/core/hle/service/friend/friend_interface.cpp index 9b18b2a32..c8b98b1f0 100644 --- a/src/core/hle/service/friend/friend_interface.cpp +++ b/src/core/hle/service/friend/friend_interface.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/friend/friend_interface.h" diff --git a/src/core/hle/service/friend/friend_interface.h b/src/core/hle/service/friend/friend_interface.h index 43d914b32..3a2184c34 100644 --- a/src/core/hle/service/friend/friend_interface.h +++ b/src/core/hle/service/friend/friend_interface.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/glue/arp.cpp b/src/core/hle/service/glue/arp.cpp index 2feead2aa..49b6d45fe 100644 --- a/src/core/hle/service/glue/arp.cpp +++ b/src/core/hle/service/glue/arp.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> @@ -154,7 +153,7 @@ class IRegistrar final : public ServiceFramework<IRegistrar> { friend class ARP_W; public: - using IssuerFn = std::function<ResultCode(u64, ApplicationLaunchProperty, std::vector<u8>)>; + using IssuerFn = std::function<Result(u64, ApplicationLaunchProperty, std::vector<u8>)>; explicit IRegistrar(Core::System& system_, IssuerFn&& issuer) : ServiceFramework{system_, "IRegistrar"}, issue_process_id{std::move(issuer)} { diff --git a/src/core/hle/service/glue/arp.h b/src/core/hle/service/glue/arp.h index 0df3c5e1f..06c992e88 100644 --- a/src/core/hle/service/glue/arp.h +++ b/src/core/hle/service/glue/arp.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/glue/bgtc.cpp b/src/core/hle/service/glue/bgtc.cpp index 564c3b750..3248091c3 100644 --- a/src/core/hle/service/glue/bgtc.cpp +++ b/src/core/hle/service/glue/bgtc.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/core.h" diff --git a/src/core/hle/service/glue/bgtc.h b/src/core/hle/service/glue/bgtc.h index 4c0142fd5..d6e2baec1 100644 --- a/src/core/hle/service/glue/bgtc.h +++ b/src/core/hle/service/glue/bgtc.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/glue/ectx.cpp b/src/core/hle/service/glue/ectx.cpp index 249c6f003..1bd9314ae 100644 --- a/src/core/hle/service/glue/ectx.cpp +++ b/src/core/hle/service/glue/ectx.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/glue/ectx.h" diff --git a/src/core/hle/service/glue/ectx.h b/src/core/hle/service/glue/ectx.h index b275e808a..a608de053 100644 --- a/src/core/hle/service/glue/ectx.h +++ b/src/core/hle/service/glue/ectx.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/glue/errors.h b/src/core/hle/service/glue/errors.h index f6647f724..d4ce7f44e 100644 --- a/src/core/hle/service/glue/errors.h +++ b/src/core/hle/service/glue/errors.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,9 +7,9 @@ namespace Service::Glue { -constexpr ResultCode ERR_INVALID_RESOURCE{ErrorModule::ARP, 30}; -constexpr ResultCode ERR_INVALID_PROCESS_ID{ErrorModule::ARP, 31}; -constexpr ResultCode ERR_INVALID_ACCESS{ErrorModule::ARP, 42}; -constexpr ResultCode ERR_NOT_REGISTERED{ErrorModule::ARP, 102}; +constexpr Result ERR_INVALID_RESOURCE{ErrorModule::ARP, 30}; +constexpr Result ERR_INVALID_PROCESS_ID{ErrorModule::ARP, 31}; +constexpr Result ERR_INVALID_ACCESS{ErrorModule::ARP, 42}; +constexpr Result ERR_NOT_REGISTERED{ErrorModule::ARP, 102}; } // namespace Service::Glue diff --git a/src/core/hle/service/glue/glue.cpp b/src/core/hle/service/glue/glue.cpp index b24d469cf..717f2562b 100644 --- a/src/core/hle/service/glue/glue.cpp +++ b/src/core/hle/service/glue/glue.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> #include "core/core.h" diff --git a/src/core/hle/service/glue/glue.h b/src/core/hle/service/glue/glue.h index 112cd238b..ae7c6d235 100644 --- a/src/core/hle/service/glue/glue.h +++ b/src/core/hle/service/glue/glue.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/glue/glue_manager.cpp b/src/core/hle/service/glue/glue_manager.cpp index 48e133b48..8a654cdca 100644 --- a/src/core/hle/service/glue/glue_manager.cpp +++ b/src/core/hle/service/glue/glue_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/glue/errors.h" #include "core/hle/service/glue/glue_manager.h" @@ -42,8 +41,8 @@ ResultVal<std::vector<u8>> ARPManager::GetControlProperty(u64 title_id) const { return iter->second.control; } -ResultCode ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch, - std::vector<u8> control) { +Result ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch, + std::vector<u8> control) { if (title_id == 0) { return ERR_INVALID_PROCESS_ID; } @@ -57,7 +56,7 @@ ResultCode ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch, return ResultSuccess; } -ResultCode ARPManager::Unregister(u64 title_id) { +Result ARPManager::Unregister(u64 title_id) { if (title_id == 0) { return ERR_INVALID_PROCESS_ID; } diff --git a/src/core/hle/service/glue/glue_manager.h b/src/core/hle/service/glue/glue_manager.h index 4bc5297c6..cd0b092ac 100644 --- a/src/core/hle/service/glue/glue_manager.h +++ b/src/core/hle/service/glue/glue_manager.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -43,12 +42,12 @@ public: // Adds a new entry to the internal database with the provided parameters, returning // ERR_INVALID_ACCESS if attempting to re-register a title ID without an intermediate Unregister // step, and ERR_INVALID_PROCESS_ID if the title ID is 0. - ResultCode Register(u64 title_id, ApplicationLaunchProperty launch, std::vector<u8> control); + Result Register(u64 title_id, ApplicationLaunchProperty launch, std::vector<u8> control); // Removes the registration for the provided title ID from the database, returning // ERR_NOT_REGISTERED if it doesn't exist in the database and ERR_INVALID_PROCESS_ID if the // title ID is 0. - ResultCode Unregister(u64 title_id); + Result Unregister(u64 title_id); // Removes all entries from the database, always succeeds. Should only be used when resetting // system state. diff --git a/src/core/hle/service/glue/notif.cpp b/src/core/hle/service/glue/notif.cpp index c559ec9df..3ace2dabd 100644 --- a/src/core/hle/service/glue/notif.cpp +++ b/src/core/hle/service/glue/notif.cpp @@ -1,7 +1,11 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#include <algorithm> +#include <cstring> + +#include "common/assert.h" +#include "common/logging/log.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/glue/notif.h" @@ -10,11 +14,11 @@ namespace Service::Glue { NOTIF_A::NOTIF_A(Core::System& system_) : ServiceFramework{system_, "notif:a"} { // clang-format off static const FunctionInfo functions[] = { - {500, nullptr, "RegisterAlarmSetting"}, - {510, nullptr, "UpdateAlarmSetting"}, + {500, &NOTIF_A::RegisterAlarmSetting, "RegisterAlarmSetting"}, + {510, &NOTIF_A::UpdateAlarmSetting, "UpdateAlarmSetting"}, {520, &NOTIF_A::ListAlarmSettings, "ListAlarmSettings"}, - {530, nullptr, "LoadApplicationParameter"}, - {540, nullptr, "DeleteAlarmSetting"}, + {530, &NOTIF_A::LoadApplicationParameter, "LoadApplicationParameter"}, + {540, &NOTIF_A::DeleteAlarmSetting, "DeleteAlarmSetting"}, {1000, &NOTIF_A::Initialize, "Initialize"}, }; // clang-format on @@ -24,21 +28,132 @@ NOTIF_A::NOTIF_A(Core::System& system_) : ServiceFramework{system_, "notif:a"} { NOTIF_A::~NOTIF_A() = default; +void NOTIF_A::RegisterAlarmSetting(Kernel::HLERequestContext& ctx) { + const auto alarm_setting_buffer_size = ctx.GetReadBufferSize(0); + const auto application_parameter_size = ctx.GetReadBufferSize(1); + + ASSERT_MSG(alarm_setting_buffer_size == sizeof(AlarmSetting), + "alarm_setting_buffer_size is not 0x40 bytes"); + ASSERT_MSG(application_parameter_size <= sizeof(ApplicationParameter), + "application_parameter_size is bigger than 0x400 bytes"); + + AlarmSetting new_alarm{}; + memcpy(&new_alarm, ctx.ReadBuffer(0).data(), sizeof(AlarmSetting)); + + // TODO: Count alarms per game id + if (alarms.size() >= max_alarms) { + LOG_ERROR(Service_NOTIF, "Alarm limit reached"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + new_alarm.alarm_setting_id = last_alarm_setting_id++; + alarms.push_back(new_alarm); + + // TODO: Save application parameter data + + LOG_WARNING(Service_NOTIF, + "(STUBBED) called, application_parameter_size={}, setting_id={}, kind={}, muted={}", + application_parameter_size, new_alarm.alarm_setting_id, new_alarm.kind, + new_alarm.muted); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + rb.Push(new_alarm.alarm_setting_id); +} + +void NOTIF_A::UpdateAlarmSetting(Kernel::HLERequestContext& ctx) { + const auto alarm_setting_buffer_size = ctx.GetReadBufferSize(0); + const auto application_parameter_size = ctx.GetReadBufferSize(1); + + ASSERT_MSG(alarm_setting_buffer_size == sizeof(AlarmSetting), + "alarm_setting_buffer_size is not 0x40 bytes"); + ASSERT_MSG(application_parameter_size <= sizeof(ApplicationParameter), + "application_parameter_size is bigger than 0x400 bytes"); + + AlarmSetting alarm_setting{}; + memcpy(&alarm_setting, ctx.ReadBuffer(0).data(), sizeof(AlarmSetting)); + + const auto alarm_it = GetAlarmFromId(alarm_setting.alarm_setting_id); + if (alarm_it != alarms.end()) { + LOG_DEBUG(Service_NOTIF, "Alarm updated"); + *alarm_it = alarm_setting; + // TODO: Save application parameter data + } + + LOG_WARNING(Service_NOTIF, + "(STUBBED) called, application_parameter_size={}, setting_id={}, kind={}, muted={}", + application_parameter_size, alarm_setting.alarm_setting_id, alarm_setting.kind, + alarm_setting.muted); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void NOTIF_A::ListAlarmSettings(Kernel::HLERequestContext& ctx) { - // Returns an array of AlarmSetting - constexpr s32 alarm_count = 0; + LOG_INFO(Service_NOTIF, "called, alarm_count={}", alarms.size()); - LOG_WARNING(Service_NOTIF, "(STUBBED) called"); + // TODO: Only return alarms of this game id + ctx.WriteBuffer(alarms); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(alarm_count); + rb.Push(static_cast<u32>(alarms.size())); +} + +void NOTIF_A::LoadApplicationParameter(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto alarm_setting_id{rp.Pop<AlarmSettingId>()}; + + const auto alarm_it = GetAlarmFromId(alarm_setting_id); + if (alarm_it == alarms.end()) { + LOG_ERROR(Service_NOTIF, "Invalid alarm setting id={}", alarm_setting_id); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + // TODO: Read application parameter related to this setting id + ApplicationParameter application_parameter{}; + + LOG_WARNING(Service_NOTIF, "(STUBBED) called, alarm_setting_id={}", alarm_setting_id); + + ctx.WriteBuffer(application_parameter); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + rb.Push(static_cast<u32>(application_parameter.size())); +} + +void NOTIF_A::DeleteAlarmSetting(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto alarm_setting_id{rp.Pop<AlarmSettingId>()}; + + std::erase_if(alarms, [alarm_setting_id](const AlarmSetting& alarm) { + return alarm.alarm_setting_id == alarm_setting_id; + }); + + LOG_INFO(Service_NOTIF, "called, alarm_setting_id={}", alarm_setting_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); } void NOTIF_A::Initialize(Kernel::HLERequestContext& ctx) { + // TODO: Load previous alarms from config + LOG_WARNING(Service_NOTIF, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } +std::vector<NOTIF_A::AlarmSetting>::iterator NOTIF_A::GetAlarmFromId( + AlarmSettingId alarm_setting_id) { + return std::find_if(alarms.begin(), alarms.end(), + [alarm_setting_id](const AlarmSetting& alarm) { + return alarm.alarm_setting_id == alarm_setting_id; + }); +} + } // namespace Service::Glue diff --git a/src/core/hle/service/glue/notif.h b/src/core/hle/service/glue/notif.h index 6ecf2015c..4467e1f35 100644 --- a/src/core/hle/service/glue/notif.h +++ b/src/core/hle/service/glue/notif.h @@ -1,9 +1,12 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include <array> +#include <vector> + +#include "common/uuid.h" #include "core/hle/service/service.h" namespace Core { @@ -18,8 +21,52 @@ public: ~NOTIF_A() override; private: + static constexpr std::size_t max_alarms = 8; + + // This is nn::notification::AlarmSettingId + using AlarmSettingId = u16; + static_assert(sizeof(AlarmSettingId) == 0x2, "AlarmSettingId is an invalid size"); + + using ApplicationParameter = std::array<u8, 0x400>; + static_assert(sizeof(ApplicationParameter) == 0x400, "ApplicationParameter is an invalid size"); + + struct DailyAlarmSetting { + s8 hour; + s8 minute; + }; + static_assert(sizeof(DailyAlarmSetting) == 0x2, "DailyAlarmSetting is an invalid size"); + + struct WeeklyScheduleAlarmSetting { + INSERT_PADDING_BYTES(0xA); + std::array<DailyAlarmSetting, 0x7> day_of_week; + }; + static_assert(sizeof(WeeklyScheduleAlarmSetting) == 0x18, + "WeeklyScheduleAlarmSetting is an invalid size"); + + // This is nn::notification::AlarmSetting + struct AlarmSetting { + AlarmSettingId alarm_setting_id; + u8 kind; + u8 muted; + INSERT_PADDING_BYTES(0x4); + Common::UUID account_id; + u64 application_id; + INSERT_PADDING_BYTES(0x8); + WeeklyScheduleAlarmSetting schedule; + }; + static_assert(sizeof(AlarmSetting) == 0x40, "AlarmSetting is an invalid size"); + + void RegisterAlarmSetting(Kernel::HLERequestContext& ctx); + void UpdateAlarmSetting(Kernel::HLERequestContext& ctx); void ListAlarmSettings(Kernel::HLERequestContext& ctx); + void LoadApplicationParameter(Kernel::HLERequestContext& ctx); + void DeleteAlarmSetting(Kernel::HLERequestContext& ctx); void Initialize(Kernel::HLERequestContext& ctx); + + std::vector<AlarmSetting>::iterator GetAlarmFromId(AlarmSettingId alarm_setting_id); + + std::vector<AlarmSetting> alarms{}; + AlarmSettingId last_alarm_setting_id{}; }; } // namespace Service::Glue diff --git a/src/core/hle/service/grc/grc.cpp b/src/core/hle/service/grc/grc.cpp index f918bdf03..4b684f6d0 100644 --- a/src/core/hle/service/grc/grc.cpp +++ b/src/core/hle/service/grc/grc.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/grc/grc.h b/src/core/hle/service/grc/grc.h index 9069fe756..f8c2f8dab 100644 --- a/src/core/hle/service/grc/grc.h +++ b/src/core/hle/service/grc/grc.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.cpp b/src/core/hle/service/hid/controllers/console_sixaxis.cpp index a727b3582..bb3cba910 100644 --- a/src/core/hle/service/hid/controllers/console_sixaxis.cpp +++ b/src/core/hle/service/hid/controllers/console_sixaxis.cpp @@ -1,8 +1,6 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later -#include "common/settings.h" #include "core/core_timing.h" #include "core/hid/emulated_console.h" #include "core/hid/hid_core.h" @@ -11,9 +9,14 @@ namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200; -Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_) +Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_, + u8* raw_shared_memory_) : ControllerBase{hid_core_} { console = hid_core.GetEmulatedConsole(); + static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size, + "ConsoleSharedMemory is bigger than the shared memory"); + shared_memory = std::construct_at( + reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); } Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default; @@ -22,8 +25,7 @@ void Controller_ConsoleSixAxis::OnInit() {} void Controller_ConsoleSixAxis::OnRelease() {} -void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t size) { +void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated() || !is_transfer_memory_set) { seven_sixaxis_lifo.buffer_count = 0; seven_sixaxis_lifo.buffer_tail = 0; @@ -50,13 +52,11 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti -motion_status.quaternion.xyz.z, }; - console_six_axis.sampling_number++; - console_six_axis.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest; - console_six_axis.verticalization_error = motion_status.verticalization_error; - console_six_axis.gyro_bias = motion_status.gyro_bias; + shared_memory->sampling_number++; + shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest; + shared_memory->verticalization_error = motion_status.verticalization_error; + shared_memory->gyro_bias = motion_status.gyro_bias; - // Update console six axis shared memory - std::memcpy(data + SHARED_MEMORY_OFFSET, &console_six_axis, sizeof(console_six_axis)); // Update seven six axis transfer memory seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state); std::memcpy(transfer_memory, &seven_sixaxis_lifo, sizeof(seven_sixaxis_lifo)); diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.h b/src/core/hle/service/hid/controllers/console_sixaxis.h index 26d153f0c..2fd11538f 100644 --- a/src/core/hle/service/hid/controllers/console_sixaxis.h +++ b/src/core/hle/service/hid/controllers/console_sixaxis.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,7 +7,6 @@ #include "common/common_types.h" #include "common/quaternion.h" -#include "core/hid/hid_types.h" #include "core/hle/service/hid/controllers/controller_base.h" #include "core/hle/service/hid/ring_lifo.h" @@ -19,7 +17,7 @@ class EmulatedConsole; namespace Service::HID { class Controller_ConsoleSixAxis final : public ControllerBase { public: - explicit Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_); + explicit Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); ~Controller_ConsoleSixAxis() override; // Called when the controller is initialized @@ -29,7 +27,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; // Called on InitializeSevenSixAxisSensor void SetTransferMemoryPointer(u8* t_mem); @@ -63,12 +61,13 @@ private: Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{}; static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size"); - Core::HID::EmulatedConsole* console; + SevenSixAxisState next_seven_sixaxis_state{}; u8* transfer_memory = nullptr; + ConsoleSharedMemory* shared_memory = nullptr; + Core::HID::EmulatedConsole* console = nullptr; + bool is_transfer_memory_set = false; u64 last_saved_timestamp{}; u64 last_global_timestamp{}; - ConsoleSharedMemory console_six_axis{}; - SevenSixAxisState next_seven_sixaxis_state{}; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp index 788ae9ae7..c58d67d7d 100644 --- a/src/core/hle/service/hid/controllers/controller_base.cpp +++ b/src/core/hle/service/hid/controllers/controller_base.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/hid/controllers/controller_base.h" diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h index 7450eb20a..d6f7a5073 100644 --- a/src/core/hle/service/hid/controllers/controller_base.h +++ b/src/core/hle/service/hid/controllers/controller_base.h @@ -1,11 +1,9 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include "common/common_types.h" -#include "common/swap.h" namespace Core::Timing { class CoreTiming; @@ -28,12 +26,10 @@ public: virtual void OnRelease() = 0; // When the controller is requesting an update for the shared memory - virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t size) = 0; + virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing) = 0; // When the controller is requesting a motion update for the shared memory - virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t size) {} + virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {} void ActivateController(); @@ -42,6 +38,7 @@ public: bool IsControllerActivated() const; static const std::size_t hid_entry_count = 17; + static const std::size_t shared_memory_size = 0x40000; protected: bool is_activated{false}; diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index 6a6fb9cab..8ec9f4a95 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include "common/common_types.h" @@ -14,8 +13,12 @@ namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000; -Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_) +Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} { + static_assert(SHARED_MEMORY_OFFSET + sizeof(DebugPadSharedMemory) < shared_memory_size, + "DebugPadSharedMemory is bigger than the shared memory"); + shared_memory = std::construct_at( + reinterpret_cast<DebugPadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); } @@ -25,16 +28,14 @@ void Controller_DebugPad::OnInit() {} void Controller_DebugPad::OnRelease() {} -void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t size) { +void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated()) { - debug_pad_lifo.buffer_count = 0; - debug_pad_lifo.buffer_tail = 0; - std::memcpy(data + SHARED_MEMORY_OFFSET, &debug_pad_lifo, sizeof(debug_pad_lifo)); + shared_memory->debug_pad_lifo.buffer_count = 0; + shared_memory->debug_pad_lifo.buffer_tail = 0; return; } - const auto& last_entry = debug_pad_lifo.ReadCurrentEntry().state; + const auto& last_entry = shared_memory->debug_pad_lifo.ReadCurrentEntry().state; next_state.sampling_number = last_entry.sampling_number + 1; if (Settings::values.debug_pad_enabled) { @@ -48,8 +49,7 @@ void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, next_state.r_stick = stick_state.right; } - debug_pad_lifo.WriteNextEntry(next_state); - std::memcpy(data + SHARED_MEMORY_OFFSET, &debug_pad_lifo, sizeof(debug_pad_lifo)); + shared_memory->debug_pad_lifo.WriteNextEntry(next_state); } } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h index afe374fc2..68ff0ea79 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.h +++ b/src/core/hle/service/hid/controllers/debug_pad.h @@ -1,14 +1,10 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include <array> #include "common/bit_field.h" -#include "common/common_funcs.h" #include "common/common_types.h" -#include "common/swap.h" #include "core/hle/service/hid/controllers/controller_base.h" #include "core/hle/service/hid/ring_lifo.h" @@ -21,7 +17,7 @@ struct AnalogStickState; namespace Service::HID { class Controller_DebugPad final : public ControllerBase { public: - explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_); + explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); ~Controller_DebugPad() override; // Called when the controller is initialized @@ -31,7 +27,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; private: // This is nn::hid::DebugPadAttribute @@ -45,19 +41,24 @@ private: // This is nn::hid::DebugPadState struct DebugPadState { - s64 sampling_number; - DebugPadAttribute attribute; - Core::HID::DebugPadButton pad_state; - Core::HID::AnalogStickState r_stick; - Core::HID::AnalogStickState l_stick; + s64 sampling_number{}; + DebugPadAttribute attribute{}; + Core::HID::DebugPadButton pad_state{}; + Core::HID::AnalogStickState r_stick{}; + Core::HID::AnalogStickState l_stick{}; }; static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state"); - // This is nn::hid::detail::DebugPadLifo - Lifo<DebugPadState, hid_entry_count> debug_pad_lifo{}; - static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size"); - DebugPadState next_state{}; + struct DebugPadSharedMemory { + // This is nn::hid::detail::DebugPadLifo + Lifo<DebugPadState, hid_entry_count> debug_pad_lifo{}; + static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size"); + INSERT_PADDING_WORDS(0x4E); + }; + static_assert(sizeof(DebugPadSharedMemory) == 0x400, "DebugPadSharedMemory is an invalid size"); - Core::HID::EmulatedController* controller; + DebugPadState next_state{}; + DebugPadSharedMemory* shared_memory = nullptr; + Core::HID::EmulatedController* controller = nullptr; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index fe895c4f6..32e0708ba 100644 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "common/math_util.h" @@ -24,25 +23,28 @@ constexpr f32 Square(s32 num) { return static_cast<f32>(num * num); } -Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) { +Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) + : ControllerBase(hid_core_) { + static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size, + "GestureSharedMemory is bigger than the shared memory"); + shared_memory = std::construct_at( + reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); console = hid_core.GetEmulatedConsole(); } Controller_Gesture::~Controller_Gesture() = default; void Controller_Gesture::OnInit() { - gesture_lifo.buffer_count = 0; - gesture_lifo.buffer_tail = 0; + shared_memory->gesture_lifo.buffer_count = 0; + shared_memory->gesture_lifo.buffer_tail = 0; force_update = true; } void Controller_Gesture::OnRelease() {} -void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t size) { +void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated()) { - gesture_lifo.buffer_count = 0; - gesture_lifo.buffer_tail = 0; - std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo)); + shared_memory->gesture_lifo.buffer_count = 0; + shared_memory->gesture_lifo.buffer_tail = 0; return; } @@ -50,15 +52,16 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u GestureProperties gesture = GetGestureProperties(); f32 time_difference = - static_cast<f32>(gesture_lifo.timestamp - last_update_timestamp) / (1000 * 1000 * 1000); + static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) / + (1000 * 1000 * 1000); // Only update if necesary if (!ShouldUpdateGesture(gesture, time_difference)) { return; } - last_update_timestamp = gesture_lifo.timestamp; - UpdateGestureSharedMemory(data, size, gesture, time_difference); + last_update_timestamp = shared_memory->gesture_lifo.timestamp; + UpdateGestureSharedMemory(gesture, time_difference); } void Controller_Gesture::ReadTouchInput() { @@ -92,13 +95,12 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture, return false; } -void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size, - GestureProperties& gesture, +void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference) { GestureType type = GestureType::Idle; GestureAttribute attributes{}; - const auto& last_entry = gesture_lifo.ReadCurrentEntry().state; + const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state; // Reset next state to default next_state.sampling_number = last_entry.sampling_number + 1; @@ -128,8 +130,7 @@ void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size, next_state.points = gesture.points; last_gesture = gesture; - gesture_lifo.WriteNextEntry(next_state); - std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo)); + shared_memory->gesture_lifo.WriteNextEntry(next_state); } void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type, @@ -306,7 +307,7 @@ void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture, } const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const { - return gesture_lifo.ReadCurrentEntry().state; + return shared_memory->gesture_lifo.ReadCurrentEntry().state; } Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() { diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h index 0936a3fa3..0d6099ea0 100644 --- a/src/core/hle/service/hid/controllers/gesture.h +++ b/src/core/hle/service/hid/controllers/gesture.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -15,7 +14,7 @@ namespace Service::HID { class Controller_Gesture final : public ControllerBase { public: - explicit Controller_Gesture(Core::HID::HIDCore& hid_core_); + explicit Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); ~Controller_Gesture() override; // Called when the controller is initialized @@ -25,7 +24,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; private: static constexpr size_t MAX_FINGERS = 16; @@ -67,19 +66,19 @@ private: // This is nn::hid::GestureState struct GestureState { - s64 sampling_number; - s64 detection_count; - GestureType type; - GestureDirection direction; - Common::Point<s32> pos; - Common::Point<s32> delta; - f32 vel_x; - f32 vel_y; - GestureAttribute attributes; - f32 scale; - f32 rotation_angle; - s32 point_count; - std::array<Common::Point<s32>, 4> points; + s64 sampling_number{}; + s64 detection_count{}; + GestureType type{GestureType::Idle}; + GestureDirection direction{GestureDirection::None}; + Common::Point<s32> pos{}; + Common::Point<s32> delta{}; + f32 vel_x{}; + f32 vel_y{}; + GestureAttribute attributes{}; + f32 scale{}; + f32 rotation_angle{}; + s32 point_count{}; + std::array<Common::Point<s32>, 4> points{}; }; static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size"); @@ -93,6 +92,14 @@ private: f32 angle{}; }; + struct GestureSharedMemory { + // This is nn::hid::detail::GestureLifo + Lifo<GestureState, hid_entry_count> gesture_lifo{}; + static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size"); + INSERT_PADDING_WORDS(0x3E); + }; + static_assert(sizeof(GestureSharedMemory) == 0x800, "GestureSharedMemory is an invalid size"); + // Reads input from all available input engines void ReadTouchInput(); @@ -100,8 +107,7 @@ private: bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference); // Updates the shared memory to the next state - void UpdateGestureSharedMemory(u8* data, std::size_t size, GestureProperties& gesture, - f32 time_difference); + void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference); // Initializes new gesture void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes); @@ -135,12 +141,9 @@ private: // Returns the average distance, angle and middle point of the active fingers GestureProperties GetGestureProperties(); - // This is nn::hid::detail::GestureLifo - Lifo<GestureState, hid_entry_count> gesture_lifo{}; - static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size"); GestureState next_state{}; - - Core::HID::EmulatedConsole* console; + GestureSharedMemory* shared_memory = nullptr; + Core::HID::EmulatedConsole* console = nullptr; std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{}; GestureProperties last_gesture{}; diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index 9588a6910..117d87433 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include "common/common_types.h" @@ -13,8 +12,12 @@ namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; -Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_) +Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} { + static_assert(SHARED_MEMORY_OFFSET + sizeof(KeyboardSharedMemory) < shared_memory_size, + "KeyboardSharedMemory is bigger than the shared memory"); + shared_memory = std::construct_at( + reinterpret_cast<KeyboardSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); emulated_devices = hid_core.GetEmulatedDevices(); } @@ -24,16 +27,14 @@ void Controller_Keyboard::OnInit() {} void Controller_Keyboard::OnRelease() {} -void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t size) { +void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated()) { - keyboard_lifo.buffer_count = 0; - keyboard_lifo.buffer_tail = 0; - std::memcpy(data + SHARED_MEMORY_OFFSET, &keyboard_lifo, sizeof(keyboard_lifo)); + shared_memory->keyboard_lifo.buffer_count = 0; + shared_memory->keyboard_lifo.buffer_tail = 0; return; } - const auto& last_entry = keyboard_lifo.ReadCurrentEntry().state; + const auto& last_entry = shared_memory->keyboard_lifo.ReadCurrentEntry().state; next_state.sampling_number = last_entry.sampling_number + 1; if (Settings::values.keyboard_enabled) { @@ -45,8 +46,7 @@ void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, next_state.attribute.is_connected.Assign(1); } - keyboard_lifo.WriteNextEntry(next_state); - std::memcpy(data + SHARED_MEMORY_OFFSET, &keyboard_lifo, sizeof(keyboard_lifo)); + shared_memory->keyboard_lifo.WriteNextEntry(next_state); } } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h index cf62d3896..7532f53c6 100644 --- a/src/core/hle/service/hid/controllers/keyboard.h +++ b/src/core/hle/service/hid/controllers/keyboard.h @@ -1,14 +1,9 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include <array> -#include "common/bit_field.h" -#include "common/common_funcs.h" #include "common/common_types.h" -#include "common/swap.h" #include "core/hle/service/hid/controllers/controller_base.h" #include "core/hle/service/hid/ring_lifo.h" @@ -21,7 +16,7 @@ struct KeyboardKey; namespace Service::HID { class Controller_Keyboard final : public ControllerBase { public: - explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_); + explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); ~Controller_Keyboard() override; // Called when the controller is initialized @@ -31,23 +26,28 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; private: // This is nn::hid::detail::KeyboardState struct KeyboardState { - s64 sampling_number; - Core::HID::KeyboardModifier modifier; - Core::HID::KeyboardAttribute attribute; - Core::HID::KeyboardKey key; + s64 sampling_number{}; + Core::HID::KeyboardModifier modifier{}; + Core::HID::KeyboardAttribute attribute{}; + Core::HID::KeyboardKey key{}; }; static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size"); - // This is nn::hid::detail::KeyboardLifo - Lifo<KeyboardState, hid_entry_count> keyboard_lifo{}; - static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size"); - KeyboardState next_state{}; + struct KeyboardSharedMemory { + // This is nn::hid::detail::KeyboardLifo + Lifo<KeyboardState, hid_entry_count> keyboard_lifo{}; + static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size"); + INSERT_PADDING_WORDS(0xA); + }; + static_assert(sizeof(KeyboardSharedMemory) == 0x400, "KeyboardSharedMemory is an invalid size"); - Core::HID::EmulatedDevices* emulated_devices; + KeyboardState next_state{}; + KeyboardSharedMemory* shared_memory = nullptr; + Core::HID::EmulatedDevices* emulated_devices = nullptr; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp index ba79888ae..b11cb438d 100644 --- a/src/core/hle/service/hid/controllers/mouse.cpp +++ b/src/core/hle/service/hid/controllers/mouse.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include "common/common_types.h" @@ -13,7 +12,12 @@ namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; -Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} { +Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) + : ControllerBase{hid_core_} { + static_assert(SHARED_MEMORY_OFFSET + sizeof(MouseSharedMemory) < shared_memory_size, + "MouseSharedMemory is bigger than the shared memory"); + shared_memory = std::construct_at( + reinterpret_cast<MouseSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); emulated_devices = hid_core.GetEmulatedDevices(); } @@ -22,16 +26,14 @@ Controller_Mouse::~Controller_Mouse() = default; void Controller_Mouse::OnInit() {} void Controller_Mouse::OnRelease() {} -void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t size) { +void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated()) { - mouse_lifo.buffer_count = 0; - mouse_lifo.buffer_tail = 0; - std::memcpy(data + SHARED_MEMORY_OFFSET, &mouse_lifo, sizeof(mouse_lifo)); + shared_memory->mouse_lifo.buffer_count = 0; + shared_memory->mouse_lifo.buffer_tail = 0; return; } - const auto& last_entry = mouse_lifo.ReadCurrentEntry().state; + const auto& last_entry = shared_memory->mouse_lifo.ReadCurrentEntry().state; next_state.sampling_number = last_entry.sampling_number + 1; next_state.attribute.raw = 0; @@ -51,8 +53,7 @@ void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* next_state.button = mouse_button_state; } - mouse_lifo.WriteNextEntry(next_state); - std::memcpy(data + SHARED_MEMORY_OFFSET, &mouse_lifo, sizeof(mouse_lifo)); + shared_memory->mouse_lifo.WriteNextEntry(next_state); } } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h index 7559fc78d..733d00577 100644 --- a/src/core/hle/service/hid/controllers/mouse.h +++ b/src/core/hle/service/hid/controllers/mouse.h @@ -1,13 +1,9 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include <array> -#include "common/bit_field.h" #include "common/common_types.h" -#include "common/swap.h" #include "core/hle/service/hid/controllers/controller_base.h" #include "core/hle/service/hid/ring_lifo.h" @@ -20,7 +16,7 @@ struct AnalogStickState; namespace Service::HID { class Controller_Mouse final : public ControllerBase { public: - explicit Controller_Mouse(Core::HID::HIDCore& hid_core_); + explicit Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); ~Controller_Mouse() override; // Called when the controller is initialized @@ -30,15 +26,20 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; private: - // This is nn::hid::detail::MouseLifo - Lifo<Core::HID::MouseState, hid_entry_count> mouse_lifo{}; - static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size"); - Core::HID::MouseState next_state{}; + struct MouseSharedMemory { + // This is nn::hid::detail::MouseLifo + Lifo<Core::HID::MouseState, hid_entry_count> mouse_lifo{}; + static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size"); + INSERT_PADDING_WORDS(0x2C); + }; + static_assert(sizeof(MouseSharedMemory) == 0x400, "MouseSharedMemory is an invalid size"); - Core::HID::AnalogStickState last_mouse_wheel_state; - Core::HID::EmulatedDevices* emulated_devices; + Core::HID::MouseState next_state{}; + Core::HID::AnalogStickState last_mouse_wheel_state{}; + MouseSharedMemory* shared_memory = nullptr; + Core::HID::EmulatedDevices* emulated_devices = nullptr; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index e5c951e06..f8972ec7a 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -1,10 +1,11 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <array> +#include <chrono> #include <cstring> + #include "common/assert.h" #include "common/bit_field.h" #include "common/common_types.h" @@ -17,6 +18,7 @@ #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_writable_event.h" #include "core/hle/service/hid/controllers/npad.h" +#include "core/hle/service/hid/errors.h" #include "core/hle/service/kernel_helpers.h" namespace Service::HID { @@ -47,23 +49,52 @@ bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) { } } -bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) { - return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) && - device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType && - device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; +Result Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) { + const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)); + const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType; + const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; + + if (!npad_type) { + return VibrationInvalidStyleIndex; + } + if (!npad_id) { + return VibrationInvalidNpadId; + } + if (!device_index) { + return VibrationDeviceIndexOutOfRange; + } + + return ResultSuccess; } -bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle) { - return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) && - device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType && - device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; +Result Controller_NPad::VerifyValidSixAxisSensorHandle( + const Core::HID::SixAxisSensorHandle& device_handle) { + const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)); + const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; + const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType; + + if (!npad_id) { + return InvalidNpadId; + } + if (!device_index) { + return NpadDeviceIndexOutOfRange; + } + // This doesn't get validated on nnsdk + if (!npad_type) { + return NpadInvalidHandle; + } + + return ResultSuccess; } -Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, +Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, KernelHelpers::ServiceContext& service_context_) : ControllerBase{hid_core_}, service_context{service_context_} { + static_assert(NPAD_OFFSET + (NPAD_COUNT * sizeof(NpadInternalState)) < shared_memory_size); for (std::size_t i = 0; i < controller_data.size(); ++i) { auto& controller = controller_data[i]; + controller.shared_memory = std::construct_at(reinterpret_cast<NpadInternalState*>( + raw_shared_memory_ + NPAD_OFFSET + (i * sizeof(NpadInternalState)))); controller.device = hid_core.GetEmulatedControllerByIndex(i); controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value = Core::HID::DEFAULT_VIBRATION_VALUE; @@ -113,11 +144,11 @@ void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, if (!controller.device->IsConnected()) { return; } - auto& shared_memory = controller.shared_memory_entry; + auto* shared_memory = controller.shared_memory; const auto& battery_level = controller.device->GetBattery(); - shared_memory.battery_level_dual = battery_level.dual.battery_level; - shared_memory.battery_level_left = battery_level.left.battery_level; - shared_memory.battery_level_right = battery_level.right.battery_level; + shared_memory->battery_level_dual = battery_level.dual.battery_level; + shared_memory->battery_level_left = battery_level.left.battery_level; + shared_memory->battery_level_right = battery_level.right.battery_level; break; } default: @@ -132,123 +163,178 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { } LOG_DEBUG(Service_HID, "Npad connected {}", npad_id); const auto controller_type = controller.device->GetNpadStyleIndex(); - auto& shared_memory = controller.shared_memory_entry; + const auto& body_colors = controller.device->GetColors(); + const auto& battery_level = controller.device->GetBattery(); + auto* shared_memory = controller.shared_memory; if (controller_type == Core::HID::NpadStyleIndex::None) { controller.styleset_changed_event->GetWritableEvent().Signal(); return; } - shared_memory.style_tag.raw = Core::HID::NpadStyleSet::None; - shared_memory.device_type.raw = 0; - shared_memory.system_properties.raw = 0; + + // Reset memory values + shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None; + shared_memory->device_type.raw = 0; + shared_memory->system_properties.raw = 0; + shared_memory->joycon_color.attribute = ColorAttribute::NoController; + shared_memory->joycon_color.attribute = ColorAttribute::NoController; + shared_memory->fullkey_color = {}; + shared_memory->joycon_color.left = {}; + shared_memory->joycon_color.right = {}; + shared_memory->battery_level_dual = {}; + shared_memory->battery_level_left = {}; + shared_memory->battery_level_right = {}; + switch (controller_type) { case Core::HID::NpadStyleIndex::None: - UNREACHABLE(); + ASSERT(false); break; case Core::HID::NpadStyleIndex::ProController: - shared_memory.style_tag.fullkey.Assign(1); - shared_memory.device_type.fullkey.Assign(1); - shared_memory.system_properties.is_vertical.Assign(1); - shared_memory.system_properties.use_plus.Assign(1); - shared_memory.system_properties.use_minus.Assign(1); - shared_memory.applet_footer.type = AppletFooterUiType::SwitchProController; + shared_memory->fullkey_color.attribute = ColorAttribute::Ok; + shared_memory->fullkey_color.fullkey = body_colors.fullkey; + shared_memory->battery_level_dual = battery_level.dual.battery_level; + shared_memory->style_tag.fullkey.Assign(1); + shared_memory->device_type.fullkey.Assign(1); + shared_memory->system_properties.is_vertical.Assign(1); + shared_memory->system_properties.use_plus.Assign(1); + shared_memory->system_properties.use_minus.Assign(1); + shared_memory->system_properties.is_charging_joy_dual.Assign( + battery_level.dual.is_charging); + shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::SwitchProController; + shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1); break; case Core::HID::NpadStyleIndex::Handheld: - shared_memory.style_tag.handheld.Assign(1); - shared_memory.device_type.handheld_left.Assign(1); - shared_memory.device_type.handheld_right.Assign(1); - shared_memory.system_properties.is_vertical.Assign(1); - shared_memory.system_properties.use_plus.Assign(1); - shared_memory.system_properties.use_minus.Assign(1); - shared_memory.system_properties.use_directional_buttons.Assign(1); - shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual; - shared_memory.applet_footer.type = AppletFooterUiType::HandheldJoyConLeftJoyConRight; + shared_memory->fullkey_color.attribute = ColorAttribute::Ok; + shared_memory->joycon_color.attribute = ColorAttribute::Ok; + shared_memory->fullkey_color.fullkey = body_colors.fullkey; + shared_memory->joycon_color.left = body_colors.left; + shared_memory->joycon_color.right = body_colors.right; + shared_memory->style_tag.handheld.Assign(1); + shared_memory->device_type.handheld_left.Assign(1); + shared_memory->device_type.handheld_right.Assign(1); + shared_memory->system_properties.is_vertical.Assign(1); + shared_memory->system_properties.use_plus.Assign(1); + shared_memory->system_properties.use_minus.Assign(1); + shared_memory->system_properties.use_directional_buttons.Assign(1); + shared_memory->system_properties.is_charging_joy_dual.Assign( + battery_level.left.is_charging); + shared_memory->system_properties.is_charging_joy_left.Assign( + battery_level.left.is_charging); + shared_memory->system_properties.is_charging_joy_right.Assign( + battery_level.right.is_charging); + shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual; + shared_memory->applet_nfc_xcd.applet_footer.type = + AppletFooterUiType::HandheldJoyConLeftJoyConRight; + shared_memory->sixaxis_handheld_properties.is_newly_assigned.Assign(1); break; case Core::HID::NpadStyleIndex::JoyconDual: - shared_memory.style_tag.joycon_dual.Assign(1); + shared_memory->fullkey_color.attribute = ColorAttribute::Ok; + shared_memory->joycon_color.attribute = ColorAttribute::Ok; + shared_memory->style_tag.joycon_dual.Assign(1); if (controller.is_dual_left_connected) { - shared_memory.device_type.joycon_left.Assign(1); - shared_memory.system_properties.use_minus.Assign(1); + shared_memory->joycon_color.left = body_colors.left; + shared_memory->battery_level_left = battery_level.left.battery_level; + shared_memory->device_type.joycon_left.Assign(1); + shared_memory->system_properties.use_minus.Assign(1); + shared_memory->system_properties.is_charging_joy_left.Assign( + battery_level.left.is_charging); + shared_memory->sixaxis_dual_left_properties.is_newly_assigned.Assign(1); } if (controller.is_dual_right_connected) { - shared_memory.device_type.joycon_right.Assign(1); - shared_memory.system_properties.use_plus.Assign(1); + shared_memory->joycon_color.right = body_colors.right; + shared_memory->battery_level_right = battery_level.right.battery_level; + shared_memory->device_type.joycon_right.Assign(1); + shared_memory->system_properties.use_plus.Assign(1); + shared_memory->system_properties.is_charging_joy_right.Assign( + battery_level.right.is_charging); + shared_memory->sixaxis_dual_right_properties.is_newly_assigned.Assign(1); } - shared_memory.system_properties.use_directional_buttons.Assign(1); - shared_memory.system_properties.is_vertical.Assign(1); - shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual; + shared_memory->system_properties.use_directional_buttons.Assign(1); + shared_memory->system_properties.is_vertical.Assign(1); + shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual; + if (controller.is_dual_left_connected && controller.is_dual_right_connected) { - shared_memory.applet_footer.type = AppletFooterUiType::JoyDual; + shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDual; + shared_memory->fullkey_color.fullkey = body_colors.left; + shared_memory->battery_level_dual = battery_level.left.battery_level; + shared_memory->system_properties.is_charging_joy_dual.Assign( + battery_level.left.is_charging); } else if (controller.is_dual_left_connected) { - shared_memory.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly; + shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly; + shared_memory->fullkey_color.fullkey = body_colors.left; + shared_memory->battery_level_dual = battery_level.left.battery_level; + shared_memory->system_properties.is_charging_joy_dual.Assign( + battery_level.left.is_charging); } else { - shared_memory.applet_footer.type = AppletFooterUiType::JoyDualRightOnly; + shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualRightOnly; + shared_memory->fullkey_color.fullkey = body_colors.right; + shared_memory->battery_level_dual = battery_level.right.battery_level; + shared_memory->system_properties.is_charging_joy_dual.Assign( + battery_level.right.is_charging); } break; case Core::HID::NpadStyleIndex::JoyconLeft: - shared_memory.style_tag.joycon_left.Assign(1); - shared_memory.device_type.joycon_left.Assign(1); - shared_memory.system_properties.is_horizontal.Assign(1); - shared_memory.system_properties.use_minus.Assign(1); - shared_memory.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal; + shared_memory->joycon_color.attribute = ColorAttribute::Ok; + shared_memory->joycon_color.left = body_colors.left; + shared_memory->battery_level_dual = battery_level.left.battery_level; + shared_memory->style_tag.joycon_left.Assign(1); + shared_memory->device_type.joycon_left.Assign(1); + shared_memory->system_properties.is_horizontal.Assign(1); + shared_memory->system_properties.use_minus.Assign(1); + shared_memory->system_properties.is_charging_joy_left.Assign( + battery_level.left.is_charging); + shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal; + shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1); break; case Core::HID::NpadStyleIndex::JoyconRight: - shared_memory.style_tag.joycon_right.Assign(1); - shared_memory.device_type.joycon_right.Assign(1); - shared_memory.system_properties.is_horizontal.Assign(1); - shared_memory.system_properties.use_plus.Assign(1); - shared_memory.applet_footer.type = AppletFooterUiType::JoyRightHorizontal; + shared_memory->joycon_color.attribute = ColorAttribute::Ok; + shared_memory->joycon_color.right = body_colors.right; + shared_memory->battery_level_right = battery_level.right.battery_level; + shared_memory->style_tag.joycon_right.Assign(1); + shared_memory->device_type.joycon_right.Assign(1); + shared_memory->system_properties.is_horizontal.Assign(1); + shared_memory->system_properties.use_plus.Assign(1); + shared_memory->system_properties.is_charging_joy_right.Assign( + battery_level.right.is_charging); + shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyRightHorizontal; + shared_memory->sixaxis_right_properties.is_newly_assigned.Assign(1); break; case Core::HID::NpadStyleIndex::GameCube: - shared_memory.style_tag.gamecube.Assign(1); - shared_memory.device_type.fullkey.Assign(1); - shared_memory.system_properties.is_vertical.Assign(1); - shared_memory.system_properties.use_plus.Assign(1); + shared_memory->style_tag.gamecube.Assign(1); + shared_memory->device_type.fullkey.Assign(1); + shared_memory->system_properties.is_vertical.Assign(1); + shared_memory->system_properties.use_plus.Assign(1); break; case Core::HID::NpadStyleIndex::Pokeball: - shared_memory.style_tag.palma.Assign(1); - shared_memory.device_type.palma.Assign(1); + shared_memory->style_tag.palma.Assign(1); + shared_memory->device_type.palma.Assign(1); + shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1); break; case Core::HID::NpadStyleIndex::NES: - shared_memory.style_tag.lark.Assign(1); - shared_memory.device_type.fullkey.Assign(1); + shared_memory->style_tag.lark.Assign(1); + shared_memory->device_type.fullkey.Assign(1); break; case Core::HID::NpadStyleIndex::SNES: - shared_memory.style_tag.lucia.Assign(1); - shared_memory.device_type.fullkey.Assign(1); - shared_memory.applet_footer.type = AppletFooterUiType::Lucia; + shared_memory->style_tag.lucia.Assign(1); + shared_memory->device_type.fullkey.Assign(1); + shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::Lucia; break; case Core::HID::NpadStyleIndex::N64: - shared_memory.style_tag.lagoon.Assign(1); - shared_memory.device_type.fullkey.Assign(1); - shared_memory.applet_footer.type = AppletFooterUiType::Lagon; + shared_memory->style_tag.lagoon.Assign(1); + shared_memory->device_type.fullkey.Assign(1); + shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::Lagon; break; case Core::HID::NpadStyleIndex::SegaGenesis: - shared_memory.style_tag.lager.Assign(1); - shared_memory.device_type.fullkey.Assign(1); + shared_memory->style_tag.lager.Assign(1); + shared_memory->device_type.fullkey.Assign(1); break; default: break; } - const auto& body_colors = controller.device->GetColors(); - - shared_memory.fullkey_color.attribute = ColorAttribute::Ok; - shared_memory.fullkey_color.fullkey = body_colors.fullkey; - - shared_memory.joycon_color.attribute = ColorAttribute::Ok; - shared_memory.joycon_color.left = body_colors.left; - shared_memory.joycon_color.right = body_colors.right; - - // TODO: Investigate when we should report all batery types - const auto& battery_level = controller.device->GetBattery(); - shared_memory.battery_level_dual = battery_level.dual.battery_level; - shared_memory.battery_level_left = battery_level.left.battery_level; - shared_memory.battery_level_right = battery_level.right.battery_level; - controller.is_connected = true; controller.device->Connect(); SignalStyleSetChangedEvent(npad_id); - WriteEmptyEntry(controller.shared_memory_entry); + WriteEmptyEntry(controller.shared_memory); } void Controller_NPad::OnInit() { @@ -262,23 +348,18 @@ void Controller_NPad::OnInit() { service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i)); } - if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) { - // We want to support all controllers - hid_core.SetSupportedStyleTag({Core::HID::NpadStyleSet::All}); - } - supported_npad_id_types.resize(npad_id_list.size()); std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), npad_id_list.size() * sizeof(Core::HID::NpadIdType)); // Prefill controller buffers for (auto& controller : controller_data) { - auto& npad = controller.shared_memory_entry; - npad.fullkey_color = { + auto* npad = controller.shared_memory; + npad->fullkey_color = { .attribute = ColorAttribute::NoController, .fullkey = {}, }; - npad.joycon_color = { + npad->joycon_color = { .attribute = ColorAttribute::NoController, .left = {}, .right = {}, @@ -288,38 +369,31 @@ void Controller_NPad::OnInit() { WriteEmptyEntry(npad); } } - - // Connect controllers - for (auto& controller : controller_data) { - const auto& device = controller.device; - if (device->IsConnected()) { - AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType()); - } - } } -void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) { +void Controller_NPad::WriteEmptyEntry(NpadInternalState* npad) { NPadGenericState dummy_pad_state{}; NpadGcTriggerState dummy_gc_state{}; - dummy_pad_state.sampling_number = npad.fullkey_lifo.ReadCurrentEntry().sampling_number + 1; - npad.fullkey_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad.handheld_lifo.ReadCurrentEntry().sampling_number + 1; - npad.handheld_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad.joy_dual_lifo.ReadCurrentEntry().sampling_number + 1; - npad.joy_dual_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad.joy_left_lifo.ReadCurrentEntry().sampling_number + 1; - npad.joy_left_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad.joy_right_lifo.ReadCurrentEntry().sampling_number + 1; - npad.joy_right_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad.palma_lifo.ReadCurrentEntry().sampling_number + 1; - npad.palma_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad.system_ext_lifo.ReadCurrentEntry().sampling_number + 1; - npad.system_ext_lifo.WriteNextEntry(dummy_pad_state); - dummy_gc_state.sampling_number = npad.gc_trigger_lifo.ReadCurrentEntry().sampling_number + 1; - npad.gc_trigger_lifo.WriteNextEntry(dummy_gc_state); + dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().sampling_number + 1; + npad->fullkey_lifo.WriteNextEntry(dummy_pad_state); + dummy_pad_state.sampling_number = npad->handheld_lifo.ReadCurrentEntry().sampling_number + 1; + npad->handheld_lifo.WriteNextEntry(dummy_pad_state); + dummy_pad_state.sampling_number = npad->joy_dual_lifo.ReadCurrentEntry().sampling_number + 1; + npad->joy_dual_lifo.WriteNextEntry(dummy_pad_state); + dummy_pad_state.sampling_number = npad->joy_left_lifo.ReadCurrentEntry().sampling_number + 1; + npad->joy_left_lifo.WriteNextEntry(dummy_pad_state); + dummy_pad_state.sampling_number = npad->joy_right_lifo.ReadCurrentEntry().sampling_number + 1; + npad->joy_right_lifo.WriteNextEntry(dummy_pad_state); + dummy_pad_state.sampling_number = npad->palma_lifo.ReadCurrentEntry().sampling_number + 1; + npad->palma_lifo.WriteNextEntry(dummy_pad_state); + dummy_pad_state.sampling_number = npad->system_ext_lifo.ReadCurrentEntry().sampling_number + 1; + npad->system_ext_lifo.WriteNextEntry(dummy_pad_state); + dummy_gc_state.sampling_number = npad->gc_trigger_lifo.ReadCurrentEntry().sampling_number + 1; + npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state); } void Controller_NPad::OnRelease() { + is_controller_initialized = false; for (std::size_t i = 0; i < controller_data.size(); ++i) { auto& controller = controller_data[i]; service_context.CloseEvent(controller.styleset_changed_event); @@ -330,7 +404,7 @@ void Controller_NPad::OnRelease() { } void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; auto& controller = GetControllerFromNpadIdType(npad_id); const auto controller_type = controller.device->GetNpadStyleIndex(); if (!controller.device->IsConnected()) { @@ -381,23 +455,19 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { } } -void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t data_len) { +void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated()) { return; } for (std::size_t i = 0; i < controller_data.size(); ++i) { auto& controller = controller_data[i]; - auto& npad = controller.shared_memory_entry; + auto* npad = controller.shared_memory; const auto& controller_type = controller.device->GetNpadStyleIndex(); if (controller_type == Core::HID::NpadStyleIndex::None || !controller.device->IsConnected()) { - // Refresh shared memory - std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)), - &controller.shared_memory_entry, sizeof(NpadInternalState)); continue; } @@ -412,7 +482,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* libnx_state.connection_status.is_connected.Assign(1); switch (controller_type) { case Core::HID::NpadStyleIndex::None: - UNREACHABLE(); + ASSERT(false); break; case Core::HID::NpadStyleIndex::ProController: case Core::HID::NpadStyleIndex::NES: @@ -425,8 +495,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* libnx_state.connection_status.is_wired.Assign(1); pad_state.sampling_number = - npad.fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; - npad.fullkey_lifo.WriteNextEntry(pad_state); + npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->fullkey_lifo.WriteNextEntry(pad_state); break; case Core::HID::NpadStyleIndex::Handheld: pad_state.connection_status.raw = 0; @@ -443,8 +513,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* libnx_state.connection_status.is_left_wired.Assign(1); libnx_state.connection_status.is_right_wired.Assign(1); pad_state.sampling_number = - npad.handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; - npad.handheld_lifo.WriteNextEntry(pad_state); + npad->handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->handheld_lifo.WriteNextEntry(pad_state); break; case Core::HID::NpadStyleIndex::JoyconDual: pad_state.connection_status.raw = 0; @@ -459,8 +529,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* } pad_state.sampling_number = - npad.joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1; - npad.joy_dual_lifo.WriteNextEntry(pad_state); + npad->joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->joy_dual_lifo.WriteNextEntry(pad_state); break; case Core::HID::NpadStyleIndex::JoyconLeft: pad_state.connection_status.raw = 0; @@ -469,8 +539,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* libnx_state.connection_status.is_left_connected.Assign(1); pad_state.sampling_number = - npad.joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1; - npad.joy_left_lifo.WriteNextEntry(pad_state); + npad->joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->joy_left_lifo.WriteNextEntry(pad_state); break; case Core::HID::NpadStyleIndex::JoyconRight: pad_state.connection_status.raw = 0; @@ -479,8 +549,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* libnx_state.connection_status.is_right_connected.Assign(1); pad_state.sampling_number = - npad.joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1; - npad.joy_right_lifo.WriteNextEntry(pad_state); + npad->joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->joy_right_lifo.WriteNextEntry(pad_state); break; case Core::HID::NpadStyleIndex::GameCube: pad_state.connection_status.raw = 0; @@ -489,18 +559,18 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* libnx_state.connection_status.is_wired.Assign(1); pad_state.sampling_number = - npad.fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; trigger_state.sampling_number = - npad.gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1; - npad.fullkey_lifo.WriteNextEntry(pad_state); - npad.gc_trigger_lifo.WriteNextEntry(trigger_state); + npad->gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->fullkey_lifo.WriteNextEntry(pad_state); + npad->gc_trigger_lifo.WriteNextEntry(trigger_state); break; case Core::HID::NpadStyleIndex::Pokeball: pad_state.connection_status.raw = 0; pad_state.connection_status.is_connected.Assign(1); pad_state.sampling_number = - npad.palma_lifo.ReadCurrentEntry().state.sampling_number + 1; - npad.palma_lifo.WriteNextEntry(pad_state); + npad->palma_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->palma_lifo.WriteNextEntry(pad_state); break; default: break; @@ -509,17 +579,13 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw; libnx_state.l_stick = pad_state.l_stick; libnx_state.r_stick = pad_state.r_stick; - npad.system_ext_lifo.WriteNextEntry(pad_state); + npad->system_ext_lifo.WriteNextEntry(pad_state); press_state |= static_cast<u64>(pad_state.npad_buttons.raw); - - std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)), - &controller.shared_memory_entry, sizeof(NpadInternalState)); } } -void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t data_len) { +void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated()) { return; } @@ -534,7 +600,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing continue; } - auto& npad = controller.shared_memory_entry; + auto* npad = controller.shared_memory; const auto& motion_state = controller.device->GetMotions(); auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state; auto& sixaxis_handheld_state = controller.sixaxis_handheld_state; @@ -543,6 +609,14 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state; auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state; + // Clear previous state + sixaxis_fullkey_state = {}; + sixaxis_handheld_state = {}; + sixaxis_dual_left_state = {}; + sixaxis_dual_right_state = {}; + sixaxis_left_lifo_state = {}; + sixaxis_right_lifo_state = {}; + if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) { controller.sixaxis_at_rest = true; for (std::size_t e = 0; e < motion_state.size(); ++e) { @@ -551,109 +625,116 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing } } + const auto set_motion_state = [&](SixAxisSensorState& state, + const Core::HID::ControllerMotion& hid_state) { + using namespace std::literals::chrono_literals; + static constexpr SixAxisSensorState default_motion_state = { + .delta_time = std::chrono::nanoseconds(5ms).count(), + .accel = {0, 0, -1.0f}, + .orientation = + { + Common::Vec3f{1.0f, 0, 0}, + Common::Vec3f{0, 1.0f, 0}, + Common::Vec3f{0, 0, 1.0f}, + }, + .attribute = {1}, + }; + if (!controller.sixaxis_sensor_enabled) { + state = default_motion_state; + return; + } + if (!Settings::values.motion_enabled.GetValue()) { + state = default_motion_state; + return; + } + state.attribute.is_connected.Assign(1); + state.delta_time = std::chrono::nanoseconds(5ms).count(); + state.accel = hid_state.accel; + state.gyro = hid_state.gyro; + state.rotation = hid_state.rotation; + state.orientation = hid_state.orientation; + }; + switch (controller_type) { case Core::HID::NpadStyleIndex::None: - UNREACHABLE(); + ASSERT(false); break; case Core::HID::NpadStyleIndex::ProController: - sixaxis_fullkey_state.attribute.raw = 0; - if (controller.sixaxis_sensor_enabled) { - sixaxis_fullkey_state.attribute.is_connected.Assign(1); - sixaxis_fullkey_state.accel = motion_state[0].accel; - sixaxis_fullkey_state.gyro = motion_state[0].gyro; - sixaxis_fullkey_state.rotation = motion_state[0].rotation; - sixaxis_fullkey_state.orientation = motion_state[0].orientation; - } + set_motion_state(sixaxis_fullkey_state, motion_state[0]); break; case Core::HID::NpadStyleIndex::Handheld: - sixaxis_handheld_state.attribute.raw = 0; - if (controller.sixaxis_sensor_enabled) { - sixaxis_handheld_state.attribute.is_connected.Assign(1); - sixaxis_handheld_state.accel = motion_state[0].accel; - sixaxis_handheld_state.gyro = motion_state[0].gyro; - sixaxis_handheld_state.rotation = motion_state[0].rotation; - sixaxis_handheld_state.orientation = motion_state[0].orientation; - } + set_motion_state(sixaxis_handheld_state, motion_state[0]); break; case Core::HID::NpadStyleIndex::JoyconDual: - sixaxis_dual_left_state.attribute.raw = 0; - sixaxis_dual_right_state.attribute.raw = 0; - if (controller.sixaxis_sensor_enabled) { - // Set motion for the left joycon - sixaxis_dual_left_state.attribute.is_connected.Assign(1); - sixaxis_dual_left_state.accel = motion_state[0].accel; - sixaxis_dual_left_state.gyro = motion_state[0].gyro; - sixaxis_dual_left_state.rotation = motion_state[0].rotation; - sixaxis_dual_left_state.orientation = motion_state[0].orientation; - } - if (controller.sixaxis_sensor_enabled) { - // Set motion for the right joycon - sixaxis_dual_right_state.attribute.is_connected.Assign(1); - sixaxis_dual_right_state.accel = motion_state[1].accel; - sixaxis_dual_right_state.gyro = motion_state[1].gyro; - sixaxis_dual_right_state.rotation = motion_state[1].rotation; - sixaxis_dual_right_state.orientation = motion_state[1].orientation; - } + set_motion_state(sixaxis_dual_left_state, motion_state[0]); + set_motion_state(sixaxis_dual_right_state, motion_state[1]); break; case Core::HID::NpadStyleIndex::JoyconLeft: - sixaxis_left_lifo_state.attribute.raw = 0; - if (controller.sixaxis_sensor_enabled) { - sixaxis_left_lifo_state.attribute.is_connected.Assign(1); - sixaxis_left_lifo_state.accel = motion_state[0].accel; - sixaxis_left_lifo_state.gyro = motion_state[0].gyro; - sixaxis_left_lifo_state.rotation = motion_state[0].rotation; - sixaxis_left_lifo_state.orientation = motion_state[0].orientation; - } + set_motion_state(sixaxis_left_lifo_state, motion_state[0]); break; case Core::HID::NpadStyleIndex::JoyconRight: - sixaxis_right_lifo_state.attribute.raw = 0; - if (controller.sixaxis_sensor_enabled) { - sixaxis_right_lifo_state.attribute.is_connected.Assign(1); - sixaxis_right_lifo_state.accel = motion_state[1].accel; - sixaxis_right_lifo_state.gyro = motion_state[1].gyro; - sixaxis_right_lifo_state.rotation = motion_state[1].rotation; - sixaxis_right_lifo_state.orientation = motion_state[1].orientation; - } + set_motion_state(sixaxis_right_lifo_state, motion_state[1]); + break; + case Core::HID::NpadStyleIndex::Pokeball: + using namespace std::literals::chrono_literals; + set_motion_state(sixaxis_fullkey_state, motion_state[0]); + sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count(); break; default: break; } sixaxis_fullkey_state.sampling_number = - npad.sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; sixaxis_handheld_state.sampling_number = - npad.sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; sixaxis_dual_left_state.sampling_number = - npad.sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1; sixaxis_dual_right_state.sampling_number = - npad.sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1; sixaxis_left_lifo_state.sampling_number = - npad.sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1; sixaxis_right_lifo_state.sampling_number = - npad.sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1; if (Core::HID::IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) { // This buffer only is updated on handheld on HW - npad.sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state); + npad->sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state); } else { // Handheld doesn't update this buffer on HW - npad.sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state); + npad->sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state); } - npad.sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state); - npad.sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state); - npad.sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state); - npad.sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state); - std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)), - &controller.shared_memory_entry, sizeof(NpadInternalState)); + npad->sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state); + npad->sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state); + npad->sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state); + npad->sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state); } } void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { hid_core.SetSupportedStyleTag(style_set); + + if (is_controller_initialized) { + return; + } + + // Once SetSupportedStyleSet is called controllers are fully initialized + is_controller_initialized = true; + + // Connect all active controllers + for (auto& controller : controller_data) { + const auto& device = controller.device; + if (device->IsConnected()) { + AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType()); + } + } } Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const { + if (!is_controller_initialized) { + return {Core::HID::NpadStyleSet::None}; + } return hid_core.GetSupportedStyleTag(); } @@ -674,6 +755,12 @@ std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const { } void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) { + if (joy_hold_type != NpadJoyHoldType::Horizontal && + joy_hold_type != NpadJoyHoldType::Vertical) { + LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}", + joy_hold_type); + return; + } hold_type = joy_hold_type; } @@ -682,6 +769,11 @@ Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const { } void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { + if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) { + ASSERT_MSG(false, "Activation mode should be always None, Single or Dual"); + return; + } + handheld_activation_mode = activation_mode; } @@ -697,20 +789,21 @@ Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode return communication_mode; } -void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type, - NpadJoyAssignmentMode assignment_mode) { +Result Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, + NpadJoyDeviceType npad_device_type, + NpadJoyAssignmentMode assignment_mode) { if (!IsNpadIdValid(npad_id)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); - return; + return InvalidNpadId; } auto& controller = GetControllerFromNpadIdType(npad_id); - if (controller.shared_memory_entry.assignment_mode != assignment_mode) { - controller.shared_memory_entry.assignment_mode = assignment_mode; + if (controller.shared_memory->assignment_mode != assignment_mode) { + controller.shared_memory->assignment_mode = assignment_mode; } if (!controller.device->IsConnected()) { - return; + return ResultSuccess; } if (assignment_mode == NpadJoyAssignmentMode::Dual) { @@ -719,34 +812,34 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceTy controller.is_dual_left_connected = true; controller.is_dual_right_connected = false; UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true); - return; + return ResultSuccess; } if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) { DisconnectNpad(npad_id); controller.is_dual_left_connected = false; controller.is_dual_right_connected = true; UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true); - return; + return ResultSuccess; } - return; + return ResultSuccess; } // This is for NpadJoyAssignmentMode::Single // Only JoyconDual get affected by this function if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::JoyconDual) { - return; + return ResultSuccess; } if (controller.is_dual_left_connected && !controller.is_dual_right_connected) { DisconnectNpad(npad_id); UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true); - return; + return ResultSuccess; } if (!controller.is_dual_left_connected && controller.is_dual_right_connected) { DisconnectNpad(npad_id); UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true); - return; + return ResultSuccess; } // We have two controllers connected to the same npad_id we need to split them @@ -764,6 +857,7 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceTy controller_2.is_dual_right_connected = false; UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true); } + return ResultSuccess; } bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, @@ -795,11 +889,11 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, const auto now = steady_clock::now(); - // Filter out non-zero vibrations that are within 10ms of each other. + // Filter out non-zero vibrations that are within 15ms of each other. if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) && duration_cast<milliseconds>( now - controller.vibration[device_index].last_vibration_timepoint) < - milliseconds(10)) { + milliseconds(15)) { return false; } @@ -815,7 +909,7 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, void Controller_NPad::VibrateController( const Core::HID::VibrationDeviceHandle& vibration_device_handle, const Core::HID::VibrationValue& vibration_value) { - if (!IsDeviceHandleValid(vibration_device_handle)) { + if (IsDeviceHandleValid(vibration_device_handle).IsError()) { return; } @@ -831,7 +925,7 @@ void Controller_NPad::VibrateController( } if (vibration_device_handle.device_index == Core::HID::DeviceIndex::None) { - UNREACHABLE_MSG("DeviceIndex should never be None!"); + ASSERT_MSG(false, "DeviceIndex should never be None!"); return; } @@ -878,7 +972,7 @@ void Controller_NPad::VibrateControllers( Core::HID::VibrationValue Controller_NPad::GetLastVibration( const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { - if (!IsDeviceHandleValid(vibration_device_handle)) { + if (IsDeviceHandleValid(vibration_device_handle).IsError()) { return {}; } @@ -889,7 +983,7 @@ Core::HID::VibrationValue Controller_NPad::GetLastVibration( void Controller_NPad::InitializeVibrationDevice( const Core::HID::VibrationDeviceHandle& vibration_device_handle) { - if (!IsDeviceHandleValid(vibration_device_handle)) { + if (IsDeviceHandleValid(vibration_device_handle).IsError()) { return; } @@ -916,7 +1010,7 @@ void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { bool Controller_NPad::IsVibrationDeviceMounted( const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { - if (!IsDeviceHandleValid(vibration_device_handle)) { + if (IsDeviceHandleValid(vibration_device_handle).IsError()) { return false; } @@ -959,10 +1053,10 @@ void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, InitNewlyAddedController(npad_id); } -void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { +Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { if (!IsNpadIdValid(npad_id)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); - return; + return InvalidNpadId; } LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id); @@ -973,188 +1067,300 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { controller.vibration[device_idx].device_mounted = false; } - auto& shared_memory_entry = controller.shared_memory_entry; - // Don't reset shared_memory_entry.assignment_mode this value is persistent - shared_memory_entry.style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out - shared_memory_entry.device_type.raw = 0; - shared_memory_entry.system_properties.raw = 0; - shared_memory_entry.button_properties.raw = 0; - shared_memory_entry.battery_level_dual = 0; - shared_memory_entry.battery_level_left = 0; - shared_memory_entry.battery_level_right = 0; - shared_memory_entry.fullkey_color = { + auto* shared_memory = controller.shared_memory; + // Don't reset shared_memory->assignment_mode this value is persistent + shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out + shared_memory->device_type.raw = 0; + shared_memory->system_properties.raw = 0; + shared_memory->button_properties.raw = 0; + shared_memory->sixaxis_fullkey_properties.raw = 0; + shared_memory->sixaxis_handheld_properties.raw = 0; + shared_memory->sixaxis_dual_left_properties.raw = 0; + shared_memory->sixaxis_dual_right_properties.raw = 0; + shared_memory->sixaxis_left_properties.raw = 0; + shared_memory->sixaxis_right_properties.raw = 0; + shared_memory->battery_level_dual = 0; + shared_memory->battery_level_left = 0; + shared_memory->battery_level_right = 0; + shared_memory->fullkey_color = { .attribute = ColorAttribute::NoController, .fullkey = {}, }; - shared_memory_entry.joycon_color = { + shared_memory->joycon_color = { .attribute = ColorAttribute::NoController, .left = {}, .right = {}, }; - shared_memory_entry.applet_footer.type = AppletFooterUiType::None; + shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::None; controller.is_dual_left_connected = true; controller.is_dual_right_connected = true; controller.is_connected = false; controller.device->Disconnect(); SignalStyleSetChangedEvent(npad_id); - WriteEmptyEntry(controller.shared_memory_entry); + WriteEmptyEntry(shared_memory); + return ResultSuccess; } +Result Controller_NPad::SetGyroscopeZeroDriftMode( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, GyroscopeZeroDriftMode drift_mode) { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } -void Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, - GyroscopeZeroDriftMode drift_mode) { - if (!IsDeviceHandleValid(sixaxis_handle)) { - LOG_ERROR(Service_HID, "Invalid handle"); - return; + auto& sixaxis = GetSixaxisState(sixaxis_handle); + sixaxis.gyroscope_zero_drift_mode = drift_mode; + + return ResultSuccess; +} + +Result Controller_NPad::GetGyroscopeZeroDriftMode( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, + GyroscopeZeroDriftMode& drift_mode) const { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; } - auto& controller = GetControllerFromHandle(sixaxis_handle); - controller.gyroscope_zero_drift_mode = drift_mode; + + const auto& sixaxis = GetSixaxisState(sixaxis_handle); + drift_mode = sixaxis.gyroscope_zero_drift_mode; + + return ResultSuccess; } -Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode( - Core::HID::SixAxisSensorHandle sixaxis_handle) const { - if (!IsDeviceHandleValid(sixaxis_handle)) { - LOG_ERROR(Service_HID, "Invalid handle"); - // Return the default value - return GyroscopeZeroDriftMode::Standard; +Result Controller_NPad::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + bool& is_at_rest) const { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; } + const auto& controller = GetControllerFromHandle(sixaxis_handle); - return controller.gyroscope_zero_drift_mode; + is_at_rest = controller.sixaxis_at_rest; + return ResultSuccess; } -bool Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const { - if (!IsDeviceHandleValid(sixaxis_handle)) { - LOG_ERROR(Service_HID, "Invalid handle"); - // Return the default value - return true; +Result Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; } - const auto& controller = GetControllerFromHandle(sixaxis_handle); - return controller.sixaxis_at_rest; + + const auto& sixaxis_properties = GetSixaxisProperties(sixaxis_handle); + is_firmware_available = sixaxis_properties.is_firmware_update_available != 0; + return ResultSuccess; } -void Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, - bool sixaxis_status) { - if (!IsDeviceHandleValid(sixaxis_handle)) { - LOG_ERROR(Service_HID, "Invalid handle"); - return; +Result Controller_NPad::EnableSixAxisSensorUnalteredPassthrough( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + + auto& sixaxis = GetSixaxisState(sixaxis_handle); + sixaxis.unaltered_passtrough = is_enabled; + return ResultSuccess; +} + +Result Controller_NPad::IsSixAxisSensorUnalteredPassthroughEnabled( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + + const auto& sixaxis = GetSixaxisState(sixaxis_handle); + is_enabled = sixaxis.unaltered_passtrough; + return ResultSuccess; +} + +Result Controller_NPad::LoadSixAxisSensorCalibrationParameter( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::SixAxisSensorCalibrationParameter& calibration) const { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + + // TODO: Request this data to the controller. On error return 0xd8ca + const auto& sixaxis = GetSixaxisState(sixaxis_handle); + calibration = sixaxis.calibration; + return ResultSuccess; +} + +Result Controller_NPad::GetSixAxisSensorIcInformation( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::SixAxisSensorIcInformation& ic_information) const { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + + // TODO: Request this data to the controller. On error return 0xd8ca + const auto& sixaxis = GetSixaxisState(sixaxis_handle); + ic_information = sixaxis.ic_information; + return ResultSuccess; +} + +Result Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned( + const Core::HID::SixAxisSensorHandle& sixaxis_handle) { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; } + + auto& sixaxis_properties = GetSixaxisProperties(sixaxis_handle); + sixaxis_properties.is_newly_assigned.Assign(0); + + return ResultSuccess; +} + +Result Controller_NPad::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + bool sixaxis_status) { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + auto& controller = GetControllerFromHandle(sixaxis_handle); controller.sixaxis_sensor_enabled = sixaxis_status; + return ResultSuccess; } -void Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, - bool sixaxis_fusion_status) { - if (!IsDeviceHandleValid(sixaxis_handle)) { - LOG_ERROR(Service_HID, "Invalid handle"); - return; +Result Controller_NPad::IsSixAxisSensorFusionEnabled( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_fusion_enabled) const { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; } - auto& controller = GetControllerFromHandle(sixaxis_handle); - controller.sixaxis_fusion_enabled = sixaxis_fusion_status; + + const auto& sixaxis = GetSixaxisState(sixaxis_handle); + is_fusion_enabled = sixaxis.is_fusion_enabled; + + return ResultSuccess; +} +Result Controller_NPad::SetSixAxisFusionEnabled( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_fusion_enabled) { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; + } + + auto& sixaxis = GetSixaxisState(sixaxis_handle); + sixaxis.is_fusion_enabled = is_fusion_enabled; + + return ResultSuccess; } -void Controller_NPad::SetSixAxisFusionParameters( - Core::HID::SixAxisSensorHandle sixaxis_handle, +Result Controller_NPad::SetSixAxisFusionParameters( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) { - if (!IsDeviceHandleValid(sixaxis_handle)) { - LOG_ERROR(Service_HID, "Invalid handle"); - return; + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; } - auto& controller = GetControllerFromHandle(sixaxis_handle); - controller.sixaxis_fusion = sixaxis_fusion_parameters; -} -Core::HID::SixAxisSensorFusionParameters Controller_NPad::GetSixAxisFusionParameters( - Core::HID::SixAxisSensorHandle sixaxis_handle) { - if (!IsDeviceHandleValid(sixaxis_handle)) { - LOG_ERROR(Service_HID, "Invalid handle"); - // Since these parameters are unknow just return zeros - return {}; + const auto param1 = sixaxis_fusion_parameters.parameter1; + if (param1 < 0.0f || param1 > 1.0f) { + return InvalidSixAxisFusionRange; } - auto& controller = GetControllerFromHandle(sixaxis_handle); - return controller.sixaxis_fusion; + + auto& sixaxis = GetSixaxisState(sixaxis_handle); + sixaxis.fusion = sixaxis_fusion_parameters; + + return ResultSuccess; } -void Controller_NPad::ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle) { - if (!IsDeviceHandleValid(sixaxis_handle)) { - LOG_ERROR(Service_HID, "Invalid handle"); - return; +Result Controller_NPad::GetSixAxisFusionParameters( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::SixAxisSensorFusionParameters& parameters) const { + const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); + if (is_valid.IsError()) { + LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); + return is_valid; } - auto& controller = GetControllerFromHandle(sixaxis_handle); - // Since these parameters are unknow just fill with zeros - controller.sixaxis_fusion = {}; + + const auto& sixaxis = GetSixaxisState(sixaxis_handle); + parameters = sixaxis.fusion; + + return ResultSuccess; } -void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, - Core::HID::NpadIdType npad_id_2) { +Result Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, + Core::HID::NpadIdType npad_id_2) { if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, npad_id_2); - return; + return InvalidNpadId; } auto& controller_1 = GetControllerFromNpadIdType(npad_id_1); auto& controller_2 = GetControllerFromNpadIdType(npad_id_2); - const auto controller_style_1 = controller_1.device->GetNpadStyleIndex(); - const auto controller_style_2 = controller_2.device->GetNpadStyleIndex(); - bool merge_controllers = false; + auto controller_style_1 = controller_1.device->GetNpadStyleIndex(); + auto controller_style_2 = controller_2.device->GetNpadStyleIndex(); + + // Simplify this code by converting dualjoycon with only a side connected to single joycons + if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual) { + if (controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) { + controller_style_1 = Core::HID::NpadStyleIndex::JoyconLeft; + } + if (!controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) { + controller_style_1 = Core::HID::NpadStyleIndex::JoyconRight; + } + } + if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual) { + if (controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) { + controller_style_2 = Core::HID::NpadStyleIndex::JoyconLeft; + } + if (!controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) { + controller_style_2 = Core::HID::NpadStyleIndex::JoyconRight; + } + } - // If the controllers at both npad indices form a pair of left and right joycons, merge them. - // Otherwise, do nothing. + // Invalid merge errors + if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual || + controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual) { + return NpadIsDualJoycon; + } if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft && + controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft) { + return NpadIsSameType; + } + if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight && controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight) { - merge_controllers = true; - } - if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft && - controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight) { - merge_controllers = true; - } - if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual && - controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight && - controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) { - merge_controllers = true; - } - if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual && - controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft && - !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) { - merge_controllers = true; - } - if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual && - controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight && - controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) { - merge_controllers = true; - } - if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual && - controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft && - !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) { - merge_controllers = true; - } - if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual && - controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual && - controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected && - !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) { - merge_controllers = true; - } - if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual && - controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual && - !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected && - controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) { - merge_controllers = true; - } - - if (merge_controllers) { - // Disconnect the joycon at the second id and connect the dual joycon at the first index. - DisconnectNpad(npad_id_2); - controller_1.is_dual_left_connected = true; - controller_1.is_dual_right_connected = true; - AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1); - return; + return NpadIsSameType; + } + + // These exceptions are handled as if they where dual joycon + if (controller_style_1 != Core::HID::NpadStyleIndex::JoyconLeft && + controller_style_1 != Core::HID::NpadStyleIndex::JoyconRight) { + return NpadIsDualJoycon; + } + if (controller_style_2 != Core::HID::NpadStyleIndex::JoyconLeft && + controller_style_2 != Core::HID::NpadStyleIndex::JoyconRight) { + return NpadIsDualJoycon; } - LOG_WARNING(Service_HID, - "Controllers can't be merged npad_id_1:{}, npad_id_2:{}, type_1:{}, type_2:{}, " - "dual_1(left/right):{}/{}, dual_2(left/right):{}/{}", - npad_id_1, npad_id_2, controller_1.device->GetNpadStyleIndex(), - controller_2.device->GetNpadStyleIndex(), controller_1.is_dual_left_connected, - controller_1.is_dual_right_connected, controller_2.is_dual_left_connected, - controller_2.is_dual_right_connected); + + // Disconnect the joycon at the second id and connect the dual joycon at the first index. + DisconnectNpad(npad_id_2); + controller_1.is_dual_left_connected = true; + controller_1.is_dual_right_connected = true; + AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1); + return ResultSuccess; } void Controller_NPad::StartLRAssignmentMode() { @@ -1167,17 +1373,17 @@ void Controller_NPad::StopLRAssignmentMode() { is_in_lr_assignment_mode = false; } -bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, - Core::HID::NpadIdType npad_id_2) { +Result Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, + Core::HID::NpadIdType npad_id_2) { if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, npad_id_2); - return false; + return InvalidNpadId; } if (npad_id_1 == Core::HID::NpadIdType::Handheld || npad_id_2 == Core::HID::NpadIdType::Handheld || npad_id_1 == Core::HID::NpadIdType::Other || npad_id_2 == Core::HID::NpadIdType::Other) { - return true; + return ResultSuccess; } const auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device; const auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device; @@ -1187,46 +1393,49 @@ bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, const auto is_connected_2 = controller_2->IsConnected(); if (!IsControllerSupported(type_index_1) && is_connected_1) { - return false; + return NpadNotConnected; } if (!IsControllerSupported(type_index_2) && is_connected_2) { - return false; + return NpadNotConnected; } UpdateControllerAt(type_index_2, npad_id_1, is_connected_2); UpdateControllerAt(type_index_1, npad_id_2, is_connected_1); - return true; + return ResultSuccess; } -Core::HID::LedPattern Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id) { +Result Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id, + Core::HID::LedPattern& pattern) const { if (!IsNpadIdValid(npad_id)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); - return Core::HID::LedPattern{0, 0, 0, 0}; + return InvalidNpadId; } const auto& controller = GetControllerFromNpadIdType(npad_id).device; - return controller->GetLedPattern(); + pattern = controller->GetLedPattern(); + return ResultSuccess; } -bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled( - Core::HID::NpadIdType npad_id) const { +Result Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, + bool& is_valid) const { if (!IsNpadIdValid(npad_id)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); - // Return the default value - return false; + return InvalidNpadId; } const auto& controller = GetControllerFromNpadIdType(npad_id); - return controller.unintended_home_button_input_protection; + is_valid = controller.unintended_home_button_input_protection; + return ResultSuccess; } -void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, - Core::HID::NpadIdType npad_id) { +Result Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled( + bool is_protection_enabled, Core::HID::NpadIdType npad_id) { if (!IsNpadIdValid(npad_id)) { LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); - return; + return InvalidNpadId; } auto& controller = GetControllerFromNpadIdType(npad_id); controller.unintended_home_button_input_protection = is_protection_enabled; + return ResultSuccess; } void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { @@ -1364,4 +1573,96 @@ const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpa return controller_data[npad_index]; } +Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( + const Core::HID::SixAxisSensorHandle& sixaxis_handle) { + auto& controller = GetControllerFromHandle(sixaxis_handle); + switch (sixaxis_handle.npad_type) { + case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Pokeball: + return controller.shared_memory->sixaxis_fullkey_properties; + case Core::HID::NpadStyleIndex::Handheld: + return controller.shared_memory->sixaxis_handheld_properties; + case Core::HID::NpadStyleIndex::JoyconDual: + if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { + return controller.shared_memory->sixaxis_dual_left_properties; + } + return controller.shared_memory->sixaxis_dual_right_properties; + case Core::HID::NpadStyleIndex::JoyconLeft: + return controller.shared_memory->sixaxis_left_properties; + case Core::HID::NpadStyleIndex::JoyconRight: + return controller.shared_memory->sixaxis_right_properties; + default: + return controller.shared_memory->sixaxis_fullkey_properties; + } +} + +const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( + const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { + const auto& controller = GetControllerFromHandle(sixaxis_handle); + switch (sixaxis_handle.npad_type) { + case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Pokeball: + return controller.shared_memory->sixaxis_fullkey_properties; + case Core::HID::NpadStyleIndex::Handheld: + return controller.shared_memory->sixaxis_handheld_properties; + case Core::HID::NpadStyleIndex::JoyconDual: + if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { + return controller.shared_memory->sixaxis_dual_left_properties; + } + return controller.shared_memory->sixaxis_dual_right_properties; + case Core::HID::NpadStyleIndex::JoyconLeft: + return controller.shared_memory->sixaxis_left_properties; + case Core::HID::NpadStyleIndex::JoyconRight: + return controller.shared_memory->sixaxis_right_properties; + default: + return controller.shared_memory->sixaxis_fullkey_properties; + } +} + +Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState( + const Core::HID::SixAxisSensorHandle& sixaxis_handle) { + auto& controller = GetControllerFromHandle(sixaxis_handle); + switch (sixaxis_handle.npad_type) { + case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Pokeball: + return controller.sixaxis_fullkey; + case Core::HID::NpadStyleIndex::Handheld: + return controller.sixaxis_handheld; + case Core::HID::NpadStyleIndex::JoyconDual: + if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { + return controller.sixaxis_dual_left; + } + return controller.sixaxis_dual_right; + case Core::HID::NpadStyleIndex::JoyconLeft: + return controller.sixaxis_left; + case Core::HID::NpadStyleIndex::JoyconRight: + return controller.sixaxis_right; + default: + return controller.sixaxis_unknown; + } +} + +const Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState( + const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { + const auto& controller = GetControllerFromHandle(sixaxis_handle); + switch (sixaxis_handle.npad_type) { + case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Pokeball: + return controller.sixaxis_fullkey; + case Core::HID::NpadStyleIndex::Handheld: + return controller.sixaxis_handheld; + case Core::HID::NpadStyleIndex::JoyconDual: + if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { + return controller.sixaxis_dual_left; + } + return controller.sixaxis_dual_right; + case Core::HID::NpadStyleIndex::JoyconLeft: + return controller.sixaxis_left; + case Core::HID::NpadStyleIndex::JoyconRight: + return controller.sixaxis_right; + default: + return controller.sixaxis_unknown; + } +} + } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 6b2872bad..1a589cca2 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -10,7 +9,8 @@ #include "common/bit_field.h" #include "common/common_types.h" -#include "common/quaternion.h" +#include "common/vector_math.h" + #include "core/hid/hid_types.h" #include "core/hle/service/hid/controllers/controller_base.h" #include "core/hle/service/hid/ring_lifo.h" @@ -27,13 +27,15 @@ class KReadableEvent; namespace Service::KernelHelpers { class ServiceContext; -} +} // namespace Service::KernelHelpers + +union Result; namespace Service::HID { class Controller_NPad final : public ControllerBase { public: - explicit Controller_NPad(Core::HID::HIDCore& hid_core_, + explicit Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, KernelHelpers::ServiceContext& service_context_); ~Controller_NPad() override; @@ -44,11 +46,10 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; // When the controller is requesting a motion update for the shared memory - void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t size) override; + void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) override; // This is nn::hid::GyroscopeZeroDriftMode enum class GyroscopeZeroDriftMode : u32 { @@ -80,6 +81,7 @@ public: Dual = 0, Single = 1, None = 2, + MaxActivationMode = 3, }; // This is nn::hid::NpadCommunicationMode @@ -106,8 +108,8 @@ public: void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_); NpadCommunicationMode GetNpadCommunicationMode() const; - void SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type, - NpadJoyAssignmentMode assignment_mode); + Result SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type, + NpadJoyAssignmentMode assignment_mode); bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index, const Core::HID::VibrationValue& vibration_value); @@ -140,46 +142,68 @@ public: void UpdateControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id, bool connected); - void DisconnectNpad(Core::HID::NpadIdType npad_id); - - void SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, - GyroscopeZeroDriftMode drift_mode); - GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode( - Core::HID::SixAxisSensorHandle sixaxis_handle) const; - bool IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const; - void SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, bool sixaxis_status); - void SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, - bool sixaxis_fusion_status); - void SetSixAxisFusionParameters( - Core::HID::SixAxisSensorHandle sixaxis_handle, + Result DisconnectNpad(Core::HID::NpadIdType npad_id); + + Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + GyroscopeZeroDriftMode drift_mode); + Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + GyroscopeZeroDriftMode& drift_mode) const; + Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + bool& is_at_rest) const; + Result IsFirmwareUpdateAvailableForSixAxisSensor( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const; + Result EnableSixAxisSensorUnalteredPassthrough( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled); + Result IsSixAxisSensorUnalteredPassthroughEnabled( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const; + Result LoadSixAxisSensorCalibrationParameter( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::SixAxisSensorCalibrationParameter& calibration) const; + Result GetSixAxisSensorIcInformation( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::SixAxisSensorIcInformation& ic_information) const; + Result ResetIsSixAxisSensorDeviceNewlyAssigned( + const Core::HID::SixAxisSensorHandle& sixaxis_handle); + Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + bool sixaxis_status); + Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + bool& is_fusion_enabled) const; + Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + bool is_fusion_enabled); + Result SetSixAxisFusionParameters( + const Core::HID::SixAxisSensorHandle& sixaxis_handle, Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters); - Core::HID::SixAxisSensorFusionParameters GetSixAxisFusionParameters( - Core::HID::SixAxisSensorHandle sixaxis_handle); - void ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle); - Core::HID::LedPattern GetLedPattern(Core::HID::NpadIdType npad_id); - bool IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id) const; - void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, - Core::HID::NpadIdType npad_id); + Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle, + Core::HID::SixAxisSensorFusionParameters& parameters) const; + Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const; + Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, + bool& is_enabled) const; + Result SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, + Core::HID::NpadIdType npad_id); void SetAnalogStickUseCenterClamp(bool use_center_clamp); void ClearAllConnectedControllers(); void DisconnectAllConnectedControllers(); void ConnectAllDisconnectedControllers(); void ClearAllControllers(); - void MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2); + Result MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, + Core::HID::NpadIdType npad_id_2); void StartLRAssignmentMode(); void StopLRAssignmentMode(); - bool SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2); + Result SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2); // Logical OR for all buttons presses on all controllers // Specifically for cheat engine and other features. Core::HID::NpadButton GetAndResetPressState(); static bool IsNpadIdValid(Core::HID::NpadIdType npad_id); - static bool IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle); - static bool IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle); + static Result IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle); + static Result VerifyValidSixAxisSensorHandle( + const Core::HID::SixAxisSensorHandle& device_handle); private: + static constexpr std::size_t NPAD_COUNT = 10; + // This is nn::hid::detail::ColorAttribute enum class ColorAttribute : u32 { Ok = 0, @@ -190,16 +214,16 @@ private: // This is nn::hid::detail::NpadFullKeyColorState struct NpadFullKeyColorState { - ColorAttribute attribute; - Core::HID::NpadControllerColor fullkey; + ColorAttribute attribute{ColorAttribute::NoController}; + Core::HID::NpadControllerColor fullkey{}; }; static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size"); // This is nn::hid::detail::NpadJoyColorState struct NpadJoyColorState { - ColorAttribute attribute; - Core::HID::NpadControllerColor left; - Core::HID::NpadControllerColor right; + ColorAttribute attribute{ColorAttribute::NoController}; + Core::HID::NpadControllerColor left{}; + Core::HID::NpadControllerColor right{}; }; static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size"); @@ -225,11 +249,11 @@ private: // This is nn::hid::NpadPalmaState // This is nn::hid::NpadSystemExtState struct NPadGenericState { - s64_le sampling_number; - Core::HID::NpadButtonState npad_buttons; - Core::HID::AnalogStickState l_stick; - Core::HID::AnalogStickState r_stick; - NpadAttribute connection_status; + s64_le sampling_number{}; + Core::HID::NpadButtonState npad_buttons{}; + Core::HID::AnalogStickState l_stick{}; + Core::HID::AnalogStickState r_stick{}; + NpadAttribute connection_status{}; INSERT_PADDING_BYTES(4); // Reserved }; static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); @@ -252,7 +276,7 @@ private: Common::Vec3f gyro{}; Common::Vec3f rotation{}; std::array<Common::Vec3f, 3> orientation{}; - SixAxisSensorAttribute attribute; + SixAxisSensorAttribute attribute{}; INSERT_PADDING_BYTES(4); // Reserved }; static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size"); @@ -324,11 +348,11 @@ private: // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl struct NfcXcdDeviceHandleStateImpl { - u64 handle; - bool is_available; - bool is_activated; + u64 handle{}; + bool is_available{}; + bool is_activated{}; INSERT_PADDING_BYTES(0x6); // Reserved - u64 sampling_number; + u64 sampling_number{}; }; static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, "NfcXcdDeviceHandleStateImpl is an invalid size"); @@ -365,8 +389,8 @@ private: }; struct AppletFooterUi { - AppletFooterUiAttributes attributes; - AppletFooterUiType type; + AppletFooterUiAttributes attributes{}; + AppletFooterUiType type{AppletFooterUiType::None}; INSERT_PADDING_BYTES(0x5B); // Reserved }; static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size"); @@ -401,46 +425,54 @@ private: U, }; + struct AppletNfcXcd { + union { + AppletFooterUi applet_footer{}; + Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo; + }; + }; + // This is nn::hid::detail::NpadInternalState struct NpadInternalState { - Core::HID::NpadStyleTag style_tag; - NpadJoyAssignmentMode assignment_mode; - NpadFullKeyColorState fullkey_color; - NpadJoyColorState joycon_color; - Lifo<NPadGenericState, hid_entry_count> fullkey_lifo; - Lifo<NPadGenericState, hid_entry_count> handheld_lifo; - Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo; - Lifo<NPadGenericState, hid_entry_count> joy_left_lifo; - Lifo<NPadGenericState, hid_entry_count> joy_right_lifo; - Lifo<NPadGenericState, hid_entry_count> palma_lifo; - Lifo<NPadGenericState, hid_entry_count> system_ext_lifo; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo; - Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo; - DeviceType device_type; + Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None}; + NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual}; + NpadFullKeyColorState fullkey_color{}; + NpadJoyColorState joycon_color{}; + Lifo<NPadGenericState, hid_entry_count> fullkey_lifo{}; + Lifo<NPadGenericState, hid_entry_count> handheld_lifo{}; + Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo{}; + Lifo<NPadGenericState, hid_entry_count> joy_left_lifo{}; + Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{}; + Lifo<NPadGenericState, hid_entry_count> palma_lifo{}; + Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{}; + Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{}; + Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{}; + Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{}; + Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{}; + Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{}; + Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{}; + DeviceType device_type{}; INSERT_PADDING_BYTES(0x4); // Reserved - NPadSystemProperties system_properties; - NpadSystemButtonProperties button_properties; - Core::HID::NpadBatteryLevel battery_level_dual; - Core::HID::NpadBatteryLevel battery_level_left; - Core::HID::NpadBatteryLevel battery_level_right; - union { - Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo{}; - AppletFooterUi applet_footer; - }; + NPadSystemProperties system_properties{}; + NpadSystemButtonProperties button_properties{}; + Core::HID::NpadBatteryLevel battery_level_dual{}; + Core::HID::NpadBatteryLevel battery_level_left{}; + Core::HID::NpadBatteryLevel battery_level_right{}; + AppletNfcXcd applet_nfc_xcd{}; INSERT_PADDING_BYTES(0x20); // Unknown - Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo; - NpadLarkType lark_type_l_and_main; - NpadLarkType lark_type_r; - NpadLuciaType lucia_type; - NpadLagonType lagon_type; - NpadLagerType lager_type; - // FW 13.x Investigate there is some sort of bitflag related to joycons - INSERT_PADDING_BYTES(0x4); - INSERT_PADDING_BYTES(0xc08); // Unknown + Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo{}; + NpadLarkType lark_type_l_and_main{}; + NpadLarkType lark_type_r{}; + NpadLuciaType lucia_type{}; + NpadLagonType lagon_type{}; + NpadLagerType lager_type{}; + Core::HID::SixAxisSensorProperties sixaxis_fullkey_properties; + Core::HID::SixAxisSensorProperties sixaxis_handheld_properties; + Core::HID::SixAxisSensorProperties sixaxis_dual_left_properties; + Core::HID::SixAxisSensorProperties sixaxis_dual_right_properties; + Core::HID::SixAxisSensorProperties sixaxis_left_properties; + Core::HID::SixAxisSensorProperties sixaxis_right_properties; + INSERT_PADDING_BYTES(0xc06); // Unknown }; static_assert(sizeof(NpadInternalState) == 0x5000, "NpadInternalState is an invalid size"); @@ -450,10 +482,19 @@ private: std::chrono::steady_clock::time_point last_vibration_timepoint{}; }; + struct SixaxisParameters { + bool is_fusion_enabled{true}; + bool unaltered_passtrough{false}; + Core::HID::SixAxisSensorFusionParameters fusion{}; + Core::HID::SixAxisSensorCalibrationParameter calibration{}; + Core::HID::SixAxisSensorIcInformation ic_information{}; + GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; + }; + struct NpadControllerData { - Core::HID::EmulatedController* device; Kernel::KEvent* styleset_changed_event{}; - NpadInternalState shared_memory_entry{}; + NpadInternalState* shared_memory = nullptr; + Core::HID::EmulatedController* device = nullptr; std::array<VibrationData, 2> vibration{}; bool unintended_home_button_input_protection{}; @@ -466,9 +507,13 @@ private: // Motion parameters bool sixaxis_at_rest{true}; bool sixaxis_sensor_enabled{true}; - bool sixaxis_fusion_enabled{false}; - Core::HID::SixAxisSensorFusionParameters sixaxis_fusion{}; - GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; + SixaxisParameters sixaxis_fullkey{}; + SixaxisParameters sixaxis_handheld{}; + SixaxisParameters sixaxis_dual_left{}; + SixaxisParameters sixaxis_dual_right{}; + SixaxisParameters sixaxis_left{}; + SixaxisParameters sixaxis_right{}; + SixaxisParameters sixaxis_unknown{}; // Current pad state NPadGenericState npad_pad_state{}; @@ -480,14 +525,14 @@ private: SixAxisSensorState sixaxis_dual_right_state{}; SixAxisSensorState sixaxis_left_lifo_state{}; SixAxisSensorState sixaxis_right_lifo_state{}; - int callback_key; + int callback_key{}; }; void ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx); void InitNewlyAddedController(Core::HID::NpadIdType npad_id); bool IsControllerSupported(Core::HID::NpadStyleIndex controller) const; void RequestPadStateUpdate(Core::HID::NpadIdType npad_id); - void WriteEmptyEntry(NpadInternalState& npad); + void WriteEmptyEntry(NpadInternalState* npad); NpadControllerData& GetControllerFromHandle( const Core::HID::SixAxisSensorHandle& device_handle); @@ -500,9 +545,17 @@ private: NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id); const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const; + Core::HID::SixAxisSensorProperties& GetSixaxisProperties( + const Core::HID::SixAxisSensorHandle& device_handle); + const Core::HID::SixAxisSensorProperties& GetSixaxisProperties( + const Core::HID::SixAxisSensorHandle& device_handle) const; + SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle); + const SixaxisParameters& GetSixaxisState( + const Core::HID::SixAxisSensorHandle& device_handle) const; + std::atomic<u64> press_state{}; - std::array<NpadControllerData, 10> controller_data{}; + std::array<NpadControllerData, NPAD_COUNT> controller_data{}; KernelHelpers::ServiceContext& service_context; std::mutex mutex; std::vector<Core::HID::NpadIdType> supported_npad_id_types{}; @@ -510,7 +563,8 @@ private: NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; NpadCommunicationMode communication_mode{NpadCommunicationMode::Default}; bool permit_vibration_session_enabled{false}; - bool analog_stick_use_center_clamp{}; + bool analog_stick_use_center_clamp{false}; bool is_in_lr_assignment_mode{false}; + bool is_controller_initialized{false}; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/palma.cpp b/src/core/hle/service/hid/controllers/palma.cpp new file mode 100644 index 000000000..575d4e626 --- /dev/null +++ b/src/core/hle/service/hid/controllers/palma.cpp @@ -0,0 +1,229 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core_timing.h" +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hid/hid_types.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/service/hid/controllers/palma.h" +#include "core/hle/service/kernel_helpers.h" + +namespace Service::HID { + +Controller_Palma::Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, + KernelHelpers::ServiceContext& service_context_) + : ControllerBase{hid_core_}, service_context{service_context_} { + controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); + operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent"); +} + +Controller_Palma::~Controller_Palma() = default; + +void Controller_Palma::OnInit() {} + +void Controller_Palma::OnRelease() {} + +void Controller_Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) { + if (!IsControllerActivated()) { + return; + } +} + +Result Controller_Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, + PalmaConnectionHandle& handle) { + active_handle.npad_id = npad_id; + handle = active_handle; + return ResultSuccess; +} + +Result Controller_Palma::InitializePalma(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + ActivateController(); + return ResultSuccess; +} + +Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent( + const PalmaConnectionHandle& handle) const { + if (handle.npad_id != active_handle.npad_id) { + LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id); + } + return operation_complete_event->GetReadableEvent(); +} + +Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle, + PalmaOperationType& operation_type, + PalmaOperationData& data) const { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation_type = operation.operation; + data = operation.data; + return ResultSuccess; +} + +Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, + u64 palma_activity) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::PlayActivity; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, + PalmaFrModeType fr_mode_) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + fr_mode = fr_mode_; + return ResultSuccess; +} + +Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::ReadStep; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +Result Controller_Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + return ResultSuccess; +} + +Result Controller_Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + return ResultSuccess; +} + +void Controller_Palma::ReadPalmaApplicationSection() {} + +void Controller_Palma::WritePalmaApplicationSection() {} + +Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::ReadUniqueCode; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::SetUniqueCodeInvalid; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +void Controller_Palma::WritePalmaActivityEntry() {} + +Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, + u64 unknown) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::WriteRgbLedPatternEntry; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, + u8* t_mem, u64 size) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::WriteWaveEntry; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle, + s32 database_id_version_) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + database_id_version = database_id_version_; + operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion; + operation.result = PalmaResultSuccess; + operation.data[0] = {}; + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +Result Controller_Palma::GetPalmaDataBaseIdentificationVersion( + const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion; + operation.result = PalmaResultSuccess; + operation.data = {}; + operation.data[0] = static_cast<u8>(database_id_version); + operation_complete_event->GetWritableEvent().Signal(); + return ResultSuccess; +} + +void Controller_Palma::SuspendPalmaFeature() {} + +Result Controller_Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + return operation.result; +} +void Controller_Palma::ReadPalmaPlayLog() {} + +void Controller_Palma::ResetPalmaPlayLog() {} + +void Controller_Palma::SetIsPalmaAllConnectable(bool is_all_connectable) { + // If true controllers are able to be paired + is_connectable = is_all_connectable; +} + +void Controller_Palma::SetIsPalmaPairedConnectable() {} + +Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) { + if (handle.npad_id != active_handle.npad_id) { + return InvalidPalmaHandle; + } + // TODO: Do something + return ResultSuccess; +} + +void Controller_Palma::SetPalmaBoostMode(bool boost_mode) {} + +void Controller_Palma::CancelWritePalmaWaveEntry() {} + +void Controller_Palma::EnablePalmaBoostMode() {} + +void Controller_Palma::GetPalmaBluetoothAddress() {} + +void Controller_Palma::SetDisallowedPalmaConnection() {} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/palma.h b/src/core/hle/service/hid/controllers/palma.h new file mode 100644 index 000000000..1d7fc94e1 --- /dev/null +++ b/src/core/hle/service/hid/controllers/palma.h @@ -0,0 +1,163 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/service/hid/controllers/controller_base.h" +#include "core/hle/service/hid/errors.h" + +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + +namespace Service::KernelHelpers { +class ServiceContext; +} + +namespace Core::HID { +class EmulatedController; +} // namespace Core::HID + +namespace Service::HID { +class Controller_Palma final : public ControllerBase { +public: + using PalmaOperationData = std::array<u8, 0x140>; + + // This is nn::hid::PalmaOperationType + enum class PalmaOperationType { + PlayActivity, + SetFrModeType, + ReadStep, + EnableStep, + ResetStep, + ReadApplicationSection, + WriteApplicationSection, + ReadUniqueCode, + SetUniqueCodeInvalid, + WriteActivityEntry, + WriteRgbLedPatternEntry, + WriteWaveEntry, + ReadDataBaseIdentificationVersion, + WriteDataBaseIdentificationVersion, + SuspendFeature, + ReadPlayLog, + ResetPlayLog, + }; + + // This is nn::hid::PalmaWaveSet + enum class PalmaWaveSet : u64 { + Small, + Medium, + Large, + }; + + // This is nn::hid::PalmaFrModeType + enum class PalmaFrModeType : u64 { + Off, + B01, + B02, + B03, + Downloaded, + }; + + // This is nn::hid::PalmaFeature + enum class PalmaFeature : u64 { + FrMode, + RumbleFeedback, + Step, + MuteSwitch, + }; + + // This is nn::hid::PalmaOperationInfo + struct PalmaOperationInfo { + PalmaOperationType operation{}; + Result result{PalmaResultSuccess}; + PalmaOperationData data{}; + }; + static_assert(sizeof(PalmaOperationInfo) == 0x148, "PalmaOperationInfo is an invalid size"); + + // This is nn::hid::PalmaActivityEntry + struct PalmaActivityEntry { + u32 rgb_led_pattern_index; + INSERT_PADDING_BYTES(2); + PalmaWaveSet wave_set; + u32 wave_index; + INSERT_PADDING_BYTES(12); + }; + static_assert(sizeof(PalmaActivityEntry) == 0x20, "PalmaActivityEntry is an invalid size"); + + struct PalmaConnectionHandle { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_BYTES(4); // Unknown + }; + static_assert(sizeof(PalmaConnectionHandle) == 0x8, + "PalmaConnectionHandle has incorrect size."); + + explicit Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, + KernelHelpers::ServiceContext& service_context_); + ~Controller_Palma() override; + + // Called when the controller is initialized + void OnInit() override; + + // When the controller is released + void OnRelease() override; + + // When the controller is requesting an update for the shared memory + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; + + Result GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, PalmaConnectionHandle& handle); + Result InitializePalma(const PalmaConnectionHandle& handle); + Kernel::KReadableEvent& AcquirePalmaOperationCompleteEvent( + const PalmaConnectionHandle& handle) const; + Result GetPalmaOperationInfo(const PalmaConnectionHandle& handle, + PalmaOperationType& operation_type, + PalmaOperationData& data) const; + Result PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity); + Result SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_); + Result ReadPalmaStep(const PalmaConnectionHandle& handle); + Result EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled); + Result ResetPalmaStep(const PalmaConnectionHandle& handle); + Result ReadPalmaUniqueCode(const PalmaConnectionHandle& handle); + Result SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle); + Result WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown); + Result WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, u8* t_mem, + u64 size); + Result SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle, + s32 database_id_version_); + Result GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle); + Result GetPalmaOperationResult(const PalmaConnectionHandle& handle) const; + void SetIsPalmaAllConnectable(bool is_all_connectable); + Result PairPalma(const PalmaConnectionHandle& handle); + void SetPalmaBoostMode(bool boost_mode); + +private: + void ReadPalmaApplicationSection(); + void WritePalmaApplicationSection(); + void WritePalmaActivityEntry(); + void SuspendPalmaFeature(); + void ReadPalmaPlayLog(); + void ResetPalmaPlayLog(); + void SetIsPalmaPairedConnectable(); + void CancelWritePalmaWaveEntry(); + void EnablePalmaBoostMode(); + void GetPalmaBluetoothAddress(); + void SetDisallowedPalmaConnection(); + + bool is_connectable{}; + s32 database_id_version{}; + PalmaOperationInfo operation{}; + PalmaFrModeType fr_mode{}; + PalmaConnectionHandle active_handle{}; + + Core::HID::EmulatedController* controller; + + Kernel::KEvent* operation_complete_event; + KernelHelpers::ServiceContext& service_context; +}; + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp index b7d7a5756..df9ee0c3f 100644 --- a/src/core/hle/service/hid/controllers/stubbed.cpp +++ b/src/core/hle/service/hid/controllers/stubbed.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include "common/common_types.h" @@ -10,15 +9,18 @@ namespace Service::HID { -Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {} +Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) + : ControllerBase{hid_core_} { + raw_shared_memory = raw_shared_memory_; +} + Controller_Stubbed::~Controller_Stubbed() = default; void Controller_Stubbed::OnInit() {} void Controller_Stubbed::OnRelease() {} -void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t size) { +void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!smart_update) { return; } @@ -29,7 +31,7 @@ void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u header.entry_count = 0; header.last_entry_index = 0; - std::memcpy(data + common_offset, &header, sizeof(CommonHeader)); + std::memcpy(raw_shared_memory + common_offset, &header, sizeof(CommonHeader)); } void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) { diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h index 0044a4efa..1483a968e 100644 --- a/src/core/hle/service/hid/controllers/stubbed.h +++ b/src/core/hle/service/hid/controllers/stubbed.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -10,7 +9,7 @@ namespace Service::HID { class Controller_Stubbed final : public ControllerBase { public: - explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_); + explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); ~Controller_Stubbed() override; // Called when the controller is initialized @@ -20,19 +19,20 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; void SetCommonHeaderOffset(std::size_t off); private: struct CommonHeader { - s64 timestamp; - s64 total_entry_count; - s64 last_entry_index; - s64 entry_count; + s64 timestamp{}; + s64 total_entry_count{}; + s64 last_entry_index{}; + s64 entry_count{}; }; static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size"); + u8* raw_shared_memory = nullptr; bool smart_update{}; std::size_t common_offset{}; }; diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index 48978e5c6..1da8d3eb0 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp @@ -1,11 +1,9 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <cstring> #include "common/common_types.h" -#include "common/logging/log.h" #include "common/settings.h" #include "core/core.h" #include "core/core_timing.h" @@ -17,8 +15,13 @@ namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; -Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_) +Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_, + u8* raw_shared_memory_) : ControllerBase{hid_core_} { + static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size, + "TouchSharedMemory is bigger than the shared memory"); + shared_memory = std::construct_at( + reinterpret_cast<TouchSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); console = hid_core.GetEmulatedConsole(); } @@ -28,14 +31,12 @@ void Controller_Touchscreen::OnInit() {} void Controller_Touchscreen::OnRelease() {} -void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t size) { - touch_screen_lifo.timestamp = core_timing.GetCPUTicks(); +void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { + shared_memory->touch_screen_lifo.timestamp = core_timing.GetCPUTicks(); if (!IsControllerActivated()) { - touch_screen_lifo.buffer_count = 0; - touch_screen_lifo.buffer_tail = 0; - std::memcpy(data, &touch_screen_lifo, sizeof(touch_screen_lifo)); + shared_memory->touch_screen_lifo.buffer_count = 0; + shared_memory->touch_screen_lifo.buffer_tail = 0; return; } @@ -43,7 +44,6 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin for (std::size_t id = 0; id < MAX_FINGERS; id++) { const auto& current_touch = touch_status[id]; auto& finger = fingers[id]; - finger.position = current_touch.position; finger.id = current_touch.id; if (finger.attribute.start_touch) { @@ -60,13 +60,18 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin if (!finger.pressed && current_touch.pressed) { finger.attribute.start_touch.Assign(1); finger.pressed = true; + finger.position = current_touch.position; continue; } if (finger.pressed && !current_touch.pressed) { finger.attribute.raw = 0; finger.attribute.end_touch.Assign(1); + continue; } + + // Only update position if touch is not on a special frame + finger.position = current_touch.position; } std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers; @@ -76,7 +81,7 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); const u64 tick = core_timing.GetCPUTicks(); - const auto& last_entry = touch_screen_lifo.ReadCurrentEntry().state; + const auto& last_entry = shared_memory->touch_screen_lifo.ReadCurrentEntry().state; next_state.sampling_number = last_entry.sampling_number + 1; next_state.entry_count = static_cast<s32>(active_fingers_count); @@ -108,8 +113,7 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin } } - touch_screen_lifo.WriteNextEntry(next_state); - std::memcpy(data + SHARED_MEMORY_OFFSET, &touch_screen_lifo, sizeof(touch_screen_lifo)); + shared_memory->touch_screen_lifo.WriteNextEntry(next_state); } } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h index 708dde4f0..e57a3a80e 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.h +++ b/src/core/hle/service/hid/controllers/touchscreen.h @@ -1,14 +1,10 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" -#include "common/point.h" -#include "common/swap.h" #include "core/hid/hid_types.h" #include "core/hle/service/hid/controllers/controller_base.h" #include "core/hle/service/hid/ring_lifo.h" @@ -29,14 +25,14 @@ public: // This is nn::hid::TouchScreenConfigurationForNx struct TouchScreenConfigurationForNx { - TouchScreenModeForNx mode; + TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting}; INSERT_PADDING_BYTES_NOINIT(0x7); INSERT_PADDING_BYTES_NOINIT(0xF); // Reserved }; static_assert(sizeof(TouchScreenConfigurationForNx) == 0x17, "TouchScreenConfigurationForNx is an invalid size"); - explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_); + explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); ~Controller_Touchscreen() override; // Called when the controller is initialized @@ -46,26 +42,32 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; private: static constexpr std::size_t MAX_FINGERS = 16; // This is nn::hid::TouchScreenState struct TouchScreenState { - s64 sampling_number; - s32 entry_count; + s64 sampling_number{}; + s32 entry_count{}; INSERT_PADDING_BYTES(4); // Reserved - std::array<Core::HID::TouchState, MAX_FINGERS> states; + std::array<Core::HID::TouchState, MAX_FINGERS> states{}; }; static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size"); - // This is nn::hid::detail::TouchScreenLifo - Lifo<TouchScreenState, hid_entry_count> touch_screen_lifo{}; - static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size"); + struct TouchSharedMemory { + // This is nn::hid::detail::TouchScreenLifo + Lifo<TouchScreenState, hid_entry_count> touch_screen_lifo{}; + static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size"); + INSERT_PADDING_WORDS(0xF2); + }; + static_assert(sizeof(TouchSharedMemory) == 0x3000, "TouchSharedMemory is an invalid size"); + TouchScreenState next_state{}; + TouchSharedMemory* shared_memory = nullptr; + Core::HID::EmulatedConsole* console = nullptr; - std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers; - Core::HID::EmulatedConsole* console; + std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{}; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp index e4da16466..62119e2c5 100644 --- a/src/core/hle/service/hid/controllers/xpad.cpp +++ b/src/core/hle/service/hid/controllers/xpad.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include "common/common_types.h" @@ -11,28 +10,31 @@ namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; -Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {} +Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) + : ControllerBase{hid_core_} { + static_assert(SHARED_MEMORY_OFFSET + sizeof(XpadSharedMemory) < shared_memory_size, + "XpadSharedMemory is bigger than the shared memory"); + shared_memory = std::construct_at( + reinterpret_cast<XpadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); +} Controller_XPad::~Controller_XPad() = default; void Controller_XPad::OnInit() {} void Controller_XPad::OnRelease() {} -void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, - std::size_t size) { +void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { if (!IsControllerActivated()) { - basic_xpad_lifo.buffer_count = 0; - basic_xpad_lifo.buffer_tail = 0; - std::memcpy(data + SHARED_MEMORY_OFFSET, &basic_xpad_lifo, sizeof(basic_xpad_lifo)); + shared_memory->basic_xpad_lifo.buffer_count = 0; + shared_memory->basic_xpad_lifo.buffer_tail = 0; return; } - const auto& last_entry = basic_xpad_lifo.ReadCurrentEntry().state; + const auto& last_entry = shared_memory->basic_xpad_lifo.ReadCurrentEntry().state; next_state.sampling_number = last_entry.sampling_number + 1; // TODO(ogniK): Update xpad states - basic_xpad_lifo.WriteNextEntry(next_state); - std::memcpy(data + SHARED_MEMORY_OFFSET, &basic_xpad_lifo, sizeof(basic_xpad_lifo)); + shared_memory->basic_xpad_lifo.WriteNextEntry(next_state); } } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h index ba8db8d9d..d01dee5fc 100644 --- a/src/core/hle/service/hid/controllers/xpad.h +++ b/src/core/hle/service/hid/controllers/xpad.h @@ -1,13 +1,10 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include "common/bit_field.h" -#include "common/common_funcs.h" #include "common/common_types.h" -#include "common/swap.h" #include "core/hid/hid_types.h" #include "core/hle/service/hid/controllers/controller_base.h" #include "core/hle/service/hid/ring_lifo.h" @@ -15,7 +12,7 @@ namespace Service::HID { class Controller_XPad final : public ControllerBase { public: - explicit Controller_XPad(Core::HID::HIDCore& hid_core_); + explicit Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); ~Controller_XPad() override; // Called when the controller is initialized @@ -25,7 +22,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; private: // This is nn::hid::BasicXpadAttributeSet @@ -93,17 +90,23 @@ private: // This is nn::hid::detail::BasicXpadState struct BasicXpadState { - s64 sampling_number; - BasicXpadAttributeSet attributes; - BasicXpadButtonSet pad_states; - Core::HID::AnalogStickState l_stick; - Core::HID::AnalogStickState r_stick; + s64 sampling_number{}; + BasicXpadAttributeSet attributes{}; + BasicXpadButtonSet pad_states{}; + Core::HID::AnalogStickState l_stick{}; + Core::HID::AnalogStickState r_stick{}; }; static_assert(sizeof(BasicXpadState) == 0x20, "BasicXpadState is an invalid size"); - // This is nn::hid::detail::BasicXpadLifo - Lifo<BasicXpadState, hid_entry_count> basic_xpad_lifo{}; - static_assert(sizeof(basic_xpad_lifo) == 0x2C8, "basic_xpad_lifo is an invalid size"); + struct XpadSharedMemory { + // This is nn::hid::detail::BasicXpadLifo + Lifo<BasicXpadState, hid_entry_count> basic_xpad_lifo{}; + static_assert(sizeof(basic_xpad_lifo) == 0x2C8, "basic_xpad_lifo is an invalid size"); + INSERT_PADDING_WORDS(0x4E); + }; + static_assert(sizeof(XpadSharedMemory) == 0x400, "XpadSharedMemory is an invalid size"); + BasicXpadState next_state{}; + XpadSharedMemory* shared_memory = nullptr; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h index 3583642e7..76208e9a4 100644 --- a/src/core/hle/service/hid/errors.h +++ b/src/core/hle/service/hid/errors.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,6 +7,24 @@ namespace Service::HID { -constexpr ResultCode ERR_NPAD_NOT_CONNECTED{ErrorModule::HID, 710}; +constexpr Result PalmaResultSuccess{ErrorModule::HID, 0}; +constexpr Result NpadInvalidHandle{ErrorModule::HID, 100}; +constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107}; +constexpr Result VibrationInvalidStyleIndex{ErrorModule::HID, 122}; +constexpr Result VibrationInvalidNpadId{ErrorModule::HID, 123}; +constexpr Result VibrationDeviceIndexOutOfRange{ErrorModule::HID, 124}; +constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423}; +constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601}; +constexpr Result NpadIsSameType{ErrorModule::HID, 602}; +constexpr Result InvalidNpadId{ErrorModule::HID, 709}; +constexpr Result NpadNotConnected{ErrorModule::HID, 710}; +constexpr Result InvalidPalmaHandle{ErrorModule::HID, 3302}; } // namespace Service::HID + +namespace Service::IRS { + +constexpr Result InvalidProcessorState{ErrorModule::Irsensor, 78}; +constexpr Result InvalidIrCameraHandle{ErrorModule::Irsensor, 204}; + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index d9202ea6c..46bad7871 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> #include "common/common_types.h" @@ -16,6 +15,7 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/service/hid/errors.h" #include "core/hle/service/hid/hid.h" +#include "core/hle/service/hid/hidbus.h" #include "core/hle/service/hid/irs.h" #include "core/hle/service/hid/xcd.h" #include "core/memory.h" @@ -27,6 +27,7 @@ #include "core/hle/service/hid/controllers/keyboard.h" #include "core/hle/service/hid/controllers/mouse.h" #include "core/hle/service/hid/controllers/npad.h" +#include "core/hle/service/hid/controllers/palma.h" #include "core/hle/service/hid/controllers/stubbed.h" #include "core/hle/service/hid/controllers/touchscreen.h" #include "core/hle/service/hid/controllers/xpad.h" @@ -35,11 +36,10 @@ namespace Service::HID { // Updating period for each HID device. // Period time is obtained by measuring the number of samples in a second on HW using a homebrew -constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 250Hz) +// Correct pad_update_ns is 4ms this is overclocked to lower input lag +constexpr auto pad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz) constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) -// TODO: Correct update rate for motion is 5ms. Check why some games don't behave at that speed -constexpr auto motion_update_ns = std::chrono::nanoseconds{10 * 1000 * 1000}; // (10ms, 100Hz) -constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; +constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) IAppletResource::IAppletResource(Core::System& system_, KernelHelpers::ServiceContext& service_context_) @@ -48,20 +48,21 @@ IAppletResource::IAppletResource(Core::System& system_, {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, }; RegisterHandlers(functions); - - MakeController<Controller_DebugPad>(HidController::DebugPad); - MakeController<Controller_Touchscreen>(HidController::Touchscreen); - MakeController<Controller_Mouse>(HidController::Mouse); - MakeController<Controller_Keyboard>(HidController::Keyboard); - MakeController<Controller_XPad>(HidController::XPad); - MakeController<Controller_Stubbed>(HidController::HomeButton); - MakeController<Controller_Stubbed>(HidController::SleepButton); - MakeController<Controller_Stubbed>(HidController::CaptureButton); - MakeController<Controller_Stubbed>(HidController::InputDetector); - MakeController<Controller_Stubbed>(HidController::UniquePad); - MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad); - MakeController<Controller_Gesture>(HidController::Gesture); - MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor); + u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer(); + MakeController<Controller_DebugPad>(HidController::DebugPad, shared_memory); + MakeController<Controller_Touchscreen>(HidController::Touchscreen, shared_memory); + MakeController<Controller_Mouse>(HidController::Mouse, shared_memory); + MakeController<Controller_Keyboard>(HidController::Keyboard, shared_memory); + MakeController<Controller_XPad>(HidController::XPad, shared_memory); + MakeController<Controller_Stubbed>(HidController::HomeButton, shared_memory); + MakeController<Controller_Stubbed>(HidController::SleepButton, shared_memory); + MakeController<Controller_Stubbed>(HidController::CaptureButton, shared_memory); + MakeController<Controller_Stubbed>(HidController::InputDetector, shared_memory); + MakeController<Controller_Stubbed>(HidController::UniquePad, shared_memory); + MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad, shared_memory); + MakeController<Controller_Gesture>(HidController::Gesture, shared_memory); + MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor, shared_memory); + MakeControllerWithServiceContext<Controller_Palma>(HidController::Palma, shared_memory); // Homebrew doesn't try to activate some controllers, so we activate them by default GetController<Controller_NPad>(HidController::NPad).ActivateController(); @@ -76,26 +77,34 @@ IAppletResource::IAppletResource(Core::System& system_, // Register update callbacks pad_update_event = Core::Timing::CreateEvent( "HID::UpdatePadCallback", - [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { const auto guard = LockService(); UpdateControllers(user_data, ns_late); + return std::nullopt; }); mouse_keyboard_update_event = Core::Timing::CreateEvent( "HID::UpdateMouseKeyboardCallback", - [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { const auto guard = LockService(); UpdateMouseKeyboard(user_data, ns_late); + return std::nullopt; }); motion_update_event = Core::Timing::CreateEvent( "HID::UpdateMotionCallback", - [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { const auto guard = LockService(); UpdateMotion(user_data, ns_late); + return std::nullopt; }); - system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event); - system.CoreTiming().ScheduleEvent(mouse_keyboard_update_ns, mouse_keyboard_update_event); - system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event); + system.CoreTiming().ScheduleLoopingEvent(pad_update_ns, pad_update_ns, pad_update_event); + system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns, + mouse_keyboard_update_event); + system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns, + motion_update_event); system.HIDCore().ReloadInputDevices(); } @@ -135,47 +144,22 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data, if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) { continue; } - controller->OnUpdate(core_timing, system.Kernel().GetHidSharedMem().GetPointer(), - SHARED_MEMORY_SIZE); - } - - // If ns_late is higher than the update rate ignore the delay - if (ns_late > pad_update_ns) { - ns_late = {}; + controller->OnUpdate(core_timing); } - - core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event); } void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { auto& core_timing = system.CoreTiming(); - controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate( - core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE); - controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate( - core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE); - - // If ns_late is higher than the update rate ignore the delay - if (ns_late > mouse_keyboard_update_ns) { - ns_late = {}; - } - - core_timing.ScheduleEvent(mouse_keyboard_update_ns - ns_late, mouse_keyboard_update_event); + controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing); + controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing); } void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { auto& core_timing = system.CoreTiming(); - controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate( - core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE); - - // If ns_late is higher than the update rate ignore the delay - if (ns_late > motion_update_ns) { - ns_late = {}; - } - - core_timing.ScheduleEvent(motion_update_ns - ns_late, motion_update_event); + controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing); } class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { @@ -247,7 +231,7 @@ Hid::Hid(Core::System& system_) {65, nullptr, "GetJoySixAxisSensorLifoHandle"}, {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"}, - {68, nullptr, "IsSixAxisSensorFusionEnabled"}, + {68, &Hid::IsSixAxisSensorFusionEnabled, "IsSixAxisSensorFusionEnabled"}, {69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"}, {70, &Hid::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"}, {71, &Hid::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"}, @@ -263,12 +247,12 @@ Hid::Hid(Core::System& system_) {81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"}, {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, {83, &Hid::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"}, - {84, nullptr, "EnableSixAxisSensorUnalteredPassthrough"}, - {85, nullptr, "IsSixAxisSensorUnalteredPassthroughEnabled"}, + {84, &Hid::EnableSixAxisSensorUnalteredPassthrough, "EnableSixAxisSensorUnalteredPassthrough"}, + {85, &Hid::IsSixAxisSensorUnalteredPassthroughEnabled, "IsSixAxisSensorUnalteredPassthroughEnabled"}, {86, nullptr, "StoreSixAxisSensorCalibrationParameter"}, - {87, nullptr, "LoadSixAxisSensorCalibrationParameter"}, - {88, nullptr, "GetSixAxisSensorIcInformation"}, - {89, nullptr, "ResetIsSixAxisSensorDeviceNewlyAssigned"}, + {87, &Hid::LoadSixAxisSensorCalibrationParameter, "LoadSixAxisSensorCalibrationParameter"}, + {88, &Hid::GetSixAxisSensorIcInformation, "GetSixAxisSensorIcInformation"}, + {89, &Hid::ResetIsSixAxisSensorDeviceNewlyAssigned, "ResetIsSixAxisSensorDeviceNewlyAssigned"}, {91, &Hid::ActivateGesture, "ActivateGesture"}, {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, @@ -329,40 +313,40 @@ Hid::Hid(Core::System& system_) {406, nullptr, "GetNpadLeftRightInterfaceType"}, {407, nullptr, "GetNpadOfHighestBatteryLevel"}, {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"}, - {500, nullptr, "GetPalmaConnectionHandle"}, - {501, nullptr, "InitializePalma"}, - {502, nullptr, "AcquirePalmaOperationCompleteEvent"}, - {503, nullptr, "GetPalmaOperationInfo"}, - {504, nullptr, "PlayPalmaActivity"}, - {505, nullptr, "SetPalmaFrModeType"}, - {506, nullptr, "ReadPalmaStep"}, - {507, nullptr, "EnablePalmaStep"}, - {508, nullptr, "ResetPalmaStep"}, - {509, nullptr, "ReadPalmaApplicationSection"}, - {510, nullptr, "WritePalmaApplicationSection"}, - {511, nullptr, "ReadPalmaUniqueCode"}, - {512, nullptr, "SetPalmaUniqueCodeInvalid"}, - {513, nullptr, "WritePalmaActivityEntry"}, - {514, nullptr, "WritePalmaRgbLedPatternEntry"}, - {515, nullptr, "WritePalmaWaveEntry"}, - {516, nullptr, "SetPalmaDataBaseIdentificationVersion"}, - {517, nullptr, "GetPalmaDataBaseIdentificationVersion"}, - {518, nullptr, "SuspendPalmaFeature"}, - {519, nullptr, "GetPalmaOperationResult"}, - {520, nullptr, "ReadPalmaPlayLog"}, - {521, nullptr, "ResetPalmaPlayLog"}, + {500, &Hid::GetPalmaConnectionHandle, "GetPalmaConnectionHandle"}, + {501, &Hid::InitializePalma, "InitializePalma"}, + {502, &Hid::AcquirePalmaOperationCompleteEvent, "AcquirePalmaOperationCompleteEvent"}, + {503, &Hid::GetPalmaOperationInfo, "GetPalmaOperationInfo"}, + {504, &Hid::PlayPalmaActivity, "PlayPalmaActivity"}, + {505, &Hid::SetPalmaFrModeType, "SetPalmaFrModeType"}, + {506, &Hid::ReadPalmaStep, "ReadPalmaStep"}, + {507, &Hid::EnablePalmaStep, "EnablePalmaStep"}, + {508, &Hid::ResetPalmaStep, "ResetPalmaStep"}, + {509, &Hid::ReadPalmaApplicationSection, "ReadPalmaApplicationSection"}, + {510, &Hid::WritePalmaApplicationSection, "WritePalmaApplicationSection"}, + {511, &Hid::ReadPalmaUniqueCode, "ReadPalmaUniqueCode"}, + {512, &Hid::SetPalmaUniqueCodeInvalid, "SetPalmaUniqueCodeInvalid"}, + {513, &Hid::WritePalmaActivityEntry, "WritePalmaActivityEntry"}, + {514, &Hid::WritePalmaRgbLedPatternEntry, "WritePalmaRgbLedPatternEntry"}, + {515, &Hid::WritePalmaWaveEntry, "WritePalmaWaveEntry"}, + {516, &Hid::SetPalmaDataBaseIdentificationVersion, "SetPalmaDataBaseIdentificationVersion"}, + {517, &Hid::GetPalmaDataBaseIdentificationVersion, "GetPalmaDataBaseIdentificationVersion"}, + {518, &Hid::SuspendPalmaFeature, "SuspendPalmaFeature"}, + {519, &Hid::GetPalmaOperationResult, "GetPalmaOperationResult"}, + {520, &Hid::ReadPalmaPlayLog, "ReadPalmaPlayLog"}, + {521, &Hid::ResetPalmaPlayLog, "ResetPalmaPlayLog"}, {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"}, - {523, nullptr, "SetIsPalmaPairedConnectable"}, - {524, nullptr, "PairPalma"}, + {523, &Hid::SetIsPalmaPairedConnectable, "SetIsPalmaPairedConnectable"}, + {524, &Hid::PairPalma, "PairPalma"}, {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"}, - {526, nullptr, "CancelWritePalmaWaveEntry"}, - {527, nullptr, "EnablePalmaBoostMode"}, - {528, nullptr, "GetPalmaBluetoothAddress"}, - {529, nullptr, "SetDisallowedPalmaConnection"}, + {526, &Hid::CancelWritePalmaWaveEntry, "CancelWritePalmaWaveEntry"}, + {527, &Hid::EnablePalmaBoostMode, "EnablePalmaBoostMode"}, + {528, &Hid::GetPalmaBluetoothAddress, "GetPalmaBluetoothAddress"}, + {529, &Hid::SetDisallowedPalmaConnection, "SetDisallowedPalmaConnection"}, {1000, &Hid::SetNpadCommunicationMode, "SetNpadCommunicationMode"}, {1001, &Hid::GetNpadCommunicationMode, "GetNpadCommunicationMode"}, {1002, &Hid::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"}, - {1003, nullptr, "IsFirmwareUpdateNeededForNotification"}, + {1003, &Hid::IsFirmwareUpdateNeededForNotification, "IsFirmwareUpdateNeededForNotification"}, {2000, nullptr, "ActivateDigitizer"}, }; // clang-format on @@ -527,8 +511,8 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetSixAxisEnabled(parameters.sixaxis_handle, true); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, true); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", @@ -536,7 +520,7 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { @@ -550,8 +534,8 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetSixAxisEnabled(parameters.sixaxis_handle, false); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, false); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", @@ -559,7 +543,33 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); +} + +void Hid::IsSixAxisSensorFusionEnabled(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + bool is_enabled{}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = + controller.IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.Push(is_enabled); } void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { @@ -574,9 +584,9 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetSixAxisFusionEnabled(parameters.sixaxis_handle, - parameters.enable_sixaxis_sensor_fusion); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, + parameters.enable_sixaxis_sensor_fusion); LOG_DEBUG(Service_HID, "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, " @@ -586,7 +596,7 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { @@ -601,8 +611,9 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = + controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, " @@ -612,7 +623,7 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { @@ -626,9 +637,11 @@ void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - const auto sixaxis_fusion_parameters = - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .GetSixAxisFusionParameters(parameters.sixaxis_handle); + Core::HID::SixAxisSensorFusionParameters fusion_parameters{}; + const auto& controller = + GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = + controller.GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", @@ -636,8 +649,8 @@ void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushRaw(sixaxis_fusion_parameters); + rb.Push(result); + rb.PushRaw(fusion_parameters); } void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { @@ -651,8 +664,15 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .ResetSixAxisFusionParameters(parameters.sixaxis_handle); + // Since these parameters are unknow just use what HW outputs + const Core::HID::SixAxisSensorFusionParameters fusion_parameters{ + .parameter1 = 0.03f, + .parameter2 = 0.4f, + }; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result1 = + controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); + const auto result2 = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, true); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", @@ -660,7 +680,11 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + if (result1.IsError()) { + rb.Push(result1); + return; + } + rb.Push(result2); } void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { @@ -669,8 +693,8 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, " @@ -679,7 +703,7 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { drift_mode, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { @@ -693,15 +717,18 @@ void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; + auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); + LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad) - .GetGyroscopeZeroDriftMode(parameters.sixaxis_handle)); + rb.Push(result); + rb.PushEnum(drift_mode); } void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { @@ -714,10 +741,10 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); const auto parameters{rp.PopRaw<Parameters>()}; - const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); + const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", @@ -725,7 +752,7 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { @@ -739,6 +766,10 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; + bool is_at_rest{}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest); + LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, @@ -746,8 +777,7 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) - .IsSixAxisSensorAtRest(parameters.sixaxis_handle)); + rb.Push(is_at_rest); } void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) { @@ -761,6 +791,11 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c const auto parameters{rp.PopRaw<Parameters>()}; + bool is_firmware_available{}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + controller.IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle, + is_firmware_available); + LOG_WARNING( Service_HID, "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", @@ -769,7 +804,145 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(false); + rb.Push(is_firmware_available); +} + +void Hid::EnableSixAxisSensorUnalteredPassthrough(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool enabled; + Core::HID::SixAxisSensorHandle sixaxis_handle; + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.EnableSixAxisSensorUnalteredPassthrough( + parameters.sixaxis_handle, parameters.enabled); + + LOG_DEBUG(Service_HID, + "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, " + "applet_resource_user_id={}", + parameters.enabled, parameters.sixaxis_handle.npad_type, + parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, + parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::IsSixAxisSensorUnalteredPassthroughEnabled(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + bool is_unaltered_sisxaxis_enabled{}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.IsSixAxisSensorUnalteredPassthroughEnabled( + parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled); + + LOG_DEBUG( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.Push(is_unaltered_sisxaxis_enabled); +} + +void Hid::LoadSixAxisSensorCalibrationParameter(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + Core::HID::SixAxisSensorCalibrationParameter calibration{}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = + controller.LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration); + + LOG_WARNING( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + if (result.IsSuccess()) { + ctx.WriteBuffer(calibration); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::GetSixAxisSensorIcInformation(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + Core::HID::SixAxisSensorIcInformation ic_information{}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = + controller.GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information); + + LOG_WARNING( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + if (result.IsSuccess()) { + ctx.WriteBuffer(ic_information); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::ResetIsSixAxisSensorDeviceNewlyAssigned(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = + controller.ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle); + + LOG_WARNING( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); } void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { @@ -878,6 +1051,10 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); + // Games expect this event to be signaled after calling this function + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .SignalStyleSetChangedEvent(parameters.npad_id); + IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad) @@ -895,8 +1072,8 @@ void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .DisconnectNpad(parameters.npad_id); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + controller.DisconnectNpad(parameters.npad_id); LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, parameters.applet_resource_user_id); @@ -909,13 +1086,15 @@ void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()}; + Core::HID::LedPattern pattern{0, 0, 0, 0}; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.GetLedPattern(npad_id, pattern); + LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id); IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) - .GetLedPattern(npad_id) - .raw); + rb.Push(result); + rb.Push(pattern.raw); } void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) { @@ -975,9 +1154,9 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left, - Controller_NPad::NpadJoyAssignmentMode::Single); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + controller.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left, + Controller_NPad::NpadJoyAssignmentMode::Single); LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, parameters.applet_resource_user_id); @@ -998,9 +1177,9 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type, - Controller_NPad::NpadJoyAssignmentMode::Single); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + controller.SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type, + Controller_NPad::NpadJoyAssignmentMode::Single); LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", parameters.npad_id, parameters.applet_resource_user_id, @@ -1021,8 +1200,8 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + controller.SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual); LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, parameters.applet_resource_user_id); @@ -1037,14 +1216,14 @@ void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", npad_id_1, npad_id_2, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) { @@ -1104,19 +1283,14 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; - const bool res = applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SwapNpadAssignment(npad_id_1, npad_id_2); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.SwapNpadAssignment(npad_id_1, npad_id_2); LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", npad_id_1, npad_id_2, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - if (res) { - rb.Push(ResultSuccess); - } else { - LOG_ERROR(Service_HID, "Npads are not connected!"); - rb.Push(ERR_NPAD_NOT_CONNECTED); - } + rb.Push(result); } void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) { @@ -1130,13 +1304,17 @@ void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext const auto parameters{rp.PopRaw<Parameters>()}; + bool is_enabled = false; + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = + controller.IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled); + LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) - .IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id)); + rb.Push(result); + rb.Push(is_enabled); } void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) { @@ -1151,9 +1329,9 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c const auto parameters{rp.PopRaw<Parameters>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetUnintendedHomeButtonInputProtectionEnabled( - parameters.unintended_home_button_input_protection, parameters.npad_id); + auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); + const auto result = controller.SetUnintendedHomeButtonInputProtectionEnabled( + parameters.unintended_home_button_input_protection, parameters.npad_id); LOG_WARNING(Service_HID, "(STUBBED) called, unintended_home_button_input_protection={}, npad_id={}," @@ -1162,7 +1340,7 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) { @@ -1222,8 +1400,11 @@ void Hid::ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx) { void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; + const auto& controller = + GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); Core::HID::VibrationDeviceInfo vibration_device_info; + bool check_device_index = false; switch (vibration_device_handle.npad_type) { case Core::HID::NpadStyleIndex::ProController: @@ -1231,34 +1412,46 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::JoyconLeft: case Core::HID::NpadStyleIndex::JoyconRight: - default: vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator; + check_device_index = true; break; case Core::HID::NpadStyleIndex::GameCube: vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm; break; - case Core::HID::NpadStyleIndex::Pokeball: + case Core::HID::NpadStyleIndex::N64: + vibration_device_info.type = Core::HID::VibrationDeviceType::N64; + break; + default: vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown; break; } - switch (vibration_device_handle.device_index) { - case Core::HID::DeviceIndex::Left: - vibration_device_info.position = Core::HID::VibrationDevicePosition::Left; - break; - case Core::HID::DeviceIndex::Right: - vibration_device_info.position = Core::HID::VibrationDevicePosition::Right; - break; - case Core::HID::DeviceIndex::None: - default: - UNREACHABLE_MSG("DeviceIndex should never be None!"); - vibration_device_info.position = Core::HID::VibrationDevicePosition::None; - break; + vibration_device_info.position = Core::HID::VibrationDevicePosition::None; + if (check_device_index) { + switch (vibration_device_handle.device_index) { + case Core::HID::DeviceIndex::Left: + vibration_device_info.position = Core::HID::VibrationDevicePosition::Left; + break; + case Core::HID::DeviceIndex::Right: + vibration_device_info.position = Core::HID::VibrationDevicePosition::Right; + break; + case Core::HID::DeviceIndex::None: + default: + ASSERT_MSG(false, "DeviceIndex should never be None!"); + break; + } } LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}", vibration_device_info.type, vibration_device_info.position); + const auto result = controller.IsDeviceHandleValid(vibration_device_handle); + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); rb.PushRaw(vibration_device_info); @@ -1324,6 +1517,8 @@ void Hid::PermitVibration(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto can_vibrate{rp.Pop<bool>()}; + // nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value + // by converting it to a bool Settings::values.vibration_enabled.SetValue(can_vibrate); LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); @@ -1335,9 +1530,12 @@ void Hid::PermitVibration(Kernel::HLERequestContext& ctx) { void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_HID, "called"); + // nnSDK checks if a float is greater than zero. We return the bool we stored earlier + const auto is_enabled = Settings::values.vibration_enabled.GetValue(); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(Settings::values.vibration_enabled.GetValue()); + rb.Push(is_enabled); } void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { @@ -1683,14 +1881,361 @@ void Hid::IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx) { rb.Push(false); } +void Hid::GetPalmaConnectionHandle(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", + parameters.npad_id, parameters.applet_resource_user_id); + + Controller_Palma::PalmaConnectionHandle handle; + auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); + const auto result = controller.GetPalmaConnectionHandle(parameters.npad_id, handle); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.PushRaw(handle); +} + +void Hid::InitializePalma(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); + const auto result = controller.InitializePalma(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::AcquirePalmaOperationCompleteEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(controller.AcquirePalmaOperationCompleteEvent(connection_handle)); +} + +void Hid::GetPalmaOperationInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + Controller_Palma::PalmaOperationType operation_type; + Controller_Palma::PalmaOperationData data; + auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); + const auto result = controller.GetPalmaOperationInfo(connection_handle, operation_type, data); + + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + } + + ctx.WriteBuffer(data); + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.Push(static_cast<u64>(operation_type)); +} + +void Hid::PlayPalmaActivity(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + const auto palma_activity{rp.Pop<u64>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}", + connection_handle.npad_id, palma_activity); + + auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); + const auto result = controller.PlayPalmaActivity(connection_handle, palma_activity); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::SetPalmaFrModeType(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + const auto fr_mode{rp.PopEnum<Controller_Palma::PalmaFrModeType>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}", + connection_handle.npad_id, fr_mode); + + auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); + const auto result = controller.SetPalmaFrModeType(connection_handle, fr_mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::ReadPalmaStep(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); + const auto result = controller.ReadPalmaStep(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::EnablePalmaStep(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool is_enabled; + INSERT_PADDING_WORDS_NOINIT(1); + Controller_Palma::PalmaConnectionHandle connection_handle; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}", + parameters.connection_handle.npad_id, parameters.is_enabled); + + auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); + const auto result = + controller.EnablePalmaStep(parameters.connection_handle, parameters.is_enabled); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::ResetPalmaStep(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); + const auto result = controller.ResetPalmaStep(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::ReadPalmaApplicationSection(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::WritePalmaApplicationSection(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::ReadPalmaUniqueCode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + applet_resource->GetController<Controller_Palma>(HidController::Palma) + .ReadPalmaUniqueCode(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::SetPalmaUniqueCodeInvalid(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + applet_resource->GetController<Controller_Palma>(HidController::Palma) + .SetPalmaUniqueCodeInvalid(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::WritePalmaActivityEntry(Kernel::HLERequestContext& ctx) { + LOG_CRITICAL(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::WritePalmaRgbLedPatternEntry(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + const auto unknown{rp.Pop<u64>()}; + + const auto buffer = ctx.ReadBuffer(); + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}", + connection_handle.npad_id, unknown); + + applet_resource->GetController<Controller_Palma>(HidController::Palma) + .WritePalmaRgbLedPatternEntry(connection_handle, unknown); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::WritePalmaWaveEntry(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + const auto wave_set{rp.PopEnum<Controller_Palma::PalmaWaveSet>()}; + const auto unknown{rp.Pop<u64>()}; + const auto t_mem_size{rp.Pop<u64>()}; + const auto t_mem_handle{ctx.GetCopyHandle(0)}; + const auto size{rp.Pop<u64>()}; + + ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes"); + + auto t_mem = + system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle); + + if (t_mem.IsNull()) { + LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + ASSERT_MSG(t_mem->GetSize() == 0x3000, "t_mem has incorrect size"); + + LOG_WARNING(Service_HID, + "(STUBBED) called, connection_handle={}, wave_set={}, unkown={}, " + "t_mem_handle=0x{:08X}, t_mem_size={}, size={}", + connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size); + + applet_resource->GetController<Controller_Palma>(HidController::Palma) + .WritePalmaWaveEntry(connection_handle, wave_set, + system.Memory().GetPointer(t_mem->GetSourceAddress()), t_mem_size); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::SetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + s32 database_id_version; + INSERT_PADDING_WORDS_NOINIT(1); + Controller_Palma::PalmaConnectionHandle connection_handle; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}", + parameters.connection_handle.npad_id, parameters.database_id_version); + + applet_resource->GetController<Controller_Palma>(HidController::Palma) + .SetPalmaDataBaseIdentificationVersion(parameters.connection_handle, + parameters.database_id_version); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::GetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + applet_resource->GetController<Controller_Palma>(HidController::Palma) + .GetPalmaDataBaseIdentificationVersion(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::SuspendPalmaFeature(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::GetPalmaOperationResult(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + const auto result = applet_resource->GetController<Controller_Palma>(HidController::Palma) + .GetPalmaOperationResult(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Hid::ReadPalmaPlayLog(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::ResetPalmaPlayLog(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - const auto is_palma_all_connectable{rp.Pop<bool>()}; + struct Parameters { + bool is_palma_all_connectable; + INSERT_PADDING_BYTES_NOINIT(7); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; LOG_WARNING(Service_HID, - "(STUBBED) called, applet_resource_user_id={}, is_palma_all_connectable={}", - applet_resource_user_id, is_palma_all_connectable); + "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}", + parameters.is_palma_all_connectable, parameters.applet_resource_user_id); + + applet_resource->GetController<Controller_Palma>(HidController::Palma) + .SetIsPalmaAllConnectable(parameters.is_palma_all_connectable); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::SetIsPalmaPairedConnectable(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::PairPalma(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + applet_resource->GetController<Controller_Palma>(HidController::Palma) + .PairPalma(connection_handle); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -1702,6 +2247,37 @@ void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode); + applet_resource->GetController<Controller_Palma>(HidController::Palma) + .SetPalmaBoostMode(palma_boost_mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::CancelWritePalmaWaveEntry(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::EnablePalmaBoostMode(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::GetPalmaBluetoothAddress(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Hid::SetDisallowedPalmaConnection(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } @@ -1744,6 +2320,25 @@ void Hid::SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx) { rb.Push(ResultSuccess); } +void Hid::IsFirmwareUpdateNeededForNotification(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + s32 unknown; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}", + parameters.unknown, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(false); +} + class HidDbg final : public ServiceFramework<HidDbg> { public: explicit HidDbg(Core::System& system_) : ServiceFramework{system_, "hid:dbg"} { @@ -1932,12 +2527,18 @@ public: {324, nullptr, "GetUniquePadButtonSet"}, {325, nullptr, "GetUniquePadColor"}, {326, nullptr, "GetUniquePadAppletDetailedUiType"}, + {327, nullptr, "GetAbstractedPadIdDataFromNpad"}, + {328, nullptr, "AttachAbstractedPadToNpad"}, + {329, nullptr, "DetachAbstractedPadAll"}, + {330, nullptr, "CheckAbstractedPadConnection"}, {500, nullptr, "SetAppletResourceUserId"}, {501, nullptr, "RegisterAppletResourceUserId"}, {502, nullptr, "UnregisterAppletResourceUserId"}, {503, nullptr, "EnableAppletToGetInput"}, {504, nullptr, "SetAruidValidForVibration"}, {505, nullptr, "EnableAppletToGetSixAxisSensor"}, + {506, nullptr, "EnableAppletToGetPadInput"}, + {507, nullptr, "EnableAppletToGetTouchScreen"}, {510, nullptr, "SetVibrationMasterVolume"}, {511, nullptr, "GetVibrationMasterVolume"}, {512, nullptr, "BeginPermitVibrationSession"}, @@ -2124,32 +2725,6 @@ public: } }; -class HidBus final : public ServiceFramework<HidBus> { -public: - explicit HidBus(Core::System& system_) : ServiceFramework{system_, "hidbus"} { - // clang-format off - static const FunctionInfo functions[] = { - {1, nullptr, "GetBusHandle"}, - {2, nullptr, "IsExternalDeviceConnected"}, - {3, nullptr, "Initialize"}, - {4, nullptr, "Finalize"}, - {5, nullptr, "EnableExternalDevice"}, - {6, nullptr, "GetExternalDeviceId"}, - {7, nullptr, "SendCommandAsync"}, - {8, nullptr, "GetSendCommandAsynceResult"}, - {9, nullptr, "SetEventForSendCommandAsycResult"}, - {10, nullptr, "GetSharedMemoryHandle"}, - {11, nullptr, "EnableJoyPollingReceiveMode"}, - {12, nullptr, "DisableJoyPollingReceiveMode"}, - {13, nullptr, "GetPollingData"}, - {14, nullptr, "SetStatusManagerType"}, - }; - // clang-format on - - RegisterHandlers(functions); - } -}; - void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { std::make_shared<Hid>(system)->InstallAsService(service_manager); std::make_shared<HidBus>(system)->InstallAsService(service_manager); @@ -2157,8 +2732,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system std::make_shared<HidSys>(system)->InstallAsService(service_manager); std::make_shared<HidTmp>(system)->InstallAsService(service_manager); - std::make_shared<IRS>(system)->InstallAsService(service_manager); - std::make_shared<IRS_SYS>(system)->InstallAsService(service_manager); + std::make_shared<Service::IRS::IRS>(system)->InstallAsService(service_manager); + std::make_shared<Service::IRS::IRS_SYS>(system)->InstallAsService(service_manager); std::make_shared<XCD_SYS>(system)->InstallAsService(service_manager); } diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index c281081a7..340d26fdc 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -34,6 +33,7 @@ enum class HidController : std::size_t { NPad, Gesture, ConsoleSixAxisSensor, + Palma, MaxControllers, }; @@ -59,13 +59,14 @@ public: private: template <typename T> - void MakeController(HidController controller) { - controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system.HIDCore()); + void MakeController(HidController controller, u8* shared_memory) { + controllers[static_cast<std::size_t>(controller)] = + std::make_unique<T>(system.HIDCore(), shared_memory); } template <typename T> - void MakeControllerWithServiceContext(HidController controller) { + void MakeControllerWithServiceContext(HidController controller, u8* shared_memory) { controllers[static_cast<std::size_t>(controller)] = - std::make_unique<T>(system.HIDCore(), service_context); + std::make_unique<T>(system.HIDCore(), shared_memory, service_context); } void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); @@ -103,6 +104,7 @@ private: void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx); void StartSixAxisSensor(Kernel::HLERequestContext& ctx); void StopSixAxisSensor(Kernel::HLERequestContext& ctx); + void IsSixAxisSensorFusionEnabled(Kernel::HLERequestContext& ctx); void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx); void SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx); void GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx); @@ -112,6 +114,11 @@ private: void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx); void IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx); + void EnableSixAxisSensorUnalteredPassthrough(Kernel::HLERequestContext& ctx); + void IsSixAxisSensorUnalteredPassthroughEnabled(Kernel::HLERequestContext& ctx); + void LoadSixAxisSensorCalibrationParameter(Kernel::HLERequestContext& ctx); + void GetSixAxisSensorIcInformation(Kernel::HLERequestContext& ctx); + void ResetIsSixAxisSensorDeviceNewlyAssigned(Kernel::HLERequestContext& ctx); void ActivateGesture(Kernel::HLERequestContext& ctx); void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); @@ -160,11 +167,40 @@ private: void FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); void ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx); void IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx); + void GetPalmaConnectionHandle(Kernel::HLERequestContext& ctx); + void InitializePalma(Kernel::HLERequestContext& ctx); + void AcquirePalmaOperationCompleteEvent(Kernel::HLERequestContext& ctx); + void GetPalmaOperationInfo(Kernel::HLERequestContext& ctx); + void PlayPalmaActivity(Kernel::HLERequestContext& ctx); + void SetPalmaFrModeType(Kernel::HLERequestContext& ctx); + void ReadPalmaStep(Kernel::HLERequestContext& ctx); + void EnablePalmaStep(Kernel::HLERequestContext& ctx); + void ResetPalmaStep(Kernel::HLERequestContext& ctx); + void ReadPalmaApplicationSection(Kernel::HLERequestContext& ctx); + void WritePalmaApplicationSection(Kernel::HLERequestContext& ctx); + void ReadPalmaUniqueCode(Kernel::HLERequestContext& ctx); + void SetPalmaUniqueCodeInvalid(Kernel::HLERequestContext& ctx); + void WritePalmaActivityEntry(Kernel::HLERequestContext& ctx); + void WritePalmaRgbLedPatternEntry(Kernel::HLERequestContext& ctx); + void WritePalmaWaveEntry(Kernel::HLERequestContext& ctx); + void SetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx); + void GetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx); + void SuspendPalmaFeature(Kernel::HLERequestContext& ctx); + void GetPalmaOperationResult(Kernel::HLERequestContext& ctx); + void ReadPalmaPlayLog(Kernel::HLERequestContext& ctx); + void ResetPalmaPlayLog(Kernel::HLERequestContext& ctx); void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); + void SetIsPalmaPairedConnectable(Kernel::HLERequestContext& ctx); + void PairPalma(Kernel::HLERequestContext& ctx); void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); + void CancelWritePalmaWaveEntry(Kernel::HLERequestContext& ctx); + void EnablePalmaBoostMode(Kernel::HLERequestContext& ctx); + void GetPalmaBluetoothAddress(Kernel::HLERequestContext& ctx); + void SetDisallowedPalmaConnection(Kernel::HLERequestContext& ctx); void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx); void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx); void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx); + void IsFirmwareUpdateNeededForNotification(Kernel::HLERequestContext& ctx); std::shared_ptr<IAppletResource> applet_resource; diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp new file mode 100644 index 000000000..e5e50845f --- /dev/null +++ b/src/core/hle/service/hid/hidbus.cpp @@ -0,0 +1,524 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "common/settings.h" +#include "core/core.h" +#include "core/core_timing.h" +#include "core/core_timing_util.h" +#include "core/hid/hid_types.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/kernel/k_transfer_memory.h" +#include "core/hle/service/hid/hidbus.h" +#include "core/hle/service/hid/hidbus/ringcon.h" +#include "core/hle/service/hid/hidbus/starlink.h" +#include "core/hle/service/hid/hidbus/stubbed.h" +#include "core/hle/service/service.h" +#include "core/memory.h" + +namespace Service::HID { +// (15ms, 66Hz) +constexpr auto hidbus_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; + +HidBus::HidBus(Core::System& system_) + : ServiceFramework{system_, "hidbus"}, service_context{system_, service_name} { + + // clang-format off + static const FunctionInfo functions[] = { + {1, &HidBus::GetBusHandle, "GetBusHandle"}, + {2, &HidBus::IsExternalDeviceConnected, "IsExternalDeviceConnected"}, + {3, &HidBus::Initialize, "Initialize"}, + {4, &HidBus::Finalize, "Finalize"}, + {5, &HidBus::EnableExternalDevice, "EnableExternalDevice"}, + {6, &HidBus::GetExternalDeviceId, "GetExternalDeviceId"}, + {7, &HidBus::SendCommandAsync, "SendCommandAsync"}, + {8, &HidBus::GetSendCommandAsynceResult, "GetSendCommandAsynceResult"}, + {9, &HidBus::SetEventForSendCommandAsycResult, "SetEventForSendCommandAsycResult"}, + {10, &HidBus::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, + {11, &HidBus::EnableJoyPollingReceiveMode, "EnableJoyPollingReceiveMode"}, + {12, &HidBus::DisableJoyPollingReceiveMode, "DisableJoyPollingReceiveMode"}, + {13, nullptr, "GetPollingData"}, + {14, &HidBus::SetStatusManagerType, "SetStatusManagerType"}, + }; + // clang-format on + + RegisterHandlers(functions); + + // Register update callbacks + hidbus_update_event = Core::Timing::CreateEvent( + "Hidbus::UpdateCallback", + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { + const auto guard = LockService(); + UpdateHidbus(user_data, ns_late); + return std::nullopt; + }); + + system_.CoreTiming().ScheduleLoopingEvent(hidbus_update_ns, hidbus_update_ns, + hidbus_update_event); +} + +HidBus::~HidBus() { + system.CoreTiming().UnscheduleEvent(hidbus_update_event, 0); +} + +void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + if (is_hidbus_enabled) { + for (std::size_t i = 0; i < devices.size(); ++i) { + if (!devices[i].is_device_initializated) { + continue; + } + auto& device = devices[i].device; + device->OnUpdate(); + auto& cur_entry = hidbus_status.entries[devices[i].handle.internal_index]; + cur_entry.is_polling_mode = device->IsPollingMode(); + cur_entry.polling_mode = device->GetPollingMode(); + cur_entry.is_enabled = device->IsEnabled(); + + u8* shared_memory = system.Kernel().GetHidBusSharedMem().GetPointer(); + std::memcpy(shared_memory + (i * sizeof(HidbusStatusManagerEntry)), &hidbus_status, + sizeof(HidbusStatusManagerEntry)); + } + } +} + +std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const { + for (std::size_t i = 0; i < devices.size(); ++i) { + const auto& device_handle = devices[i].handle; + if (handle.abstracted_pad_id == device_handle.abstracted_pad_id && + handle.internal_index == device_handle.internal_index && + handle.player_number == device_handle.player_number && + handle.bus_type == device_handle.bus_type && + handle.is_valid == device_handle.is_valid) { + return i; + } + } + return std::nullopt; +} + +void HidBus::GetBusHandle(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + BusType bus_type; + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_INFO(Service_HID, "called, npad_id={}, bus_type={}, applet_resource_user_id={}", + parameters.npad_id, parameters.bus_type, parameters.applet_resource_user_id); + + bool is_handle_found = 0; + std::size_t handle_index = 0; + + for (std::size_t i = 0; i < devices.size(); i++) { + const auto& handle = devices[i].handle; + if (!handle.is_valid) { + continue; + } + if (static_cast<Core::HID::NpadIdType>(handle.player_number) == parameters.npad_id && + handle.bus_type == parameters.bus_type) { + is_handle_found = true; + handle_index = i; + break; + } + } + + // Handle not found. Create a new one + if (!is_handle_found) { + for (std::size_t i = 0; i < devices.size(); i++) { + if (devices[i].handle.is_valid) { + continue; + } + devices[i].handle = { + .abstracted_pad_id = static_cast<u8>(i), + .internal_index = static_cast<u8>(i), + .player_number = static_cast<u8>(parameters.npad_id), + .bus_type = parameters.bus_type, + .is_valid = true, + }; + handle_index = i; + break; + } + } + + struct OutData { + bool is_valid; + INSERT_PADDING_BYTES(7); + BusHandle handle; + }; + static_assert(sizeof(OutData) == 0x10, "OutData has incorrect size."); + + const OutData out_data{ + .is_valid = true, + .handle = devices[handle_index].handle, + }; + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.PushRaw(out_data); +} + +void HidBus::IsExternalDeviceConnected(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + + LOG_INFO(Service_HID, + "Called, abstracted_pad_id={}, bus_type={}, internal_index={}, " + "player_number={}, is_valid={}", + bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, + bus_handle_.player_number, bus_handle_.is_valid); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + const auto& device = devices[device_index.value()].device; + const bool is_attached = device->IsDeviceActivated(); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(is_attached); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +} + +void HidBus::Initialize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_INFO(Service_HID, + "called, abstracted_pad_id={} bus_type={} internal_index={} " + "player_number={} is_valid={}, applet_resource_user_id={}", + bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, + bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id); + + is_hidbus_enabled = true; + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + const auto entry_index = devices[device_index.value()].handle.internal_index; + auto& cur_entry = hidbus_status.entries[entry_index]; + + if (bus_handle_.internal_index == 0 && Settings::values.enable_ring_controller) { + MakeDevice<RingController>(bus_handle_); + devices[device_index.value()].is_device_initializated = true; + devices[device_index.value()].device->ActivateDevice(); + cur_entry.is_in_focus = true; + cur_entry.is_connected = true; + cur_entry.is_connected_result = ResultSuccess; + cur_entry.is_enabled = false; + cur_entry.is_polling_mode = false; + } else { + MakeDevice<HidbusStubbed>(bus_handle_); + devices[device_index.value()].is_device_initializated = true; + cur_entry.is_in_focus = true; + cur_entry.is_connected = false; + cur_entry.is_connected_result = ResultSuccess; + cur_entry.is_enabled = false; + cur_entry.is_polling_mode = false; + } + + std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status, + sizeof(hidbus_status)); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +} + +void HidBus::Finalize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_INFO(Service_HID, + "called, abstracted_pad_id={}, bus_type={}, internal_index={}, " + "player_number={}, is_valid={}, applet_resource_user_id={}", + bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, + bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + const auto entry_index = devices[device_index.value()].handle.internal_index; + auto& cur_entry = hidbus_status.entries[entry_index]; + auto& device = devices[device_index.value()].device; + devices[device_index.value()].is_device_initializated = false; + device->DeactivateDevice(); + + cur_entry.is_in_focus = true; + cur_entry.is_connected = false; + cur_entry.is_connected_result = ResultSuccess; + cur_entry.is_enabled = false; + cur_entry.is_polling_mode = false; + std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status, + sizeof(hidbus_status)); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +} + +void HidBus::EnableExternalDevice(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool enable; + INSERT_PADDING_BYTES_NOINIT(7); + BusHandle bus_handle; + u64 inval; + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_INFO(Service_HID, + "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, " + "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}", + parameters.enable, parameters.bus_handle.abstracted_pad_id, + parameters.bus_handle.bus_type, parameters.bus_handle.internal_index, + parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval, + parameters.applet_resource_user_id); + + const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle); + + if (device_index) { + auto& device = devices[device_index.value()].device; + device->Enable(parameters.enable); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +} + +void HidBus::GetExternalDeviceId(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + + LOG_INFO(Service_HID, + "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " + "is_valid={}", + bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, + bus_handle_.player_number, bus_handle_.is_valid); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + const auto& device = devices[device_index.value()].device; + u32 device_id = device->GetDeviceId(); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u32>(device_id); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +} + +void HidBus::SendCommandAsync(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto data = ctx.ReadBuffer(); + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + + LOG_DEBUG(Service_HID, + "called, data_size={}, abstracted_pad_id={}, bus_type={}, internal_index={}, " + "player_number={}, is_valid={}", + data.size(), bus_handle_.abstracted_pad_id, bus_handle_.bus_type, + bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + auto& device = devices[device_index.value()].device; + device->SetCommand(data); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +}; + +void HidBus::GetSendCommandAsynceResult(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + + LOG_DEBUG(Service_HID, + "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " + "is_valid={}", + bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, + bus_handle_.player_number, bus_handle_.is_valid); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + const auto& device = devices[device_index.value()].device; + const std::vector<u8> data = device->GetReply(); + const u64 data_size = ctx.WriteBuffer(data); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push<u64>(data_size); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +}; + +void HidBus::SetEventForSendCommandAsycResult(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + + LOG_INFO(Service_HID, + "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " + "is_valid={}", + bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, + bus_handle_.player_number, bus_handle_.is_valid); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + const auto& device = devices[device_index.value()].device; + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(device->GetSendCommandAsycEvent()); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +}; + +void HidBus::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_HID, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(&system.Kernel().GetHidBusSharedMem()); +} + +void HidBus::EnableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto t_mem_size{rp.Pop<u32>()}; + const auto t_mem_handle{ctx.GetCopyHandle(0)}; + const auto polling_mode_{rp.PopEnum<JoyPollingMode>()}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + + ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes"); + + auto t_mem = + system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle); + + if (t_mem.IsNull()) { + LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + ASSERT_MSG(t_mem->GetSize() == 0x1000, "t_mem has incorrect size"); + + LOG_INFO(Service_HID, + "called, t_mem_handle=0x{:08X}, polling_mode={}, abstracted_pad_id={}, bus_type={}, " + "internal_index={}, player_number={}, is_valid={}", + t_mem_handle, polling_mode_, bus_handle_.abstracted_pad_id, bus_handle_.bus_type, + bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + auto& device = devices[device_index.value()].device; + device->SetPollingMode(polling_mode_); + device->SetTransferMemoryPointer(system.Memory().GetPointer(t_mem->GetSourceAddress())); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +} + +void HidBus::DisableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto bus_handle_{rp.PopRaw<BusHandle>()}; + + LOG_INFO(Service_HID, + "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " + "is_valid={}", + bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, + bus_handle_.player_number, bus_handle_.is_valid); + + const auto device_index = GetDeviceIndexFromHandle(bus_handle_); + + if (device_index) { + auto& device = devices[device_index.value()].device; + device->DisablePollingMode(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + return; + } + + LOG_ERROR(Service_HID, "Invalid handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; +} + +void HidBus::SetStatusManagerType(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto manager_type{rp.PopEnum<StatusManagerType>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, manager_type={}", manager_type); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +}; +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus.h b/src/core/hle/service/hid/hidbus.h new file mode 100644 index 000000000..8c687f678 --- /dev/null +++ b/src/core/hle/service/hid/hidbus.h @@ -0,0 +1,130 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <functional> + +#include "core/hle/service/hid/hidbus/hidbus_base.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +namespace Core::Timing { +struct EventType; +} // namespace Core::Timing + +namespace Core { +class System; +} // namespace Core + +namespace Service::HID { + +class HidBus final : public ServiceFramework<HidBus> { +public: + explicit HidBus(Core::System& system_); + ~HidBus() override; + +private: + static const std::size_t max_number_of_handles = 0x13; + + enum class HidBusDeviceId : std::size_t { + RingController = 0x20, + FamicomRight = 0x21, + Starlink = 0x28, + }; + + // This is nn::hidbus::detail::StatusManagerType + enum class StatusManagerType : u32 { + None, + Type16, + Type32, + }; + + // This is nn::hidbus::BusType + enum class BusType : u8 { + LeftJoyRail, + RightJoyRail, + InternalBus, // Lark microphone + + MaxBusType, + }; + + // This is nn::hidbus::BusHandle + struct BusHandle { + u32 abstracted_pad_id; + u8 internal_index; + u8 player_number; + BusType bus_type; + bool is_valid; + }; + static_assert(sizeof(BusHandle) == 0x8, "BusHandle is an invalid size"); + + // This is nn::hidbus::JoyPollingReceivedData + struct JoyPollingReceivedData { + std::array<u8, 0x30> data; + u64 out_size; + u64 sampling_number; + }; + static_assert(sizeof(JoyPollingReceivedData) == 0x40, + "JoyPollingReceivedData is an invalid size"); + + struct HidbusStatusManagerEntry { + u8 is_connected{}; + INSERT_PADDING_BYTES(0x3); + Result is_connected_result{0}; + u8 is_enabled{}; + u8 is_in_focus{}; + u8 is_polling_mode{}; + u8 reserved{}; + JoyPollingMode polling_mode{}; + INSERT_PADDING_BYTES(0x70); // Unknown + }; + static_assert(sizeof(HidbusStatusManagerEntry) == 0x80, + "HidbusStatusManagerEntry is an invalid size"); + + struct HidbusStatusManager { + std::array<HidbusStatusManagerEntry, max_number_of_handles> entries{}; + INSERT_PADDING_BYTES(0x680); // Unused + }; + static_assert(sizeof(HidbusStatusManager) <= 0x1000, "HidbusStatusManager is an invalid size"); + + struct HidbusDevice { + bool is_device_initializated{}; + BusHandle handle{}; + std::unique_ptr<HidbusBase> device{nullptr}; + }; + + void GetBusHandle(Kernel::HLERequestContext& ctx); + void IsExternalDeviceConnected(Kernel::HLERequestContext& ctx); + void Initialize(Kernel::HLERequestContext& ctx); + void Finalize(Kernel::HLERequestContext& ctx); + void EnableExternalDevice(Kernel::HLERequestContext& ctx); + void GetExternalDeviceId(Kernel::HLERequestContext& ctx); + void SendCommandAsync(Kernel::HLERequestContext& ctx); + void GetSendCommandAsynceResult(Kernel::HLERequestContext& ctx); + void SetEventForSendCommandAsycResult(Kernel::HLERequestContext& ctx); + void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); + void EnableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx); + void DisableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx); + void SetStatusManagerType(Kernel::HLERequestContext& ctx); + + void UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); + std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const; + + template <typename T> + void MakeDevice(BusHandle handle) { + const auto device_index = GetDeviceIndexFromHandle(handle); + if (device_index) { + devices[device_index.value()].device = + std::make_unique<T>(system.HIDCore(), service_context); + } + } + + bool is_hidbus_enabled{false}; + HidbusStatusManager hidbus_status{}; + std::array<HidbusDevice, max_number_of_handles> devices{}; + std::shared_ptr<Core::Timing::EventType> hidbus_update_event; + KernelHelpers::ServiceContext service_context; +}; + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.cpp b/src/core/hle/service/hid/hidbus/hidbus_base.cpp new file mode 100644 index 000000000..b569b3c20 --- /dev/null +++ b/src/core/hle/service/hid/hidbus/hidbus_base.cpp @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hid/hid_core.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/service/hid/hidbus/hidbus_base.h" +#include "core/hle/service/kernel_helpers.h" + +namespace Service::HID { + +HidbusBase::HidbusBase(KernelHelpers::ServiceContext& service_context_) + : service_context(service_context_) { + send_command_async_event = service_context.CreateEvent("hidbus:SendCommandAsyncEvent"); +} +HidbusBase::~HidbusBase() = default; + +void HidbusBase::ActivateDevice() { + if (is_activated) { + return; + } + is_activated = true; + OnInit(); +} + +void HidbusBase::DeactivateDevice() { + if (is_activated) { + OnRelease(); + } + is_activated = false; +} + +bool HidbusBase::IsDeviceActivated() const { + return is_activated; +} + +void HidbusBase::Enable(bool enable) { + device_enabled = enable; +} + +bool HidbusBase::IsEnabled() const { + return device_enabled; +} + +bool HidbusBase::IsPollingMode() const { + return polling_mode_enabled; +} + +JoyPollingMode HidbusBase::GetPollingMode() const { + return polling_mode; +} + +void HidbusBase::SetPollingMode(JoyPollingMode mode) { + polling_mode = mode; + polling_mode_enabled = true; +} + +void HidbusBase::DisablePollingMode() { + polling_mode_enabled = false; +} + +void HidbusBase::SetTransferMemoryPointer(u8* t_mem) { + is_transfer_memory_set = true; + transfer_memory = t_mem; +} + +Kernel::KReadableEvent& HidbusBase::GetSendCommandAsycEvent() const { + return send_command_async_event->GetReadableEvent(); +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.h b/src/core/hle/service/hid/hidbus/hidbus_base.h new file mode 100644 index 000000000..d3960f506 --- /dev/null +++ b/src/core/hle/service/hid/hidbus/hidbus_base.h @@ -0,0 +1,178 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include "common/common_types.h" +#include "core/hle/result.h" + +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + +namespace Service::KernelHelpers { +class ServiceContext; +} + +namespace Service::HID { + +// This is nn::hidbus::JoyPollingMode +enum class JoyPollingMode : u32 { + SixAxisSensorDisable, + SixAxisSensorEnable, + ButtonOnly, +}; + +struct DataAccessorHeader { + Result result{ResultUnknown}; + INSERT_PADDING_WORDS(0x1); + std::array<u8, 0x18> unused{}; + u64 latest_entry{}; + u64 total_entries{}; +}; +static_assert(sizeof(DataAccessorHeader) == 0x30, "DataAccessorHeader is an invalid size"); + +struct JoyDisableSixAxisPollingData { + std::array<u8, 0x26> data; + u8 out_size; + INSERT_PADDING_BYTES(0x1); + u64 sampling_number; +}; +static_assert(sizeof(JoyDisableSixAxisPollingData) == 0x30, + "JoyDisableSixAxisPollingData is an invalid size"); + +struct JoyEnableSixAxisPollingData { + std::array<u8, 0x8> data; + u8 out_size; + INSERT_PADDING_BYTES(0x7); + u64 sampling_number; +}; +static_assert(sizeof(JoyEnableSixAxisPollingData) == 0x18, + "JoyEnableSixAxisPollingData is an invalid size"); + +struct JoyButtonOnlyPollingData { + std::array<u8, 0x2c> data; + u8 out_size; + INSERT_PADDING_BYTES(0x3); + u64 sampling_number; +}; +static_assert(sizeof(JoyButtonOnlyPollingData) == 0x38, + "JoyButtonOnlyPollingData is an invalid size"); + +struct JoyDisableSixAxisPollingEntry { + u64 sampling_number; + JoyDisableSixAxisPollingData polling_data; +}; +static_assert(sizeof(JoyDisableSixAxisPollingEntry) == 0x38, + "JoyDisableSixAxisPollingEntry is an invalid size"); + +struct JoyEnableSixAxisPollingEntry { + u64 sampling_number; + JoyEnableSixAxisPollingData polling_data; +}; +static_assert(sizeof(JoyEnableSixAxisPollingEntry) == 0x20, + "JoyEnableSixAxisPollingEntry is an invalid size"); + +struct JoyButtonOnlyPollingEntry { + u64 sampling_number; + JoyButtonOnlyPollingData polling_data; +}; +static_assert(sizeof(JoyButtonOnlyPollingEntry) == 0x40, + "JoyButtonOnlyPollingEntry is an invalid size"); + +struct JoyDisableSixAxisDataAccessor { + DataAccessorHeader header{}; + std::array<JoyDisableSixAxisPollingEntry, 0xb> entries{}; +}; +static_assert(sizeof(JoyDisableSixAxisDataAccessor) == 0x298, + "JoyDisableSixAxisDataAccessor is an invalid size"); + +struct JoyEnableSixAxisDataAccessor { + DataAccessorHeader header{}; + std::array<JoyEnableSixAxisPollingEntry, 0xb> entries{}; +}; +static_assert(sizeof(JoyEnableSixAxisDataAccessor) == 0x190, + "JoyEnableSixAxisDataAccessor is an invalid size"); + +struct ButtonOnlyPollingDataAccessor { + DataAccessorHeader header; + std::array<JoyButtonOnlyPollingEntry, 0xb> entries; +}; +static_assert(sizeof(ButtonOnlyPollingDataAccessor) == 0x2F0, + "ButtonOnlyPollingDataAccessor is an invalid size"); + +class HidbusBase { +public: + explicit HidbusBase(KernelHelpers::ServiceContext& service_context_); + virtual ~HidbusBase(); + + void ActivateDevice(); + + void DeactivateDevice(); + + bool IsDeviceActivated() const; + + // Enables/disables the device + void Enable(bool enable); + + // returns true if device is enabled + bool IsEnabled() const; + + // returns true if polling mode is enabled + bool IsPollingMode() const; + + // returns polling mode + JoyPollingMode GetPollingMode() const; + + // Sets and enables JoyPollingMode + void SetPollingMode(JoyPollingMode mode); + + // Disables JoyPollingMode + void DisablePollingMode(); + + // Called on EnableJoyPollingReceiveMode + void SetTransferMemoryPointer(u8* t_mem); + + Kernel::KReadableEvent& GetSendCommandAsycEvent() const; + + virtual void OnInit() {} + + virtual void OnRelease() {} + + // Updates device transfer memory + virtual void OnUpdate() {} + + // Returns the device ID of the joycon + virtual u8 GetDeviceId() const { + return {}; + } + + // Assigns a command from data + virtual bool SetCommand(const std::vector<u8>& data) { + return {}; + } + + // Returns a reply from a command + virtual std::vector<u8> GetReply() const { + return {}; + } + +protected: + bool is_activated{}; + bool device_enabled{}; + bool polling_mode_enabled{}; + JoyPollingMode polling_mode = {}; + // TODO(German77): All data accessors need to be replaced with a ring lifo object + JoyDisableSixAxisDataAccessor disable_sixaxis_data{}; + JoyEnableSixAxisDataAccessor enable_sixaxis_data{}; + ButtonOnlyPollingDataAccessor button_only_data{}; + + u8* transfer_memory{nullptr}; + bool is_transfer_memory_set{}; + + Kernel::KEvent* send_command_async_event; + KernelHelpers::ServiceContext& service_context; +}; +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/ringcon.cpp b/src/core/hle/service/hid/hidbus/ringcon.cpp new file mode 100644 index 000000000..ad223d649 --- /dev/null +++ b/src/core/hle/service/hid/hidbus/ringcon.cpp @@ -0,0 +1,285 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hid/emulated_devices.h" +#include "core/hid/hid_core.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/service/hid/hidbus/ringcon.h" + +namespace Service::HID { + +RingController::RingController(Core::HID::HIDCore& hid_core_, + KernelHelpers::ServiceContext& service_context_) + : HidbusBase(service_context_) { + input = hid_core_.GetEmulatedDevices(); +} + +RingController::~RingController() = default; + +void RingController::OnInit() { + return; +} + +void RingController::OnRelease() { + return; +}; + +void RingController::OnUpdate() { + if (!is_activated) { + return; + } + + if (!device_enabled) { + return; + } + + if (!polling_mode_enabled || !is_transfer_memory_set) { + return; + } + + // TODO: Increment multitasking counters from motion and sensor data + + switch (polling_mode) { + case JoyPollingMode::SixAxisSensorEnable: { + enable_sixaxis_data.header.total_entries = 10; + enable_sixaxis_data.header.result = ResultSuccess; + const auto& last_entry = + enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry]; + + enable_sixaxis_data.header.latest_entry = + (enable_sixaxis_data.header.latest_entry + 1) % 10; + auto& curr_entry = enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry]; + + curr_entry.sampling_number = last_entry.sampling_number + 1; + curr_entry.polling_data.sampling_number = curr_entry.sampling_number; + + const RingConData ringcon_value = GetSensorValue(); + curr_entry.polling_data.out_size = sizeof(ringcon_value); + std::memcpy(curr_entry.polling_data.data.data(), &ringcon_value, sizeof(ringcon_value)); + + std::memcpy(transfer_memory, &enable_sixaxis_data, sizeof(enable_sixaxis_data)); + break; + } + default: + LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode); + break; + } +} + +RingController::RingConData RingController::GetSensorValue() const { + RingConData ringcon_sensor_value{ + .status = DataValid::Valid, + .data = 0, + }; + + const f32 force_value = input->GetRingSensorForce().force * range; + ringcon_sensor_value.data = static_cast<s16>(force_value) + idle_value; + + return ringcon_sensor_value; +} + +u8 RingController::GetDeviceId() const { + return device_id; +} + +std::vector<u8> RingController::GetReply() const { + const RingConCommands current_command = command; + + switch (current_command) { + case RingConCommands::GetFirmwareVersion: + return GetFirmwareVersionReply(); + case RingConCommands::ReadId: + return GetReadIdReply(); + case RingConCommands::c20105: + return GetC020105Reply(); + case RingConCommands::ReadUnkCal: + return GetReadUnkCalReply(); + case RingConCommands::ReadFactoryCal: + return GetReadFactoryCalReply(); + case RingConCommands::ReadUserCal: + return GetReadUserCalReply(); + case RingConCommands::ReadRepCount: + return GetReadRepCountReply(); + case RingConCommands::ReadTotalPushCount: + return GetReadTotalPushCountReply(); + case RingConCommands::ResetRepCount: + return GetResetRepCountReply(); + case RingConCommands::SaveCalData: + return GetSaveDataReply(); + default: + return GetErrorReply(); + } +} + +bool RingController::SetCommand(const std::vector<u8>& data) { + if (data.size() < 4) { + LOG_ERROR(Service_HID, "Command size not supported {}", data.size()); + command = RingConCommands::Error; + return false; + } + + std::memcpy(&command, data.data(), sizeof(RingConCommands)); + + switch (command) { + case RingConCommands::GetFirmwareVersion: + case RingConCommands::ReadId: + case RingConCommands::c20105: + case RingConCommands::ReadUnkCal: + case RingConCommands::ReadFactoryCal: + case RingConCommands::ReadUserCal: + case RingConCommands::ReadRepCount: + case RingConCommands::ReadTotalPushCount: + ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes"); + send_command_async_event->GetWritableEvent().Signal(); + return true; + case RingConCommands::ResetRepCount: + ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes"); + total_rep_count = 0; + send_command_async_event->GetWritableEvent().Signal(); + return true; + case RingConCommands::SaveCalData: { + ASSERT_MSG(data.size() == 0x14, "data.size is not 0x14 bytes"); + + SaveCalData save_info{}; + std::memcpy(&save_info, data.data(), sizeof(SaveCalData)); + user_calibration = save_info.calibration; + send_command_async_event->GetWritableEvent().Signal(); + return true; + } + default: + LOG_ERROR(Service_HID, "Command not implemented {}", command); + command = RingConCommands::Error; + // Signal a reply to avoid softlocking the game + send_command_async_event->GetWritableEvent().Signal(); + return false; + } +} + +std::vector<u8> RingController::GetFirmwareVersionReply() const { + const FirmwareVersionReply reply{ + .status = DataValid::Valid, + .firmware = version, + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetReadIdReply() const { + // The values are hardcoded from a real joycon + const ReadIdReply reply{ + .status = DataValid::Valid, + .id_l_x0 = 8, + .id_l_x0_2 = 41, + .id_l_x4 = 22294, + .id_h_x0 = 19777, + .id_h_x0_2 = 13621, + .id_h_x4 = 8245, + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetC020105Reply() const { + const Cmd020105Reply reply{ + .status = DataValid::Valid, + .data = 1, + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetReadUnkCalReply() const { + const ReadUnkCalReply reply{ + .status = DataValid::Valid, + .data = 0, + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetReadFactoryCalReply() const { + const ReadFactoryCalReply reply{ + .status = DataValid::Valid, + .calibration = factory_calibration, + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetReadUserCalReply() const { + const ReadUserCalReply reply{ + .status = DataValid::Valid, + .calibration = user_calibration, + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetReadRepCountReply() const { + const GetThreeByteReply reply{ + .status = DataValid::Valid, + .data = {total_rep_count, 0, 0}, + .crc = GetCrcValue({total_rep_count, 0, 0, 0}), + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetReadTotalPushCountReply() const { + const GetThreeByteReply reply{ + .status = DataValid::Valid, + .data = {total_push_count, 0, 0}, + .crc = GetCrcValue({total_push_count, 0, 0, 0}), + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetResetRepCountReply() const { + return GetReadRepCountReply(); +} + +std::vector<u8> RingController::GetSaveDataReply() const { + const StatusReply reply{ + .status = DataValid::Valid, + }; + + return GetDataVector(reply); +} + +std::vector<u8> RingController::GetErrorReply() const { + const ErrorReply reply{ + .status = DataValid::BadCRC, + }; + + return GetDataVector(reply); +} + +u8 RingController::GetCrcValue(const std::vector<u8>& data) const { + u8 crc = 0; + for (std::size_t index = 0; index < data.size(); index++) { + for (u8 i = 0x80; i > 0; i >>= 1) { + bool bit = (crc & 0x80) != 0; + if ((data[index] & i) != 0) { + bit = !bit; + } + crc <<= 1; + if (bit) { + crc ^= 0x8d; + } + } + } + return crc; +} + +template <typename T> +std::vector<u8> RingController::GetDataVector(const T& reply) const { + static_assert(std::is_trivially_copyable_v<T>); + std::vector<u8> data; + data.resize(sizeof(reply)); + std::memcpy(data.data(), &reply, sizeof(reply)); + return data; +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/ringcon.h b/src/core/hle/service/hid/hidbus/ringcon.h new file mode 100644 index 000000000..b37df50ac --- /dev/null +++ b/src/core/hle/service/hid/hidbus/ringcon.h @@ -0,0 +1,253 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> + +#include "common/common_types.h" +#include "core/hle/service/hid/hidbus/hidbus_base.h" + +namespace Core::HID { +class EmulatedDevices; +} // namespace Core::HID + +namespace Service::HID { + +class RingController final : public HidbusBase { +public: + explicit RingController(Core::HID::HIDCore& hid_core_, + KernelHelpers::ServiceContext& service_context_); + ~RingController() override; + + void OnInit() override; + + void OnRelease() override; + + // Updates ringcon transfer memory + void OnUpdate() override; + + // Returns the device ID of the joycon + u8 GetDeviceId() const override; + + // Assigns a command from data + bool SetCommand(const std::vector<u8>& data) override; + + // Returns a reply from a command + std::vector<u8> GetReply() const override; + +private: + // These values are obtained from a real ring controller + static constexpr s16 idle_value = 2280; + static constexpr s16 idle_deadzone = 120; + static constexpr s16 range = 2500; + + // Most missing command names are leftovers from other firmware versions + enum class RingConCommands : u32 { + GetFirmwareVersion = 0x00020000, + ReadId = 0x00020100, + JoyPolling = 0x00020101, + Unknown1 = 0x00020104, + c20105 = 0x00020105, + Unknown2 = 0x00020204, + Unknown3 = 0x00020304, + Unknown4 = 0x00020404, + ReadUnkCal = 0x00020504, + ReadFactoryCal = 0x00020A04, + Unknown5 = 0x00021104, + Unknown6 = 0x00021204, + Unknown7 = 0x00021304, + ReadUserCal = 0x00021A04, + ReadRepCount = 0x00023104, + ReadTotalPushCount = 0x00023204, + ResetRepCount = 0x04013104, + Unknown8 = 0x04011104, + Unknown9 = 0x04011204, + Unknown10 = 0x04011304, + SaveCalData = 0x10011A04, + Error = 0xFFFFFFFF, + }; + + enum class DataValid : u32 { + Valid, + BadCRC, + Cal, + }; + + struct FirmwareVersion { + u8 sub; + u8 main; + }; + static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid size"); + + struct FactoryCalibration { + s32_le os_max; + s32_le hk_max; + s32_le zero_min; + s32_le zero_max; + }; + static_assert(sizeof(FactoryCalibration) == 0x10, "FactoryCalibration is an invalid size"); + + struct CalibrationValue { + s16 value; + u16 crc; + }; + static_assert(sizeof(CalibrationValue) == 0x4, "CalibrationValue is an invalid size"); + + struct UserCalibration { + CalibrationValue os_max; + CalibrationValue hk_max; + CalibrationValue zero; + }; + static_assert(sizeof(UserCalibration) == 0xC, "UserCalibration is an invalid size"); + + struct SaveCalData { + RingConCommands command; + UserCalibration calibration; + INSERT_PADDING_BYTES_NOINIT(4); + }; + static_assert(sizeof(SaveCalData) == 0x14, "SaveCalData is an invalid size"); + static_assert(std::is_trivially_copyable_v<SaveCalData>, + "SaveCalData must be trivially copyable"); + + struct FirmwareVersionReply { + DataValid status; + FirmwareVersion firmware; + INSERT_PADDING_BYTES(0x2); + }; + static_assert(sizeof(FirmwareVersionReply) == 0x8, "FirmwareVersionReply is an invalid size"); + + struct Cmd020105Reply { + DataValid status; + u8 data; + INSERT_PADDING_BYTES(0x3); + }; + static_assert(sizeof(Cmd020105Reply) == 0x8, "Cmd020105Reply is an invalid size"); + + struct StatusReply { + DataValid status; + }; + static_assert(sizeof(StatusReply) == 0x4, "StatusReply is an invalid size"); + + struct GetThreeByteReply { + DataValid status; + std::array<u8, 3> data; + u8 crc; + }; + static_assert(sizeof(GetThreeByteReply) == 0x8, "GetThreeByteReply is an invalid size"); + + struct ReadUnkCalReply { + DataValid status; + u16 data; + INSERT_PADDING_BYTES(0x2); + }; + static_assert(sizeof(ReadUnkCalReply) == 0x8, "ReadUnkCalReply is an invalid size"); + + struct ReadFactoryCalReply { + DataValid status; + FactoryCalibration calibration; + }; + static_assert(sizeof(ReadFactoryCalReply) == 0x14, "ReadFactoryCalReply is an invalid size"); + + struct ReadUserCalReply { + DataValid status; + UserCalibration calibration; + INSERT_PADDING_BYTES(0x4); + }; + static_assert(sizeof(ReadUserCalReply) == 0x14, "ReadUserCalReply is an invalid size"); + + struct ReadIdReply { + DataValid status; + u16 id_l_x0; + u16 id_l_x0_2; + u16 id_l_x4; + u16 id_h_x0; + u16 id_h_x0_2; + u16 id_h_x4; + }; + static_assert(sizeof(ReadIdReply) == 0x10, "ReadIdReply is an invalid size"); + + struct ErrorReply { + DataValid status; + INSERT_PADDING_BYTES(0x3); + }; + static_assert(sizeof(ErrorReply) == 0x8, "ErrorReply is an invalid size"); + + struct RingConData { + DataValid status; + s16_le data; + INSERT_PADDING_BYTES(0x2); + }; + static_assert(sizeof(RingConData) == 0x8, "RingConData is an invalid size"); + + // Returns RingConData struct with pressure sensor values + RingConData GetSensorValue() const; + + // Returns 8 byte reply with firmware version + std::vector<u8> GetFirmwareVersionReply() const; + + // Returns 16 byte reply with ID values + std::vector<u8> GetReadIdReply() const; + + // (STUBBED) Returns 8 byte reply + std::vector<u8> GetC020105Reply() const; + + // (STUBBED) Returns 8 byte empty reply + std::vector<u8> GetReadUnkCalReply() const; + + // Returns 20 byte reply with factory calibration values + std::vector<u8> GetReadFactoryCalReply() const; + + // Returns 20 byte reply with user calibration values + std::vector<u8> GetReadUserCalReply() const; + + // Returns 8 byte reply + std::vector<u8> GetReadRepCountReply() const; + + // Returns 8 byte reply + std::vector<u8> GetReadTotalPushCountReply() const; + + // Returns 8 byte reply + std::vector<u8> GetResetRepCountReply() const; + + // Returns 4 byte save data reply + std::vector<u8> GetSaveDataReply() const; + + // Returns 8 byte error reply + std::vector<u8> GetErrorReply() const; + + // Returns 8 bit redundancy check from provided data + u8 GetCrcValue(const std::vector<u8>& data) const; + + // Converts structs to an u8 vector equivalent + template <typename T> + std::vector<u8> GetDataVector(const T& reply) const; + + RingConCommands command{RingConCommands::Error}; + + // These counters are used in multitasking mode while the switch is sleeping + // Total steps taken + u8 total_rep_count = 0; + // Total times the ring was pushed + u8 total_push_count = 0; + + const u8 device_id = 0x20; + const FirmwareVersion version = { + .sub = 0x0, + .main = 0x2c, + }; + const FactoryCalibration factory_calibration = { + .os_max = idle_value + range + idle_deadzone, + .hk_max = idle_value - range - idle_deadzone, + .zero_min = idle_value - idle_deadzone, + .zero_max = idle_value + idle_deadzone, + }; + UserCalibration user_calibration = { + .os_max = {.value = range, .crc = 228}, + .hk_max = {.value = -range, .crc = 239}, + .zero = {.value = idle_value, .crc = 225}, + }; + + Core::HID::EmulatedDevices* input; +}; +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/starlink.cpp b/src/core/hle/service/hid/hidbus/starlink.cpp new file mode 100644 index 000000000..dd439f60a --- /dev/null +++ b/src/core/hle/service/hid/hidbus/starlink.cpp @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hle/service/hid/hidbus/starlink.h" + +namespace Service::HID { +constexpr u8 DEVICE_ID = 0x28; + +Starlink::Starlink(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_) + : HidbusBase(service_context_) {} +Starlink::~Starlink() = default; + +void Starlink::OnInit() { + return; +} + +void Starlink::OnRelease() { + return; +}; + +void Starlink::OnUpdate() { + if (!is_activated) { + return; + } + if (!device_enabled) { + return; + } + if (!polling_mode_enabled || !is_transfer_memory_set) { + return; + } + + LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode); +} + +u8 Starlink::GetDeviceId() const { + return DEVICE_ID; +} + +std::vector<u8> Starlink::GetReply() const { + return {}; +} + +bool Starlink::SetCommand(const std::vector<u8>& data) { + LOG_ERROR(Service_HID, "Command not implemented"); + return false; +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/starlink.h b/src/core/hle/service/hid/hidbus/starlink.h new file mode 100644 index 000000000..0b1b7ba49 --- /dev/null +++ b/src/core/hle/service/hid/hidbus/starlink.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/service/hid/hidbus/hidbus_base.h" + +namespace Core::HID { +class EmulatedController; +} // namespace Core::HID + +namespace Service::HID { + +class Starlink final : public HidbusBase { +public: + explicit Starlink(Core::HID::HIDCore& hid_core_, + KernelHelpers::ServiceContext& service_context_); + ~Starlink() override; + + void OnInit() override; + + void OnRelease() override; + + // Updates ringcon transfer memory + void OnUpdate() override; + + // Returns the device ID of the joycon + u8 GetDeviceId() const override; + + // Assigns a command from data + bool SetCommand(const std::vector<u8>& data) override; + + // Returns a reply from a command + std::vector<u8> GetReply() const override; +}; + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/stubbed.cpp b/src/core/hle/service/hid/hidbus/stubbed.cpp new file mode 100644 index 000000000..e477443e3 --- /dev/null +++ b/src/core/hle/service/hid/hidbus/stubbed.cpp @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hle/service/hid/hidbus/stubbed.h" + +namespace Service::HID { +constexpr u8 DEVICE_ID = 0xFF; + +HidbusStubbed::HidbusStubbed(Core::HID::HIDCore& hid_core_, + KernelHelpers::ServiceContext& service_context_) + : HidbusBase(service_context_) {} +HidbusStubbed::~HidbusStubbed() = default; + +void HidbusStubbed::OnInit() { + return; +} + +void HidbusStubbed::OnRelease() { + return; +}; + +void HidbusStubbed::OnUpdate() { + if (!is_activated) { + return; + } + if (!device_enabled) { + return; + } + if (!polling_mode_enabled || !is_transfer_memory_set) { + return; + } + + LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode); +} + +u8 HidbusStubbed::GetDeviceId() const { + return DEVICE_ID; +} + +std::vector<u8> HidbusStubbed::GetReply() const { + return {}; +} + +bool HidbusStubbed::SetCommand(const std::vector<u8>& data) { + LOG_ERROR(Service_HID, "Command not implemented"); + return false; +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hidbus/stubbed.h b/src/core/hle/service/hid/hidbus/stubbed.h new file mode 100644 index 000000000..91165ceff --- /dev/null +++ b/src/core/hle/service/hid/hidbus/stubbed.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/service/hid/hidbus/hidbus_base.h" + +namespace Core::HID { +class EmulatedController; +} // namespace Core::HID + +namespace Service::HID { + +class HidbusStubbed final : public HidbusBase { +public: + explicit HidbusStubbed(Core::HID::HIDCore& hid_core_, + KernelHelpers::ServiceContext& service_context_); + ~HidbusStubbed() override; + + void OnInit() override; + + void OnRelease() override; + + // Updates ringcon transfer memory + void OnUpdate() override; + + // Returns the device ID of the joycon + u8 GetDeviceId() const override; + + // Assigns a command from data + bool SetCommand(const std::vector<u8>& data) override; + + // Returns a reply from a command + std::vector<u8> GetReply() const override; +}; + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index 8812b8ecb..6a3453457 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp @@ -1,15 +1,28 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <algorithm> +#include <random> #include "core/core.h" #include "core/core_timing.h" +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/service/hid/errors.h" #include "core/hle/service/hid/irs.h" +#include "core/hle/service/hid/irsensor/clustering_processor.h" +#include "core/hle/service/hid/irsensor/image_transfer_processor.h" +#include "core/hle/service/hid/irsensor/ir_led_processor.h" +#include "core/hle/service/hid/irsensor/moment_processor.h" +#include "core/hle/service/hid/irsensor/pointing_processor.h" +#include "core/hle/service/hid/irsensor/tera_plugin_processor.h" +#include "core/memory.h" -namespace Service::HID { +namespace Service::IRS { IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} { // clang-format off @@ -35,25 +48,41 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} { }; // clang-format on + u8* raw_shared_memory = system.Kernel().GetIrsSharedMem().GetPointer(); RegisterHandlers(functions); + shared_memory = std::construct_at(reinterpret_cast<StatusManager*>(raw_shared_memory)); + + npad_device = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); } +IRS::~IRS() = default; void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", + applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", + applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_IRS, "called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); @@ -61,114 +90,462 @@ void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) { } void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::IrSensor::IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.applet_resource_user_id); + + auto result = IsIrCameraHandleValid(parameters.camera_handle); + if (result.IsSuccess()) { + // TODO: Stop Image processor + result = ResultSuccess; + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::IrSensor::IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + Core::IrSensor::PackedMomentProcessorConfig processor_config; + }; + static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.applet_resource_user_id); + + const auto result = IsIrCameraHandleValid(parameters.camera_handle); + + if (result.IsSuccess()) { + auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); + MakeProcessor<MomentProcessor>(parameters.camera_handle, device); + auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); + image_transfer_processor.SetConfig(parameters.processor_config); + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::IrSensor::IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + Core::IrSensor::PackedClusteringProcessorConfig processor_config; + }; + static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.applet_resource_user_id); + + auto result = IsIrCameraHandleValid(parameters.camera_handle); + + if (result.IsSuccess()) { + auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); + MakeProcessorWithCoreContext<ClusteringProcessor>(parameters.camera_handle, device); + auto& image_transfer_processor = + GetProcessor<ClusteringProcessor>(parameters.camera_handle); + image_transfer_processor.SetConfig(parameters.processor_config); + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::IrSensor::IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + Core::IrSensor::PackedImageTransferProcessorConfig processor_config; + u32 transfer_memory_size; + }; + static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + const auto t_mem_handle{ctx.GetCopyHandle(0)}; + + auto t_mem = + system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle); + + if (t_mem.IsNull()) { + LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + ASSERT_MSG(t_mem->GetSize() == parameters.transfer_memory_size, "t_mem has incorrect size"); + + u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress()); + + LOG_INFO(Service_IRS, + "called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, " + "applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.transfer_memory_size, t_mem->GetSize(), parameters.applet_resource_user_id); + + const auto result = IsIrCameraHandleValid(parameters.camera_handle); + + if (result.IsSuccess()) { + auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); + MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device); + auto& image_transfer_processor = + GetProcessor<ImageTransferProcessor>(parameters.camera_handle); + image_transfer_processor.SetConfig(parameters.processor_config); + image_transfer_processor.SetTransferMemoryPointer(transfer_memory); + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::IrSensor::IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.applet_resource_user_id); + + const auto result = IsIrCameraHandleValid(parameters.camera_handle); + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + + const auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); + + if (device.mode != Core::IrSensor::IrSensorMode::ImageTransferProcessor) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidProcessorState); + return; + } - IPC::ResponseBuilder rb{ctx, 5}; + std::vector<u8> data{}; + const auto& image_transfer_processor = + GetProcessor<ImageTransferProcessor>(parameters.camera_handle); + const auto& state = image_transfer_processor.GetState(data); + + ctx.WriteBuffer(data); + IPC::ResponseBuilder rb{ctx, 6}; rb.Push(ResultSuccess); - rb.PushRaw<u64>(system.CoreTiming().GetCPUTicks()); - rb.PushRaw<u32>(0); + rb.PushRaw(state); } void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::IrSensor::IrCameraHandle camera_handle; + Core::IrSensor::PackedTeraPluginProcessorConfig processor_config; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING( + Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, " + "applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.processor_config.mode, parameters.processor_config.required_mcu_version.major, + parameters.processor_config.required_mcu_version.minor, parameters.applet_resource_user_id); + + const auto result = IsIrCameraHandleValid(parameters.camera_handle); + + if (result.IsSuccess()) { + auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); + MakeProcessor<TeraPluginProcessor>(parameters.camera_handle, device); + auto& image_transfer_processor = + GetProcessor<TeraPluginProcessor>(parameters.camera_handle); + image_transfer_processor.SetConfig(parameters.processor_config); + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()}; + + if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid && + npad_id != Core::HID::NpadIdType::Handheld) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(Service::HID::InvalidNpadId); + return; + } + + Core::IrSensor::IrCameraHandle camera_handle{ + .npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)), + .npad_type = Core::HID::NpadStyleIndex::None, + }; + + LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id, + camera_handle.npad_id, camera_handle.npad_type); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.PushRaw<u32>(device_handle); + rb.PushRaw(camera_handle); } void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; + const auto processor_config{rp.PopRaw<Core::IrSensor::PackedPointingProcessorConfig>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING( + Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}", + camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major, + processor_config.required_mcu_version.minor, applet_resource_user_id); + + auto result = IsIrCameraHandleValid(camera_handle); + + if (result.IsSuccess()) { + auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); + MakeProcessor<PointingProcessor>(camera_handle, device); + auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle); + image_transfer_processor.SetConfig(processor_config); + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::IrSensor::IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.applet_resource_user_id); + + auto result = IsIrCameraHandleValid(parameters.camera_handle); + if (result.IsSuccess()) { + // TODO: Suspend image processor + result = ResultSuccess; + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; + const auto mcu_version{rp.PopRaw<Core::IrSensor::PackedMcuVersion>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING( + Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}, mcu_version={}.{}", + camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major, + mcu_version.minor); + + auto result = IsIrCameraHandleValid(camera_handle); + if (result.IsSuccess()) { + // TODO: Check firmware version + result = ResultSuccess; + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; + const auto function_level{rp.PopRaw<Core::IrSensor::PackedFunctionLevel>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING( + Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}", + camera_handle.npad_type, camera_handle.npad_id, function_level.function_level, + applet_resource_user_id); + + auto result = IsIrCameraHandleValid(camera_handle); + if (result.IsSuccess()) { + // TODO: Set Function level + result = ResultSuccess; + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::IrSensor::IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + Core::IrSensor::PackedImageTransferProcessorExConfig processor_config; + u64 transfer_memory_size; + }; + static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + const auto t_mem_handle{ctx.GetCopyHandle(0)}; + + auto t_mem = + system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle); + + u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress()); + + LOG_INFO(Service_IRS, + "called, npad_type={}, npad_id={}, transfer_memory_size={}, " + "applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.transfer_memory_size, parameters.applet_resource_user_id); + + auto result = IsIrCameraHandleValid(parameters.camera_handle); + + if (result.IsSuccess()) { + auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); + MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device); + auto& image_transfer_processor = + GetProcessor<ImageTransferProcessor>(parameters.camera_handle); + image_transfer_processor.SetConfig(parameters.processor_config); + image_transfer_processor.SetTransferMemoryPointer(transfer_memory); + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; + const auto processor_config{rp.PopRaw<Core::IrSensor::PackedIrLedProcessorConfig>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, light_target={}, mcu_version={}.{} " + "applet_resource_user_id={}", + camera_handle.npad_type, camera_handle.npad_id, processor_config.light_target, + processor_config.required_mcu_version.major, + processor_config.required_mcu_version.minor, applet_resource_user_id); + + auto result = IsIrCameraHandleValid(camera_handle); + + if (result.IsSuccess()) { + auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); + MakeProcessor<IrLedProcessor>(camera_handle, device); + auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle); + image_transfer_processor.SetConfig(processor_config); + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::IrSensor::IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.applet_resource_user_id); + + auto result = IsIrCameraHandleValid(parameters.camera_handle); + if (result.IsSuccess()) { + // TODO: Stop image processor async + result = ResultSuccess; + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::IrSensor::PackedFunctionLevel function_level; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_IRS, "(STUBBED) called, function_level={}, applet_resource_user_id={}", + parameters.function_level.function_level, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } -IRS::~IRS() = default; +Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const { + if (camera_handle.npad_id > + static_cast<u8>(NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) { + return InvalidIrCameraHandle; + } + if (camera_handle.npad_type != Core::HID::NpadStyleIndex::None) { + return InvalidIrCameraHandle; + } + return ResultSuccess; +} + +Core::IrSensor::DeviceFormat& IRS::GetIrCameraSharedMemoryDeviceEntry( + const Core::IrSensor::IrCameraHandle& camera_handle) { + const auto npad_id_max_index = static_cast<u8>(sizeof(StatusManager::device)); + ASSERT_MSG(camera_handle.npad_id < npad_id_max_index, "invalid npad_id"); + return shared_memory->device[camera_handle.npad_id]; +} IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} { // clang-format off @@ -185,4 +562,4 @@ IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} { IRS_SYS::~IRS_SYS() = default; -} // namespace Service::HID +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h index 9bc6462b0..2e6115c73 100644 --- a/src/core/hle/service/hid/irs.h +++ b/src/core/hle/service/hid/irs.h @@ -1,16 +1,22 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include "core/hid/hid_types.h" +#include "core/hid/irs_types.h" +#include "core/hle/service/hid/irsensor/processor_base.h" #include "core/hle/service/service.h" namespace Core { class System; } -namespace Service::HID { +namespace Core::HID { +class EmulatedController; +} // namespace Core::HID + +namespace Service::IRS { class IRS final : public ServiceFramework<IRS> { public: @@ -18,6 +24,20 @@ public: ~IRS() override; private: + // This is nn::irsensor::detail::AruidFormat + struct AruidFormat { + u64 sensor_aruid; + u64 sensor_aruid_status; + }; + static_assert(sizeof(AruidFormat) == 0x10, "AruidFormat is an invalid size"); + + // This is nn::irsensor::detail::StatusManager + struct StatusManager { + std::array<Core::IrSensor::DeviceFormat, 9> device; + std::array<AruidFormat, 5> aruid; + }; + static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size"); + void ActivateIrsensor(Kernel::HLERequestContext& ctx); void DeactivateIrsensor(Kernel::HLERequestContext& ctx); void GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx); @@ -37,7 +57,55 @@ private: void StopImageProcessorAsync(Kernel::HLERequestContext& ctx); void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx); - const u32 device_handle{0xABCD}; + Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const; + Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry( + const Core::IrSensor::IrCameraHandle& camera_handle); + + template <typename T> + void MakeProcessor(const Core::IrSensor::IrCameraHandle& handle, + Core::IrSensor::DeviceFormat& device_state) { + const auto index = static_cast<std::size_t>(handle.npad_id); + if (index > sizeof(processors)) { + LOG_CRITICAL(Service_IRS, "Invalid index {}", index); + return; + } + processors[index] = std::make_unique<T>(device_state); + } + + template <typename T> + void MakeProcessorWithCoreContext(const Core::IrSensor::IrCameraHandle& handle, + Core::IrSensor::DeviceFormat& device_state) { + const auto index = static_cast<std::size_t>(handle.npad_id); + if (index > sizeof(processors)) { + LOG_CRITICAL(Service_IRS, "Invalid index {}", index); + return; + } + processors[index] = std::make_unique<T>(system.HIDCore(), device_state, index); + } + + template <typename T> + T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) { + const auto index = static_cast<std::size_t>(handle.npad_id); + if (index > sizeof(processors)) { + LOG_CRITICAL(Service_IRS, "Invalid index {}", index); + return static_cast<T&>(*processors[0]); + } + return static_cast<T&>(*processors[index]); + } + + template <typename T> + const T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) const { + const auto index = static_cast<std::size_t>(handle.npad_id); + if (index > sizeof(processors)) { + LOG_CRITICAL(Service_IRS, "Invalid index {}", index); + return static_cast<T&>(*processors[0]); + } + return static_cast<T&>(*processors[index]); + } + + Core::HID::EmulatedController* npad_device = nullptr; + StatusManager* shared_memory = nullptr; + std::array<std::unique_ptr<ProcessorBase>, 9> processors{}; }; class IRS_SYS final : public ServiceFramework<IRS_SYS> { @@ -46,4 +114,4 @@ public: ~IRS_SYS() override; }; -} // namespace Service::HID +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irs_ring_lifo.h b/src/core/hle/service/hid/irs_ring_lifo.h new file mode 100644 index 000000000..255d1d296 --- /dev/null +++ b/src/core/hle/service/hid/irs_ring_lifo.h @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <array> + +#include "common/common_types.h" + +namespace Service::IRS { + +template <typename State, std::size_t max_buffer_size> +struct Lifo { + s64 sampling_number{}; + s64 buffer_count{}; + std::array<State, max_buffer_size> entries{}; + + const State& ReadCurrentEntry() const { + return entries[GetBufferTail()]; + } + + const State& ReadPreviousEntry() const { + return entries[GetPreviousEntryIndex()]; + } + + s64 GetBufferTail() const { + return sampling_number % max_buffer_size; + } + + std::size_t GetPreviousEntryIndex() const { + return static_cast<size_t>((GetBufferTail() + max_buffer_size - 1) % max_buffer_size); + } + + std::size_t GetNextEntryIndex() const { + return static_cast<size_t>((GetBufferTail() + 1) % max_buffer_size); + } + + void WriteNextEntry(const State& new_state) { + if (buffer_count < static_cast<s64>(max_buffer_size)) { + buffer_count++; + } + sampling_number++; + entries[GetBufferTail()] = new_state; + } +}; + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.cpp b/src/core/hle/service/hid/irsensor/clustering_processor.cpp new file mode 100644 index 000000000..e2f4ae876 --- /dev/null +++ b/src/core/hle/service/hid/irsensor/clustering_processor.cpp @@ -0,0 +1,265 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include <queue> + +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hle/service/hid/irsensor/clustering_processor.h" + +namespace Service::IRS { +ClusteringProcessor::ClusteringProcessor(Core::HID::HIDCore& hid_core_, + Core::IrSensor::DeviceFormat& device_format, + std::size_t npad_index) + : device{device_format} { + npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index); + + device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor; + device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; + SetDefaultConfig(); + + shared_memory = std::construct_at( + reinterpret_cast<ClusteringSharedMemory*>(&device_format.state.processor_raw_data)); + + Core::HID::ControllerUpdateCallback engine_callback{ + .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); }, + .is_npad_service = true, + }; + callback_key = npad_device->SetCallback(engine_callback); +} + +ClusteringProcessor::~ClusteringProcessor() { + npad_device->DeleteCallback(callback_key); +}; + +void ClusteringProcessor::StartProcessor() { + device.camera_status = Core::IrSensor::IrCameraStatus::Available; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready; +} + +void ClusteringProcessor::SuspendProcessor() {} + +void ClusteringProcessor::StopProcessor() {} + +void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) { + if (type != Core::HID::ControllerTriggerType::IrSensor) { + return; + } + + next_state = {}; + const auto camera_data = npad_device->GetCamera(); + auto filtered_image = camera_data.data; + + RemoveLowIntensityData(filtered_image); + + const auto window_start_x = static_cast<std::size_t>(current_config.window_of_interest.x); + const auto window_start_y = static_cast<std::size_t>(current_config.window_of_interest.y); + const auto window_end_x = + window_start_x + static_cast<std::size_t>(current_config.window_of_interest.width); + const auto window_end_y = + window_start_y + static_cast<std::size_t>(current_config.window_of_interest.height); + + for (std::size_t y = window_start_y; y < window_end_y; y++) { + for (std::size_t x = window_start_x; x < window_end_x; x++) { + u8 pixel = GetPixel(filtered_image, x, y); + if (pixel == 0) { + continue; + } + const auto cluster = GetClusterProperties(filtered_image, x, y); + if (cluster.pixel_count > current_config.pixel_count_max) { + continue; + } + if (cluster.pixel_count < current_config.pixel_count_min) { + continue; + } + // Cluster object limit reached + if (next_state.object_count >= next_state.data.size()) { + continue; + } + next_state.data[next_state.object_count] = cluster; + next_state.object_count++; + } + } + + next_state.sampling_number = camera_data.sample; + next_state.timestamp = next_state.timestamp + 131; + next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; + shared_memory->clustering_lifo.WriteNextEntry(next_state); + + if (!IsProcessorActive()) { + StartProcessor(); + } +} + +void ClusteringProcessor::RemoveLowIntensityData(std::vector<u8>& data) { + for (u8& pixel : data) { + if (pixel < current_config.pixel_count_min) { + pixel = 0; + } + } +} + +ClusteringProcessor::ClusteringData ClusteringProcessor::GetClusterProperties(std::vector<u8>& data, + std::size_t x, + std::size_t y) { + using DataPoint = Common::Point<std::size_t>; + std::queue<DataPoint> search_points{}; + ClusteringData current_cluster = GetPixelProperties(data, x, y); + SetPixel(data, x, y, 0); + search_points.emplace<DataPoint>({x, y}); + + while (!search_points.empty()) { + const auto point = search_points.front(); + search_points.pop(); + + // Avoid negative numbers + if (point.x == 0 || point.y == 0) { + continue; + } + + std::array<DataPoint, 4> new_points{ + DataPoint{point.x - 1, point.y}, + {point.x, point.y - 1}, + {point.x + 1, point.y}, + {point.x, point.y + 1}, + }; + + for (const auto new_point : new_points) { + if (new_point.x >= width) { + continue; + } + if (new_point.y >= height) { + continue; + } + if (GetPixel(data, new_point.x, new_point.y) < current_config.object_intensity_min) { + continue; + } + const ClusteringData cluster = GetPixelProperties(data, new_point.x, new_point.y); + current_cluster = MergeCluster(current_cluster, cluster); + SetPixel(data, new_point.x, new_point.y, 0); + search_points.emplace<DataPoint>({new_point.x, new_point.y}); + } + } + + return current_cluster; +} + +ClusteringProcessor::ClusteringData ClusteringProcessor::GetPixelProperties( + const std::vector<u8>& data, std::size_t x, std::size_t y) const { + return { + .average_intensity = GetPixel(data, x, y) / 255.0f, + .centroid = + { + .x = static_cast<f32>(x), + .y = static_cast<f32>(y), + + }, + .pixel_count = 1, + .bound = + { + .x = static_cast<s16>(x), + .y = static_cast<s16>(y), + .width = 1, + .height = 1, + }, + }; +} + +ClusteringProcessor::ClusteringData ClusteringProcessor::MergeCluster( + const ClusteringData a, const ClusteringData b) const { + const f32 a_pixel_count = static_cast<f32>(a.pixel_count); + const f32 b_pixel_count = static_cast<f32>(b.pixel_count); + const f32 pixel_count = a_pixel_count + b_pixel_count; + const f32 average_intensity = + (a.average_intensity * a_pixel_count + b.average_intensity * b_pixel_count) / pixel_count; + const Core::IrSensor::IrsCentroid centroid = { + .x = (a.centroid.x * a_pixel_count + b.centroid.x * b_pixel_count) / pixel_count, + .y = (a.centroid.y * a_pixel_count + b.centroid.y * b_pixel_count) / pixel_count, + }; + s16 bound_start_x = a.bound.x < b.bound.x ? a.bound.x : b.bound.x; + s16 bound_start_y = a.bound.y < b.bound.y ? a.bound.y : b.bound.y; + s16 a_bound_end_x = a.bound.x + a.bound.width; + s16 a_bound_end_y = a.bound.y + a.bound.height; + s16 b_bound_end_x = b.bound.x + b.bound.width; + s16 b_bound_end_y = b.bound.y + b.bound.height; + + const Core::IrSensor::IrsRect bound = { + .x = bound_start_x, + .y = bound_start_y, + .width = a_bound_end_x > b_bound_end_x ? static_cast<s16>(a_bound_end_x - bound_start_x) + : static_cast<s16>(b_bound_end_x - bound_start_x), + .height = a_bound_end_y > b_bound_end_y ? static_cast<s16>(a_bound_end_y - bound_start_y) + : static_cast<s16>(b_bound_end_y - bound_start_y), + }; + + return { + .average_intensity = average_intensity, + .centroid = centroid, + .pixel_count = static_cast<u32>(pixel_count), + .bound = bound, + }; +} + +u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const { + if ((y * width) + x > data.size()) { + return 0; + } + return data[(y * width) + x]; +} + +void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) { + if ((y * width) + x > data.size()) { + return; + } + data[(y * width) + x] = value; +} + +void ClusteringProcessor::SetDefaultConfig() { + using namespace std::literals::chrono_literals; + current_config.camera_config.exposure_time = std::chrono::microseconds(200ms).count(); + current_config.camera_config.gain = 2; + current_config.camera_config.is_negative_used = false; + current_config.camera_config.light_target = Core::IrSensor::CameraLightTarget::BrightLeds; + current_config.window_of_interest = { + .x = 0, + .y = 0, + .width = width, + .height = height, + }; + current_config.pixel_count_min = 3; + current_config.pixel_count_max = static_cast<u32>(GetDataSize(format)); + current_config.is_external_light_filter_enabled = true; + current_config.object_intensity_min = 150; + + npad_device->SetCameraFormat(format); +} + +void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) { + current_config.camera_config.exposure_time = config.camera_config.exposure_time; + current_config.camera_config.gain = config.camera_config.gain; + current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; + current_config.camera_config.light_target = + static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); + current_config.window_of_interest = config.window_of_interest; + current_config.pixel_count_min = config.pixel_count_min; + current_config.pixel_count_max = config.pixel_count_max; + current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled; + current_config.object_intensity_min = config.object_intensity_min; + + LOG_INFO(Service_IRS, + "Processor config, exposure_time={}, gain={}, is_negative_used={}, " + "light_target={}, window_of_interest=({}, {}, {}, {}), pixel_count_min={}, " + "pixel_count_max={}, is_external_light_filter_enabled={}, object_intensity_min={}", + current_config.camera_config.exposure_time, current_config.camera_config.gain, + current_config.camera_config.is_negative_used, + current_config.camera_config.light_target, current_config.window_of_interest.x, + current_config.window_of_interest.y, current_config.window_of_interest.width, + current_config.window_of_interest.height, current_config.pixel_count_min, + current_config.pixel_count_max, current_config.is_external_light_filter_enabled, + current_config.object_intensity_min); + + npad_device->SetCameraFormat(format); +} + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.h b/src/core/hle/service/hid/irsensor/clustering_processor.h new file mode 100644 index 000000000..dc01a8ea7 --- /dev/null +++ b/src/core/hle/service/hid/irsensor/clustering_processor.h @@ -0,0 +1,110 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hid/irs_types.h" +#include "core/hle/service/hid/irs_ring_lifo.h" +#include "core/hle/service/hid/irsensor/processor_base.h" + +namespace Core::HID { +class EmulatedController; +} // namespace Core::HID + +namespace Service::IRS { +class ClusteringProcessor final : public ProcessorBase { +public: + explicit ClusteringProcessor(Core::HID::HIDCore& hid_core_, + Core::IrSensor::DeviceFormat& device_format, + std::size_t npad_index); + ~ClusteringProcessor() override; + + // Called when the processor is initialized + void StartProcessor() override; + + // Called when the processor is suspended + void SuspendProcessor() override; + + // Called when the processor is stopped + void StopProcessor() override; + + // Sets config parameters of the camera + void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config); + +private: + static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size320x240; + static constexpr std::size_t width = 320; + static constexpr std::size_t height = 240; + + // This is nn::irsensor::ClusteringProcessorConfig + struct ClusteringProcessorConfig { + Core::IrSensor::CameraConfig camera_config; + Core::IrSensor::IrsRect window_of_interest; + u32 pixel_count_min; + u32 pixel_count_max; + u32 object_intensity_min; + bool is_external_light_filter_enabled; + INSERT_PADDING_BYTES(3); + }; + static_assert(sizeof(ClusteringProcessorConfig) == 0x30, + "ClusteringProcessorConfig is an invalid size"); + + // This is nn::irsensor::AdaptiveClusteringProcessorConfig + struct AdaptiveClusteringProcessorConfig { + Core::IrSensor::AdaptiveClusteringMode mode; + Core::IrSensor::AdaptiveClusteringTargetDistance target_distance; + }; + static_assert(sizeof(AdaptiveClusteringProcessorConfig) == 0x8, + "AdaptiveClusteringProcessorConfig is an invalid size"); + + // This is nn::irsensor::ClusteringData + struct ClusteringData { + f32 average_intensity; + Core::IrSensor::IrsCentroid centroid; + u32 pixel_count; + Core::IrSensor::IrsRect bound; + }; + static_assert(sizeof(ClusteringData) == 0x18, "ClusteringData is an invalid size"); + + // This is nn::irsensor::ClusteringProcessorState + struct ClusteringProcessorState { + s64 sampling_number; + u64 timestamp; + u8 object_count; + INSERT_PADDING_BYTES(3); + Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; + std::array<ClusteringData, 0x10> data; + }; + static_assert(sizeof(ClusteringProcessorState) == 0x198, + "ClusteringProcessorState is an invalid size"); + + struct ClusteringSharedMemory { + Service::IRS::Lifo<ClusteringProcessorState, 6> clustering_lifo; + static_assert(sizeof(clustering_lifo) == 0x9A0, "clustering_lifo is an invalid size"); + INSERT_PADDING_WORDS(0x11F); + }; + static_assert(sizeof(ClusteringSharedMemory) == 0xE20, + "ClusteringSharedMemory is an invalid size"); + + void OnControllerUpdate(Core::HID::ControllerTriggerType type); + void RemoveLowIntensityData(std::vector<u8>& data); + ClusteringData GetClusterProperties(std::vector<u8>& data, std::size_t x, std::size_t y); + ClusteringData GetPixelProperties(const std::vector<u8>& data, std::size_t x, + std::size_t y) const; + ClusteringData MergeCluster(const ClusteringData a, const ClusteringData b) const; + u8 GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const; + void SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value); + + // Sets config parameters of the camera + void SetDefaultConfig(); + + ClusteringSharedMemory* shared_memory = nullptr; + ClusteringProcessorState next_state{}; + + ClusteringProcessorConfig current_config{}; + Core::IrSensor::DeviceFormat& device; + Core::HID::EmulatedController* npad_device; + int callback_key{}; +}; +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp new file mode 100644 index 000000000..98f0c579d --- /dev/null +++ b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp @@ -0,0 +1,150 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hle/service/hid/irsensor/image_transfer_processor.h" + +namespace Service::IRS { +ImageTransferProcessor::ImageTransferProcessor(Core::HID::HIDCore& hid_core_, + Core::IrSensor::DeviceFormat& device_format, + std::size_t npad_index) + : device{device_format} { + npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index); + + Core::HID::ControllerUpdateCallback engine_callback{ + .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); }, + .is_npad_service = true, + }; + callback_key = npad_device->SetCallback(engine_callback); + + device.mode = Core::IrSensor::IrSensorMode::ImageTransferProcessor; + device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; +} + +ImageTransferProcessor::~ImageTransferProcessor() { + npad_device->DeleteCallback(callback_key); +}; + +void ImageTransferProcessor::StartProcessor() { + is_active = true; + device.camera_status = Core::IrSensor::IrCameraStatus::Available; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready; + processor_state.sampling_number = 0; + processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; +} + +void ImageTransferProcessor::SuspendProcessor() {} + +void ImageTransferProcessor::StopProcessor() {} + +void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) { + if (type != Core::HID::ControllerTriggerType::IrSensor) { + return; + } + if (!is_transfer_memory_set) { + return; + } + + const auto camera_data = npad_device->GetCamera(); + + // This indicates how much ambient light is precent + processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; + processor_state.sampling_number = camera_data.sample; + + if (camera_data.format != current_config.origin_format) { + LOG_WARNING(Service_IRS, "Wrong Input format {} expected {}", camera_data.format, + current_config.origin_format); + memset(transfer_memory, 0, GetDataSize(current_config.trimming_format)); + return; + } + + if (current_config.origin_format > current_config.trimming_format) { + LOG_WARNING(Service_IRS, "Origin format {} is smaller than trimming format {}", + current_config.origin_format, current_config.trimming_format); + memset(transfer_memory, 0, GetDataSize(current_config.trimming_format)); + return; + } + + std::vector<u8> window_data{}; + const auto origin_width = GetDataWidth(current_config.origin_format); + const auto origin_height = GetDataHeight(current_config.origin_format); + const auto trimming_width = GetDataWidth(current_config.trimming_format); + const auto trimming_height = GetDataHeight(current_config.trimming_format); + window_data.resize(GetDataSize(current_config.trimming_format)); + + if (trimming_width + current_config.trimming_start_x > origin_width || + trimming_height + current_config.trimming_start_y > origin_height) { + LOG_WARNING(Service_IRS, + "Trimming area ({}, {}, {}, {}) is outside of origin area ({}, {})", + current_config.trimming_start_x, current_config.trimming_start_y, + trimming_width, trimming_height, origin_width, origin_height); + memset(transfer_memory, 0, GetDataSize(current_config.trimming_format)); + return; + } + + for (std::size_t y = 0; y < trimming_height; y++) { + for (std::size_t x = 0; x < trimming_width; x++) { + const std::size_t window_index = (y * trimming_width) + x; + const std::size_t origin_index = + ((y + current_config.trimming_start_y) * origin_width) + x + + current_config.trimming_start_x; + window_data[window_index] = camera_data.data[origin_index]; + } + } + + memcpy(transfer_memory, window_data.data(), GetDataSize(current_config.trimming_format)); + + if (!IsProcessorActive()) { + StartProcessor(); + } +} + +void ImageTransferProcessor::SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config) { + current_config.camera_config.exposure_time = config.camera_config.exposure_time; + current_config.camera_config.gain = config.camera_config.gain; + current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; + current_config.camera_config.light_target = + static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); + current_config.origin_format = + static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format); + current_config.trimming_format = + static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format); + current_config.trimming_start_x = 0; + current_config.trimming_start_y = 0; + + npad_device->SetCameraFormat(current_config.origin_format); +} + +void ImageTransferProcessor::SetConfig( + Core::IrSensor::PackedImageTransferProcessorExConfig config) { + current_config.camera_config.exposure_time = config.camera_config.exposure_time; + current_config.camera_config.gain = config.camera_config.gain; + current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; + current_config.camera_config.light_target = + static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); + current_config.origin_format = + static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.origin_format); + current_config.trimming_format = + static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.trimming_format); + current_config.trimming_start_x = config.trimming_start_x; + current_config.trimming_start_y = config.trimming_start_y; + + npad_device->SetCameraFormat(current_config.origin_format); +} + +void ImageTransferProcessor::SetTransferMemoryPointer(u8* t_mem) { + is_transfer_memory_set = true; + transfer_memory = t_mem; +} + +Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState( + std::vector<u8>& data) const { + const auto size = GetDataSize(current_config.trimming_format); + data.resize(size); + memcpy(data.data(), transfer_memory, size); + return processor_state; +} + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/image_transfer_processor.h b/src/core/hle/service/hid/irsensor/image_transfer_processor.h new file mode 100644 index 000000000..393df492d --- /dev/null +++ b/src/core/hle/service/hid/irsensor/image_transfer_processor.h @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hid/irs_types.h" +#include "core/hle/service/hid/irsensor/processor_base.h" + +namespace Core::HID { +class EmulatedController; +} // namespace Core::HID + +namespace Service::IRS { +class ImageTransferProcessor final : public ProcessorBase { +public: + explicit ImageTransferProcessor(Core::HID::HIDCore& hid_core_, + Core::IrSensor::DeviceFormat& device_format, + std::size_t npad_index); + ~ImageTransferProcessor() override; + + // Called when the processor is initialized + void StartProcessor() override; + + // Called when the processor is suspended + void SuspendProcessor() override; + + // Called when the processor is stopped + void StopProcessor() override; + + // Sets config parameters of the camera + void SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config); + void SetConfig(Core::IrSensor::PackedImageTransferProcessorExConfig config); + + // Transfer memory where the image data will be stored + void SetTransferMemoryPointer(u8* t_mem); + + Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const; + +private: + // This is nn::irsensor::ImageTransferProcessorConfig + struct ImageTransferProcessorConfig { + Core::IrSensor::CameraConfig camera_config; + Core::IrSensor::ImageTransferProcessorFormat format; + }; + static_assert(sizeof(ImageTransferProcessorConfig) == 0x20, + "ImageTransferProcessorConfig is an invalid size"); + + // This is nn::irsensor::ImageTransferProcessorExConfig + struct ImageTransferProcessorExConfig { + Core::IrSensor::CameraConfig camera_config; + Core::IrSensor::ImageTransferProcessorFormat origin_format; + Core::IrSensor::ImageTransferProcessorFormat trimming_format; + u16 trimming_start_x; + u16 trimming_start_y; + bool is_external_light_filter_enabled; + INSERT_PADDING_BYTES(3); + }; + static_assert(sizeof(ImageTransferProcessorExConfig) == 0x28, + "ImageTransferProcessorExConfig is an invalid size"); + + void OnControllerUpdate(Core::HID::ControllerTriggerType type); + + ImageTransferProcessorExConfig current_config{}; + Core::IrSensor::ImageTransferProcessorState processor_state{}; + Core::IrSensor::DeviceFormat& device; + Core::HID::EmulatedController* npad_device; + int callback_key{}; + + u8* transfer_memory = nullptr; + bool is_transfer_memory_set = false; +}; +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/ir_led_processor.cpp b/src/core/hle/service/hid/irsensor/ir_led_processor.cpp new file mode 100644 index 000000000..8e6dd99e4 --- /dev/null +++ b/src/core/hle/service/hid/irsensor/ir_led_processor.cpp @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/hid/irsensor/ir_led_processor.h" + +namespace Service::IRS { +IrLedProcessor::IrLedProcessor(Core::IrSensor::DeviceFormat& device_format) + : device(device_format) { + device.mode = Core::IrSensor::IrSensorMode::IrLedProcessor; + device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; +} + +IrLedProcessor::~IrLedProcessor() = default; + +void IrLedProcessor::StartProcessor() {} + +void IrLedProcessor::SuspendProcessor() {} + +void IrLedProcessor::StopProcessor() {} + +void IrLedProcessor::SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config) { + current_config.light_target = + static_cast<Core::IrSensor::CameraLightTarget>(config.light_target); +} + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/ir_led_processor.h b/src/core/hle/service/hid/irsensor/ir_led_processor.h new file mode 100644 index 000000000..c3d8693c9 --- /dev/null +++ b/src/core/hle/service/hid/irsensor/ir_led_processor.h @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/bit_field.h" +#include "common/common_types.h" +#include "core/hid/irs_types.h" +#include "core/hle/service/hid/irsensor/processor_base.h" + +namespace Service::IRS { +class IrLedProcessor final : public ProcessorBase { +public: + explicit IrLedProcessor(Core::IrSensor::DeviceFormat& device_format); + ~IrLedProcessor() override; + + // Called when the processor is initialized + void StartProcessor() override; + + // Called when the processor is suspended + void SuspendProcessor() override; + + // Called when the processor is stopped + void StopProcessor() override; + + // Sets config parameters of the camera + void SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config); + +private: + // This is nn::irsensor::IrLedProcessorConfig + struct IrLedProcessorConfig { + Core::IrSensor::CameraLightTarget light_target; + }; + static_assert(sizeof(IrLedProcessorConfig) == 0x4, "IrLedProcessorConfig is an invalid size"); + + struct IrLedProcessorState { + s64 sampling_number; + u64 timestamp; + std::array<u8, 0x8> data; + }; + static_assert(sizeof(IrLedProcessorState) == 0x18, "IrLedProcessorState is an invalid size"); + + IrLedProcessorConfig current_config{}; + Core::IrSensor::DeviceFormat& device; +}; + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/moment_processor.cpp b/src/core/hle/service/hid/irsensor/moment_processor.cpp new file mode 100644 index 000000000..dbaca420a --- /dev/null +++ b/src/core/hle/service/hid/irsensor/moment_processor.cpp @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/hid/irsensor/moment_processor.h" + +namespace Service::IRS { +MomentProcessor::MomentProcessor(Core::IrSensor::DeviceFormat& device_format) + : device(device_format) { + device.mode = Core::IrSensor::IrSensorMode::MomentProcessor; + device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; +} + +MomentProcessor::~MomentProcessor() = default; + +void MomentProcessor::StartProcessor() {} + +void MomentProcessor::SuspendProcessor() {} + +void MomentProcessor::StopProcessor() {} + +void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) { + current_config.camera_config.exposure_time = config.camera_config.exposure_time; + current_config.camera_config.gain = config.camera_config.gain; + current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; + current_config.camera_config.light_target = + static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); + current_config.window_of_interest = config.window_of_interest; + current_config.preprocess = + static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess); + current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold; +} + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/moment_processor.h b/src/core/hle/service/hid/irsensor/moment_processor.h new file mode 100644 index 000000000..d4bd22e0f --- /dev/null +++ b/src/core/hle/service/hid/irsensor/moment_processor.h @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/bit_field.h" +#include "common/common_types.h" +#include "core/hid/irs_types.h" +#include "core/hle/service/hid/irsensor/processor_base.h" + +namespace Service::IRS { +class MomentProcessor final : public ProcessorBase { +public: + explicit MomentProcessor(Core::IrSensor::DeviceFormat& device_format); + ~MomentProcessor() override; + + // Called when the processor is initialized + void StartProcessor() override; + + // Called when the processor is suspended + void SuspendProcessor() override; + + // Called when the processor is stopped + void StopProcessor() override; + + // Sets config parameters of the camera + void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config); + +private: + // This is nn::irsensor::MomentProcessorConfig + struct MomentProcessorConfig { + Core::IrSensor::CameraConfig camera_config; + Core::IrSensor::IrsRect window_of_interest; + Core::IrSensor::MomentProcessorPreprocess preprocess; + u32 preprocess_intensity_threshold; + }; + static_assert(sizeof(MomentProcessorConfig) == 0x28, + "MomentProcessorConfig is an invalid size"); + + // This is nn::irsensor::MomentStatistic + struct MomentStatistic { + f32 average_intensity; + Core::IrSensor::IrsCentroid centroid; + }; + static_assert(sizeof(MomentStatistic) == 0xC, "MomentStatistic is an invalid size"); + + // This is nn::irsensor::MomentProcessorState + struct MomentProcessorState { + s64 sampling_number; + u64 timestamp; + Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; + INSERT_PADDING_BYTES(4); + std::array<MomentStatistic, 0x30> stadistic; + }; + static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size"); + + MomentProcessorConfig current_config{}; + Core::IrSensor::DeviceFormat& device; +}; + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/pointing_processor.cpp b/src/core/hle/service/hid/irsensor/pointing_processor.cpp new file mode 100644 index 000000000..929f177fc --- /dev/null +++ b/src/core/hle/service/hid/irsensor/pointing_processor.cpp @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/hid/irsensor/pointing_processor.h" + +namespace Service::IRS { +PointingProcessor::PointingProcessor(Core::IrSensor::DeviceFormat& device_format) + : device(device_format) { + device.mode = Core::IrSensor::IrSensorMode::PointingProcessorMarker; + device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; +} + +PointingProcessor::~PointingProcessor() = default; + +void PointingProcessor::StartProcessor() {} + +void PointingProcessor::SuspendProcessor() {} + +void PointingProcessor::StopProcessor() {} + +void PointingProcessor::SetConfig(Core::IrSensor::PackedPointingProcessorConfig config) { + current_config.window_of_interest = config.window_of_interest; +} + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/pointing_processor.h b/src/core/hle/service/hid/irsensor/pointing_processor.h new file mode 100644 index 000000000..cf4930794 --- /dev/null +++ b/src/core/hle/service/hid/irsensor/pointing_processor.h @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hid/irs_types.h" +#include "core/hle/service/hid/irsensor/processor_base.h" + +namespace Service::IRS { +class PointingProcessor final : public ProcessorBase { +public: + explicit PointingProcessor(Core::IrSensor::DeviceFormat& device_format); + ~PointingProcessor() override; + + // Called when the processor is initialized + void StartProcessor() override; + + // Called when the processor is suspended + void SuspendProcessor() override; + + // Called when the processor is stopped + void StopProcessor() override; + + // Sets config parameters of the camera + void SetConfig(Core::IrSensor::PackedPointingProcessorConfig config); + +private: + // This is nn::irsensor::PointingProcessorConfig + struct PointingProcessorConfig { + Core::IrSensor::IrsRect window_of_interest; + }; + static_assert(sizeof(PointingProcessorConfig) == 0x8, + "PointingProcessorConfig is an invalid size"); + + struct PointingProcessorMarkerData { + u8 pointing_status; + INSERT_PADDING_BYTES(3); + u32 unknown; + float unkown_float1; + float position_x; + float position_y; + float unkown_float2; + Core::IrSensor::IrsRect window_of_interest; + }; + static_assert(sizeof(PointingProcessorMarkerData) == 0x20, + "PointingProcessorMarkerData is an invalid size"); + + struct PointingProcessorMarkerState { + s64 sampling_number; + u64 timestamp; + std::array<PointingProcessorMarkerData, 0x3> data; + }; + static_assert(sizeof(PointingProcessorMarkerState) == 0x70, + "PointingProcessorMarkerState is an invalid size"); + + PointingProcessorConfig current_config{}; + Core::IrSensor::DeviceFormat& device; +}; + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/processor_base.cpp b/src/core/hle/service/hid/irsensor/processor_base.cpp new file mode 100644 index 000000000..4d43ca17a --- /dev/null +++ b/src/core/hle/service/hid/irsensor/processor_base.cpp @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/hid/irsensor/processor_base.h" + +namespace Service::IRS { + +ProcessorBase::ProcessorBase() {} +ProcessorBase::~ProcessorBase() = default; + +bool ProcessorBase::IsProcessorActive() const { + return is_active; +} + +std::size_t ProcessorBase::GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const { + switch (format) { + case Core::IrSensor::ImageTransferProcessorFormat::Size320x240: + return 320 * 240; + case Core::IrSensor::ImageTransferProcessorFormat::Size160x120: + return 160 * 120; + case Core::IrSensor::ImageTransferProcessorFormat::Size80x60: + return 80 * 60; + case Core::IrSensor::ImageTransferProcessorFormat::Size40x30: + return 40 * 30; + case Core::IrSensor::ImageTransferProcessorFormat::Size20x15: + return 20 * 15; + default: + return 0; + } +} + +std::size_t ProcessorBase::GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const { + switch (format) { + case Core::IrSensor::ImageTransferProcessorFormat::Size320x240: + return 320; + case Core::IrSensor::ImageTransferProcessorFormat::Size160x120: + return 160; + case Core::IrSensor::ImageTransferProcessorFormat::Size80x60: + return 80; + case Core::IrSensor::ImageTransferProcessorFormat::Size40x30: + return 40; + case Core::IrSensor::ImageTransferProcessorFormat::Size20x15: + return 20; + default: + return 0; + } +} + +std::size_t ProcessorBase::GetDataHeight( + Core::IrSensor::ImageTransferProcessorFormat format) const { + switch (format) { + case Core::IrSensor::ImageTransferProcessorFormat::Size320x240: + return 240; + case Core::IrSensor::ImageTransferProcessorFormat::Size160x120: + return 120; + case Core::IrSensor::ImageTransferProcessorFormat::Size80x60: + return 60; + case Core::IrSensor::ImageTransferProcessorFormat::Size40x30: + return 30; + case Core::IrSensor::ImageTransferProcessorFormat::Size20x15: + return 15; + default: + return 0; + } +} + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/processor_base.h b/src/core/hle/service/hid/irsensor/processor_base.h new file mode 100644 index 000000000..bc0d2977b --- /dev/null +++ b/src/core/hle/service/hid/irsensor/processor_base.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hid/irs_types.h" + +namespace Service::IRS { +class ProcessorBase { +public: + explicit ProcessorBase(); + virtual ~ProcessorBase(); + + virtual void StartProcessor() = 0; + virtual void SuspendProcessor() = 0; + virtual void StopProcessor() = 0; + + bool IsProcessorActive() const; + +protected: + /// Returns the number of bytes the image uses + std::size_t GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const; + + /// Returns the width of the image + std::size_t GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const; + + /// Returns the height of the image + std::size_t GetDataHeight(Core::IrSensor::ImageTransferProcessorFormat format) const; + + bool is_active{false}; +}; +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp b/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp new file mode 100644 index 000000000..e691c840a --- /dev/null +++ b/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/hid/irsensor/tera_plugin_processor.h" + +namespace Service::IRS { +TeraPluginProcessor::TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format) + : device(device_format) { + device.mode = Core::IrSensor::IrSensorMode::TeraPluginProcessor; + device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; +} + +TeraPluginProcessor::~TeraPluginProcessor() = default; + +void TeraPluginProcessor::StartProcessor() {} + +void TeraPluginProcessor::SuspendProcessor() {} + +void TeraPluginProcessor::StopProcessor() {} + +void TeraPluginProcessor::SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config) { + current_config.mode = config.mode; + current_config.unknown_1 = config.unknown_1; + current_config.unknown_2 = config.unknown_2; + current_config.unknown_3 = config.unknown_3; +} + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/tera_plugin_processor.h b/src/core/hle/service/hid/irsensor/tera_plugin_processor.h new file mode 100644 index 000000000..bbea7ed0b --- /dev/null +++ b/src/core/hle/service/hid/irsensor/tera_plugin_processor.h @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/bit_field.h" +#include "common/common_types.h" +#include "core/hid/irs_types.h" +#include "core/hle/service/hid/irsensor/processor_base.h" + +namespace Service::IRS { +class TeraPluginProcessor final : public ProcessorBase { +public: + explicit TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format); + ~TeraPluginProcessor() override; + + // Called when the processor is initialized + void StartProcessor() override; + + // Called when the processor is suspended + void SuspendProcessor() override; + + // Called when the processor is stopped + void StopProcessor() override; + + // Sets config parameters of the camera + void SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config); + +private: + // This is nn::irsensor::TeraPluginProcessorConfig + struct TeraPluginProcessorConfig { + u8 mode; + u8 unknown_1; + u8 unknown_2; + u8 unknown_3; + }; + static_assert(sizeof(TeraPluginProcessorConfig) == 0x4, + "TeraPluginProcessorConfig is an invalid size"); + + struct TeraPluginProcessorState { + s64 sampling_number; + u64 timestamp; + Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; + std::array<u8, 0x12c> data; + }; + static_assert(sizeof(TeraPluginProcessorState) == 0x140, + "TeraPluginProcessorState is an invalid size"); + + TeraPluginProcessorConfig current_config{}; + Core::IrSensor::DeviceFormat& device; +}; + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/ring_lifo.h b/src/core/hle/service/hid/ring_lifo.h index 44c20d967..65eb7ea02 100644 --- a/src/core/hle/service/hid/ring_lifo.h +++ b/src/core/hle/service/hid/ring_lifo.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/hid/xcd.cpp b/src/core/hle/service/hid/xcd.cpp index b1efa3d05..75cc266ea 100644 --- a/src/core/hle/service/hid/xcd.cpp +++ b/src/core/hle/service/hid/xcd.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/hid/xcd.h" diff --git a/src/core/hle/service/hid/xcd.h b/src/core/hle/service/hid/xcd.h index 54932c228..fe0b91b91 100644 --- a/src/core/hle/service/hid/xcd.h +++ b/src/core/hle/service/hid/xcd.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp new file mode 100644 index 000000000..8f2920c51 --- /dev/null +++ b/src/core/hle/service/jit/jit.cpp @@ -0,0 +1,404 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/arm/symbols.h" +#include "core/core.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_code_memory.h" +#include "core/hle/kernel/k_transfer_memory.h" +#include "core/hle/result.h" +#include "core/hle/service/jit/jit.h" +#include "core/hle/service/jit/jit_context.h" +#include "core/hle/service/service.h" +#include "core/memory.h" + +namespace Service::JIT { + +struct CodeRange { + u64 offset; + u64 size; +}; + +class IJitEnvironment final : public ServiceFramework<IJitEnvironment> { +public: + explicit IJitEnvironment(Core::System& system_, Kernel::KProcess& process_, CodeRange user_rx, + CodeRange user_ro) + : ServiceFramework{system_, "IJitEnvironment", ServiceThreadType::CreateNew}, + process{&process_}, context{system_.Memory()} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IJitEnvironment::GenerateCode, "GenerateCode"}, + {1, &IJitEnvironment::Control, "Control"}, + {1000, &IJitEnvironment::LoadPlugin, "LoadPlugin"}, + {1001, &IJitEnvironment::GetCodeAddress, "GetCodeAddress"}, + }; + // clang-format on + + RegisterHandlers(functions); + + // Identity map user code range into sysmodule context + configuration.user_ro_memory = user_ro; + configuration.user_rx_memory = user_rx; + configuration.sys_ro_memory = user_ro; + configuration.sys_rx_memory = user_rx; + } + + void GenerateCode(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_JIT, "called"); + + struct InputParameters { + u32 data_size; + u64 command; + std::array<CodeRange, 2> ranges; + Struct32 data; + }; + + struct OutputParameters { + s32 return_value; + std::array<CodeRange, 2> ranges; + }; + + IPC::RequestParser rp{ctx}; + const auto parameters{rp.PopRaw<InputParameters>()}; + + // Optional input/output buffers + std::vector<u8> input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::vector<u8>()}; + std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0); + + // Function call prototype: + // void GenerateCode(s32* ret, CodeRange* c0_out, CodeRange* c1_out, JITConfiguration* cfg, + // u64 cmd, u8* input_buf, size_t input_size, CodeRange* c0_in, + // CodeRange* c1_in, Struct32* data, size_t data_size, u8* output_buf, + // size_t output_size); + // + // The command argument is used to control the behavior of the plugin during code + // generation. The configuration allows the plugin to access the output code ranges, and the + // other arguments are used to transfer state between the game and the plugin. + + const VAddr ret_ptr{context.AddHeap(0u)}; + const VAddr c0_in_ptr{context.AddHeap(parameters.ranges[0])}; + const VAddr c1_in_ptr{context.AddHeap(parameters.ranges[1])}; + const VAddr c0_out_ptr{context.AddHeap(ClearSize(parameters.ranges[0]))}; + const VAddr c1_out_ptr{context.AddHeap(ClearSize(parameters.ranges[1]))}; + + const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())}; + const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())}; + const VAddr data_ptr{context.AddHeap(parameters.data)}; + const VAddr configuration_ptr{context.AddHeap(configuration)}; + + // The callback does not directly return a value, it only writes to the output pointer + context.CallFunction(callbacks.GenerateCode, ret_ptr, c0_out_ptr, c1_out_ptr, + configuration_ptr, parameters.command, input_ptr, input_buffer.size(), + c0_in_ptr, c1_in_ptr, data_ptr, parameters.data_size, output_ptr, + output_buffer.size()); + + const s32 return_value{context.GetHeap<s32>(ret_ptr)}; + + if (return_value == 0) { + // The callback has written to the output executable code range, + // requiring an instruction cache invalidation + system.InvalidateCpuInstructionCacheRange(configuration.user_rx_memory.offset, + configuration.user_rx_memory.size); + + // Write back to the IPC output buffer, if provided + if (ctx.CanWriteBuffer()) { + context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size()); + ctx.WriteBuffer(output_buffer.data(), output_buffer.size()); + } + + const OutputParameters out{ + .return_value = return_value, + .ranges = + { + context.GetHeap<CodeRange>(c0_out_ptr), + context.GetHeap<CodeRange>(c1_out_ptr), + }, + }; + + IPC::ResponseBuilder rb{ctx, 8}; + rb.Push(ResultSuccess); + rb.PushRaw(out); + } else { + LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + } + }; + + void Control(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_JIT, "called"); + + IPC::RequestParser rp{ctx}; + const auto command{rp.PopRaw<u64>()}; + + // Optional input/output buffers + std::vector<u8> input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::vector<u8>()}; + std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0); + + // Function call prototype: + // u64 Control(s32* ret, JITConfiguration* cfg, u64 cmd, u8* input_buf, size_t input_size, + // u8* output_buf, size_t output_size); + // + // This function is used to set up the state of the plugin before code generation, generally + // passing objects like pointers to VM state from the game. It is usually called once. + + const VAddr ret_ptr{context.AddHeap(0u)}; + const VAddr configuration_ptr{context.AddHeap(configuration)}; + const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())}; + const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())}; + + const u64 wrapper_value{context.CallFunction(callbacks.Control, ret_ptr, configuration_ptr, + command, input_ptr, input_buffer.size(), + output_ptr, output_buffer.size())}; + + const s32 return_value{context.GetHeap<s32>(ret_ptr)}; + + if (wrapper_value == 0 && return_value == 0) { + // Write back to the IPC output buffer, if provided + if (ctx.CanWriteBuffer()) { + context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size()); + ctx.WriteBuffer(output_buffer.data(), output_buffer.size()); + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(return_value); + } else { + LOG_WARNING(Service_JIT, "plugin Control callback failed"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + } + } + + void LoadPlugin(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_JIT, "called"); + + IPC::RequestParser rp{ctx}; + const auto tmem_size{rp.PopRaw<u64>()}; + const auto tmem_handle{ctx.GetCopyHandle(0)}; + const auto nro_plugin{ctx.ReadBuffer(1)}; + + if (tmem_size == 0) { + LOG_ERROR(Service_JIT, "attempted to load plugin with empty transfer memory"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + auto tmem{process->GetHandleTable().GetObject<Kernel::KTransferMemory>(tmem_handle)}; + if (tmem.IsNull()) { + LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + // Set up the configuration with the required TransferMemory address + configuration.transfer_memory.offset = tmem->GetSourceAddress(); + configuration.transfer_memory.size = tmem_size; + + // Gather up all the callbacks from the loaded plugin + auto symbols{Core::Symbols::GetSymbols(nro_plugin, true)}; + const auto GetSymbol{[&](const std::string& name) { return symbols[name].first; }}; + + callbacks.rtld_fini = GetSymbol("_fini"); + callbacks.rtld_init = GetSymbol("_init"); + callbacks.Control = GetSymbol("nnjitpluginControl"); + callbacks.ResolveBasicSymbols = GetSymbol("nnjitpluginResolveBasicSymbols"); + callbacks.SetupDiagnostics = GetSymbol("nnjitpluginSetupDiagnostics"); + callbacks.Configure = GetSymbol("nnjitpluginConfigure"); + callbacks.GenerateCode = GetSymbol("nnjitpluginGenerateCode"); + callbacks.GetVersion = GetSymbol("nnjitpluginGetVersion"); + callbacks.OnPrepared = GetSymbol("nnjitpluginOnPrepared"); + callbacks.Keeper = GetSymbol("nnjitpluginKeeper"); + + if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 || + callbacks.OnPrepared == 0) { + LOG_ERROR(Service_JIT, "plugin does not implement all necessary functionality"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + if (!context.LoadNRO(nro_plugin)) { + LOG_ERROR(Service_JIT, "failed to load plugin"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + context.MapProcessMemory(configuration.sys_ro_memory.offset, + configuration.sys_ro_memory.size); + context.MapProcessMemory(configuration.sys_rx_memory.offset, + configuration.sys_rx_memory.size); + context.MapProcessMemory(configuration.transfer_memory.offset, + configuration.transfer_memory.size); + + // Run ELF constructors, if needed + if (callbacks.rtld_init != 0) { + context.CallFunction(callbacks.rtld_init); + } + + // Function prototype: + // u64 GetVersion(); + const auto version{context.CallFunction(callbacks.GetVersion)}; + if (version != 1) { + LOG_ERROR(Service_JIT, "unknown plugin version {}", version); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + // Function prototype: + // void ResolveBasicSymbols(void (*resolver)(const char* name)); + const auto resolve{context.GetHelper("_resolve")}; + if (callbacks.ResolveBasicSymbols != 0) { + context.CallFunction(callbacks.ResolveBasicSymbols, resolve); + } + + // Function prototype: + // void SetupDiagnostics(u32 enabled, void (**resolver)(const char* name)); + const auto resolve_ptr{context.AddHeap(resolve)}; + if (callbacks.SetupDiagnostics != 0) { + context.CallFunction(callbacks.SetupDiagnostics, 0u, resolve_ptr); + } + + // Function prototype: + // void Configure(u32* memory_flags); + context.CallFunction(callbacks.Configure, 0ull); + + // Function prototype: + // void OnPrepared(JITConfiguration* cfg); + const auto configuration_ptr{context.AddHeap(configuration)}; + context.CallFunction(callbacks.OnPrepared, configuration_ptr); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void GetCodeAddress(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_JIT, "called"); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.Push(configuration.user_rx_memory.offset); + rb.Push(configuration.user_ro_memory.offset); + } + +private: + using Struct32 = std::array<u8, 32>; + + struct GuestCallbacks { + VAddr rtld_fini; + VAddr rtld_init; + VAddr Control; + VAddr ResolveBasicSymbols; + VAddr SetupDiagnostics; + VAddr Configure; + VAddr GenerateCode; + VAddr GetVersion; + VAddr Keeper; + VAddr OnPrepared; + }; + + struct JITConfiguration { + CodeRange user_rx_memory; + CodeRange user_ro_memory; + CodeRange transfer_memory; + CodeRange sys_rx_memory; + CodeRange sys_ro_memory; + }; + + static CodeRange ClearSize(CodeRange in) { + in.size = 0; + return in; + } + + Kernel::KScopedAutoObject<Kernel::KProcess> process; + GuestCallbacks callbacks; + JITConfiguration configuration; + JITContext context; +}; + +class JITU final : public ServiceFramework<JITU> { +public: + explicit JITU(Core::System& system_) : ServiceFramework{system_, "jit:u"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &JITU::CreateJitEnvironment, "CreateJitEnvironment"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + + void CreateJitEnvironment(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_JIT, "called"); + + struct Parameters { + u64 rx_size; + u64 ro_size; + }; + + IPC::RequestParser rp{ctx}; + const auto parameters{rp.PopRaw<Parameters>()}; + const auto process_handle{ctx.GetCopyHandle(0)}; + const auto rx_mem_handle{ctx.GetCopyHandle(1)}; + const auto ro_mem_handle{ctx.GetCopyHandle(2)}; + + if (parameters.rx_size == 0 || parameters.ro_size == 0) { + LOG_ERROR(Service_JIT, "attempted to init with empty code regions"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + // Fetch using the handle table for the current process here, + // since we are not multiprocess yet. + const auto& handle_table{system.CurrentProcess()->GetHandleTable()}; + + auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)}; + if (process.IsNull()) { + LOG_ERROR(Service_JIT, "process is null for handle=0x{:08X}", process_handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + auto rx_mem{handle_table.GetObject<Kernel::KCodeMemory>(rx_mem_handle)}; + if (rx_mem.IsNull()) { + LOG_ERROR(Service_JIT, "rx_mem is null for handle=0x{:08X}", rx_mem_handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + auto ro_mem{handle_table.GetObject<Kernel::KCodeMemory>(ro_mem_handle)}; + if (ro_mem.IsNull()) { + LOG_ERROR(Service_JIT, "ro_mem is null for handle=0x{:08X}", ro_mem_handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + const CodeRange user_rx{ + .offset = rx_mem->GetSourceAddress(), + .size = parameters.rx_size, + }; + + const CodeRange user_ro{ + .offset = ro_mem->GetSourceAddress(), + .size = parameters.ro_size, + }; + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IJitEnvironment>(system, *process, user_rx, user_ro); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { + std::make_shared<JITU>(system)->InstallAsService(sm); +} + +} // namespace Service::JIT diff --git a/src/core/hle/service/jit/jit.h b/src/core/hle/service/jit/jit.h new file mode 100644 index 000000000..af0f5b4f3 --- /dev/null +++ b/src/core/hle/service/jit/jit.h @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +namespace Core { +class System; +} + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::JIT { + +/// Registers all JIT services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); + +} // namespace Service::JIT diff --git a/src/core/hle/service/jit/jit_context.cpp b/src/core/hle/service/jit/jit_context.cpp new file mode 100644 index 000000000..4ed3f02e2 --- /dev/null +++ b/src/core/hle/service/jit/jit_context.cpp @@ -0,0 +1,426 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <array> +#include <map> +#include <span> +#include <boost/icl/interval_set.hpp> +#include <dynarmic/interface/A64/a64.h> +#include <dynarmic/interface/A64/config.h> + +#include "common/alignment.h" +#include "common/common_funcs.h" +#include "common/div_ceil.h" +#include "common/elf.h" +#include "common/logging/log.h" +#include "core/hle/service/jit/jit_context.h" +#include "core/memory.h" + +using namespace Common::ELF; + +namespace Service::JIT { + +constexpr std::array<u8, 8> SVC0_ARM64 = { + 0x01, 0x00, 0x00, 0xd4, // svc #0 + 0xc0, 0x03, 0x5f, 0xd6, // ret +}; + +constexpr std::array HELPER_FUNCTIONS{ + "_stop", "_resolve", "_panic", "memcpy", "memmove", "memset", +}; + +constexpr size_t STACK_ALIGN = 16; + +class JITContextImpl; + +using IntervalSet = boost::icl::interval_set<VAddr>::type; +using IntervalType = boost::icl::interval_set<VAddr>::interval_type; + +class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { +public: + explicit DynarmicCallbacks64(Core::Memory::Memory& memory_, std::vector<u8>& local_memory_, + IntervalSet& mapped_ranges_, JITContextImpl& parent_) + : memory{memory_}, local_memory{local_memory_}, + mapped_ranges{mapped_ranges_}, parent{parent_} {} + + u8 MemoryRead8(u64 vaddr) override { + return ReadMemory<u8>(vaddr); + } + u16 MemoryRead16(u64 vaddr) override { + return ReadMemory<u16>(vaddr); + } + u32 MemoryRead32(u64 vaddr) override { + return ReadMemory<u32>(vaddr); + } + u64 MemoryRead64(u64 vaddr) override { + return ReadMemory<u64>(vaddr); + } + u128 MemoryRead128(u64 vaddr) override { + return ReadMemory<u128>(vaddr); + } + std::string MemoryReadCString(u64 vaddr) { + std::string result; + u8 next; + + while ((next = MemoryRead8(vaddr++)) != 0) { + result += next; + } + + return result; + } + + void MemoryWrite8(u64 vaddr, u8 value) override { + WriteMemory<u8>(vaddr, value); + } + void MemoryWrite16(u64 vaddr, u16 value) override { + WriteMemory<u16>(vaddr, value); + } + void MemoryWrite32(u64 vaddr, u32 value) override { + WriteMemory<u32>(vaddr, value); + } + void MemoryWrite64(u64 vaddr, u64 value) override { + WriteMemory<u64>(vaddr, value); + } + void MemoryWrite128(u64 vaddr, u128 value) override { + WriteMemory<u128>(vaddr, value); + } + + bool MemoryWriteExclusive8(u64 vaddr, u8 value, u8) override { + return WriteMemory<u8>(vaddr, value); + } + bool MemoryWriteExclusive16(u64 vaddr, u16 value, u16) override { + return WriteMemory<u16>(vaddr, value); + } + bool MemoryWriteExclusive32(u64 vaddr, u32 value, u32) override { + return WriteMemory<u32>(vaddr, value); + } + bool MemoryWriteExclusive64(u64 vaddr, u64 value, u64) override { + return WriteMemory<u64>(vaddr, value); + } + bool MemoryWriteExclusive128(u64 vaddr, u128 value, u128) override { + return WriteMemory<u128>(vaddr, value); + } + + void CallSVC(u32 swi) override; + void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override; + void InterpreterFallback(u64 pc, size_t num_instructions) override; + + void AddTicks(u64 ticks) override {} + u64 GetTicksRemaining() override { + return std::numeric_limits<u32>::max(); + } + u64 GetCNTPCT() override { + return 0; + } + + template <class T> + T ReadMemory(u64 vaddr) { + T ret{}; + if (boost::icl::contains(mapped_ranges, vaddr)) { + memory.ReadBlock(vaddr, &ret, sizeof(T)); + } else if (vaddr + sizeof(T) > local_memory.size()) { + LOG_CRITICAL(Service_JIT, "plugin: unmapped read @ 0x{:016x}", vaddr); + } else { + std::memcpy(&ret, local_memory.data() + vaddr, sizeof(T)); + } + return ret; + } + + template <class T> + bool WriteMemory(u64 vaddr, const T value) { + if (boost::icl::contains(mapped_ranges, vaddr)) { + memory.WriteBlock(vaddr, &value, sizeof(T)); + } else if (vaddr + sizeof(T) > local_memory.size()) { + LOG_CRITICAL(Service_JIT, "plugin: unmapped write @ 0x{:016x}", vaddr); + } else { + std::memcpy(local_memory.data() + vaddr, &value, sizeof(T)); + } + return true; + } + +private: + Core::Memory::Memory& memory; + std::vector<u8>& local_memory; + IntervalSet& mapped_ranges; + JITContextImpl& parent; +}; + +class JITContextImpl { +public: + explicit JITContextImpl(Core::Memory::Memory& memory_) : memory{memory_} { + callbacks = + std::make_unique<DynarmicCallbacks64>(memory, local_memory, mapped_ranges, *this); + user_config.callbacks = callbacks.get(); + jit = std::make_unique<Dynarmic::A64::Jit>(user_config); + } + + bool LoadNRO(std::span<const u8> data) { + local_memory.clear(); + local_memory.insert(local_memory.end(), data.begin(), data.end()); + + if (FixupRelocations()) { + InsertHelperFunctions(); + InsertStack(); + return true; + } + + return false; + } + + bool FixupRelocations() { + // The loaded NRO file has ELF relocations that must be processed before it can run. + // Normally this would be processed by RTLD, but in HLE context, we don't have + // the linker available, so we have to do it ourselves. + + const VAddr mod_offset{callbacks->MemoryRead32(4)}; + if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) { + return false; + } + + // For more info about dynamic entries, see the ELF ABI specification: + // https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html + // https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html + VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)}; + VAddr rela_dyn = 0; + size_t num_rela = 0; + while (true) { + const auto dyn{callbacks->ReadMemory<Elf64_Dyn>(dynamic_offset)}; + dynamic_offset += sizeof(Elf64_Dyn); + + if (!dyn.d_tag) { + break; + } + if (dyn.d_tag == ElfDtRela) { + rela_dyn = dyn.d_un.d_ptr; + } + if (dyn.d_tag == ElfDtRelasz) { + num_rela = dyn.d_un.d_val / sizeof(Elf64_Rela); + } + } + + for (size_t i = 0; i < num_rela; i++) { + const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))}; + if (Elf64RelType(rela.r_info) != ElfAArch64Relative) { + continue; + } + const VAddr contents{callbacks->MemoryRead64(rela.r_offset)}; + callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend); + } + + return true; + } + + void InsertHelperFunctions() { + for (const auto& name : HELPER_FUNCTIONS) { + helpers[name] = local_memory.size(); + local_memory.insert(local_memory.end(), SVC0_ARM64.begin(), SVC0_ARM64.end()); + } + } + + void InsertStack() { + // Allocate enough space to avoid any reasonable risk of + // overflowing the stack during plugin execution + const u64 pad_amount{Common::AlignUp(local_memory.size(), STACK_ALIGN) - + local_memory.size()}; + local_memory.insert(local_memory.end(), 0x10000 + pad_amount, 0); + top_of_stack = local_memory.size(); + heap_pointer = top_of_stack; + } + + void MapProcessMemory(VAddr dest_address, std::size_t size) { + mapped_ranges.add(IntervalType{dest_address, dest_address + size}); + } + + void PushArgument(const void* data, size_t size) { + const size_t num_words = Common::DivCeil(size, sizeof(u64)); + const size_t current_pos = argument_stack.size(); + argument_stack.insert(argument_stack.end(), num_words, 0); + std::memcpy(argument_stack.data() + current_pos, data, size); + } + + void SetupArguments() { + // The first 8 integer registers are used for the first 8 integer + // arguments. Floating-point arguments are not handled at this time. + // + // If a function takes more than 8 arguments, then stack space is reserved + // for the remaining arguments, and the remaining arguments are inserted in + // ascending memory order, each argument aligned to an 8-byte boundary. The + // stack pointer must remain aligned to 16 bytes. + // + // For more info, see the AArch64 ABI PCS: + // https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst + + for (size_t i = 0; i < 8 && i < argument_stack.size(); i++) { + jit->SetRegister(i, argument_stack[i]); + } + + if (argument_stack.size() > 8) { + const VAddr new_sp = Common::AlignDown( + top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN); + for (size_t i = 8; i < argument_stack.size(); i++) { + callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]); + } + jit->SetSP(new_sp); + } + + // Reset the call state for the next invocation + argument_stack.clear(); + heap_pointer = top_of_stack; + } + + u64 CallFunction(VAddr func) { + jit->SetRegister(30, helpers["_stop"]); + jit->SetSP(top_of_stack); + SetupArguments(); + + jit->SetPC(func); + jit->Run(); + return jit->GetRegister(0); + } + + VAddr GetHelper(const std::string& name) { + return helpers[name]; + } + + VAddr AddHeap(const void* data, size_t size) { + // Require all heap data types to have the same alignment as the + // stack pointer, for compatibility + const size_t num_bytes{Common::AlignUp(size, STACK_ALIGN)}; + + // Make additional memory space if required + if (heap_pointer + num_bytes > local_memory.size()) { + local_memory.insert(local_memory.end(), + (heap_pointer + num_bytes) - local_memory.size(), 0); + } + + const VAddr location{heap_pointer}; + std::memcpy(local_memory.data() + location, data, size); + heap_pointer += num_bytes; + return location; + } + + void GetHeap(VAddr location, void* data, size_t size) { + std::memcpy(data, local_memory.data() + location, size); + } + + std::unique_ptr<DynarmicCallbacks64> callbacks; + std::vector<u8> local_memory; + std::vector<u64> argument_stack; + IntervalSet mapped_ranges; + Dynarmic::A64::UserConfig user_config; + std::unique_ptr<Dynarmic::A64::Jit> jit; + std::map<std::string, VAddr, std::less<>> helpers; + Core::Memory::Memory& memory; + VAddr top_of_stack; + VAddr heap_pointer; +}; + +void DynarmicCallbacks64::CallSVC(u32 swi) { + // Service calls are used to implement helper functionality. + // + // The most important of these is the _stop helper, which transfers control + // from the plugin back to HLE context to return a value. However, a few more + // are also implemented to reduce the need for direct ARM implementations of + // basic functionality, like memory operations. + // + // When we receive a helper request, the swi number will be zero, and the call + // will have originated from an address we know is a helper function. Otherwise, + // the plugin may be trying to issue a service call, which we shouldn't handle. + + if (swi != 0) { + LOG_CRITICAL(Service_JIT, "plugin issued unknown service call {}", swi); + parent.jit->HaltExecution(); + return; + } + + u64 pc{parent.jit->GetPC() - 4}; + auto& helpers{parent.helpers}; + + if (pc == helpers["memcpy"] || pc == helpers["memmove"]) { + const VAddr dest{parent.jit->GetRegister(0)}; + const VAddr src{parent.jit->GetRegister(1)}; + const size_t n{parent.jit->GetRegister(2)}; + + if (dest < src) { + for (size_t i = 0; i < n; i++) { + MemoryWrite8(dest + i, MemoryRead8(src + i)); + } + } else { + for (size_t i = n; i > 0; i--) { + MemoryWrite8(dest + i - 1, MemoryRead8(src + i - 1)); + } + } + } else if (pc == helpers["memset"]) { + const VAddr dest{parent.jit->GetRegister(0)}; + const u64 c{parent.jit->GetRegister(1)}; + const size_t n{parent.jit->GetRegister(2)}; + + for (size_t i = 0; i < n; i++) { + MemoryWrite8(dest + i, static_cast<u8>(c)); + } + } else if (pc == helpers["_resolve"]) { + // X0 contains a char* for a symbol to resolve + const auto name{MemoryReadCString(parent.jit->GetRegister(0))}; + const auto helper{helpers[name]}; + + if (helper != 0) { + parent.jit->SetRegister(0, helper); + } else { + LOG_WARNING(Service_JIT, "plugin requested unknown function {}", name); + parent.jit->SetRegister(0, helpers["_panic"]); + } + } else if (pc == helpers["_stop"]) { + parent.jit->HaltExecution(); + } else if (pc == helpers["_panic"]) { + LOG_CRITICAL(Service_JIT, "plugin panicked!"); + parent.jit->HaltExecution(); + } else { + LOG_CRITICAL(Service_JIT, "plugin issued syscall at unknown address 0x{:x}", pc); + parent.jit->HaltExecution(); + } +} + +void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) { + LOG_CRITICAL(Service_JIT, "Illegal operation PC @ {:08x}", pc); + parent.jit->HaltExecution(); +} + +void DynarmicCallbacks64::InterpreterFallback(u64 pc, size_t num_instructions) { + LOG_CRITICAL(Service_JIT, "Unimplemented instruction PC @ {:08x}", pc); + parent.jit->HaltExecution(); +} + +JITContext::JITContext(Core::Memory::Memory& memory) + : impl{std::make_unique<JITContextImpl>(memory)} {} + +JITContext::~JITContext() {} + +bool JITContext::LoadNRO(std::span<const u8> data) { + return impl->LoadNRO(data); +} + +void JITContext::MapProcessMemory(VAddr dest_address, std::size_t size) { + impl->MapProcessMemory(dest_address, size); +} + +u64 JITContext::CallFunction(VAddr func) { + return impl->CallFunction(func); +} + +void JITContext::PushArgument(const void* data, size_t size) { + impl->PushArgument(data, size); +} + +VAddr JITContext::GetHelper(const std::string& name) { + return impl->GetHelper(name); +} + +VAddr JITContext::AddHeap(const void* data, size_t size) { + return impl->AddHeap(data, size); +} + +void JITContext::GetHeap(VAddr location, void* data, size_t size) { + impl->GetHeap(location, data, size); +} + +} // namespace Service::JIT diff --git a/src/core/hle/service/jit/jit_context.h b/src/core/hle/service/jit/jit_context.h new file mode 100644 index 000000000..f17fc5e24 --- /dev/null +++ b/src/core/hle/service/jit/jit_context.h @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> +#include <span> +#include <string> + +#include "common/common_types.h" + +namespace Core::Memory { +class Memory; +} + +namespace Service::JIT { + +class JITContextImpl; + +class JITContext { +public: + explicit JITContext(Core::Memory::Memory& memory); + ~JITContext(); + + [[nodiscard]] bool LoadNRO(std::span<const u8> data); + void MapProcessMemory(VAddr dest_address, std::size_t size); + + template <typename T, typename... Ts> + u64 CallFunction(VAddr func, T argument, Ts... rest) { + static_assert(std::is_trivially_copyable_v<T>); + static_assert(!std::is_floating_point_v<T>); + PushArgument(&argument, sizeof(argument)); + + if constexpr (sizeof...(rest) > 0) { + return CallFunction(func, rest...); + } else { + return CallFunction(func); + } + } + + u64 CallFunction(VAddr func); + VAddr GetHelper(const std::string& name); + + template <typename T> + VAddr AddHeap(T argument) { + return AddHeap(&argument, sizeof(argument)); + } + VAddr AddHeap(const void* data, size_t size); + + template <typename T> + T GetHeap(VAddr location) { + static_assert(std::is_trivially_copyable_v<T>); + T result; + GetHeap(location, &result, sizeof(result)); + return result; + } + void GetHeap(VAddr location, void* data, size_t size); + +private: + std::unique_ptr<JITContextImpl> impl; + + void PushArgument(const void* data, size_t size); +}; + +} // namespace Service::JIT diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp index 62f4cdfb2..3e317367b 100644 --- a/src/core/hle/service/kernel_helpers.cpp +++ b/src/core/hle/service/kernel_helpers.cpp @@ -1,9 +1,10 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" +#include "core/core_timing.h" #include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_memory_manager.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_resource_limit.h" @@ -15,9 +16,11 @@ namespace Service::KernelHelpers { ServiceContext::ServiceContext(Core::System& system_, std::string name_) : kernel(system_.Kernel()) { + // Create the process. process = Kernel::KProcess::Create(kernel); ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_), - Kernel::KProcess::ProcessType::Userland) + Kernel::KProcess::ProcessType::KernelInternal, + kernel.GetSystemResourceLimit()) .IsSuccess()); } @@ -43,7 +46,7 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) { } // Initialize the event. - event->Initialize(std::move(name)); + event->Initialize(std::move(name), process); // Commit the thread reservation. event_reservation.Commit(); diff --git a/src/core/hle/service/kernel_helpers.h b/src/core/hle/service/kernel_helpers.h index 4f3e95f67..6415838e5 100644 --- a/src/core/hle/service/kernel_helpers.h +++ b/src/core/hle/service/kernel_helpers.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp index 5c8ae029c..c8415e0bf 100644 --- a/src/core/hle/service/lbl/lbl.cpp +++ b/src/core/hle/service/lbl/lbl.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cmath> #include <memory> diff --git a/src/core/hle/service/lbl/lbl.h b/src/core/hle/service/lbl/lbl.h index 9c2021026..6484105c2 100644 --- a/src/core/hle/service/lbl/lbl.h +++ b/src/core/hle/service/lbl/lbl.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/ldn/errors.h b/src/core/hle/service/ldn/errors.h deleted file mode 100644 index a718c5c66..000000000 --- a/src/core/hle/service/ldn/errors.h +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "core/hle/result.h" - -namespace Service::LDN { - -constexpr ResultCode ERROR_DISABLED{ErrorModule::LDN, 22}; - -} // namespace Service::LDN diff --git a/src/core/hle/service/ldn/lan_discovery.cpp b/src/core/hle/service/ldn/lan_discovery.cpp new file mode 100644 index 000000000..8f3c04550 --- /dev/null +++ b/src/core/hle/service/ldn/lan_discovery.cpp @@ -0,0 +1,633 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/ldn/lan_discovery.h" +#include "core/internal_network/network.h" +#include "core/internal_network/network_interface.h" + +namespace Service::LDN { + +LanStation::LanStation(s8 node_id_, LANDiscovery* discovery_) + : node_info(nullptr), status(NodeStatus::Disconnected), node_id(node_id_), + discovery(discovery_) {} + +LanStation::~LanStation() = default; + +NodeStatus LanStation::GetStatus() const { + return status; +} + +void LanStation::OnClose() { + LOG_INFO(Service_LDN, "OnClose {}", node_id); + Reset(); + discovery->UpdateNodes(); +} + +void LanStation::Reset() { + status = NodeStatus::Disconnected; +}; + +void LanStation::OverrideInfo() { + bool connected = GetStatus() == NodeStatus::Connected; + node_info->node_id = node_id; + node_info->is_connected = connected ? 1 : 0; +} + +LANDiscovery::LANDiscovery(Network::RoomNetwork& room_network_) + : stations({{{1, this}, {2, this}, {3, this}, {4, this}, {5, this}, {6, this}, {7, this}}}), + room_network{room_network_} {} + +LANDiscovery::~LANDiscovery() { + if (inited) { + Result rc = Finalize(); + LOG_INFO(Service_LDN, "Finalize: {}", rc.raw); + } +} + +void LANDiscovery::InitNetworkInfo() { + network_info.common.bssid = GetFakeMac(); + network_info.common.channel = WifiChannel::Wifi24_6; + network_info.common.link_level = LinkLevel::Good; + network_info.common.network_type = PackedNetworkType::Ldn; + network_info.common.ssid = fake_ssid; + + auto& nodes = network_info.ldn.nodes; + for (std::size_t i = 0; i < NodeCountMax; i++) { + nodes[i].node_id = static_cast<s8>(i); + nodes[i].is_connected = 0; + } +} + +void LANDiscovery::InitNodeStateChange() { + for (auto& node_update : node_changes) { + node_update.state_change = NodeStateChange::None; + } + for (auto& node_state : node_last_states) { + node_state = 0; + } +} + +State LANDiscovery::GetState() const { + return state; +} + +void LANDiscovery::SetState(State new_state) { + state = new_state; +} + +Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network) const { + if (state == State::AccessPointCreated || state == State::StationConnected) { + std::memcpy(&out_network, &network_info, sizeof(network_info)); + return ResultSuccess; + } + + return ResultBadState; +} + +Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network, + std::vector<NodeLatestUpdate>& out_updates, + std::size_t buffer_count) { + if (buffer_count > NodeCountMax) { + return ResultInvalidBufferCount; + } + + if (state == State::AccessPointCreated || state == State::StationConnected) { + std::memcpy(&out_network, &network_info, sizeof(network_info)); + for (std::size_t i = 0; i < buffer_count; i++) { + out_updates[i].state_change = node_changes[i].state_change; + node_changes[i].state_change = NodeStateChange::None; + } + return ResultSuccess; + } + + return ResultBadState; +} + +DisconnectReason LANDiscovery::GetDisconnectReason() const { + return disconnect_reason; +} + +Result LANDiscovery::Scan(std::vector<NetworkInfo>& networks, u16& count, + const ScanFilter& filter) { + if (!IsFlagSet(filter.flag, ScanFilterFlag::NetworkType) || + filter.network_type <= NetworkType::All) { + if (!IsFlagSet(filter.flag, ScanFilterFlag::Ssid) && filter.ssid.length >= SsidLengthMax) { + return ResultBadInput; + } + } + + { + std::scoped_lock lock{packet_mutex}; + scan_results.clear(); + + SendBroadcast(Network::LDNPacketType::Scan); + } + + LOG_INFO(Service_LDN, "Waiting for scan replies"); + std::this_thread::sleep_for(std::chrono::seconds(1)); + + std::scoped_lock lock{packet_mutex}; + for (const auto& [key, info] : scan_results) { + if (count >= networks.size()) { + break; + } + + if (IsFlagSet(filter.flag, ScanFilterFlag::LocalCommunicationId)) { + if (filter.network_id.intent_id.local_communication_id != + info.network_id.intent_id.local_communication_id) { + continue; + } + } + if (IsFlagSet(filter.flag, ScanFilterFlag::SessionId)) { + if (filter.network_id.session_id != info.network_id.session_id) { + continue; + } + } + if (IsFlagSet(filter.flag, ScanFilterFlag::NetworkType)) { + if (filter.network_type != static_cast<NetworkType>(info.common.network_type)) { + continue; + } + } + if (IsFlagSet(filter.flag, ScanFilterFlag::Ssid)) { + if (filter.ssid != info.common.ssid) { + continue; + } + } + if (IsFlagSet(filter.flag, ScanFilterFlag::SceneId)) { + if (filter.network_id.intent_id.scene_id != info.network_id.intent_id.scene_id) { + continue; + } + } + + networks[count++] = info; + } + + return ResultSuccess; +} + +Result LANDiscovery::SetAdvertiseData(std::span<const u8> data) { + std::scoped_lock lock{packet_mutex}; + const std::size_t size = data.size(); + if (size > AdvertiseDataSizeMax) { + return ResultAdvertiseDataTooLarge; + } + + std::memcpy(network_info.ldn.advertise_data.data(), data.data(), size); + network_info.ldn.advertise_data_size = static_cast<u16>(size); + + UpdateNodes(); + + return ResultSuccess; +} + +Result LANDiscovery::OpenAccessPoint() { + std::scoped_lock lock{packet_mutex}; + disconnect_reason = DisconnectReason::None; + if (state == State::None) { + return ResultBadState; + } + + ResetStations(); + SetState(State::AccessPointOpened); + + return ResultSuccess; +} + +Result LANDiscovery::CloseAccessPoint() { + std::scoped_lock lock{packet_mutex}; + if (state == State::None) { + return ResultBadState; + } + + if (state == State::AccessPointCreated) { + DestroyNetwork(); + } + + ResetStations(); + SetState(State::Initialized); + + return ResultSuccess; +} + +Result LANDiscovery::OpenStation() { + std::scoped_lock lock{packet_mutex}; + disconnect_reason = DisconnectReason::None; + if (state == State::None) { + return ResultBadState; + } + + ResetStations(); + SetState(State::StationOpened); + + return ResultSuccess; +} + +Result LANDiscovery::CloseStation() { + std::scoped_lock lock{packet_mutex}; + if (state == State::None) { + return ResultBadState; + } + + if (state == State::StationConnected) { + Disconnect(); + } + + ResetStations(); + SetState(State::Initialized); + + return ResultSuccess; +} + +Result LANDiscovery::CreateNetwork(const SecurityConfig& security_config, + const UserConfig& user_config, + const NetworkConfig& network_config) { + std::scoped_lock lock{packet_mutex}; + + if (state != State::AccessPointOpened) { + return ResultBadState; + } + + InitNetworkInfo(); + network_info.ldn.node_count_max = network_config.node_count_max; + network_info.ldn.security_mode = security_config.security_mode; + + if (network_config.channel == WifiChannel::Default) { + network_info.common.channel = WifiChannel::Wifi24_6; + } else { + network_info.common.channel = network_config.channel; + } + + std::independent_bits_engine<std::mt19937, 64, u64> bits_engine; + network_info.network_id.session_id.high = bits_engine(); + network_info.network_id.session_id.low = bits_engine(); + network_info.network_id.intent_id = network_config.intent_id; + + NodeInfo& node0 = network_info.ldn.nodes[0]; + const Result rc2 = GetNodeInfo(node0, user_config, network_config.local_communication_version); + if (rc2.IsError()) { + return ResultAccessPointConnectionFailed; + } + + SetState(State::AccessPointCreated); + + InitNodeStateChange(); + node0.is_connected = 1; + UpdateNodes(); + + return rc2; +} + +Result LANDiscovery::DestroyNetwork() { + for (auto local_ip : connected_clients) { + SendPacket(Network::LDNPacketType::DestroyNetwork, local_ip); + } + + ResetStations(); + + SetState(State::AccessPointOpened); + lan_event(); + + return ResultSuccess; +} + +Result LANDiscovery::Connect(const NetworkInfo& network_info_, const UserConfig& user_config, + u16 local_communication_version) { + std::scoped_lock lock{packet_mutex}; + if (network_info_.ldn.node_count == 0) { + return ResultInvalidNodeCount; + } + + Result rc = GetNodeInfo(node_info, user_config, local_communication_version); + if (rc.IsError()) { + return ResultConnectionFailed; + } + + Ipv4Address node_host = network_info_.ldn.nodes[0].ipv4_address; + std::reverse(std::begin(node_host), std::end(node_host)); // htonl + host_ip = node_host; + SendPacket(Network::LDNPacketType::Connect, node_info, *host_ip); + + InitNodeStateChange(); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + return ResultSuccess; +} + +Result LANDiscovery::Disconnect() { + if (host_ip) { + SendPacket(Network::LDNPacketType::Disconnect, node_info, *host_ip); + } + + SetState(State::StationOpened); + lan_event(); + + return ResultSuccess; +} + +Result LANDiscovery::Initialize(LanEventFunc lan_event_, bool listening) { + std::scoped_lock lock{packet_mutex}; + if (inited) { + return ResultSuccess; + } + + for (auto& station : stations) { + station.discovery = this; + station.node_info = &network_info.ldn.nodes[station.node_id]; + station.Reset(); + } + + connected_clients.clear(); + lan_event = lan_event_; + + SetState(State::Initialized); + + inited = true; + return ResultSuccess; +} + +Result LANDiscovery::Finalize() { + std::scoped_lock lock{packet_mutex}; + Result rc = ResultSuccess; + + if (inited) { + if (state == State::AccessPointCreated) { + DestroyNetwork(); + } + if (state == State::StationConnected) { + Disconnect(); + } + + ResetStations(); + inited = false; + } + + SetState(State::None); + + return rc; +} + +void LANDiscovery::ResetStations() { + for (auto& station : stations) { + station.Reset(); + } + connected_clients.clear(); +} + +void LANDiscovery::UpdateNodes() { + u8 count = 0; + for (auto& station : stations) { + bool connected = station.GetStatus() == NodeStatus::Connected; + if (connected) { + count++; + } + station.OverrideInfo(); + } + network_info.ldn.node_count = count + 1; + + for (auto local_ip : connected_clients) { + SendPacket(Network::LDNPacketType::SyncNetwork, network_info, local_ip); + } + + OnNetworkInfoChanged(); +} + +void LANDiscovery::OnSyncNetwork(const NetworkInfo& info) { + network_info = info; + if (state == State::StationOpened) { + SetState(State::StationConnected); + } + OnNetworkInfoChanged(); +} + +void LANDiscovery::OnDisconnectFromHost() { + LOG_INFO(Service_LDN, "OnDisconnectFromHost state: {}", static_cast<int>(state)); + host_ip = std::nullopt; + if (state == State::StationConnected) { + SetState(State::StationOpened); + lan_event(); + } +} + +void LANDiscovery::OnNetworkInfoChanged() { + if (IsNodeStateChanged()) { + lan_event(); + } + return; +} + +Network::IPv4Address LANDiscovery::GetLocalIp() const { + Network::IPv4Address local_ip{0xFF, 0xFF, 0xFF, 0xFF}; + if (auto room_member = room_network.GetRoomMember().lock()) { + if (room_member->IsConnected()) { + local_ip = room_member->GetFakeIpAddress(); + } + } + return local_ip; +} + +template <typename Data> +void LANDiscovery::SendPacket(Network::LDNPacketType type, const Data& data, + Ipv4Address remote_ip) { + Network::LDNPacket packet; + packet.type = type; + + packet.broadcast = false; + packet.local_ip = GetLocalIp(); + packet.remote_ip = remote_ip; + + packet.data.resize(sizeof(data)); + std::memcpy(packet.data.data(), &data, sizeof(data)); + SendPacket(packet); +} + +void LANDiscovery::SendPacket(Network::LDNPacketType type, Ipv4Address remote_ip) { + Network::LDNPacket packet; + packet.type = type; + + packet.broadcast = false; + packet.local_ip = GetLocalIp(); + packet.remote_ip = remote_ip; + + SendPacket(packet); +} + +template <typename Data> +void LANDiscovery::SendBroadcast(Network::LDNPacketType type, const Data& data) { + Network::LDNPacket packet; + packet.type = type; + + packet.broadcast = true; + packet.local_ip = GetLocalIp(); + + packet.data.resize(sizeof(data)); + std::memcpy(packet.data.data(), &data, sizeof(data)); + SendPacket(packet); +} + +void LANDiscovery::SendBroadcast(Network::LDNPacketType type) { + Network::LDNPacket packet; + packet.type = type; + + packet.broadcast = true; + packet.local_ip = GetLocalIp(); + + SendPacket(packet); +} + +void LANDiscovery::SendPacket(const Network::LDNPacket& packet) { + if (auto room_member = room_network.GetRoomMember().lock()) { + if (room_member->IsConnected()) { + room_member->SendLdnPacket(packet); + } + } +} + +void LANDiscovery::ReceivePacket(const Network::LDNPacket& packet) { + std::scoped_lock lock{packet_mutex}; + switch (packet.type) { + case Network::LDNPacketType::Scan: { + LOG_INFO(Frontend, "Scan packet received!"); + if (state == State::AccessPointCreated) { + // Reply to the sender + SendPacket(Network::LDNPacketType::ScanResp, network_info, packet.local_ip); + } + break; + } + case Network::LDNPacketType::ScanResp: { + LOG_INFO(Frontend, "ScanResp packet received!"); + + NetworkInfo info{}; + std::memcpy(&info, packet.data.data(), sizeof(NetworkInfo)); + scan_results.insert({info.common.bssid, info}); + + break; + } + case Network::LDNPacketType::Connect: { + LOG_INFO(Frontend, "Connect packet received!"); + + NodeInfo info{}; + std::memcpy(&info, packet.data.data(), sizeof(NodeInfo)); + + connected_clients.push_back(packet.local_ip); + + for (LanStation& station : stations) { + if (station.status != NodeStatus::Connected) { + *station.node_info = info; + station.status = NodeStatus::Connected; + break; + } + } + + UpdateNodes(); + + break; + } + case Network::LDNPacketType::Disconnect: { + LOG_INFO(Frontend, "Disconnect packet received!"); + + connected_clients.erase( + std::remove(connected_clients.begin(), connected_clients.end(), packet.local_ip), + connected_clients.end()); + + NodeInfo info{}; + std::memcpy(&info, packet.data.data(), sizeof(NodeInfo)); + + for (LanStation& station : stations) { + if (station.status == NodeStatus::Connected && + station.node_info->mac_address == info.mac_address) { + station.OnClose(); + break; + } + } + + break; + } + case Network::LDNPacketType::DestroyNetwork: { + ResetStations(); + OnDisconnectFromHost(); + break; + } + case Network::LDNPacketType::SyncNetwork: { + if (state == State::StationOpened || state == State::StationConnected) { + LOG_INFO(Frontend, "SyncNetwork packet received!"); + NetworkInfo info{}; + std::memcpy(&info, packet.data.data(), sizeof(NetworkInfo)); + + OnSyncNetwork(info); + } else { + LOG_INFO(Frontend, "SyncNetwork packet received but in wrong State!"); + } + + break; + } + default: { + LOG_INFO(Frontend, "ReceivePacket unhandled type {}", static_cast<int>(packet.type)); + break; + } + } +} + +bool LANDiscovery::IsNodeStateChanged() { + bool changed = false; + const auto& nodes = network_info.ldn.nodes; + for (int i = 0; i < NodeCountMax; i++) { + if (nodes[i].is_connected != node_last_states[i]) { + if (nodes[i].is_connected) { + node_changes[i].state_change |= NodeStateChange::Connect; + } else { + node_changes[i].state_change |= NodeStateChange::Disconnect; + } + node_last_states[i] = nodes[i].is_connected; + changed = true; + } + } + return changed; +} + +bool LANDiscovery::IsFlagSet(ScanFilterFlag flag, ScanFilterFlag search_flag) const { + const auto flag_value = static_cast<u32>(flag); + const auto search_flag_value = static_cast<u32>(search_flag); + return (flag_value & search_flag_value) == search_flag_value; +} + +int LANDiscovery::GetStationCount() const { + return static_cast<int>( + std::count_if(stations.begin(), stations.end(), [](const auto& station) { + return station.GetStatus() != NodeStatus::Disconnected; + })); +} + +MacAddress LANDiscovery::GetFakeMac() const { + MacAddress mac{}; + mac.raw[0] = 0x02; + mac.raw[1] = 0x00; + + const auto ip = GetLocalIp(); + memcpy(mac.raw.data() + 2, &ip, sizeof(ip)); + + return mac; +} + +Result LANDiscovery::GetNodeInfo(NodeInfo& node, const UserConfig& userConfig, + u16 localCommunicationVersion) { + const auto network_interface = Network::GetSelectedNetworkInterface(); + + if (!network_interface) { + LOG_ERROR(Service_LDN, "No network interface available"); + return ResultNoIpAddress; + } + + node.mac_address = GetFakeMac(); + node.is_connected = 1; + std::memcpy(node.user_name.data(), userConfig.user_name.data(), UserNameBytesMax + 1); + node.local_communication_version = localCommunicationVersion; + + Ipv4Address current_address = GetLocalIp(); + std::reverse(std::begin(current_address), std::end(current_address)); // ntohl + node.ipv4_address = current_address; + + return ResultSuccess; +} + +} // namespace Service::LDN diff --git a/src/core/hle/service/ldn/lan_discovery.h b/src/core/hle/service/ldn/lan_discovery.h new file mode 100644 index 000000000..3833cd764 --- /dev/null +++ b/src/core/hle/service/ldn/lan_discovery.h @@ -0,0 +1,134 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <cstring> +#include <functional> +#include <memory> +#include <mutex> +#include <optional> +#include <random> +#include <span> +#include <thread> +#include <unordered_map> + +#include "common/logging/log.h" +#include "common/socket_types.h" +#include "core/hle/result.h" +#include "core/hle/service/ldn/ldn_results.h" +#include "core/hle/service/ldn/ldn_types.h" +#include "network/network.h" + +namespace Service::LDN { + +class LANDiscovery; + +class LanStation { +public: + LanStation(s8 node_id_, LANDiscovery* discovery_); + ~LanStation(); + + void OnClose(); + NodeStatus GetStatus() const; + void Reset(); + void OverrideInfo(); + +protected: + friend class LANDiscovery; + NodeInfo* node_info; + NodeStatus status; + s8 node_id; + LANDiscovery* discovery; +}; + +class LANDiscovery { +public: + using LanEventFunc = std::function<void()>; + + LANDiscovery(Network::RoomNetwork& room_network_); + ~LANDiscovery(); + + State GetState() const; + void SetState(State new_state); + + Result GetNetworkInfo(NetworkInfo& out_network) const; + Result GetNetworkInfo(NetworkInfo& out_network, std::vector<NodeLatestUpdate>& out_updates, + std::size_t buffer_count); + + DisconnectReason GetDisconnectReason() const; + Result Scan(std::vector<NetworkInfo>& networks, u16& count, const ScanFilter& filter); + Result SetAdvertiseData(std::span<const u8> data); + + Result OpenAccessPoint(); + Result CloseAccessPoint(); + + Result OpenStation(); + Result CloseStation(); + + Result CreateNetwork(const SecurityConfig& security_config, const UserConfig& user_config, + const NetworkConfig& network_config); + Result DestroyNetwork(); + + Result Connect(const NetworkInfo& network_info_, const UserConfig& user_config, + u16 local_communication_version); + Result Disconnect(); + + Result Initialize(LanEventFunc lan_event_ = empty_func, bool listening = true); + Result Finalize(); + + void ReceivePacket(const Network::LDNPacket& packet); + +protected: + friend class LanStation; + + void InitNetworkInfo(); + void InitNodeStateChange(); + + void ResetStations(); + void UpdateNodes(); + + void OnSyncNetwork(const NetworkInfo& info); + void OnDisconnectFromHost(); + void OnNetworkInfoChanged(); + + bool IsNodeStateChanged(); + bool IsFlagSet(ScanFilterFlag flag, ScanFilterFlag search_flag) const; + int GetStationCount() const; + MacAddress GetFakeMac() const; + Result GetNodeInfo(NodeInfo& node, const UserConfig& user_config, + u16 local_communication_version); + + Network::IPv4Address GetLocalIp() const; + template <typename Data> + void SendPacket(Network::LDNPacketType type, const Data& data, Ipv4Address remote_ip); + void SendPacket(Network::LDNPacketType type, Ipv4Address remote_ip); + template <typename Data> + void SendBroadcast(Network::LDNPacketType type, const Data& data); + void SendBroadcast(Network::LDNPacketType type); + void SendPacket(const Network::LDNPacket& packet); + + static const LanEventFunc empty_func; + static constexpr Ssid fake_ssid{"YuzuFakeSsidForLdn"}; + + bool inited{}; + std::mutex packet_mutex; + std::array<LanStation, StationCountMax> stations; + std::array<NodeLatestUpdate, NodeCountMax> node_changes{}; + std::array<u8, NodeCountMax> node_last_states{}; + std::unordered_map<MacAddress, NetworkInfo, MACAddressHash> scan_results{}; + NodeInfo node_info{}; + NetworkInfo network_info{}; + State state{State::None}; + DisconnectReason disconnect_reason{DisconnectReason::None}; + + // TODO (flTobi): Should this be an std::set? + std::vector<Ipv4Address> connected_clients; + std::optional<Ipv4Address> host_ip; + + LanEventFunc lan_event; + + Network::RoomNetwork& room_network; +}; +} // namespace Service::LDN diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp index 6ccbe9623..ea3e7e55a 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp @@ -1,14 +1,19 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> -#include "core/hle/ipc_helpers.h" -#include "core/hle/result.h" -#include "core/hle/service/ldn/errors.h" +#include "core/core.h" +#include "core/hle/service/ldn/lan_discovery.h" #include "core/hle/service/ldn/ldn.h" -#include "core/hle/service/sm/sm.h" +#include "core/hle/service/ldn/ldn_results.h" +#include "core/hle/service/ldn/ldn_types.h" +#include "core/internal_network/network.h" +#include "core/internal_network/network_interface.h" +#include "network/network.h" + +// This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent +#undef CreateEvent namespace Service::LDN { @@ -101,74 +106,445 @@ class IUserLocalCommunicationService final : public ServiceFramework<IUserLocalCommunicationService> { public: explicit IUserLocalCommunicationService(Core::System& system_) - : ServiceFramework{system_, "IUserLocalCommunicationService"} { + : ServiceFramework{system_, "IUserLocalCommunicationService", ServiceThreadType::CreateNew}, + service_context{system, "IUserLocalCommunicationService"}, + room_network{system_.GetRoomNetwork()}, lan_discovery{room_network} { // clang-format off static const FunctionInfo functions[] = { {0, &IUserLocalCommunicationService::GetState, "GetState"}, - {1, nullptr, "GetNetworkInfo"}, - {2, nullptr, "GetIpv4Address"}, - {3, nullptr, "GetDisconnectReason"}, - {4, nullptr, "GetSecurityParameter"}, - {5, nullptr, "GetNetworkConfig"}, - {100, nullptr, "AttachStateChangeEvent"}, - {101, nullptr, "GetNetworkInfoLatestUpdate"}, - {102, nullptr, "Scan"}, - {103, nullptr, "ScanPrivate"}, - {104, nullptr, "SetWirelessControllerRestriction"}, - {200, nullptr, "OpenAccessPoint"}, - {201, nullptr, "CloseAccessPoint"}, - {202, nullptr, "CreateNetwork"}, - {203, nullptr, "CreateNetworkPrivate"}, - {204, nullptr, "DestroyNetwork"}, + {1, &IUserLocalCommunicationService::GetNetworkInfo, "GetNetworkInfo"}, + {2, &IUserLocalCommunicationService::GetIpv4Address, "GetIpv4Address"}, + {3, &IUserLocalCommunicationService::GetDisconnectReason, "GetDisconnectReason"}, + {4, &IUserLocalCommunicationService::GetSecurityParameter, "GetSecurityParameter"}, + {5, &IUserLocalCommunicationService::GetNetworkConfig, "GetNetworkConfig"}, + {100, &IUserLocalCommunicationService::AttachStateChangeEvent, "AttachStateChangeEvent"}, + {101, &IUserLocalCommunicationService::GetNetworkInfoLatestUpdate, "GetNetworkInfoLatestUpdate"}, + {102, &IUserLocalCommunicationService::Scan, "Scan"}, + {103, &IUserLocalCommunicationService::ScanPrivate, "ScanPrivate"}, + {104, &IUserLocalCommunicationService::SetWirelessControllerRestriction, "SetWirelessControllerRestriction"}, + {200, &IUserLocalCommunicationService::OpenAccessPoint, "OpenAccessPoint"}, + {201, &IUserLocalCommunicationService::CloseAccessPoint, "CloseAccessPoint"}, + {202, &IUserLocalCommunicationService::CreateNetwork, "CreateNetwork"}, + {203, &IUserLocalCommunicationService::CreateNetworkPrivate, "CreateNetworkPrivate"}, + {204, &IUserLocalCommunicationService::DestroyNetwork, "DestroyNetwork"}, {205, nullptr, "Reject"}, - {206, nullptr, "SetAdvertiseData"}, - {207, nullptr, "SetStationAcceptPolicy"}, - {208, nullptr, "AddAcceptFilterEntry"}, + {206, &IUserLocalCommunicationService::SetAdvertiseData, "SetAdvertiseData"}, + {207, &IUserLocalCommunicationService::SetStationAcceptPolicy, "SetStationAcceptPolicy"}, + {208, &IUserLocalCommunicationService::AddAcceptFilterEntry, "AddAcceptFilterEntry"}, {209, nullptr, "ClearAcceptFilter"}, - {300, nullptr, "OpenStation"}, - {301, nullptr, "CloseStation"}, - {302, nullptr, "Connect"}, + {300, &IUserLocalCommunicationService::OpenStation, "OpenStation"}, + {301, &IUserLocalCommunicationService::CloseStation, "CloseStation"}, + {302, &IUserLocalCommunicationService::Connect, "Connect"}, {303, nullptr, "ConnectPrivate"}, - {304, nullptr, "Disconnect"}, - {400, nullptr, "Initialize"}, - {401, nullptr, "Finalize"}, - {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"}, // 7.0.0+ + {304, &IUserLocalCommunicationService::Disconnect, "Disconnect"}, + {400, &IUserLocalCommunicationService::Initialize, "Initialize"}, + {401, &IUserLocalCommunicationService::Finalize, "Finalize"}, + {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"}, }; // clang-format on RegisterHandlers(functions); + + state_change_event = + service_context.CreateEvent("IUserLocalCommunicationService:StateChangeEvent"); + } + + ~IUserLocalCommunicationService() { + if (is_initialized) { + if (auto room_member = room_network.GetRoomMember().lock()) { + room_member->Unbind(ldn_packet_received); + } + } + + service_context.CloseEvent(state_change_event); + } + + /// Callback to parse and handle a received LDN packet. + void OnLDNPacketReceived(const Network::LDNPacket& packet) { + lan_discovery.ReceivePacket(packet); + } + + void OnEventFired() { + state_change_event->GetWritableEvent().Signal(); } void GetState(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_LDN, "(STUBBED) called"); + State state = State::Error; + + if (is_initialized) { + state = lan_discovery.GetState(); + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(state); + } + + void GetNetworkInfo(Kernel::HLERequestContext& ctx) { + const auto write_buffer_size = ctx.GetWriteBufferSize(); + + if (write_buffer_size != sizeof(NetworkInfo)) { + LOG_ERROR(Service_LDN, "Invalid buffer size {}", write_buffer_size); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultBadInput); + return; + } + + NetworkInfo network_info{}; + const auto rc = lan_discovery.GetNetworkInfo(network_info); + if (rc.IsError()) { + LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(rc); + return; + } + + ctx.WriteBuffer<NetworkInfo>(network_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + void GetIpv4Address(Kernel::HLERequestContext& ctx) { + const auto network_interface = Network::GetSelectedNetworkInterface(); + + if (!network_interface) { + LOG_ERROR(Service_LDN, "No network interface available"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultNoIpAddress); + return; + } + + Ipv4Address current_address{Network::TranslateIPv4(network_interface->ip_address)}; + Ipv4Address subnet_mask{Network::TranslateIPv4(network_interface->subnet_mask)}; + + // When we're connected to a room, spoof the hosts IP address + if (auto room_member = room_network.GetRoomMember().lock()) { + if (room_member->IsConnected()) { + current_address = room_member->GetFakeIpAddress(); + } + } + + std::reverse(std::begin(current_address), std::end(current_address)); // ntohl + std::reverse(std::begin(subnet_mask), std::end(subnet_mask)); // ntohl + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushRaw(current_address); + rb.PushRaw(subnet_mask); + } + + void GetDisconnectReason(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(lan_discovery.GetDisconnectReason()); + } - // Indicate a network error, as we do not actually emulate LDN - rb.Push(static_cast<u32>(State::Error)); + void GetSecurityParameter(Kernel::HLERequestContext& ctx) { + SecurityParameter security_parameter{}; + NetworkInfo info{}; + const Result rc = lan_discovery.GetNetworkInfo(info); + + if (rc.IsError()) { + LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(rc); + return; + } + + security_parameter.session_id = info.network_id.session_id; + std::memcpy(security_parameter.data.data(), info.ldn.security_parameter.data(), + sizeof(SecurityParameter::data)); + + IPC::ResponseBuilder rb{ctx, 10}; + rb.Push(rc); + rb.PushRaw<SecurityParameter>(security_parameter); + } + void GetNetworkConfig(Kernel::HLERequestContext& ctx) { + NetworkConfig config{}; + NetworkInfo info{}; + const Result rc = lan_discovery.GetNetworkInfo(info); + + if (rc.IsError()) { + LOG_ERROR(Service_LDN, "NetworkConfig is not valid {}", rc.raw); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(rc); + return; + } + + config.intent_id = info.network_id.intent_id; + config.channel = info.common.channel; + config.node_count_max = info.ldn.node_count_max; + config.local_communication_version = info.ldn.nodes[0].local_communication_version; + + IPC::ResponseBuilder rb{ctx, 10}; + rb.Push(rc); + rb.PushRaw<NetworkConfig>(config); + } + + void AttachStateChangeEvent(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_LDN, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); + rb.PushCopyObjects(state_change_event->GetReadableEvent()); } - void Initialize2(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_LDN, "called"); + void GetNetworkInfoLatestUpdate(Kernel::HLERequestContext& ctx) { + const std::size_t network_buffer_size = ctx.GetWriteBufferSize(0); + const std::size_t node_buffer_count = ctx.GetWriteBufferSize(1) / sizeof(NodeLatestUpdate); - is_initialized = true; + if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) { + LOG_ERROR(Service_LDN, "Invalid buffer, size = {}, count = {}", network_buffer_size, + node_buffer_count); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultBadInput); + return; + } + + NetworkInfo info{}; + std::vector<NodeLatestUpdate> latest_update(node_buffer_count); + + const auto rc = lan_discovery.GetNetworkInfo(info, latest_update, latest_update.size()); + if (rc.IsError()) { + LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(rc); + return; + } + + ctx.WriteBuffer(info, 0); + ctx.WriteBuffer(latest_update, 1); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_DISABLED); + rb.Push(ResultSuccess); + } + + void Scan(Kernel::HLERequestContext& ctx) { + ScanImpl(ctx); + } + + void ScanPrivate(Kernel::HLERequestContext& ctx) { + ScanImpl(ctx, true); + } + + void ScanImpl(Kernel::HLERequestContext& ctx, bool is_private = false) { + IPC::RequestParser rp{ctx}; + const auto channel{rp.PopEnum<WifiChannel>()}; + const auto scan_filter{rp.PopRaw<ScanFilter>()}; + + const std::size_t network_info_size = ctx.GetWriteBufferSize() / sizeof(NetworkInfo); + + if (network_info_size == 0) { + LOG_ERROR(Service_LDN, "Invalid buffer size {}", network_info_size); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultBadInput); + return; + } + + u16 count = 0; + std::vector<NetworkInfo> network_infos(network_info_size); + Result rc = lan_discovery.Scan(network_infos, count, scan_filter); + + LOG_INFO(Service_LDN, + "called, channel={}, filter_scan_flag={}, filter_network_type={}, is_private={}", + channel, scan_filter.flag, scan_filter.network_type, is_private); + + ctx.WriteBuffer(network_infos); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(rc); + rb.Push<u32>(count); + } + + void SetWirelessControllerRestriction(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_LDN, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); } -private: - enum class State { - None, - Initialized, - AccessPointOpened, - AccessPointCreated, - StationOpened, - StationConnected, - Error, - }; + void OpenAccessPoint(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_LDN, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(lan_discovery.OpenAccessPoint()); + } + + void CloseAccessPoint(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_LDN, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(lan_discovery.CloseAccessPoint()); + } + + void CreateNetwork(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_LDN, "called"); + + CreateNetworkImpl(ctx); + } + + void CreateNetworkPrivate(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_LDN, "called"); + + CreateNetworkImpl(ctx, true); + } + + void CreateNetworkImpl(Kernel::HLERequestContext& ctx, bool is_private = false) { + IPC::RequestParser rp{ctx}; + + const auto security_config{rp.PopRaw<SecurityConfig>()}; + [[maybe_unused]] const auto security_parameter{is_private ? rp.PopRaw<SecurityParameter>() + : SecurityParameter{}}; + const auto user_config{rp.PopRaw<UserConfig>()}; + rp.Pop<u32>(); // Padding + const auto network_Config{rp.PopRaw<NetworkConfig>()}; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(lan_discovery.CreateNetwork(security_config, user_config, network_Config)); + } + + void DestroyNetwork(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_LDN, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(lan_discovery.DestroyNetwork()); + } + + void SetAdvertiseData(Kernel::HLERequestContext& ctx) { + std::vector<u8> read_buffer = ctx.ReadBuffer(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(lan_discovery.SetAdvertiseData(read_buffer)); + } + + void SetStationAcceptPolicy(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_LDN, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void AddAcceptFilterEntry(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_LDN, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void OpenStation(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_LDN, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(lan_discovery.OpenStation()); + } + + void CloseStation(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_LDN, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(lan_discovery.CloseStation()); + } + + void Connect(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + SecurityConfig security_config; + UserConfig user_config; + u32 local_communication_version; + u32 option; + }; + static_assert(sizeof(Parameters) == 0x7C, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_INFO(Service_LDN, + "called, passphrase_size={}, security_mode={}, " + "local_communication_version={}", + parameters.security_config.passphrase_size, + parameters.security_config.security_mode, parameters.local_communication_version); + + const std::vector<u8> read_buffer = ctx.ReadBuffer(); + if (read_buffer.size() != sizeof(NetworkInfo)) { + LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultBadInput); + return; + } + + NetworkInfo network_info{}; + std::memcpy(&network_info, read_buffer.data(), read_buffer.size()); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(lan_discovery.Connect(network_info, parameters.user_config, + static_cast<u16>(parameters.local_communication_version))); + } + + void Disconnect(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_LDN, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(lan_discovery.Disconnect()); + } + + void Initialize(Kernel::HLERequestContext& ctx) { + const auto rc = InitializeImpl(ctx); + if (rc.IsError()) { + LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(rc); + } + + void Finalize(Kernel::HLERequestContext& ctx) { + if (auto room_member = room_network.GetRoomMember().lock()) { + room_member->Unbind(ldn_packet_received); + } + + is_initialized = false; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(lan_discovery.Finalize()); + } + + void Initialize2(Kernel::HLERequestContext& ctx) { + const auto rc = InitializeImpl(ctx); + if (rc.IsError()) { + LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(rc); + } + + Result InitializeImpl(Kernel::HLERequestContext& ctx) { + const auto network_interface = Network::GetSelectedNetworkInterface(); + if (!network_interface) { + LOG_ERROR(Service_LDN, "No network interface is set"); + return ResultAirplaneModeEnabled; + } + + if (auto room_member = room_network.GetRoomMember().lock()) { + ldn_packet_received = room_member->BindOnLdnPacketReceived( + [this](const Network::LDNPacket& packet) { OnLDNPacketReceived(packet); }); + } else { + LOG_ERROR(Service_LDN, "Couldn't bind callback!"); + return ResultAirplaneModeEnabled; + } + + lan_discovery.Initialize([&]() { OnEventFired(); }); + is_initialized = true; + return ResultSuccess; + } + + KernelHelpers::ServiceContext service_context; + Kernel::KEvent* state_change_event; + Network::RoomNetwork& room_network; + LANDiscovery lan_discovery; + + // Callback identifier for the OnLDNPacketReceived event. + Network::RoomMember::CallbackHandle<Network::LDNPacket> ldn_packet_received; bool is_initialized{}; }; @@ -274,7 +650,7 @@ public: LOG_WARNING(Service_LDN, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_DISABLED); + rb.Push(ResultDisabled); } }; diff --git a/src/core/hle/service/ldn/ldn.h b/src/core/hle/service/ldn/ldn.h index 3ccd9738b..6afe2ea6f 100644 --- a/src/core/hle/service/ldn/ldn.h +++ b/src/core/hle/service/ldn/ldn.h @@ -1,9 +1,14 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/result.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/sm/sm.h" + namespace Core { class System; } diff --git a/src/core/hle/service/ldn/ldn_results.h b/src/core/hle/service/ldn/ldn_results.h new file mode 100644 index 000000000..f340bda42 --- /dev/null +++ b/src/core/hle/service/ldn/ldn_results.h @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/result.h" + +namespace Service::LDN { + +constexpr Result ResultAdvertiseDataTooLarge{ErrorModule::LDN, 10}; +constexpr Result ResultAuthenticationFailed{ErrorModule::LDN, 20}; +constexpr Result ResultDisabled{ErrorModule::LDN, 22}; +constexpr Result ResultAirplaneModeEnabled{ErrorModule::LDN, 23}; +constexpr Result ResultInvalidNodeCount{ErrorModule::LDN, 30}; +constexpr Result ResultConnectionFailed{ErrorModule::LDN, 31}; +constexpr Result ResultBadState{ErrorModule::LDN, 32}; +constexpr Result ResultNoIpAddress{ErrorModule::LDN, 33}; +constexpr Result ResultInvalidBufferCount{ErrorModule::LDN, 50}; +constexpr Result ResultAccessPointConnectionFailed{ErrorModule::LDN, 65}; +constexpr Result ResultAuthenticationTimeout{ErrorModule::LDN, 66}; +constexpr Result ResultMaximumNodeCount{ErrorModule::LDN, 67}; +constexpr Result ResultBadInput{ErrorModule::LDN, 96}; +constexpr Result ResultLocalCommunicationIdNotFound{ErrorModule::LDN, 97}; +constexpr Result ResultLocalCommunicationVersionTooLow{ErrorModule::LDN, 113}; +constexpr Result ResultLocalCommunicationVersionTooHigh{ErrorModule::LDN, 114}; + +} // namespace Service::LDN diff --git a/src/core/hle/service/ldn/ldn_types.h b/src/core/hle/service/ldn/ldn_types.h new file mode 100644 index 000000000..44c2c773b --- /dev/null +++ b/src/core/hle/service/ldn/ldn_types.h @@ -0,0 +1,306 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <fmt/format.h> + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "network/network.h" + +namespace Service::LDN { + +constexpr size_t SsidLengthMax = 32; +constexpr size_t AdvertiseDataSizeMax = 384; +constexpr size_t UserNameBytesMax = 32; +constexpr int NodeCountMax = 8; +constexpr int StationCountMax = NodeCountMax - 1; +constexpr size_t PassphraseLengthMax = 64; + +enum class SecurityMode : u16 { + All, + Retail, + Debug, +}; + +enum class NodeStateChange : u8 { + None, + Connect, + Disconnect, + DisconnectAndConnect, +}; + +DECLARE_ENUM_FLAG_OPERATORS(NodeStateChange) + +enum class ScanFilterFlag : u32 { + None = 0, + LocalCommunicationId = 1 << 0, + SessionId = 1 << 1, + NetworkType = 1 << 2, + Ssid = 1 << 4, + SceneId = 1 << 5, + IntentId = LocalCommunicationId | SceneId, + NetworkId = IntentId | SessionId, +}; + +enum class NetworkType : u32 { + None, + General, + Ldn, + All, +}; + +enum class PackedNetworkType : u8 { + None, + General, + Ldn, + All, +}; + +enum class State : u32 { + None, + Initialized, + AccessPointOpened, + AccessPointCreated, + StationOpened, + StationConnected, + Error, +}; + +enum class DisconnectReason : s16 { + Unknown = -1, + None, + DisconnectedByUser, + DisconnectedBySystem, + DestroyedByUser, + DestroyedBySystem, + Rejected, + SignalLost, +}; + +enum class NetworkError { + Unknown = -1, + None = 0, + PortUnreachable, + TooManyPlayers, + VersionTooLow, + VersionTooHigh, + ConnectFailure, + ConnectNotFound, + ConnectTimeout, + ConnectRejected, + RejectFailed, +}; + +enum class AcceptPolicy : u8 { + AcceptAll, + RejectAll, + BlackList, + WhiteList, +}; + +enum class WifiChannel : s16 { + Default = 0, + Wifi24_1 = 1, + Wifi24_6 = 6, + Wifi24_11 = 11, + Wifi50_36 = 36, + Wifi50_40 = 40, + Wifi50_44 = 44, + Wifi50_48 = 48, +}; + +enum class LinkLevel : s8 { + Bad, + Low, + Good, + Excellent, +}; + +enum class NodeStatus : u8 { + Disconnected, + Connected, +}; + +struct NodeLatestUpdate { + NodeStateChange state_change; + INSERT_PADDING_BYTES(0x7); // Unknown +}; +static_assert(sizeof(NodeLatestUpdate) == 0x8, "NodeLatestUpdate is an invalid size"); + +struct SessionId { + u64 high; + u64 low; + + bool operator==(const SessionId&) const = default; +}; +static_assert(sizeof(SessionId) == 0x10, "SessionId is an invalid size"); + +struct IntentId { + u64 local_communication_id; + INSERT_PADDING_BYTES(0x2); // Reserved + u16 scene_id; + INSERT_PADDING_BYTES(0x4); // Reserved +}; +static_assert(sizeof(IntentId) == 0x10, "IntentId is an invalid size"); + +struct NetworkId { + IntentId intent_id; + SessionId session_id; +}; +static_assert(sizeof(NetworkId) == 0x20, "NetworkId is an invalid size"); + +struct Ssid { + u8 length{}; + std::array<char, SsidLengthMax + 1> raw{}; + + Ssid() = default; + + constexpr explicit Ssid(std::string_view data) { + length = static_cast<u8>(std::min(data.size(), SsidLengthMax)); + data.copy(raw.data(), length); + raw[length] = 0; + } + + std::string GetStringValue() const { + return std::string(raw.data()); + } + + bool operator==(const Ssid& b) const { + return (length == b.length) && (std::memcmp(raw.data(), b.raw.data(), length) == 0); + } + + bool operator!=(const Ssid& b) const { + return !operator==(b); + } +}; +static_assert(sizeof(Ssid) == 0x22, "Ssid is an invalid size"); + +using Ipv4Address = std::array<u8, 4>; +static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size"); + +struct MacAddress { + std::array<u8, 6> raw{}; + + friend bool operator==(const MacAddress& lhs, const MacAddress& rhs) = default; +}; +static_assert(sizeof(MacAddress) == 0x6, "MacAddress is an invalid size"); + +struct MACAddressHash { + size_t operator()(const MacAddress& address) const { + u64 value{}; + std::memcpy(&value, address.raw.data(), sizeof(address.raw)); + return value; + } +}; + +struct ScanFilter { + NetworkId network_id; + NetworkType network_type; + MacAddress mac_address; + Ssid ssid; + INSERT_PADDING_BYTES(0x10); + ScanFilterFlag flag; +}; +static_assert(sizeof(ScanFilter) == 0x60, "ScanFilter is an invalid size"); + +struct CommonNetworkInfo { + MacAddress bssid; + Ssid ssid; + WifiChannel channel; + LinkLevel link_level; + PackedNetworkType network_type; + INSERT_PADDING_BYTES(0x4); +}; +static_assert(sizeof(CommonNetworkInfo) == 0x30, "CommonNetworkInfo is an invalid size"); + +struct NodeInfo { + Ipv4Address ipv4_address; + MacAddress mac_address; + s8 node_id; + u8 is_connected; + std::array<u8, UserNameBytesMax + 1> user_name; + INSERT_PADDING_BYTES(0x1); // Reserved + s16 local_communication_version; + INSERT_PADDING_BYTES(0x10); // Reserved +}; +static_assert(sizeof(NodeInfo) == 0x40, "NodeInfo is an invalid size"); + +struct LdnNetworkInfo { + std::array<u8, 0x10> security_parameter; + SecurityMode security_mode; + AcceptPolicy station_accept_policy; + u8 has_action_frame; + INSERT_PADDING_BYTES(0x2); // Padding + u8 node_count_max; + u8 node_count; + std::array<NodeInfo, NodeCountMax> nodes; + INSERT_PADDING_BYTES(0x2); // Reserved + u16 advertise_data_size; + std::array<u8, AdvertiseDataSizeMax> advertise_data; + INSERT_PADDING_BYTES(0x8C); // Reserved + u64 random_authentication_id; +}; +static_assert(sizeof(LdnNetworkInfo) == 0x430, "LdnNetworkInfo is an invalid size"); + +struct NetworkInfo { + NetworkId network_id; + CommonNetworkInfo common; + LdnNetworkInfo ldn; +}; +static_assert(sizeof(NetworkInfo) == 0x480, "NetworkInfo is an invalid size"); + +struct SecurityConfig { + SecurityMode security_mode; + u16 passphrase_size; + std::array<u8, PassphraseLengthMax> passphrase; +}; +static_assert(sizeof(SecurityConfig) == 0x44, "SecurityConfig is an invalid size"); + +struct UserConfig { + std::array<u8, UserNameBytesMax + 1> user_name; + INSERT_PADDING_BYTES(0xF); // Reserved +}; +static_assert(sizeof(UserConfig) == 0x30, "UserConfig is an invalid size"); + +#pragma pack(push, 4) +struct ConnectRequest { + SecurityConfig security_config; + UserConfig user_config; + u32 local_communication_version; + u32 option_unknown; + NetworkInfo network_info; +}; +static_assert(sizeof(ConnectRequest) == 0x4FC, "ConnectRequest is an invalid size"); +#pragma pack(pop) + +struct SecurityParameter { + std::array<u8, 0x10> data; // Data, used with the same key derivation as SecurityConfig + SessionId session_id; +}; +static_assert(sizeof(SecurityParameter) == 0x20, "SecurityParameter is an invalid size"); + +struct NetworkConfig { + IntentId intent_id; + WifiChannel channel; + u8 node_count_max; + INSERT_PADDING_BYTES(0x1); // Reserved + u16 local_communication_version; + INSERT_PADDING_BYTES(0xA); // Reserved +}; +static_assert(sizeof(NetworkConfig) == 0x20, "NetworkConfig is an invalid size"); + +struct AddressEntry { + Ipv4Address ipv4_address; + MacAddress mac_address; + INSERT_PADDING_BYTES(0x2); // Reserved +}; +static_assert(sizeof(AddressEntry) == 0xC, "AddressEntry is an invalid size"); + +struct AddressList { + std::array<AddressEntry, 0x8> addresses; +}; +static_assert(sizeof(AddressList) == 0x60, "AddressList is an invalid size"); + +} // namespace Service::LDN diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 9fc7bb1b1..becd6d1b9 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> #include <fmt/format.h> @@ -12,7 +11,6 @@ #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_page_table.h" -#include "core/hle/kernel/k_system_control.h" #include "core/hle/kernel/svc_results.h" #include "core/hle/kernel/svc_types.h" #include "core/hle/service/ldr/ldr.h" @@ -22,20 +20,20 @@ namespace Service::LDR { -constexpr ResultCode ERROR_INSUFFICIENT_ADDRESS_SPACE{ErrorModule::RO, 2}; - -[[maybe_unused]] constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51}; -constexpr ResultCode ERROR_INVALID_NRO{ErrorModule::Loader, 52}; -constexpr ResultCode ERROR_INVALID_NRR{ErrorModule::Loader, 53}; -constexpr ResultCode ERROR_MISSING_NRR_HASH{ErrorModule::Loader, 54}; -constexpr ResultCode ERROR_MAXIMUM_NRO{ErrorModule::Loader, 55}; -constexpr ResultCode ERROR_MAXIMUM_NRR{ErrorModule::Loader, 56}; -constexpr ResultCode ERROR_ALREADY_LOADED{ErrorModule::Loader, 57}; -constexpr ResultCode ERROR_INVALID_ALIGNMENT{ErrorModule::Loader, 81}; -constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::Loader, 82}; -constexpr ResultCode ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84}; -[[maybe_unused]] constexpr ResultCode ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85}; -constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87}; +constexpr Result ERROR_INSUFFICIENT_ADDRESS_SPACE{ErrorModule::RO, 2}; + +[[maybe_unused]] constexpr Result ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51}; +constexpr Result ERROR_INVALID_NRO{ErrorModule::Loader, 52}; +constexpr Result ERROR_INVALID_NRR{ErrorModule::Loader, 53}; +constexpr Result ERROR_MISSING_NRR_HASH{ErrorModule::Loader, 54}; +constexpr Result ERROR_MAXIMUM_NRO{ErrorModule::Loader, 55}; +constexpr Result ERROR_MAXIMUM_NRR{ErrorModule::Loader, 56}; +constexpr Result ERROR_ALREADY_LOADED{ErrorModule::Loader, 57}; +constexpr Result ERROR_INVALID_ALIGNMENT{ErrorModule::Loader, 81}; +constexpr Result ERROR_INVALID_SIZE{ErrorModule::Loader, 82}; +constexpr Result ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84}; +[[maybe_unused]] constexpr Result ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85}; +constexpr Result ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87}; constexpr std::size_t MAXIMUM_LOADED_RO{0x40}; constexpr std::size_t MAXIMUM_MAP_RETRIES{0x200}; @@ -161,7 +159,8 @@ public: class RelocatableObject final : public ServiceFramework<RelocatableObject> { public: - explicit RelocatableObject(Core::System& system_) : ServiceFramework{system_, "ldr:ro"} { + explicit RelocatableObject(Core::System& system_) + : ServiceFramework{system_, "ldr:ro", ServiceThreadType::CreateNew} { // clang-format off static const FunctionInfo functions[] = { {0, &RelocatableObject::LoadModule, "LoadModule"}, @@ -288,7 +287,7 @@ public: } bool ValidateRegionForMap(Kernel::KPageTable& page_table, VAddr start, std::size_t size) const { - constexpr std::size_t padding_size{4 * Kernel::PageSize}; + const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize}; const auto start_info{page_table.QueryInfo(start - 1)}; if (start_info.state != Kernel::KMemoryState::Free) { @@ -308,31 +307,69 @@ public: return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize()); } - VAddr GetRandomMapRegion(const Kernel::KPageTable& page_table, std::size_t size) const { - VAddr addr{}; - const std::size_t end_pages{(page_table.GetAliasCodeRegionSize() - size) >> - Kernel::PageBits}; - do { - addr = page_table.GetAliasCodeRegionStart() + - (Kernel::KSystemControl::GenerateRandomRange(0, end_pages) << Kernel::PageBits); - } while (!page_table.IsInsideAddressSpace(addr, size) || - page_table.IsInsideHeapRegion(addr, size) || - page_table.IsInsideAliasRegion(addr, size)); - return addr; + Result GetAvailableMapRegion(Kernel::KPageTable& page_table, u64 size, VAddr& out_addr) { + size = Common::AlignUp(size, Kernel::PageSize); + size += page_table.GetNumGuardPages() * Kernel::PageSize * 4; + + const auto is_region_available = [&](VAddr addr) { + const auto end_addr = addr + size; + while (addr < end_addr) { + if (system.Memory().IsValidVirtualAddress(addr)) { + return false; + } + + if (!page_table.IsInsideAddressSpace(out_addr, size)) { + return false; + } + + if (page_table.IsInsideHeapRegion(out_addr, size)) { + return false; + } + + if (page_table.IsInsideAliasRegion(out_addr, size)) { + return false; + } + + addr += Kernel::PageSize; + } + return true; + }; + + bool succeeded = false; + const auto map_region_end = + page_table.GetAliasCodeRegionStart() + page_table.GetAliasCodeRegionSize(); + while (current_map_addr < map_region_end) { + if (is_region_available(current_map_addr)) { + succeeded = true; + break; + } + current_map_addr += 0x100000; + } + + if (!succeeded) { + ASSERT_MSG(false, "Out of address space!"); + return Kernel::ResultOutOfMemory; + } + + out_addr = current_map_addr; + current_map_addr += size; + + return ResultSuccess; } - ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr baseAddress, - u64 size) const { + ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr base_addr, u64 size) { + auto& page_table{process->PageTable()}; + VAddr addr{}; + for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { - auto& page_table{process->PageTable()}; - const VAddr addr{GetRandomMapRegion(page_table, size)}; - const ResultCode result{page_table.MapCodeMemory(addr, baseAddress, size)}; + R_TRY(GetAvailableMapRegion(page_table, size, addr)); + const Result result{page_table.MapCodeMemory(addr, base_addr, size)}; if (result == Kernel::ResultInvalidCurrentMemory) { continue; } - CASCADE_CODE(result); + R_TRY(result); if (ValidateRegionForMap(page_table, addr, size)) { return addr; @@ -343,7 +380,7 @@ public: } ResultVal<VAddr> MapNro(Kernel::KProcess* process, VAddr nro_addr, std::size_t nro_size, - VAddr bss_addr, std::size_t bss_size, std::size_t size) const { + VAddr bss_addr, std::size_t bss_size, std::size_t size) { for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { auto& page_table{process->PageTable()}; VAddr addr{}; @@ -352,12 +389,15 @@ 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{ - page_table.MapCodeMemory(addr + nro_size, bss_addr, bss_size)}; + const Result result{page_table.MapCodeMemory(addr + nro_size, bss_addr, bss_size)}; if (result == Kernel::ResultInvalidCurrentMemory) { continue; @@ -378,8 +418,8 @@ public: return ERROR_INSUFFICIENT_ADDRESS_SPACE; } - ResultCode LoadNro(Kernel::KProcess* process, const NROHeader& nro_header, VAddr nro_addr, - VAddr start) const { + Result LoadNro(Kernel::KProcess* process, const NROHeader& nro_header, VAddr nro_addr, + VAddr start) const { const VAddr text_start{start + nro_header.segment_headers[TEXT_INDEX].memory_offset}; const VAddr ro_start{start + nro_header.segment_headers[RO_INDEX].memory_offset}; const VAddr data_start{start + nro_header.segment_headers[DATA_INDEX].memory_offset}; @@ -528,22 +568,26 @@ public: rb.Push(*map_result); } - ResultCode UnmapNro(const NROInfo& info) { + Result UnmapNro(const NROInfo& info) { // Each region must be unmapped separately to validate memory state 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; } @@ -597,6 +641,7 @@ public: LOG_WARNING(Service_LDR, "(STUBBED) called"); initialized = true; + current_map_addr = system.CurrentProcess()->PageTable().GetAliasCodeRegionStart(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -607,6 +652,7 @@ private: std::map<VAddr, NROInfo> nro; std::map<VAddr, std::vector<SHA256Hash>> nrr; + VAddr current_map_addr{}; bool IsValidNROHash(const SHA256Hash& hash) const { return std::any_of(nrr.begin(), nrr.end(), [&hash](const auto& p) { diff --git a/src/core/hle/service/ldr/ldr.h b/src/core/hle/service/ldr/ldr.h index 104fc15c5..25ffd8442 100644 --- a/src/core/hle/service/ldr/ldr.h +++ b/src/core/hle/service/ldr/ldr.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index e40383134..ef4b54046 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <string> diff --git a/src/core/hle/service/lm/lm.h b/src/core/hle/service/lm/lm.h index d40410b5c..266019c30 100644 --- a/src/core/hle/service/lm/lm.h +++ b/src/core/hle/service/lm/lm.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/mig/mig.cpp b/src/core/hle/service/mig/mig.cpp index 1599d941b..b9fe0cecd 100644 --- a/src/core/hle/service/mig/mig.cpp +++ b/src/core/hle/service/mig/mig.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/mig/mig.h b/src/core/hle/service/mig/mig.h index 2b24cdf2c..f1641a521 100644 --- a/src/core/hle/service/mig/mig.h +++ b/src/core/hle/service/mig/mig.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp index 0b907824d..390514fdc 100644 --- a/src/core/hle/service/mii/mii.cpp +++ b/src/core/hle/service/mii/mii.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> @@ -13,7 +12,7 @@ namespace Service::Mii { -constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1}; +constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1}; class IDatabaseService final : public ServiceFramework<IDatabaseService> { public: @@ -44,7 +43,7 @@ public: {20, nullptr, "IsBrokenDatabaseWithClearFlag"}, {21, &IDatabaseService::GetIndex, "GetIndex"}, {22, &IDatabaseService::SetInterfaceVersion, "SetInterfaceVersion"}, - {23, nullptr, "Convert"}, + {23, &IDatabaseService::Convert, "Convert"}, {24, nullptr, "ConvertCoreDataToCharInfo"}, {25, nullptr, "ConvertCharInfoToCoreData"}, {26, nullptr, "Append"}, @@ -131,7 +130,7 @@ private: return; } - std::vector<MiiInfo> values; + std::vector<CharInfo> values; for (const auto& element : *result) { values.emplace_back(element.info); } @@ -145,7 +144,7 @@ private: void UpdateLatest(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto info{rp.PopRaw<MiiInfo>()}; + const auto info{rp.PopRaw<CharInfo>()}; const auto source_flag{rp.PopRaw<SourceFlag>()}; LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); @@ -157,9 +156,9 @@ private: return; } - IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)}; + IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; rb.Push(ResultSuccess); - rb.PushRaw<MiiInfo>(*result); + rb.PushRaw<CharInfo>(*result); } void BuildRandom(Kernel::HLERequestContext& ctx) { @@ -192,9 +191,9 @@ private: return; } - IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)}; + IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; rb.Push(ResultSuccess); - rb.PushRaw<MiiInfo>(manager.BuildRandom(age, gender, race)); + rb.PushRaw<CharInfo>(manager.BuildRandom(age, gender, race)); } void BuildDefault(Kernel::HLERequestContext& ctx) { @@ -211,14 +210,14 @@ private: return; } - IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)}; + IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; rb.Push(ResultSuccess); - rb.PushRaw<MiiInfo>(manager.BuildDefault(index)); + rb.PushRaw<CharInfo>(manager.BuildDefault(index)); } void GetIndex(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto info{rp.PopRaw<MiiInfo>()}; + const auto info{rp.PopRaw<CharInfo>()}; LOG_DEBUG(Service_Mii, "called"); @@ -240,6 +239,18 @@ private: rb.Push(ResultSuccess); } + void Convert(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto mii_v3{rp.PopRaw<Ver3StoreData>()}; + + LOG_INFO(Service_Mii, "called"); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; + rb.Push(ResultSuccess); + rb.PushRaw<CharInfo>(manager.ConvertV3ToCharInfo(mii_v3)); + } + constexpr bool IsInterfaceVersionSupported(u32 interface_version) const { return current_interface_version >= interface_version; } diff --git a/src/core/hle/service/mii/mii.h b/src/core/hle/service/mii/mii.h index 9d3238e72..009d80d58 100644 --- a/src/core/hle/service/mii/mii.h +++ b/src/core/hle/service/mii/mii.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index 0a57c3cde..3a2fe938f 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include <random> @@ -12,13 +11,12 @@ #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/mii/mii_manager.h" #include "core/hle/service/mii/raw_data.h" -#include "core/hle/service/mii/types.h" namespace Service::Mii { namespace { -constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4}; +constexpr Result ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4}; constexpr std::size_t BaseMiiCount{2}; constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()}; @@ -44,7 +42,7 @@ std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& i return out; } -MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) { +CharInfo ConvertStoreDataToInfo(const MiiStoreData& data) { MiiStoreBitFields bf; std::memcpy(&bf, data.data.data.data(), sizeof(MiiStoreBitFields)); @@ -292,7 +290,7 @@ MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Commo u8 glasses_type{}; while (glasses_type_start < glasses_type_info.values[glasses_type]) { if (++glasses_type >= glasses_type_info.values_count) { - UNREACHABLE(); + ASSERT(false); break; } } @@ -411,8 +409,8 @@ u32 MiiManager::GetCount(SourceFlag source_flag) const { return static_cast<u32>(count); } -ResultVal<MiiInfo> MiiManager::UpdateLatest([[maybe_unused]] const MiiInfo& info, - SourceFlag source_flag) { +ResultVal<CharInfo> MiiManager::UpdateLatest([[maybe_unused]] const CharInfo& info, + SourceFlag source_flag) { if ((source_flag & SourceFlag::Database) == SourceFlag::None) { return ERROR_CANNOT_FIND_ENTRY; } @@ -421,14 +419,242 @@ ResultVal<MiiInfo> MiiManager::UpdateLatest([[maybe_unused]] const MiiInfo& info return ERROR_CANNOT_FIND_ENTRY; } -MiiInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) { +CharInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) { return ConvertStoreDataToInfo(BuildRandomStoreData(age, gender, race, user_id)); } -MiiInfo MiiManager::BuildDefault(std::size_t index) { +CharInfo MiiManager::BuildDefault(std::size_t index) { return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id)); } +CharInfo MiiManager::ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const { + Service::Mii::MiiManager manager; + auto mii = manager.BuildDefault(0); + + if (!ValidateV3Info(mii_v3)) { + return mii; + } + + // TODO: We are ignoring a bunch of data from the mii_v3 + + mii.gender = static_cast<u8>(mii_v3.mii_information.gender); + mii.favorite_color = static_cast<u8>(mii_v3.mii_information.favorite_color); + mii.height = mii_v3.height; + mii.build = mii_v3.build; + + // Copy name until string terminator + mii.name = {}; + for (std::size_t index = 0; index < mii.name.size() - 1; index++) { + mii.name[index] = mii_v3.mii_name[index]; + if (mii.name[index] == 0) { + break; + } + } + + mii.font_region = mii_v3.region_information.character_set; + + mii.faceline_type = mii_v3.appearance_bits1.face_shape; + mii.faceline_color = mii_v3.appearance_bits1.skin_color; + mii.faceline_wrinkle = mii_v3.appearance_bits2.wrinkles; + mii.faceline_make = mii_v3.appearance_bits2.makeup; + + mii.hair_type = mii_v3.hair_style; + mii.hair_color = mii_v3.appearance_bits3.hair_color; + mii.hair_flip = mii_v3.appearance_bits3.flip_hair; + + mii.eye_type = static_cast<u8>(mii_v3.appearance_bits4.eye_type); + mii.eye_color = static_cast<u8>(mii_v3.appearance_bits4.eye_color); + mii.eye_scale = static_cast<u8>(mii_v3.appearance_bits4.eye_scale); + mii.eye_aspect = static_cast<u8>(mii_v3.appearance_bits4.eye_vertical_stretch); + mii.eye_rotate = static_cast<u8>(mii_v3.appearance_bits4.eye_rotation); + mii.eye_x = static_cast<u8>(mii_v3.appearance_bits4.eye_spacing); + mii.eye_y = static_cast<u8>(mii_v3.appearance_bits4.eye_y_position); + + mii.eyebrow_type = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_style); + mii.eyebrow_color = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_color); + mii.eyebrow_scale = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_scale); + mii.eyebrow_aspect = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_yscale); + mii.eyebrow_rotate = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_rotation); + mii.eyebrow_x = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_spacing); + mii.eyebrow_y = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_y_position); + + mii.nose_type = static_cast<u8>(mii_v3.appearance_bits6.nose_type); + mii.nose_scale = static_cast<u8>(mii_v3.appearance_bits6.nose_scale); + mii.nose_y = static_cast<u8>(mii_v3.appearance_bits6.nose_y_position); + + mii.mouth_type = static_cast<u8>(mii_v3.appearance_bits7.mouth_type); + mii.mouth_color = static_cast<u8>(mii_v3.appearance_bits7.mouth_color); + mii.mouth_scale = static_cast<u8>(mii_v3.appearance_bits7.mouth_scale); + mii.mouth_aspect = static_cast<u8>(mii_v3.appearance_bits7.mouth_horizontal_stretch); + mii.mouth_y = static_cast<u8>(mii_v3.appearance_bits8.mouth_y_position); + + mii.mustache_type = static_cast<u8>(mii_v3.appearance_bits8.mustache_type); + mii.mustache_scale = static_cast<u8>(mii_v3.appearance_bits9.mustache_scale); + mii.mustache_y = static_cast<u8>(mii_v3.appearance_bits9.mustache_y_position); + + mii.beard_type = static_cast<u8>(mii_v3.appearance_bits9.bear_type); + mii.beard_color = static_cast<u8>(mii_v3.appearance_bits9.facial_hair_color); + + mii.glasses_type = static_cast<u8>(mii_v3.appearance_bits10.glasses_type); + mii.glasses_color = static_cast<u8>(mii_v3.appearance_bits10.glasses_color); + mii.glasses_scale = static_cast<u8>(mii_v3.appearance_bits10.glasses_scale); + mii.glasses_y = static_cast<u8>(mii_v3.appearance_bits10.glasses_y_position); + + mii.mole_type = static_cast<u8>(mii_v3.appearance_bits11.mole_enabled); + mii.mole_scale = static_cast<u8>(mii_v3.appearance_bits11.mole_scale); + mii.mole_x = static_cast<u8>(mii_v3.appearance_bits11.mole_x_position); + mii.mole_y = static_cast<u8>(mii_v3.appearance_bits11.mole_y_position); + + // TODO: Validate mii data + + return mii; +} + +Ver3StoreData MiiManager::ConvertCharInfoToV3(const CharInfo& mii) const { + Service::Mii::MiiManager manager; + Ver3StoreData mii_v3{}; + + // TODO: We are ignoring a bunch of data from the mii_v3 + + mii_v3.version = 1; + mii_v3.mii_information.gender.Assign(mii.gender); + mii_v3.mii_information.favorite_color.Assign(mii.favorite_color); + mii_v3.height = mii.height; + mii_v3.build = mii.build; + + // Copy name until string terminator + mii_v3.mii_name = {}; + for (std::size_t index = 0; index < mii.name.size() - 1; index++) { + mii_v3.mii_name[index] = mii.name[index]; + if (mii_v3.mii_name[index] == 0) { + break; + } + } + + mii_v3.region_information.character_set.Assign(mii.font_region); + + mii_v3.appearance_bits1.face_shape.Assign(mii.faceline_type); + mii_v3.appearance_bits1.skin_color.Assign(mii.faceline_color); + mii_v3.appearance_bits2.wrinkles.Assign(mii.faceline_wrinkle); + mii_v3.appearance_bits2.makeup.Assign(mii.faceline_make); + + mii_v3.hair_style = mii.hair_type; + mii_v3.appearance_bits3.hair_color.Assign(mii.hair_color); + mii_v3.appearance_bits3.flip_hair.Assign(mii.hair_flip); + + mii_v3.appearance_bits4.eye_type.Assign(mii.eye_type); + mii_v3.appearance_bits4.eye_color.Assign(mii.eye_color); + mii_v3.appearance_bits4.eye_scale.Assign(mii.eye_scale); + mii_v3.appearance_bits4.eye_vertical_stretch.Assign(mii.eye_aspect); + mii_v3.appearance_bits4.eye_rotation.Assign(mii.eye_rotate); + mii_v3.appearance_bits4.eye_spacing.Assign(mii.eye_x); + mii_v3.appearance_bits4.eye_y_position.Assign(mii.eye_y); + + mii_v3.appearance_bits5.eyebrow_style.Assign(mii.eyebrow_type); + mii_v3.appearance_bits5.eyebrow_color.Assign(mii.eyebrow_color); + mii_v3.appearance_bits5.eyebrow_scale.Assign(mii.eyebrow_scale); + mii_v3.appearance_bits5.eyebrow_yscale.Assign(mii.eyebrow_aspect); + mii_v3.appearance_bits5.eyebrow_rotation.Assign(mii.eyebrow_rotate); + mii_v3.appearance_bits5.eyebrow_spacing.Assign(mii.eyebrow_x); + mii_v3.appearance_bits5.eyebrow_y_position.Assign(mii.eyebrow_y); + + mii_v3.appearance_bits6.nose_type.Assign(mii.nose_type); + mii_v3.appearance_bits6.nose_scale.Assign(mii.nose_scale); + mii_v3.appearance_bits6.nose_y_position.Assign(mii.nose_y); + + mii_v3.appearance_bits7.mouth_type.Assign(mii.mouth_type); + mii_v3.appearance_bits7.mouth_color.Assign(mii.mouth_color); + mii_v3.appearance_bits7.mouth_scale.Assign(mii.mouth_scale); + mii_v3.appearance_bits7.mouth_horizontal_stretch.Assign(mii.mouth_aspect); + mii_v3.appearance_bits8.mouth_y_position.Assign(mii.mouth_y); + + mii_v3.appearance_bits8.mustache_type.Assign(mii.mustache_type); + mii_v3.appearance_bits9.mustache_scale.Assign(mii.mustache_scale); + mii_v3.appearance_bits9.mustache_y_position.Assign(mii.mustache_y); + + mii_v3.appearance_bits9.bear_type.Assign(mii.beard_type); + mii_v3.appearance_bits9.facial_hair_color.Assign(mii.beard_color); + + mii_v3.appearance_bits10.glasses_type.Assign(mii.glasses_type); + mii_v3.appearance_bits10.glasses_color.Assign(mii.glasses_color); + mii_v3.appearance_bits10.glasses_scale.Assign(mii.glasses_scale); + mii_v3.appearance_bits10.glasses_y_position.Assign(mii.glasses_y); + + mii_v3.appearance_bits11.mole_enabled.Assign(mii.mole_type); + mii_v3.appearance_bits11.mole_scale.Assign(mii.mole_scale); + mii_v3.appearance_bits11.mole_x_position.Assign(mii.mole_x); + mii_v3.appearance_bits11.mole_y_position.Assign(mii.mole_y); + + // TODO: Validate mii_v3 data + + return mii_v3; +} + +bool MiiManager::ValidateV3Info(const Ver3StoreData& mii_v3) const { + bool is_valid = mii_v3.version == 0 || mii_v3.version == 3; + + is_valid = is_valid && (mii_v3.mii_name[0] != 0); + + is_valid = is_valid && (mii_v3.mii_information.birth_month < 13); + is_valid = is_valid && (mii_v3.mii_information.birth_day < 32); + is_valid = is_valid && (mii_v3.mii_information.favorite_color < 12); + is_valid = is_valid && (mii_v3.height < 128); + is_valid = is_valid && (mii_v3.build < 128); + + is_valid = is_valid && (mii_v3.appearance_bits1.face_shape < 12); + is_valid = is_valid && (mii_v3.appearance_bits1.skin_color < 7); + is_valid = is_valid && (mii_v3.appearance_bits2.wrinkles < 12); + is_valid = is_valid && (mii_v3.appearance_bits2.makeup < 12); + + is_valid = is_valid && (mii_v3.hair_style < 132); + is_valid = is_valid && (mii_v3.appearance_bits3.hair_color < 8); + + is_valid = is_valid && (mii_v3.appearance_bits4.eye_type < 60); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_color < 6); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_scale < 8); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_vertical_stretch < 7); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_rotation < 8); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_spacing < 13); + is_valid = is_valid && (mii_v3.appearance_bits4.eye_y_position < 19); + + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_style < 25); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_color < 8); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_scale < 9); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_yscale < 7); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_rotation < 12); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_spacing < 12); + is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_y_position < 19); + + is_valid = is_valid && (mii_v3.appearance_bits6.nose_type < 18); + is_valid = is_valid && (mii_v3.appearance_bits6.nose_scale < 9); + is_valid = is_valid && (mii_v3.appearance_bits6.nose_y_position < 19); + + is_valid = is_valid && (mii_v3.appearance_bits7.mouth_type < 36); + is_valid = is_valid && (mii_v3.appearance_bits7.mouth_color < 5); + is_valid = is_valid && (mii_v3.appearance_bits7.mouth_scale < 9); + is_valid = is_valid && (mii_v3.appearance_bits7.mouth_horizontal_stretch < 7); + is_valid = is_valid && (mii_v3.appearance_bits8.mouth_y_position < 19); + + is_valid = is_valid && (mii_v3.appearance_bits8.mustache_type < 6); + is_valid = is_valid && (mii_v3.appearance_bits9.mustache_scale < 7); + is_valid = is_valid && (mii_v3.appearance_bits9.mustache_y_position < 17); + + is_valid = is_valid && (mii_v3.appearance_bits9.bear_type < 6); + is_valid = is_valid && (mii_v3.appearance_bits9.facial_hair_color < 8); + + is_valid = is_valid && (mii_v3.appearance_bits10.glasses_type < 9); + is_valid = is_valid && (mii_v3.appearance_bits10.glasses_color < 6); + is_valid = is_valid && (mii_v3.appearance_bits10.glasses_scale < 8); + is_valid = is_valid && (mii_v3.appearance_bits10.glasses_y_position < 21); + + is_valid = is_valid && (mii_v3.appearance_bits11.mole_enabled < 2); + is_valid = is_valid && (mii_v3.appearance_bits11.mole_scale < 9); + is_valid = is_valid && (mii_v3.appearance_bits11.mole_x_position < 17); + is_valid = is_valid && (mii_v3.appearance_bits11.mole_y_position < 31); + + return is_valid; +} + ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) { std::vector<MiiInfoElement> result; @@ -443,7 +669,7 @@ ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_ return result; } -ResultCode MiiManager::GetIndex([[maybe_unused]] const MiiInfo& info, u32& index) { +Result MiiManager::GetIndex([[maybe_unused]] const CharInfo& info, u32& index) { constexpr u32 INVALID_INDEX{0xFFFFFFFF}; index = INVALID_INDEX; diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h index 6999d15b1..83ad3d343 100644 --- a/src/core/hle/service/mii/mii_manager.h +++ b/src/core/hle/service/mii/mii_manager.h @@ -1,315 +1,15 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include <array> #include <vector> -#include "common/bit_field.h" -#include "common/common_funcs.h" -#include "common/uuid.h" + #include "core/hle/result.h" #include "core/hle/service/mii/types.h" namespace Service::Mii { -enum class Source : u32 { - Database = 0, - Default = 1, - Account = 2, - Friend = 3, -}; - -enum class SourceFlag : u32 { - None = 0, - Database = 1 << 0, - Default = 1 << 1, -}; -DECLARE_ENUM_FLAG_OPERATORS(SourceFlag); - -struct MiiInfo { - Common::UUID uuid; - std::array<char16_t, 11> name; - u8 font_region; - u8 favorite_color; - u8 gender; - u8 height; - u8 build; - u8 type; - u8 region_move; - u8 faceline_type; - u8 faceline_color; - u8 faceline_wrinkle; - u8 faceline_make; - u8 hair_type; - u8 hair_color; - u8 hair_flip; - u8 eye_type; - u8 eye_color; - u8 eye_scale; - u8 eye_aspect; - u8 eye_rotate; - u8 eye_x; - u8 eye_y; - u8 eyebrow_type; - u8 eyebrow_color; - u8 eyebrow_scale; - u8 eyebrow_aspect; - u8 eyebrow_rotate; - u8 eyebrow_x; - u8 eyebrow_y; - u8 nose_type; - u8 nose_scale; - u8 nose_y; - u8 mouth_type; - u8 mouth_color; - u8 mouth_scale; - u8 mouth_aspect; - u8 mouth_y; - u8 beard_color; - u8 beard_type; - u8 mustache_type; - u8 mustache_scale; - u8 mustache_y; - u8 glasses_type; - u8 glasses_color; - u8 glasses_scale; - u8 glasses_y; - u8 mole_type; - u8 mole_scale; - u8 mole_x; - u8 mole_y; - u8 padding; - - std::u16string Name() const; -}; -static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size."); -static_assert(std::has_unique_object_representations_v<MiiInfo>, - "All bits of MiiInfo must contribute to its value."); - -#pragma pack(push, 4) - -struct MiiInfoElement { - MiiInfoElement(const MiiInfo& info_, Source source_) : info{info_}, source{source_} {} - - MiiInfo info{}; - Source source{}; -}; -static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size."); - -struct MiiStoreBitFields { - union { - u32 word_0{}; - - BitField<0, 8, u32> hair_type; - BitField<8, 7, u32> height; - BitField<15, 1, u32> mole_type; - BitField<16, 7, u32> build; - BitField<23, 1, HairFlip> hair_flip; - BitField<24, 7, u32> hair_color; - BitField<31, 1, u32> type; - }; - - union { - u32 word_1{}; - - BitField<0, 7, u32> eye_color; - BitField<7, 1, Gender> gender; - BitField<8, 7, u32> eyebrow_color; - BitField<16, 7, u32> mouth_color; - BitField<24, 7, u32> beard_color; - }; - - union { - u32 word_2{}; - - BitField<0, 7, u32> glasses_color; - BitField<8, 6, u32> eye_type; - BitField<14, 2, u32> region_move; - BitField<16, 6, u32> mouth_type; - BitField<22, 2, FontRegion> font_region; - BitField<24, 5, u32> eye_y; - BitField<29, 3, u32> glasses_scale; - }; - - union { - u32 word_3{}; - - BitField<0, 5, u32> eyebrow_type; - BitField<5, 3, MustacheType> mustache_type; - BitField<8, 5, u32> nose_type; - BitField<13, 3, BeardType> beard_type; - BitField<16, 5, u32> nose_y; - BitField<21, 3, u32> mouth_aspect; - BitField<24, 5, u32> mouth_y; - BitField<29, 3, u32> eyebrow_aspect; - }; - - union { - u32 word_4{}; - - BitField<0, 5, u32> mustache_y; - BitField<5, 3, u32> eye_rotate; - BitField<8, 5, u32> glasses_y; - BitField<13, 3, u32> eye_aspect; - BitField<16, 5, u32> mole_x; - BitField<21, 3, u32> eye_scale; - BitField<24, 5, u32> mole_y; - }; - - union { - u32 word_5{}; - - BitField<0, 5, u32> glasses_type; - BitField<8, 4, u32> favorite_color; - BitField<12, 4, u32> faceline_type; - BitField<16, 4, u32> faceline_color; - BitField<20, 4, u32> faceline_wrinkle; - BitField<24, 4, u32> faceline_makeup; - BitField<28, 4, u32> eye_x; - }; - - union { - u32 word_6{}; - - BitField<0, 4, u32> eyebrow_scale; - BitField<4, 4, u32> eyebrow_rotate; - BitField<8, 4, u32> eyebrow_x; - BitField<12, 4, u32> eyebrow_y; - BitField<16, 4, u32> nose_scale; - BitField<20, 4, u32> mouth_scale; - BitField<24, 4, u32> mustache_scale; - BitField<28, 4, u32> mole_scale; - }; -}; -static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size."); -static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>, - "MiiStoreBitFields is not trivially copyable."); - -struct MiiStoreData { - using Name = std::array<char16_t, 10>; - - MiiStoreData(); - MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields, - const Common::UUID& user_id); - - // This corresponds to the above structure MiiStoreBitFields. I did it like this because the - // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is - // not suitable for our uses. - struct { - std::array<u8, 0x1C> data{}; - static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size."); - - Name name{}; - Common::UUID uuid{}; - } data; - - u16 data_crc{}; - u16 device_crc{}; -}; -static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size."); - -struct MiiStoreDataElement { - MiiStoreData data{}; - Source source{}; -}; -static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size."); - -struct MiiDatabase { - u32 magic{}; // 'NFDB' - std::array<MiiStoreData, 0x64> miis{}; - INSERT_PADDING_BYTES(1); - u8 count{}; - u16 crc{}; -}; -static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size."); - -struct RandomMiiValues { - std::array<u8, 0xbc> values{}; -}; -static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size."); - -struct RandomMiiData4 { - Gender gender{}; - Age age{}; - Race race{}; - u32 values_count{}; - std::array<u32, 47> values{}; -}; -static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size."); - -struct RandomMiiData3 { - u32 arg_1; - u32 arg_2; - u32 values_count; - std::array<u32, 47> values{}; -}; -static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size."); - -struct RandomMiiData2 { - u32 arg_1; - u32 values_count; - std::array<u32, 47> values{}; -}; -static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size."); - -struct DefaultMii { - u32 face_type{}; - u32 face_color{}; - u32 face_wrinkle{}; - u32 face_makeup{}; - u32 hair_type{}; - u32 hair_color{}; - u32 hair_flip{}; - u32 eye_type{}; - u32 eye_color{}; - u32 eye_scale{}; - u32 eye_aspect{}; - u32 eye_rotate{}; - u32 eye_x{}; - u32 eye_y{}; - u32 eyebrow_type{}; - u32 eyebrow_color{}; - u32 eyebrow_scale{}; - u32 eyebrow_aspect{}; - u32 eyebrow_rotate{}; - u32 eyebrow_x{}; - u32 eyebrow_y{}; - u32 nose_type{}; - u32 nose_scale{}; - u32 nose_y{}; - u32 mouth_type{}; - u32 mouth_color{}; - u32 mouth_scale{}; - u32 mouth_aspect{}; - u32 mouth_y{}; - u32 mustache_type{}; - u32 beard_type{}; - u32 beard_color{}; - u32 mustache_scale{}; - u32 mustache_y{}; - u32 glasses_type{}; - u32 glasses_color{}; - u32 glasses_scale{}; - u32 glasses_y{}; - u32 mole_type{}; - u32 mole_scale{}; - u32 mole_x{}; - u32 mole_y{}; - u32 height{}; - u32 weight{}; - Gender gender{}; - u32 favorite_color{}; - u32 region{}; - FontRegion font_region{}; - u32 type{}; - INSERT_PADDING_WORDS(5); -}; -static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size."); - -#pragma pack(pop) - // The Mii manager is responsible for loading and storing the Miis to the database in NAND along // with providing an easy interface for HLE emulation of the mii service. class MiiManager { @@ -319,11 +19,14 @@ public: bool CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter); bool IsFullDatabase() const; u32 GetCount(SourceFlag source_flag) const; - ResultVal<MiiInfo> UpdateLatest(const MiiInfo& info, SourceFlag source_flag); - MiiInfo BuildRandom(Age age, Gender gender, Race race); - MiiInfo BuildDefault(std::size_t index); + ResultVal<CharInfo> UpdateLatest(const CharInfo& info, SourceFlag source_flag); + CharInfo BuildRandom(Age age, Gender gender, Race race); + CharInfo BuildDefault(std::size_t index); + CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const; + Ver3StoreData ConvertCharInfoToV3(const CharInfo& mii) const; + bool ValidateV3Info(const Ver3StoreData& mii_v3) const; ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag); - ResultCode GetIndex(const MiiInfo& info, u32& index); + Result GetIndex(const CharInfo& info, u32& index); private: const Common::UUID user_id{}; diff --git a/src/core/hle/service/mii/raw_data.cpp b/src/core/hle/service/mii/raw_data.cpp index 9d3c8017a..1442280c8 100644 --- a/src/core/hle/service/mii/raw_data.cpp +++ b/src/core/hle/service/mii/raw_data.cpp @@ -1,22 +1,5 @@ -// MIT License -// -// Copyright (c) Ryujinx Team and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -// associated documentation files (the "Software"), to deal in the Software without restriction, -// including without limitation the rights to use, copy, modify, merge, publish, distribute, -// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT -// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// +// SPDX-FileCopyrightText: Ryujinx Team and Contributors +// SPDX-License-Identifier: MIT #include "core/hle/service/mii/raw_data.h" diff --git a/src/core/hle/service/mii/raw_data.h b/src/core/hle/service/mii/raw_data.h index bd90c2162..c2bec68d4 100644 --- a/src/core/hle/service/mii/raw_data.h +++ b/src/core/hle/service/mii/raw_data.h @@ -1,12 +1,11 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include <array> -#include "core/hle/service/mii/mii_manager.h" +#include "core/hle/service/mii/types.h" namespace Service::Mii::RawData { diff --git a/src/core/hle/service/mii/types.h b/src/core/hle/service/mii/types.h index d65a1055e..9e3247397 100644 --- a/src/core/hle/service/mii/types.h +++ b/src/core/hle/service/mii/types.h @@ -1,11 +1,15 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include <array> +#include <type_traits> + +#include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" +#include "common/uuid.h" namespace Service::Mii { @@ -25,7 +29,11 @@ enum class BeardType : u32 { Beard5, }; -enum class BeardAndMustacheFlag : u32 { Beard = 1, Mustache, All = Beard | Mustache }; +enum class BeardAndMustacheFlag : u32 { + Beard = 1, + Mustache, + All = Beard | Mustache, +}; DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag); enum class FontRegion : u32 { @@ -64,4 +72,424 @@ enum class Race : u32 { All, }; +enum class Source : u32 { + Database = 0, + Default = 1, + Account = 2, + Friend = 3, +}; + +enum class SourceFlag : u32 { + None = 0, + Database = 1 << 0, + Default = 1 << 1, +}; +DECLARE_ENUM_FLAG_OPERATORS(SourceFlag); + +// nn::mii::CharInfo +struct CharInfo { + Common::UUID uuid; + std::array<char16_t, 11> name; + u8 font_region; + u8 favorite_color; + u8 gender; + u8 height; + u8 build; + u8 type; + u8 region_move; + u8 faceline_type; + u8 faceline_color; + u8 faceline_wrinkle; + u8 faceline_make; + u8 hair_type; + u8 hair_color; + u8 hair_flip; + u8 eye_type; + u8 eye_color; + u8 eye_scale; + u8 eye_aspect; + u8 eye_rotate; + u8 eye_x; + u8 eye_y; + u8 eyebrow_type; + u8 eyebrow_color; + u8 eyebrow_scale; + u8 eyebrow_aspect; + u8 eyebrow_rotate; + u8 eyebrow_x; + u8 eyebrow_y; + u8 nose_type; + u8 nose_scale; + u8 nose_y; + u8 mouth_type; + u8 mouth_color; + u8 mouth_scale; + u8 mouth_aspect; + u8 mouth_y; + u8 beard_color; + u8 beard_type; + u8 mustache_type; + u8 mustache_scale; + u8 mustache_y; + u8 glasses_type; + u8 glasses_color; + u8 glasses_scale; + u8 glasses_y; + u8 mole_type; + u8 mole_scale; + u8 mole_x; + u8 mole_y; + u8 padding; +}; +static_assert(sizeof(CharInfo) == 0x58, "CharInfo has incorrect size."); +static_assert(std::has_unique_object_representations_v<CharInfo>, + "All bits of CharInfo must contribute to its value."); + +#pragma pack(push, 4) + +struct MiiInfoElement { + MiiInfoElement(const CharInfo& info_, Source source_) : info{info_}, source{source_} {} + + CharInfo info{}; + Source source{}; +}; +static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size."); + +struct MiiStoreBitFields { + union { + u32 word_0{}; + + BitField<0, 8, u32> hair_type; + BitField<8, 7, u32> height; + BitField<15, 1, u32> mole_type; + BitField<16, 7, u32> build; + BitField<23, 1, HairFlip> hair_flip; + BitField<24, 7, u32> hair_color; + BitField<31, 1, u32> type; + }; + + union { + u32 word_1{}; + + BitField<0, 7, u32> eye_color; + BitField<7, 1, Gender> gender; + BitField<8, 7, u32> eyebrow_color; + BitField<16, 7, u32> mouth_color; + BitField<24, 7, u32> beard_color; + }; + + union { + u32 word_2{}; + + BitField<0, 7, u32> glasses_color; + BitField<8, 6, u32> eye_type; + BitField<14, 2, u32> region_move; + BitField<16, 6, u32> mouth_type; + BitField<22, 2, FontRegion> font_region; + BitField<24, 5, u32> eye_y; + BitField<29, 3, u32> glasses_scale; + }; + + union { + u32 word_3{}; + + BitField<0, 5, u32> eyebrow_type; + BitField<5, 3, MustacheType> mustache_type; + BitField<8, 5, u32> nose_type; + BitField<13, 3, BeardType> beard_type; + BitField<16, 5, u32> nose_y; + BitField<21, 3, u32> mouth_aspect; + BitField<24, 5, u32> mouth_y; + BitField<29, 3, u32> eyebrow_aspect; + }; + + union { + u32 word_4{}; + + BitField<0, 5, u32> mustache_y; + BitField<5, 3, u32> eye_rotate; + BitField<8, 5, u32> glasses_y; + BitField<13, 3, u32> eye_aspect; + BitField<16, 5, u32> mole_x; + BitField<21, 3, u32> eye_scale; + BitField<24, 5, u32> mole_y; + }; + + union { + u32 word_5{}; + + BitField<0, 5, u32> glasses_type; + BitField<8, 4, u32> favorite_color; + BitField<12, 4, u32> faceline_type; + BitField<16, 4, u32> faceline_color; + BitField<20, 4, u32> faceline_wrinkle; + BitField<24, 4, u32> faceline_makeup; + BitField<28, 4, u32> eye_x; + }; + + union { + u32 word_6{}; + + BitField<0, 4, u32> eyebrow_scale; + BitField<4, 4, u32> eyebrow_rotate; + BitField<8, 4, u32> eyebrow_x; + BitField<12, 4, u32> eyebrow_y; + BitField<16, 4, u32> nose_scale; + BitField<20, 4, u32> mouth_scale; + BitField<24, 4, u32> mustache_scale; + BitField<28, 4, u32> mole_scale; + }; +}; +static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size."); +static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>, + "MiiStoreBitFields is not trivially copyable."); + +// This is nn::mii::Ver3StoreData +// Based on citra HLE::Applets::MiiData and PretendoNetwork. +// https://github.com/citra-emu/citra/blob/master/src/core/hle/applets/mii_selector.h#L48 +// https://github.com/PretendoNetwork/mii-js/blob/master/mii.js#L299 +struct Ver3StoreData { + u8 version; + union { + u8 raw; + + BitField<0, 1, u8> allow_copying; + BitField<1, 1, u8> profanity_flag; + BitField<2, 2, u8> region_lock; + BitField<4, 2, u8> character_set; + } region_information; + u16_be mii_id; + u64_be system_id; + u32_be specialness_and_creation_date; + std::array<u8, 0x6> creator_mac; + u16_be padding; + union { + u16 raw; + + BitField<0, 1, u16> gender; + BitField<1, 4, u16> birth_month; + BitField<5, 5, u16> birth_day; + BitField<10, 4, u16> favorite_color; + BitField<14, 1, u16> favorite; + } mii_information; + std::array<char16_t, 0xA> mii_name; + u8 height; + u8 build; + union { + u8 raw; + + BitField<0, 1, u8> disable_sharing; + BitField<1, 4, u8> face_shape; + BitField<5, 3, u8> skin_color; + } appearance_bits1; + union { + u8 raw; + + BitField<0, 4, u8> wrinkles; + BitField<4, 4, u8> makeup; + } appearance_bits2; + u8 hair_style; + union { + u8 raw; + + BitField<0, 3, u8> hair_color; + BitField<3, 1, u8> flip_hair; + } appearance_bits3; + union { + u32 raw; + + BitField<0, 6, u32> eye_type; + BitField<6, 3, u32> eye_color; + BitField<9, 4, u32> eye_scale; + BitField<13, 3, u32> eye_vertical_stretch; + BitField<16, 5, u32> eye_rotation; + BitField<21, 4, u32> eye_spacing; + BitField<25, 5, u32> eye_y_position; + } appearance_bits4; + union { + u32 raw; + + BitField<0, 5, u32> eyebrow_style; + BitField<5, 3, u32> eyebrow_color; + BitField<8, 4, u32> eyebrow_scale; + BitField<12, 3, u32> eyebrow_yscale; + BitField<16, 4, u32> eyebrow_rotation; + BitField<21, 4, u32> eyebrow_spacing; + BitField<25, 5, u32> eyebrow_y_position; + } appearance_bits5; + union { + u16 raw; + + BitField<0, 5, u16> nose_type; + BitField<5, 4, u16> nose_scale; + BitField<9, 5, u16> nose_y_position; + } appearance_bits6; + union { + u16 raw; + + BitField<0, 6, u16> mouth_type; + BitField<6, 3, u16> mouth_color; + BitField<9, 4, u16> mouth_scale; + BitField<13, 3, u16> mouth_horizontal_stretch; + } appearance_bits7; + union { + u8 raw; + + BitField<0, 5, u8> mouth_y_position; + BitField<5, 3, u8> mustache_type; + } appearance_bits8; + u8 allow_copying; + union { + u16 raw; + + BitField<0, 3, u16> bear_type; + BitField<3, 3, u16> facial_hair_color; + BitField<6, 4, u16> mustache_scale; + BitField<10, 5, u16> mustache_y_position; + } appearance_bits9; + union { + u16 raw; + + BitField<0, 4, u16> glasses_type; + BitField<4, 3, u16> glasses_color; + BitField<7, 4, u16> glasses_scale; + BitField<11, 5, u16> glasses_y_position; + } appearance_bits10; + union { + u16 raw; + + BitField<0, 1, u16> mole_enabled; + BitField<1, 4, u16> mole_scale; + BitField<5, 5, u16> mole_x_position; + BitField<10, 5, u16> mole_y_position; + } appearance_bits11; + + std::array<u16_le, 0xA> author_name; + INSERT_PADDING_BYTES(0x4); +}; +static_assert(sizeof(Ver3StoreData) == 0x60, "Ver3StoreData is an invalid size"); + +struct MiiStoreData { + using Name = std::array<char16_t, 10>; + + MiiStoreData(); + MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields, + const Common::UUID& user_id); + + // This corresponds to the above structure MiiStoreBitFields. I did it like this because the + // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is + // not suitable for our uses. + struct { + std::array<u8, 0x1C> data{}; + static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size."); + + Name name{}; + Common::UUID uuid{}; + } data; + + u16 data_crc{}; + u16 device_crc{}; +}; +static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size."); + +struct MiiStoreDataElement { + MiiStoreData data{}; + Source source{}; +}; +static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size."); + +struct MiiDatabase { + u32 magic{}; // 'NFDB' + std::array<MiiStoreData, 0x64> miis{}; + INSERT_PADDING_BYTES(1); + u8 count{}; + u16 crc{}; +}; +static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size."); + +struct RandomMiiValues { + std::array<u8, 0xbc> values{}; +}; +static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size."); + +struct RandomMiiData4 { + Gender gender{}; + Age age{}; + Race race{}; + u32 values_count{}; + std::array<u32, 47> values{}; +}; +static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size."); + +struct RandomMiiData3 { + u32 arg_1; + u32 arg_2; + u32 values_count; + std::array<u32, 47> values{}; +}; +static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size."); + +struct RandomMiiData2 { + u32 arg_1; + u32 values_count; + std::array<u32, 47> values{}; +}; +static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size."); + +struct DefaultMii { + u32 face_type{}; + u32 face_color{}; + u32 face_wrinkle{}; + u32 face_makeup{}; + u32 hair_type{}; + u32 hair_color{}; + u32 hair_flip{}; + u32 eye_type{}; + u32 eye_color{}; + u32 eye_scale{}; + u32 eye_aspect{}; + u32 eye_rotate{}; + u32 eye_x{}; + u32 eye_y{}; + u32 eyebrow_type{}; + u32 eyebrow_color{}; + u32 eyebrow_scale{}; + u32 eyebrow_aspect{}; + u32 eyebrow_rotate{}; + u32 eyebrow_x{}; + u32 eyebrow_y{}; + u32 nose_type{}; + u32 nose_scale{}; + u32 nose_y{}; + u32 mouth_type{}; + u32 mouth_color{}; + u32 mouth_scale{}; + u32 mouth_aspect{}; + u32 mouth_y{}; + u32 mustache_type{}; + u32 beard_type{}; + u32 beard_color{}; + u32 mustache_scale{}; + u32 mustache_y{}; + u32 glasses_type{}; + u32 glasses_color{}; + u32 glasses_scale{}; + u32 glasses_y{}; + u32 mole_type{}; + u32 mole_scale{}; + u32 mole_x{}; + u32 mole_y{}; + u32 height{}; + u32 weight{}; + Gender gender{}; + u32 favorite_color{}; + u32 region{}; + FontRegion font_region{}; + u32 type{}; + INSERT_PADDING_WORDS(5); +}; +static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size."); + +#pragma pack(pop) + } // namespace Service::Mii diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp index 0ccfe91cd..ba8c0e230 100644 --- a/src/core/hle/service/mm/mm_u.cpp +++ b/src/core/hle/service/mm/mm_u.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" @@ -47,7 +46,7 @@ private: IPC::RequestParser rp{ctx}; min = rp.Pop<u32>(); max = rp.Pop<u32>(); - LOG_WARNING(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max); + LOG_DEBUG(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max); current = min; IPC::ResponseBuilder rb{ctx, 2}; @@ -55,7 +54,7 @@ private: } void GetOld(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_MM, "(STUBBED) called"); + LOG_DEBUG(Service_MM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); @@ -82,8 +81,8 @@ private: u32 input_id = rp.Pop<u32>(); min = rp.Pop<u32>(); max = rp.Pop<u32>(); - LOG_WARNING(Service_MM, "(STUBBED) called, input_id=0x{:X}, min=0x{:X}, max=0x{:X}", - input_id, min, max); + LOG_DEBUG(Service_MM, "(STUBBED) called, input_id=0x{:X}, min=0x{:X}, max=0x{:X}", input_id, + min, max); current = min; IPC::ResponseBuilder rb{ctx, 2}; @@ -91,7 +90,7 @@ private: } void Get(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_MM, "(STUBBED) called"); + LOG_DEBUG(Service_MM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); diff --git a/src/core/hle/service/mm/mm_u.h b/src/core/hle/service/mm/mm_u.h index 49b6a3355..b40941e35 100644 --- a/src/core/hle/service/mm/mm_u.h +++ b/src/core/hle/service/mm/mm_u.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/mnpp/mnpp_app.cpp b/src/core/hle/service/mnpp/mnpp_app.cpp new file mode 100644 index 000000000..c3aad5714 --- /dev/null +++ b/src/core/hle/service/mnpp/mnpp_app.cpp @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/mnpp/mnpp_app.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::MNPP { + +class MNPP_APP final : public ServiceFramework<MNPP_APP> { +public: + explicit MNPP_APP(Core::System& system_) : ServiceFramework{system_, "mnpp:app"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &MNPP_APP::Unknown0, "unknown0"}, + {1, &MNPP_APP::Unknown1, "unknown1"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void Unknown0(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_MNPP, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void Unknown1(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_MNPP, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } +}; + +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { + std::make_shared<MNPP_APP>(system)->InstallAsService(service_manager); +} + +} // namespace Service::MNPP diff --git a/src/core/hle/service/mnpp/mnpp_app.h b/src/core/hle/service/mnpp/mnpp_app.h new file mode 100644 index 000000000..eec75fe0e --- /dev/null +++ b/src/core/hle/service/mnpp/mnpp_app.h @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +namespace Core { +class System; +} + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::MNPP { + +/// Registers all MNPP services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); + +} // namespace Service::MNPP diff --git a/src/core/hle/service/ncm/ncm.cpp b/src/core/hle/service/ncm/ncm.cpp index 2dcda16f6..68210a108 100644 --- a/src/core/hle/service/ncm/ncm.cpp +++ b/src/core/hle/service/ncm/ncm.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/ncm/ncm.h b/src/core/hle/service/ncm/ncm.h index ee01eddc0..de3971437 100644 --- a/src/core/hle/service/ncm/ncm.h +++ b/src/core/hle/service/ncm/ncm.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index f77037842..046c5f18f 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> @@ -107,10 +106,10 @@ public: {1, &IUser::FinalizeOld, "FinalizeOld"}, {2, &IUser::GetStateOld, "GetStateOld"}, {3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"}, - {400, nullptr, "Initialize"}, - {401, nullptr, "Finalize"}, - {402, nullptr, "GetState"}, - {403, nullptr, "IsNfcEnabled"}, + {400, &IUser::InitializeOld, "Initialize"}, + {401, &IUser::FinalizeOld, "Finalize"}, + {402, &IUser::GetStateOld, "GetState"}, + {403, &IUser::IsNfcEnabledOld, "IsNfcEnabled"}, {404, nullptr, "ListDevices"}, {405, nullptr, "GetDeviceState"}, {406, nullptr, "GetNpadId"}, diff --git a/src/core/hle/service/nfc/nfc.h b/src/core/hle/service/nfc/nfc.h index 5a94b076d..0107b696c 100644 --- a/src/core/hle/service/nfc/nfc.h +++ b/src/core/hle/service/nfc/nfc.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/nfp/amiibo_crypto.cpp b/src/core/hle/service/nfp/amiibo_crypto.cpp new file mode 100644 index 000000000..c32a6816b --- /dev/null +++ b/src/core/hle/service/nfp/amiibo_crypto.cpp @@ -0,0 +1,388 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// SPDX-FileCopyrightText: Copyright 2017 socram8888/amiitool +// SPDX-License-Identifier: MIT + +#include <array> +#include <mbedtls/aes.h> +#include <mbedtls/hmac_drbg.h> + +#include "common/fs/file.h" +#include "common/fs/path_util.h" +#include "common/logging/log.h" +#include "core/hle/service/mii/mii_manager.h" +#include "core/hle/service/nfp/amiibo_crypto.h" + +namespace Service::NFP::AmiiboCrypto { + +bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { + const auto& amiibo_data = ntag_file.user_memory; + LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", ntag_file.static_lock); + LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", ntag_file.compability_container); + LOG_DEBUG(Service_NFP, "write_count={}", static_cast<u16>(amiibo_data.write_counter)); + + LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id); + LOG_DEBUG(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant); + LOG_DEBUG(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type); + LOG_DEBUG(Service_NFP, "model_number=0x{0:x}", + static_cast<u16>(amiibo_data.model_info.model_number)); + LOG_DEBUG(Service_NFP, "series={}", amiibo_data.model_info.series); + LOG_DEBUG(Service_NFP, "tag_type=0x{0:x}", amiibo_data.model_info.tag_type); + + LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", ntag_file.dynamic_lock); + LOG_DEBUG(Service_NFP, "tag_CFG0=0x{0:x}", ntag_file.CFG0); + LOG_DEBUG(Service_NFP, "tag_CFG1=0x{0:x}", ntag_file.CFG1); + + // Validate UUID + constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3` + if ((CT ^ ntag_file.uuid.uid[0] ^ ntag_file.uuid.uid[1] ^ ntag_file.uuid.uid[2]) != + ntag_file.uuid.uid[3]) { + return false; + } + if ((ntag_file.uuid.uid[4] ^ ntag_file.uuid.uid[5] ^ ntag_file.uuid.uid[6] ^ + ntag_file.uuid.nintendo_id) != ntag_file.uuid.lock_bytes[0]) { + return false; + } + + // Check against all know constants on an amiibo binary + if (ntag_file.static_lock != 0xE00F) { + return false; + } + if (ntag_file.compability_container != 0xEEFF10F1U) { + return false; + } + if (amiibo_data.constant_value != 0xA5) { + return false; + } + if (amiibo_data.model_info.tag_type != PackedTagType::Type2) { + return false; + } + if ((ntag_file.dynamic_lock & 0xFFFFFF) != 0x0F0001U) { + return false; + } + if (ntag_file.CFG0 != 0x04000000U) { + return false; + } + if (ntag_file.CFG1 != 0x5F) { + return false; + } + return true; +} + +NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { + NTAG215File encoded_data{}; + + encoded_data.uid = nfc_data.uuid.uid; + encoded_data.nintendo_id = nfc_data.uuid.nintendo_id; + encoded_data.static_lock = nfc_data.static_lock; + encoded_data.compability_container = nfc_data.compability_container; + encoded_data.hmac_data = nfc_data.user_memory.hmac_data; + encoded_data.constant_value = nfc_data.user_memory.constant_value; + encoded_data.write_counter = nfc_data.user_memory.write_counter; + encoded_data.settings = nfc_data.user_memory.settings; + encoded_data.owner_mii = nfc_data.user_memory.owner_mii; + encoded_data.title_id = nfc_data.user_memory.title_id; + encoded_data.applicaton_write_counter = nfc_data.user_memory.applicaton_write_counter; + encoded_data.application_area_id = nfc_data.user_memory.application_area_id; + encoded_data.unknown = nfc_data.user_memory.unknown; + encoded_data.unknown2 = nfc_data.user_memory.unknown2; + encoded_data.application_area = nfc_data.user_memory.application_area; + encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag; + encoded_data.lock_bytes = nfc_data.uuid.lock_bytes; + encoded_data.model_info = nfc_data.user_memory.model_info; + encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt; + encoded_data.dynamic_lock = nfc_data.dynamic_lock; + encoded_data.CFG0 = nfc_data.CFG0; + encoded_data.CFG1 = nfc_data.CFG1; + encoded_data.password = nfc_data.password; + + return encoded_data; +} + +EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { + EncryptedNTAG215File nfc_data{}; + + nfc_data.uuid.uid = encoded_data.uid; + nfc_data.uuid.nintendo_id = encoded_data.nintendo_id; + nfc_data.uuid.lock_bytes = encoded_data.lock_bytes; + nfc_data.static_lock = encoded_data.static_lock; + nfc_data.compability_container = encoded_data.compability_container; + nfc_data.user_memory.hmac_data = encoded_data.hmac_data; + nfc_data.user_memory.constant_value = encoded_data.constant_value; + nfc_data.user_memory.write_counter = encoded_data.write_counter; + nfc_data.user_memory.settings = encoded_data.settings; + nfc_data.user_memory.owner_mii = encoded_data.owner_mii; + nfc_data.user_memory.title_id = encoded_data.title_id; + nfc_data.user_memory.applicaton_write_counter = encoded_data.applicaton_write_counter; + nfc_data.user_memory.application_area_id = encoded_data.application_area_id; + nfc_data.user_memory.unknown = encoded_data.unknown; + nfc_data.user_memory.unknown2 = encoded_data.unknown2; + nfc_data.user_memory.application_area = encoded_data.application_area; + nfc_data.user_memory.hmac_tag = encoded_data.hmac_tag; + nfc_data.user_memory.model_info = encoded_data.model_info; + nfc_data.user_memory.keygen_salt = encoded_data.keygen_salt; + nfc_data.dynamic_lock = encoded_data.dynamic_lock; + nfc_data.CFG0 = encoded_data.CFG0; + nfc_data.CFG1 = encoded_data.CFG1; + nfc_data.password = encoded_data.password; + + return nfc_data; +} + +u32 GetTagPassword(const TagUuid& uuid) { + // Verifiy that the generated password is correct + u32 password = 0xAA ^ (uuid.uid[1] ^ uuid.uid[3]); + password &= (0x55 ^ (uuid.uid[2] ^ uuid.uid[4])) << 8; + password &= (0xAA ^ (uuid.uid[3] ^ uuid.uid[5])) << 16; + password &= (0x55 ^ (uuid.uid[4] ^ uuid.uid[6])) << 24; + return password; +} + +HashSeed GetSeed(const NTAG215File& data) { + HashSeed seed{ + .magic = data.write_counter, + .padding = {}, + .uid_1 = data.uid, + .nintendo_id_1 = data.nintendo_id, + .uid_2 = data.uid, + .nintendo_id_2 = data.nintendo_id, + .keygen_salt = data.keygen_salt, + }; + + return seed; +} + +std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed) { + const std::size_t seedPart1Len = sizeof(key.magic_bytes) - key.magic_length; + const std::size_t string_size = key.type_string.size(); + std::vector<u8> output(string_size + seedPart1Len); + + // Copy whole type string + memccpy(output.data(), key.type_string.data(), '\0', string_size); + + // Append (16 - magic_length) from the input seed + memcpy(output.data() + string_size, &seed, seedPart1Len); + + // Append all bytes from magicBytes + output.insert(output.end(), key.magic_bytes.begin(), + key.magic_bytes.begin() + key.magic_length); + + output.insert(output.end(), seed.uid_1.begin(), seed.uid_1.end()); + output.emplace_back(seed.nintendo_id_1); + output.insert(output.end(), seed.uid_2.begin(), seed.uid_2.end()); + output.emplace_back(seed.nintendo_id_2); + + for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) { + output.emplace_back(static_cast<u8>(seed.keygen_salt[i] ^ key.xor_pad[i])); + } + + return output; +} + +void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key, + const std::vector<u8>& seed) { + // Initialize context + ctx.used = false; + ctx.counter = 0; + ctx.buffer_size = sizeof(ctx.counter) + seed.size(); + memcpy(ctx.buffer.data() + sizeof(u16), seed.data(), seed.size()); + + // Initialize HMAC context + mbedtls_md_init(&hmac_ctx); + mbedtls_md_setup(&hmac_ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1); + mbedtls_md_hmac_starts(&hmac_ctx, hmac_key.data(), hmac_key.size()); +} + +void CryptoStep(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, DrgbOutput& output) { + // If used at least once, reinitialize the HMAC + if (ctx.used) { + mbedtls_md_hmac_reset(&hmac_ctx); + } + + ctx.used = true; + + // Store counter in big endian, and increment it + ctx.buffer[0] = static_cast<u8>(ctx.counter >> 8); + ctx.buffer[1] = static_cast<u8>(ctx.counter >> 0); + ctx.counter++; + + // Do HMAC magic + mbedtls_md_hmac_update(&hmac_ctx, reinterpret_cast<const unsigned char*>(ctx.buffer.data()), + ctx.buffer_size); + mbedtls_md_hmac_finish(&hmac_ctx, output.data()); +} + +DerivedKeys GenerateKey(const InternalKey& key, const NTAG215File& data) { + const auto seed = GetSeed(data); + + // Generate internal seed + const std::vector<u8> internal_key = GenerateInternalKey(key, seed); + + // Initialize context + CryptoCtx ctx{}; + mbedtls_md_context_t hmac_ctx; + CryptoInit(ctx, hmac_ctx, key.hmac_key, internal_key); + + // Generate derived keys + DerivedKeys derived_keys{}; + std::array<DrgbOutput, 2> temp{}; + CryptoStep(ctx, hmac_ctx, temp[0]); + CryptoStep(ctx, hmac_ctx, temp[1]); + memcpy(&derived_keys, temp.data(), sizeof(DerivedKeys)); + + // Cleanup context + mbedtls_md_free(&hmac_ctx); + + return derived_keys; +} + +void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& out_data) { + mbedtls_aes_context aes; + std::size_t nc_off = 0; + std::array<u8, sizeof(keys.aes_iv)> nonce_counter{}; + std::array<u8, sizeof(keys.aes_iv)> stream_block{}; + + const auto aes_key_size = static_cast<u32>(keys.aes_key.size() * 8); + mbedtls_aes_setkey_enc(&aes, keys.aes_key.data(), aes_key_size); + memcpy(nonce_counter.data(), keys.aes_iv.data(), sizeof(keys.aes_iv)); + + constexpr std::size_t encrypted_data_size = HMAC_TAG_START - SETTINGS_START; + mbedtls_aes_crypt_ctr(&aes, encrypted_data_size, &nc_off, nonce_counter.data(), + stream_block.data(), + reinterpret_cast<const unsigned char*>(&in_data.settings), + reinterpret_cast<unsigned char*>(&out_data.settings)); + + // Copy the rest of the data directly + out_data.uid = in_data.uid; + out_data.nintendo_id = in_data.nintendo_id; + out_data.lock_bytes = in_data.lock_bytes; + out_data.static_lock = in_data.static_lock; + out_data.compability_container = in_data.compability_container; + + out_data.constant_value = in_data.constant_value; + out_data.write_counter = in_data.write_counter; + + out_data.model_info = in_data.model_info; + out_data.keygen_salt = in_data.keygen_salt; + out_data.dynamic_lock = in_data.dynamic_lock; + out_data.CFG0 = in_data.CFG0; + out_data.CFG1 = in_data.CFG1; + out_data.password = in_data.password; +} + +bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) { + const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir); + + const Common::FS::IOFile keys_file{yuzu_keys_dir / "key_retail.bin", + Common::FS::FileAccessMode::Read, + Common::FS::FileType::BinaryFile}; + + if (!keys_file.IsOpen()) { + LOG_ERROR(Service_NFP, "No keys detected"); + return false; + } + + if (keys_file.Read(unfixed_info) != 1) { + LOG_ERROR(Service_NFP, "Failed to read unfixed_info"); + return false; + } + if (keys_file.Read(locked_secret) != 1) { + LOG_ERROR(Service_NFP, "Failed to read locked-secret"); + return false; + } + + return true; +} + +bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data) { + InternalKey locked_secret{}; + InternalKey unfixed_info{}; + + if (!LoadKeys(locked_secret, unfixed_info)) { + return false; + } + + // Generate keys + NTAG215File encoded_data = NfcDataToEncodedData(encrypted_tag_data); + const auto data_keys = GenerateKey(unfixed_info, encoded_data); + const auto tag_keys = GenerateKey(locked_secret, encoded_data); + + // Decrypt + Cipher(data_keys, encoded_data, tag_data); + + // Regenerate tag HMAC. Note: order matters, data HMAC depends on tag HMAC! + constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START; + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(), + sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uid), + input_length, reinterpret_cast<unsigned char*>(&tag_data.hmac_tag)); + + // Regenerate data HMAC + constexpr std::size_t input_length2 = DYNAMIC_LOCK_START - WRITE_COUNTER_START; + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), data_keys.hmac_key.data(), + sizeof(HmacKey), + reinterpret_cast<const unsigned char*>(&tag_data.write_counter), input_length2, + reinterpret_cast<unsigned char*>(&tag_data.hmac_data)); + + if (tag_data.hmac_data != encrypted_tag_data.user_memory.hmac_data) { + LOG_ERROR(Service_NFP, "hmac_data doesn't match"); + return false; + } + + if (tag_data.hmac_tag != encrypted_tag_data.user_memory.hmac_tag) { + LOG_ERROR(Service_NFP, "hmac_tag doesn't match"); + return false; + } + + return true; +} + +bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_tag_data) { + InternalKey locked_secret{}; + InternalKey unfixed_info{}; + + if (!LoadKeys(locked_secret, unfixed_info)) { + return false; + } + + // Generate keys + const auto data_keys = GenerateKey(unfixed_info, tag_data); + const auto tag_keys = GenerateKey(locked_secret, tag_data); + + NTAG215File encoded_tag_data{}; + + // Generate tag HMAC + constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START; + constexpr std::size_t input_length2 = HMAC_TAG_START - WRITE_COUNTER_START; + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(), + sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uid), + input_length, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag)); + + // Init mbedtls HMAC context + mbedtls_md_context_t ctx; + mbedtls_md_init(&ctx); + mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1); + + // Generate data HMAC + mbedtls_md_hmac_starts(&ctx, data_keys.hmac_key.data(), sizeof(HmacKey)); + mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char*>(&tag_data.write_counter), + input_length2); // Data + mbedtls_md_hmac_update(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag), + sizeof(HashData)); // Tag HMAC + mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char*>(&tag_data.uid), + input_length); + mbedtls_md_hmac_finish(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_data)); + + // HMAC cleanup + mbedtls_md_free(&ctx); + + // Encrypt + Cipher(data_keys, tag_data, encoded_tag_data); + + // Convert back to hardware + encrypted_tag_data = EncodedDataToNfcData(encoded_tag_data); + + return true; +} + +} // namespace Service::NFP::AmiiboCrypto diff --git a/src/core/hle/service/nfp/amiibo_crypto.h b/src/core/hle/service/nfp/amiibo_crypto.h new file mode 100644 index 000000000..0175ced91 --- /dev/null +++ b/src/core/hle/service/nfp/amiibo_crypto.h @@ -0,0 +1,100 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <array> + +#include "core/hle/service/nfp/nfp_types.h" + +struct mbedtls_md_context_t; + +namespace Service::NFP::AmiiboCrypto { +// Byte locations in Service::NFP::NTAG215File +constexpr std::size_t HMAC_DATA_START = 0x8; +constexpr std::size_t SETTINGS_START = 0x2c; +constexpr std::size_t WRITE_COUNTER_START = 0x29; +constexpr std::size_t HMAC_TAG_START = 0x1B4; +constexpr std::size_t UUID_START = 0x1D4; +constexpr std::size_t DYNAMIC_LOCK_START = 0x208; + +using HmacKey = std::array<u8, 0x10>; +using DrgbOutput = std::array<u8, 0x20>; + +struct HashSeed { + u16_be magic; + std::array<u8, 0xE> padding; + UniqueSerialNumber uid_1; + u8 nintendo_id_1; + UniqueSerialNumber uid_2; + u8 nintendo_id_2; + std::array<u8, 0x20> keygen_salt; +}; +static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size"); + +struct InternalKey { + HmacKey hmac_key; + std::array<char, 0xE> type_string; + u8 reserved; + u8 magic_length; + std::array<u8, 0x10> magic_bytes; + std::array<u8, 0x20> xor_pad; +}; +static_assert(sizeof(InternalKey) == 0x50, "InternalKey is an invalid size"); +static_assert(std::is_trivially_copyable_v<InternalKey>, "InternalKey must be trivially copyable."); + +struct CryptoCtx { + std::array<char, 480> buffer; + bool used; + std::size_t buffer_size; + s16 counter; +}; + +struct DerivedKeys { + std::array<u8, 0x10> aes_key; + std::array<u8, 0x10> aes_iv; + std::array<u8, 0x10> hmac_key; +}; +static_assert(sizeof(DerivedKeys) == 0x30, "DerivedKeys is an invalid size"); + +/// Validates that the amiibo file is not corrupted +bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file); + +/// Converts from encrypted file format to encoded file format +NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data); + +/// Converts from encoded file format to encrypted file format +EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data); + +/// Returns password needed to allow write access to protected memory +u32 GetTagPassword(const TagUuid& uuid); + +// Generates Seed needed for key derivation +HashSeed GetSeed(const NTAG215File& data); + +// Middle step on the generation of derived keys +std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed); + +// Initializes mbedtls context +void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key, + const std::vector<u8>& seed); + +// Feeds data to mbedtls context to generate the derived key +void CryptoStep(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, DrgbOutput& output); + +// Generates the derived key from amiibo data +DerivedKeys GenerateKey(const InternalKey& key, const NTAG215File& data); + +// Encodes or decodes amiibo data +void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& out_data); + +/// Loads both amiibo keys from key_retail.bin +bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info); + +/// Decodes encripted amiibo data returns true if output is valid +bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data); + +/// Encodes plain amiibo data returns true if output is valid +bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_tag_data); + +} // namespace Service::NFP::AmiiboCrypto diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 761d0d3c6..0cb55ca49 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -1,361 +1,43 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <array> -#include <atomic> +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" -#include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/k_event.h" #include "core/hle/service/nfp/nfp.h" #include "core/hle/service/nfp/nfp_user.h" namespace Service::NFP { -namespace ErrCodes { -constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152); -} // namespace ErrCodes - -Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, - const char* name) - : ServiceFramework{system_, name}, module{std::move(module_)}, service_context{system_, - "NFP::IUser"} { - nfc_tag_load = service_context.CreateEvent("NFP::IUser:NFCTagDetected"); -} - -Module::Interface::~Interface() { - service_context.CloseEvent(nfc_tag_load); -} -class IUser final : public ServiceFramework<IUser> { +class IUserManager final : public ServiceFramework<IUserManager> { public: - explicit IUser(Module::Interface& nfp_interface_, Core::System& system_, - KernelHelpers::ServiceContext& service_context_) - : ServiceFramework{system_, "NFP::IUser"}, nfp_interface{nfp_interface_}, - service_context{service_context_} { + explicit IUserManager(Core::System& system_) : ServiceFramework{system_, "nfp:user"} { + // clang-format off static const FunctionInfo functions[] = { - {0, &IUser::Initialize, "Initialize"}, - {1, &IUser::Finalize, "Finalize"}, - {2, &IUser::ListDevices, "ListDevices"}, - {3, &IUser::StartDetection, "StartDetection"}, - {4, &IUser::StopDetection, "StopDetection"}, - {5, &IUser::Mount, "Mount"}, - {6, &IUser::Unmount, "Unmount"}, - {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, - {8, &IUser::GetApplicationArea, "GetApplicationArea"}, - {9, nullptr, "SetApplicationArea"}, - {10, nullptr, "Flush"}, - {11, nullptr, "Restore"}, - {12, nullptr, "CreateApplicationArea"}, - {13, &IUser::GetTagInfo, "GetTagInfo"}, - {14, &IUser::GetRegisterInfo, "GetRegisterInfo"}, - {15, &IUser::GetCommonInfo, "GetCommonInfo"}, - {16, &IUser::GetModelInfo, "GetModelInfo"}, - {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, - {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, - {19, &IUser::GetState, "GetState"}, - {20, &IUser::GetDeviceState, "GetDeviceState"}, - {21, &IUser::GetNpadId, "GetNpadId"}, - {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"}, - {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, - {24, nullptr, "RecreateApplicationArea"}, + {0, &IUserManager::CreateUserInterface, "CreateUserInterface"}, }; - RegisterHandlers(functions); - - deactivate_event = service_context.CreateEvent("NFP::IUser:DeactivateEvent"); - availability_change_event = - service_context.CreateEvent("NFP::IUser:AvailabilityChangeEvent"); - } + // clang-format on - ~IUser() override { - service_context.CloseEvent(deactivate_event); - service_context.CloseEvent(availability_change_event); + RegisterHandlers(functions); } private: - struct TagInfo { - std::array<u8, 10> uuid; - u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it - // mean something else - std::array<u8, 0x15> padding_1; - u32_le protocol; - u32_le tag_type; - std::array<u8, 0x2c> padding_2; - }; - static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size"); - - enum class State : u32 { - NonInitialized = 0, - Initialized = 1, - }; - - enum class DeviceState : u32 { - Initialized = 0, - SearchingForTag = 1, - TagFound = 2, - TagRemoved = 3, - TagNearby = 4, - Unknown5 = 5, - Finalized = 6 - }; - - struct CommonInfo { - u16_be last_write_year; - u8 last_write_month; - u8 last_write_day; - u16_be write_counter; - u16_be version; - u32_be application_area_size; - INSERT_PADDING_BYTES(0x34); - }; - static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); - - void Initialize(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFC, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0}; - rb.Push(ResultSuccess); - - state = State::Initialized; - } - - void GetState(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFC, "called"); - - IPC::ResponseBuilder rb{ctx, 3, 0}; - rb.Push(ResultSuccess); - rb.PushRaw<u32>(static_cast<u32>(state)); - } - - void ListDevices(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u32 array_size = rp.Pop<u32>(); - LOG_DEBUG(Service_NFP, "called, array_size={}", array_size); - - ctx.WriteBuffer(device_handle); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(1); - } - - void GetNpadId(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 dev_handle = rp.Pop<u64>(); - LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(npad_id); - } - - void AttachActivateEvent(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 dev_handle = rp.Pop<u64>(); - LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(nfp_interface.GetNFCEvent()); - has_attached_handle = true; - } - - void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 dev_handle = rp.Pop<u64>(); - LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(deactivate_event->GetReadableEvent()); - } - - void StopDetection(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFP, "called"); - - switch (device_state) { - case DeviceState::TagFound: - case DeviceState::TagNearby: - deactivate_event->GetWritableEvent().Signal(); - device_state = DeviceState::Initialized; - break; - case DeviceState::SearchingForTag: - case DeviceState::TagRemoved: - device_state = DeviceState::Initialized; - break; - default: - break; - } - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void GetDeviceState(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFP, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(static_cast<u32>(device_state)); - } - - void StartDetection(Kernel::HLERequestContext& ctx) { + void CreateUserInterface(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NFP, "called"); - if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { - device_state = DeviceState::SearchingForTag; + if (user_interface == nullptr) { + user_interface = std::make_shared<IUser>(system); } - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void GetTagInfo(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFP, "called"); - - IPC::ResponseBuilder rb{ctx, 2}; - const auto& amiibo = nfp_interface.GetAmiiboBuffer(); - const TagInfo tag_info{ - .uuid = amiibo.uuid, - .uuid_length = static_cast<u8>(amiibo.uuid.size()), - .padding_1 = {}, - .protocol = 1, // TODO(ogniK): Figure out actual values - .tag_type = 2, - .padding_2 = {}, - }; - ctx.WriteBuffer(tag_info); - rb.Push(ResultSuccess); - } - - void Mount(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFP, "called"); - - device_state = DeviceState::TagNearby; - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void GetModelInfo(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFP, "called"); - - IPC::ResponseBuilder rb{ctx, 2}; - const auto& amiibo = nfp_interface.GetAmiiboBuffer(); - ctx.WriteBuffer(amiibo.model_info); - rb.Push(ResultSuccess); - } - - void Unmount(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFP, "called"); - - device_state = DeviceState::TagFound; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void Finalize(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFP, "called"); - - device_state = DeviceState::Finalized; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NFP, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(availability_change_event->GetReadableEvent()); - } - - void GetRegisterInfo(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NFP, "(STUBBED) called"); - - // TODO(ogniK): Pull Mii and owner data from amiibo - IPC::ResponseBuilder rb{ctx, 2}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); + rb.PushIpcInterface<IUser>(user_interface); } - void GetCommonInfo(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NFP, "(STUBBED) called"); - - // TODO(ogniK): Pull common information from amiibo - - CommonInfo common_info{}; - common_info.application_area_size = 0; - ctx.WriteBuffer(common_info); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void OpenApplicationArea(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NFP, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ErrCodes::ERR_NO_APPLICATION_AREA); - } - - void GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NFP, "(STUBBED) called"); - // We don't need to worry about this since we can just open the file - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub - } - - void GetApplicationArea(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NFP, "(STUBBED) called"); - - // TODO(ogniK): Pull application area from amiibo - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub - } - - Module::Interface& nfp_interface; - KernelHelpers::ServiceContext& service_context; - - bool has_attached_handle{}; - const u64 device_handle{0}; // Npad device 1 - const u32 npad_id{0}; // Player 1 controller - State state{State::NonInitialized}; - DeviceState device_state{DeviceState::Initialized}; - Kernel::KEvent* deactivate_event; - Kernel::KEvent* availability_change_event; + std::shared_ptr<IUser> user_interface; }; -void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NFP, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IUser>(*this, system, service_context); -} - -bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { - if (buffer.size() < sizeof(AmiiboFile)) { - return false; - } - - std::memcpy(&amiibo, buffer.data(), sizeof(amiibo)); - nfc_tag_load->GetWritableEvent().Signal(); - return true; -} - -Kernel::KReadableEvent& Module::Interface::GetNFCEvent() { - return nfc_tag_load->GetReadableEvent(); -} - -const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const { - return amiibo; -} - void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { - auto module = std::make_shared<Module>(); - std::make_shared<NFP_User>(module, system)->InstallAsService(service_manager); + std::make_shared<IUserManager>(system)->InstallAsService(service_manager); } } // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index 95c127efb..a25c362b8 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h @@ -1,57 +1,12 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include <array> -#include <vector> - -#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" -namespace Kernel { -class KEvent; -} - namespace Service::NFP { -class Module final { -public: - class Interface : public ServiceFramework<Interface> { - public: - explicit Interface(std::shared_ptr<Module> module_, Core::System& system_, - const char* name); - ~Interface() override; - - struct ModelInfo { - std::array<u8, 0x8> amiibo_identification_block; - INSERT_PADDING_BYTES(0x38); - }; - static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); - - struct AmiiboFile { - std::array<u8, 10> uuid; - INSERT_PADDING_BYTES(0x4a); - ModelInfo model_info; - }; - static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size"); - - void CreateUserInterface(Kernel::HLERequestContext& ctx); - bool LoadAmiibo(const std::vector<u8>& buffer); - Kernel::KReadableEvent& GetNFCEvent(); - const AmiiboFile& GetAmiiboBuffer() const; - - protected: - std::shared_ptr<Module> module; - - private: - KernelHelpers::ServiceContext service_context; - Kernel::KEvent* nfc_tag_load; - AmiiboFile amiibo{}; - }; -}; - void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); } // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp new file mode 100644 index 000000000..ec895ac01 --- /dev/null +++ b/src/core/hle/service/nfp/nfp_device.cpp @@ -0,0 +1,681 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <array> +#include <atomic> + +#include "common/fs/file.h" +#include "common/fs/path_util.h" +#include "common/input.h" +#include "common/logging/log.h" +#include "common/string_util.h" +#include "common/tiny_mt.h" +#include "core/core.h" +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hid/hid_types.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/mii/mii_manager.h" +#include "core/hle/service/nfp/amiibo_crypto.h" +#include "core/hle/service/nfp/nfp.h" +#include "core/hle/service/nfp/nfp_device.h" +#include "core/hle/service/nfp/nfp_result.h" +#include "core/hle/service/nfp/nfp_user.h" +#include "core/hle/service/time/time_manager.h" +#include "core/hle/service/time/time_zone_content_manager.h" +#include "core/hle/service/time/time_zone_types.h" + +namespace Service::NFP { +NfpDevice::NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, + KernelHelpers::ServiceContext& service_context_, + Kernel::KEvent* availability_change_event_) + : npad_id{npad_id_}, system{system_}, service_context{service_context_}, + availability_change_event{availability_change_event_} { + activate_event = service_context.CreateEvent("IUser:NFPActivateEvent"); + deactivate_event = service_context.CreateEvent("IUser:NFPDeactivateEvent"); + npad_device = system.HIDCore().GetEmulatedController(npad_id); + + Core::HID::ControllerUpdateCallback engine_callback{ + .on_change = [this](Core::HID::ControllerTriggerType type) { NpadUpdate(type); }, + .is_npad_service = false, + }; + is_controller_set = true; + callback_key = npad_device->SetCallback(engine_callback); + + auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()}; + current_posix_time = standard_steady_clock.GetCurrentTimePoint(system).time_point; +} + +NfpDevice::~NfpDevice() { + if (!is_controller_set) { + return; + } + npad_device->DeleteCallback(callback_key); + is_controller_set = false; +}; + +void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { + if (type == Core::HID::ControllerTriggerType::Connected || + type == Core::HID::ControllerTriggerType::Disconnected) { + availability_change_event->GetWritableEvent().Signal(); + return; + } + + if (type != Core::HID::ControllerTriggerType::Nfc) { + return; + } + + if (!npad_device->IsConnected()) { + return; + } + + const auto nfc_status = npad_device->GetNfc(); + switch (nfc_status.state) { + case Common::Input::NfcState::NewAmiibo: + LoadAmiibo(nfc_status.data); + break; + case Common::Input::NfcState::AmiiboRemoved: + if (device_state != DeviceState::SearchingForTag) { + CloseAmiibo(); + } + break; + default: + break; + } +} + +bool NfpDevice::LoadAmiibo(std::span<const u8> data) { + if (device_state != DeviceState::SearchingForTag) { + LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state); + return false; + } + + if (data.size() != sizeof(EncryptedNTAG215File)) { + LOG_ERROR(Service_NFP, "Not an amiibo, size={}", data.size()); + return false; + } + + memcpy(&encrypted_tag_data, data.data(), sizeof(EncryptedNTAG215File)); + + device_state = DeviceState::TagFound; + deactivate_event->GetReadableEvent().Clear(); + activate_event->GetWritableEvent().Signal(); + return true; +} + +void NfpDevice::CloseAmiibo() { + LOG_INFO(Service_NFP, "Remove amiibo"); + + if (device_state == DeviceState::TagMounted) { + Unmount(); + } + + device_state = DeviceState::TagRemoved; + encrypted_tag_data = {}; + tag_data = {}; + activate_event->GetReadableEvent().Clear(); + deactivate_event->GetWritableEvent().Signal(); +} + +Kernel::KReadableEvent& NfpDevice::GetActivateEvent() const { + return activate_event->GetReadableEvent(); +} + +Kernel::KReadableEvent& NfpDevice::GetDeactivateEvent() const { + return deactivate_event->GetReadableEvent(); +} + +void NfpDevice::Initialize() { + device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable; + encrypted_tag_data = {}; + tag_data = {}; +} + +void NfpDevice::Finalize() { + if (device_state == DeviceState::TagMounted) { + Unmount(); + } + if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { + StopDetection(); + } + device_state = DeviceState::Unavailable; +} + +Result NfpDevice::StartDetection(s32 protocol_) { + if (device_state != DeviceState::Initialized && device_state != DeviceState::TagRemoved) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + return WrongDeviceState; + } + + if (!npad_device->SetPollingMode(Common::Input::PollingMode::NFC)) { + LOG_ERROR(Service_NFP, "Nfc not supported"); + return NfcDisabled; + } + + device_state = DeviceState::SearchingForTag; + protocol = protocol_; + return ResultSuccess; +} + +Result NfpDevice::StopDetection() { + npad_device->SetPollingMode(Common::Input::PollingMode::Active); + + if (device_state == DeviceState::Initialized) { + return ResultSuccess; + } + + if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) { + CloseAmiibo(); + return ResultSuccess; + } + if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { + device_state = DeviceState::Initialized; + return ResultSuccess; + } + + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + return WrongDeviceState; +} + +Result NfpDevice::Flush() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + auto& settings = tag_data.settings; + + const auto& current_date = GetAmiiboDate(current_posix_time); + if (settings.write_date.raw_date != current_date.raw_date) { + settings.write_date = current_date; + settings.crc_counter++; + // TODO: Find how to calculate the crc check + // settings.crc = CalculateCRC(settings); + } + + tag_data.write_counter++; + + if (!AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) { + LOG_ERROR(Service_NFP, "Failed to encode data"); + return WriteAmiiboFailed; + } + + std::vector<u8> data(sizeof(encrypted_tag_data)); + memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); + + if (!npad_device->WriteNfc(data)) { + LOG_ERROR(Service_NFP, "Error writing to file"); + return WriteAmiiboFailed; + } + + is_data_moddified = false; + + return ResultSuccess; +} + +Result NfpDevice::Mount(MountTarget mount_target_) { + if (device_state != DeviceState::TagFound) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + return WrongDeviceState; + } + + if (!AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) { + LOG_ERROR(Service_NFP, "Not an amiibo"); + return NotAnAmiibo; + } + + if (!AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { + LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state); + return CorruptedData; + } + + device_state = DeviceState::TagMounted; + mount_target = mount_target_; + return ResultSuccess; +} + +Result NfpDevice::Unmount() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + // Save data before unloading the amiibo + if (is_data_moddified) { + Flush(); + } + + device_state = DeviceState::TagFound; + mount_target = MountTarget::None; + is_app_area_open = false; + + return ResultSuccess; +} + +Result NfpDevice::GetTagInfo(TagInfo& tag_info) const { + if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + tag_info = { + .uuid = encrypted_tag_data.uuid.uid, + .uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()), + .protocol = TagProtocol::TypeA, + .tag_type = TagType::Type2, + }; + + return ResultSuccess; +} + +Result NfpDevice::GetCommonInfo(CommonInfo& common_info) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + const auto& settings = tag_data.settings; + + // TODO: Validate this data + common_info = { + .last_write_date = settings.write_date.GetWriteDate(), + .write_counter = tag_data.write_counter, + .version = 0, + .application_area_size = sizeof(ApplicationArea), + }; + return ResultSuccess; +} + +Result NfpDevice::GetModelInfo(ModelInfo& model_info) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + const auto& model_info_data = encrypted_tag_data.user_memory.model_info; + model_info = { + .character_id = model_info_data.character_id, + .character_variant = model_info_data.character_variant, + .amiibo_type = model_info_data.amiibo_type, + .model_number = model_info_data.model_number, + .series = model_info_data.series, + }; + return ResultSuccess; +} + +Result NfpDevice::GetRegisterInfo(RegisterInfo& register_info) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + if (tag_data.settings.settings.amiibo_initialized == 0) { + return RegistrationIsNotInitialized; + } + + Service::Mii::MiiManager manager; + const auto& settings = tag_data.settings; + + // TODO: Validate this data + register_info = { + .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii), + .creation_date = settings.init_date.GetWriteDate(), + .amiibo_name = GetAmiiboName(settings), + .font_region = {}, + }; + + return ResultSuccess; +} + +Result NfpDevice::SetNicknameAndOwner(const AmiiboName& amiibo_name) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + Service::Mii::MiiManager manager; + auto& settings = tag_data.settings; + + settings.init_date = GetAmiiboDate(current_posix_time); + settings.write_date = GetAmiiboDate(current_posix_time); + settings.crc_counter++; + // TODO: Find how to calculate the crc check + // settings.crc = CalculateCRC(settings); + + SetAmiiboName(settings, amiibo_name); + tag_data.owner_mii = manager.ConvertCharInfoToV3(manager.BuildDefault(0)); + settings.settings.amiibo_initialized.Assign(1); + + return Flush(); +} + +Result NfpDevice::RestoreAmiibo() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + // TODO: Load amiibo from backup on system + LOG_ERROR(Service_NFP, "Not Implemented"); + return ResultSuccess; +} + +Result NfpDevice::DeleteAllData() { + const auto result = DeleteApplicationArea(); + if (result.IsError()) { + return result; + } + + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + Common::TinyMT rng{}; + rng.GenerateRandomBytes(&tag_data.owner_mii, sizeof(tag_data.owner_mii)); + tag_data.settings.settings.amiibo_initialized.Assign(0); + + return Flush(); +} + +Result NfpDevice::OpenApplicationArea(u32 access_id) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized.Value() == 0) { + LOG_WARNING(Service_NFP, "Application area is not initialized"); + return ApplicationAreaIsNotInitialized; + } + + if (tag_data.application_area_id != access_id) { + LOG_WARNING(Service_NFP, "Wrong application area id"); + return WrongApplicationAreaId; + } + + is_app_area_open = true; + + return ResultSuccess; +} + +Result NfpDevice::GetApplicationArea(std::vector<u8>& data) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + if (!is_app_area_open) { + LOG_ERROR(Service_NFP, "Application area is not open"); + return WrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized.Value() == 0) { + LOG_ERROR(Service_NFP, "Application area is not initialized"); + return ApplicationAreaIsNotInitialized; + } + + if (data.size() > sizeof(ApplicationArea)) { + data.resize(sizeof(ApplicationArea)); + } + + memcpy(data.data(), tag_data.application_area.data(), data.size()); + + return ResultSuccess; +} + +Result NfpDevice::SetApplicationArea(std::span<const u8> data) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + if (!is_app_area_open) { + LOG_ERROR(Service_NFP, "Application area is not open"); + return WrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized.Value() == 0) { + LOG_ERROR(Service_NFP, "Application area is not initialized"); + return ApplicationAreaIsNotInitialized; + } + + if (data.size() > sizeof(ApplicationArea)) { + LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); + return ResultUnknown; + } + + Common::TinyMT rng{}; + std::memcpy(tag_data.application_area.data(), data.data(), data.size()); + // Fill remaining data with random numbers + rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(), + sizeof(ApplicationArea) - data.size()); + + tag_data.applicaton_write_counter++; + is_data_moddified = true; + + return ResultSuccess; +} + +Result NfpDevice::CreateApplicationArea(u32 access_id, std::span<const u8> data) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (tag_data.settings.settings.appdata_initialized.Value() != 0) { + LOG_ERROR(Service_NFP, "Application area already exist"); + return ApplicationAreaExist; + } + + return RecreateApplicationArea(access_id, data); +} + +Result NfpDevice::RecreateApplicationArea(u32 access_id, std::span<const u8> data) { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + if (data.size() > sizeof(ApplicationArea)) { + LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); + return WrongApplicationAreaSize; + } + + Common::TinyMT rng{}; + std::memcpy(tag_data.application_area.data(), data.data(), data.size()); + // Fill remaining data with random numbers + rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(), + sizeof(ApplicationArea) - data.size()); + + // TODO: Investigate why the title id needs to be moddified + tag_data.title_id = system.GetCurrentProcessProgramID(); + tag_data.title_id = tag_data.title_id | 0x30000000ULL; + tag_data.settings.settings.appdata_initialized.Assign(1); + tag_data.application_area_id = access_id; + tag_data.applicaton_write_counter++; + tag_data.unknown = {}; + + return Flush(); +} + +Result NfpDevice::DeleteApplicationArea() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + Common::TinyMT rng{}; + rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(ApplicationArea)); + rng.GenerateRandomBytes(&tag_data.title_id, sizeof(u64)); + rng.GenerateRandomBytes(&tag_data.application_area_id, sizeof(u32)); + tag_data.settings.settings.appdata_initialized.Assign(0); + tag_data.applicaton_write_counter++; + tag_data.unknown = {}; + + return Flush(); +} + +u64 NfpDevice::GetHandle() const { + // Generate a handle based of the npad id + return static_cast<u64>(npad_id); +} + +u32 NfpDevice::GetApplicationAreaSize() const { + return sizeof(ApplicationArea); +} + +DeviceState NfpDevice::GetCurrentState() const { + return device_state; +} + +Core::HID::NpadIdType NfpDevice::GetNpadId() const { + return npad_id; +} + +AmiiboName NfpDevice::GetAmiiboName(const AmiiboSettings& settings) const { + std::array<char16_t, amiibo_name_length> settings_amiibo_name{}; + AmiiboName amiibo_name{}; + + // Convert from big endian to little endian + for (std::size_t i = 0; i < amiibo_name_length; i++) { + settings_amiibo_name[i] = static_cast<u16>(settings.amiibo_name[i]); + } + + // Convert from utf16 to utf8 + const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data()); + memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size()); + + return amiibo_name; +} + +void NfpDevice::SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name) { + std::array<char16_t, amiibo_name_length> settings_amiibo_name{}; + + // Convert from utf8 to utf16 + const auto amiibo_name_utf16 = Common::UTF8ToUTF16(amiibo_name.data()); + memcpy(settings_amiibo_name.data(), amiibo_name_utf16.data(), + amiibo_name_utf16.size() * sizeof(char16_t)); + + // Convert from little endian to big endian + for (std::size_t i = 0; i < amiibo_name_length; i++) { + settings.amiibo_name[i] = static_cast<u16_be>(settings_amiibo_name[i]); + } +} + +AmiiboDate NfpDevice::GetAmiiboDate(s64 posix_time) const { + const auto& time_zone_manager = + system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager(); + Time::TimeZone::CalendarInfo calendar_info{}; + AmiiboDate amiibo_date{}; + + amiibo_date.SetYear(2000); + amiibo_date.SetMonth(1); + amiibo_date.SetDay(1); + + if (time_zone_manager.ToCalendarTime({}, posix_time, calendar_info) == ResultSuccess) { + amiibo_date.SetYear(calendar_info.time.year); + amiibo_date.SetMonth(calendar_info.time.month); + amiibo_date.SetDay(calendar_info.time.day); + } + + return amiibo_date; +} + +} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h new file mode 100644 index 000000000..a5b72cf19 --- /dev/null +++ b/src/core/hle/service/nfp/nfp_device.h @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <vector> + +#include "common/common_funcs.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/mii/types.h" +#include "core/hle/service/nfp/nfp_types.h" +#include "core/hle/service/service.h" + +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + +namespace Core { +class System; +} // namespace Core + +namespace Core::HID { +class EmulatedController; +enum class ControllerTriggerType; +enum class NpadIdType : u32; +} // namespace Core::HID + +namespace Service::NFP { +class NfpDevice { +public: + NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, + KernelHelpers::ServiceContext& service_context_, + Kernel::KEvent* availability_change_event_); + ~NfpDevice(); + + void Initialize(); + void Finalize(); + + Result StartDetection(s32 protocol_); + Result StopDetection(); + Result Mount(MountTarget mount_target); + Result Unmount(); + Result Flush(); + + Result GetTagInfo(TagInfo& tag_info) const; + Result GetCommonInfo(CommonInfo& common_info) const; + Result GetModelInfo(ModelInfo& model_info) const; + Result GetRegisterInfo(RegisterInfo& register_info) const; + + Result SetNicknameAndOwner(const AmiiboName& amiibo_name); + Result RestoreAmiibo(); + Result DeleteAllData(); + + Result OpenApplicationArea(u32 access_id); + Result GetApplicationArea(std::vector<u8>& data) const; + Result SetApplicationArea(std::span<const u8> data); + Result CreateApplicationArea(u32 access_id, std::span<const u8> data); + Result RecreateApplicationArea(u32 access_id, std::span<const u8> data); + Result DeleteApplicationArea(); + + u64 GetHandle() const; + u32 GetApplicationAreaSize() const; + DeviceState GetCurrentState() const; + Core::HID::NpadIdType GetNpadId() const; + + Kernel::KReadableEvent& GetActivateEvent() const; + Kernel::KReadableEvent& GetDeactivateEvent() const; + +private: + void NpadUpdate(Core::HID::ControllerTriggerType type); + bool LoadAmiibo(std::span<const u8> data); + void CloseAmiibo(); + + AmiiboName GetAmiiboName(const AmiiboSettings& settings) const; + void SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name); + AmiiboDate GetAmiiboDate(s64 posix_time) const; + + bool is_controller_set{}; + int callback_key; + const Core::HID::NpadIdType npad_id; + Core::System& system; + Core::HID::EmulatedController* npad_device = nullptr; + KernelHelpers::ServiceContext& service_context; + Kernel::KEvent* activate_event = nullptr; + Kernel::KEvent* deactivate_event = nullptr; + Kernel::KEvent* availability_change_event = nullptr; + + bool is_data_moddified{}; + bool is_app_area_open{}; + s32 protocol{}; + s64 current_posix_time{}; + MountTarget mount_target{MountTarget::None}; + DeviceState device_state{DeviceState::Unavailable}; + + NTAG215File tag_data{}; + EncryptedNTAG215File encrypted_tag_data{}; +}; + +} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_result.h b/src/core/hle/service/nfp/nfp_result.h new file mode 100644 index 000000000..d8e4cf094 --- /dev/null +++ b/src/core/hle/service/nfp/nfp_result.h @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/result.h" + +namespace Service::NFP { + +constexpr Result DeviceNotFound(ErrorModule::NFP, 64); +constexpr Result InvalidArgument(ErrorModule::NFP, 65); +constexpr Result WrongApplicationAreaSize(ErrorModule::NFP, 68); +constexpr Result WrongDeviceState(ErrorModule::NFP, 73); +constexpr Result NfcDisabled(ErrorModule::NFP, 80); +constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88); +constexpr Result TagRemoved(ErrorModule::NFP, 97); +constexpr Result RegistrationIsNotInitialized(ErrorModule::NFP, 120); +constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); +constexpr Result CorruptedData(ErrorModule::NFP, 144); +constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152); +constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168); +constexpr Result NotAnAmiibo(ErrorModule::NFP, 178); + +} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h new file mode 100644 index 000000000..867ea2f36 --- /dev/null +++ b/src/core/hle/service/nfp/nfp_types.h @@ -0,0 +1,320 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <array> + +#include "common/swap.h" +#include "core/hle/service/mii/types.h" + +namespace Service::NFP { +static constexpr std::size_t amiibo_name_length = 0xA; + +enum class ServiceType : u32 { + User, + Debug, + System, +}; + +enum class State : u32 { + NonInitialized, + Initialized, +}; + +enum class DeviceState : u32 { + Initialized, + SearchingForTag, + TagFound, + TagRemoved, + TagMounted, + Unavailable, + Finalized, +}; + +enum class ModelType : u32 { + Amiibo, +}; + +enum class MountTarget : u32 { + None, + Rom, + Ram, + All, +}; + +enum class AmiiboType : u8 { + Figure, + Card, + Yarn, +}; + +enum class AmiiboSeries : u8 { + SuperSmashBros, + SuperMario, + ChibiRobo, + YoshiWoollyWorld, + Splatoon, + AnimalCrossing, + EightBitMario, + Skylanders, + Unknown8, + TheLegendOfZelda, + ShovelKnight, + Unknown11, + Kiby, + Pokemon, + MarioSportsSuperstars, + MonsterHunter, + BoxBoy, + Pikmin, + FireEmblem, + Metroid, + Others, + MegaMan, + Diablo, +}; + +enum class TagType : u32 { + None, + Type1, // ISO14443A RW 96-2k bytes 106kbit/s + Type2, // ISO14443A RW/RO 540 bytes 106kbit/s + Type3, // Sony Felica RW/RO 2k bytes 212kbit/s + Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s + Type5, // ISO15693 RW/RO 540 bytes 106kbit/s +}; + +enum class PackedTagType : u8 { + None, + Type1, // ISO14443A RW 96-2k bytes 106kbit/s + Type2, // ISO14443A RW/RO 540 bytes 106kbit/s + Type3, // Sony Felica RW/RO 2k bytes 212kbit/s + Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s + Type5, // ISO15693 RW/RO 540 bytes 106kbit/s +}; + +enum class TagProtocol : u32 { + None, + TypeA, // ISO14443A + TypeB, // ISO14443B + TypeF, // Sony Felica +}; + +using UniqueSerialNumber = std::array<u8, 7>; +using LockBytes = std::array<u8, 2>; +using HashData = std::array<u8, 0x20>; +using ApplicationArea = std::array<u8, 0xD8>; +using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>; + +struct TagUuid { + UniqueSerialNumber uid; + u8 nintendo_id; + LockBytes lock_bytes; +}; +static_assert(sizeof(TagUuid) == 10, "TagUuid is an invalid size"); + +struct WriteDate { + u16 year; + u8 month; + u8 day; +}; +static_assert(sizeof(WriteDate) == 0x4, "WriteDate is an invalid size"); + +struct AmiiboDate { + u16 raw_date{}; + + u16 GetValue() const { + return Common::swap16(raw_date); + } + + u16 GetYear() const { + return static_cast<u16>(((GetValue() & 0xFE00) >> 9) + 2000); + } + u8 GetMonth() const { + return static_cast<u8>((GetValue() & 0x01E0) >> 5); + } + u8 GetDay() const { + return static_cast<u8>(GetValue() & 0x001F); + } + + WriteDate GetWriteDate() const { + if (!IsValidDate()) { + return { + .year = 2000, + .month = 1, + .day = 1, + }; + } + return { + .year = GetYear(), + .month = GetMonth(), + .day = GetDay(), + }; + } + + void SetYear(u16 year) { + const u16 year_converted = static_cast<u16>((year - 2000) << 9); + raw_date = Common::swap16((GetValue() & ~0xFE00) | year_converted); + } + void SetMonth(u8 month) { + const u16 month_converted = static_cast<u16>(month << 5); + raw_date = Common::swap16((GetValue() & ~0x01E0) | month_converted); + } + void SetDay(u8 day) { + const u16 day_converted = static_cast<u16>(day); + raw_date = Common::swap16((GetValue() & ~0x001F) | day_converted); + } + + bool IsValidDate() const { + const bool is_day_valid = GetDay() > 0 && GetDay() < 32; + const bool is_month_valid = GetMonth() >= 0 && GetMonth() < 13; + const bool is_year_valid = GetYear() >= 2000; + return is_year_valid && is_month_valid && is_day_valid; + } +}; +static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size"); + +struct Settings { + union { + u8 raw{}; + + BitField<4, 1, u8> amiibo_initialized; + BitField<5, 1, u8> appdata_initialized; + }; +}; +static_assert(sizeof(Settings) == 1, "AmiiboDate is an invalid size"); + +struct AmiiboSettings { + Settings settings; + u8 country_code_id; + u16_be crc_counter; // Incremented each time crc is changed + AmiiboDate init_date; + AmiiboDate write_date; + u32_be crc; + std::array<u16_be, amiibo_name_length> amiibo_name; // UTF-16 text +}; +static_assert(sizeof(AmiiboSettings) == 0x20, "AmiiboSettings is an invalid size"); + +struct AmiiboModelInfo { + u16 character_id; + u8 character_variant; + AmiiboType amiibo_type; + u16_be model_number; + AmiiboSeries series; + PackedTagType tag_type; + INSERT_PADDING_BYTES(0x4); // Unknown +}; +static_assert(sizeof(AmiiboModelInfo) == 0xC, "AmiiboModelInfo is an invalid size"); + +struct NTAG215Password { + u32 PWD; // Password to allow write access + u16 PACK; // Password acknowledge reply + u16 RFUI; // Reserved for future use +}; +static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size"); + +#pragma pack(1) +struct EncryptedAmiiboFile { + u8 constant_value; // Must be A5 + u16_be write_counter; // Number of times the amiibo has been written? + INSERT_PADDING_BYTES(0x1); // Unknown 1 + AmiiboSettings settings; // Encrypted amiibo settings + HashData hmac_tag; // Hash + AmiiboModelInfo model_info; // Encrypted amiibo model info + HashData keygen_salt; // Salt + HashData hmac_data; // Hash + Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data + u64_be title_id; // Encrypted Game id + u16_be applicaton_write_counter; // Encrypted Counter + u32_be application_area_id; // Encrypted Game id + std::array<u8, 0x2> unknown; + std::array<u32, 0x8> unknown2; + ApplicationArea application_area; // Encrypted Game data +}; +static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); + +struct NTAG215File { + LockBytes lock_bytes; // Tag UUID + u16 static_lock; // Set defined pages as read only + u32 compability_container; // Defines available memory + HashData hmac_data; // Hash + u8 constant_value; // Must be A5 + u16_be write_counter; // Number of times the amiibo has been written? + INSERT_PADDING_BYTES(0x1); // Unknown 1 + AmiiboSettings settings; + Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data + u64_be title_id; + u16_be applicaton_write_counter; // Encrypted Counter + u32_be application_area_id; + std::array<u8, 0x2> unknown; + std::array<u32, 0x8> unknown2; + ApplicationArea application_area; // Encrypted Game data + HashData hmac_tag; // Hash + UniqueSerialNumber uid; // Unique serial number + u8 nintendo_id; // Tag UUID + AmiiboModelInfo model_info; + HashData keygen_salt; // Salt + u32 dynamic_lock; // Dynamic lock + u32 CFG0; // Defines memory protected by password + u32 CFG1; // Defines number of verification attempts + NTAG215Password password; // Password data +}; +static_assert(sizeof(NTAG215File) == 0x21C, "NTAG215File is an invalid size"); +static_assert(std::is_trivially_copyable_v<NTAG215File>, "NTAG215File must be trivially copyable."); +#pragma pack() + +struct EncryptedNTAG215File { + TagUuid uuid; // Unique serial number + u16 static_lock; // Set defined pages as read only + u32 compability_container; // Defines available memory + EncryptedAmiiboFile user_memory; // Writable data + u32 dynamic_lock; // Dynamic lock + u32 CFG0; // Defines memory protected by password + u32 CFG1; // Defines number of verification attempts + NTAG215Password password; // Password data +}; +static_assert(sizeof(EncryptedNTAG215File) == 0x21C, "EncryptedNTAG215File is an invalid size"); +static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>, + "EncryptedNTAG215File must be trivially copyable."); + +struct TagInfo { + UniqueSerialNumber uuid; + INSERT_PADDING_BYTES(0x3); + u8 uuid_length; + INSERT_PADDING_BYTES(0x15); + TagProtocol protocol; + TagType tag_type; + INSERT_PADDING_BYTES(0x30); +}; +static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size"); + +struct CommonInfo { + WriteDate last_write_date; + u16 write_counter; + u8 version; + INSERT_PADDING_BYTES(0x1); + u32 application_area_size; + INSERT_PADDING_BYTES(0x34); +}; +static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); + +struct ModelInfo { + u16 character_id; + u8 character_variant; + AmiiboType amiibo_type; + u16 model_number; + AmiiboSeries series; + INSERT_PADDING_BYTES(0x39); // Unknown +}; +static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); + +struct RegisterInfo { + Service::Mii::CharInfo mii_char_info; + WriteDate creation_date; + AmiiboName amiibo_name; + u8 font_region; + INSERT_PADDING_BYTES(0x7A); +}; +static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size"); + +} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp index 10b0ef944..4ed53b534 100644 --- a/src/core/hle/service/nfp/nfp_user.cpp +++ b/src/core/hle/service/nfp/nfp_user.cpp @@ -1,19 +1,674 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#include <array> +#include <atomic> + +#include "common/logging/log.h" +#include "core/core.h" +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hid/hid_types.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/mii/mii_manager.h" +#include "core/hle/service/nfp/nfp_device.h" +#include "core/hle/service/nfp/nfp_result.h" #include "core/hle/service/nfp/nfp_user.h" namespace Service::NFP { -NFP_User::NFP_User(std::shared_ptr<Module> module_, Core::System& system_) - : Interface(std::move(module_), system_, "nfp:user") { +IUser::IUser(Core::System& system_) + : ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name} { static const FunctionInfo functions[] = { - {0, &NFP_User::CreateUserInterface, "CreateUserInterface"}, + {0, &IUser::Initialize, "Initialize"}, + {1, &IUser::Finalize, "Finalize"}, + {2, &IUser::ListDevices, "ListDevices"}, + {3, &IUser::StartDetection, "StartDetection"}, + {4, &IUser::StopDetection, "StopDetection"}, + {5, &IUser::Mount, "Mount"}, + {6, &IUser::Unmount, "Unmount"}, + {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, + {8, &IUser::GetApplicationArea, "GetApplicationArea"}, + {9, &IUser::SetApplicationArea, "SetApplicationArea"}, + {10, &IUser::Flush, "Flush"}, + {11, &IUser::Restore, "Restore"}, + {12, &IUser::CreateApplicationArea, "CreateApplicationArea"}, + {13, &IUser::GetTagInfo, "GetTagInfo"}, + {14, &IUser::GetRegisterInfo, "GetRegisterInfo"}, + {15, &IUser::GetCommonInfo, "GetCommonInfo"}, + {16, &IUser::GetModelInfo, "GetModelInfo"}, + {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, + {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, + {19, &IUser::GetState, "GetState"}, + {20, &IUser::GetDeviceState, "GetDeviceState"}, + {21, &IUser::GetNpadId, "GetNpadId"}, + {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"}, + {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, + {24, &IUser::RecreateApplicationArea, "RecreateApplicationArea"}, }; RegisterHandlers(functions); + + availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent"); + + for (u32 device_index = 0; device_index < 10; device_index++) { + devices[device_index] = + std::make_shared<NfpDevice>(Core::HID::IndexToNpadIdType(device_index), system, + service_context, availability_change_event); + } +} + +void IUser::Initialize(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFC, "called"); + + state = State::Initialized; + + for (auto& device : devices) { + device->Initialize(); + } + + IPC::ResponseBuilder rb{ctx, 2, 0}; + rb.Push(ResultSuccess); +} + +void IUser::Finalize(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFP, "called"); + + state = State::NonInitialized; + + for (auto& device : devices) { + device->Finalize(); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IUser::ListDevices(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFP, "called"); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + if (!ctx.CanWriteBuffer()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + + if (ctx.GetWriteBufferSize() == 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + + std::vector<u64> nfp_devices; + const std::size_t max_allowed_devices = ctx.GetWriteBufferSize() / sizeof(u64); + + for (auto& device : devices) { + if (nfp_devices.size() >= max_allowed_devices) { + continue; + } + if (device->GetCurrentState() != DeviceState::Unavailable) { + nfp_devices.push_back(device->GetHandle()); + } + } + + if (nfp_devices.size() == 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + ctx.WriteBuffer(nfp_devices); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast<s32>(nfp_devices.size())); +} + +void IUser::StartDetection(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + const auto nfp_protocol{rp.Pop<s32>()}; + LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->StartDetection(nfp_protocol); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::StopDetection(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->StopDetection(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::Mount(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + const auto model_type{rp.PopEnum<ModelType>()}; + const auto mount_target{rp.PopEnum<MountTarget>()}; + LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle, + model_type, mount_target); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->Mount(mount_target); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::Unmount(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->Unmount(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::OpenApplicationArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + const auto access_id{rp.Pop<u32>()}; + LOG_INFO(Service_NFP, "called, device_handle={}, access_id={}", device_handle, access_id); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->OpenApplicationArea(access_id); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::GetApplicationArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + const auto data_size = ctx.GetWriteBufferSize(); + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + if (!ctx.CanWriteBuffer()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + std::vector<u8> data(data_size); + const auto result = device.value()->GetApplicationArea(data); + ctx.WriteBuffer(data); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.Push(static_cast<u32>(data_size)); +} + +void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + const auto data{ctx.ReadBuffer()}; + LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}", device_handle, data.size()); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + if (!ctx.CanReadBuffer()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->SetApplicationArea(data); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::Flush(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->Flush(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::Restore(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->RestoreAmiibo(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + const auto access_id{rp.Pop<u32>()}; + const auto data{ctx.ReadBuffer()}; + LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle, + access_id, data.size()); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + if (!ctx.CanReadBuffer()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidArgument); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->CreateApplicationArea(access_id, data); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + TagInfo tag_info{}; + const auto result = device.value()->GetTagInfo(tag_info); + ctx.WriteBuffer(tag_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::GetRegisterInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + RegisterInfo register_info{}; + const auto result = device.value()->GetRegisterInfo(register_info); + ctx.WriteBuffer(register_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::GetCommonInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + CommonInfo common_info{}; + const auto result = device.value()->GetCommonInfo(common_info); + ctx.WriteBuffer(common_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::GetModelInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + ModelInfo model_info{}; + const auto result = device.value()->GetModelInfo(model_info); + ctx.WriteBuffer(model_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IUser::AttachActivateEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(device.value()->GetActivateEvent()); +} + +void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(device.value()->GetDeactivateEvent()); +} + +void IUser::GetState(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "called"); + + IPC::ResponseBuilder rb{ctx, 3, 0}; + rb.Push(ResultSuccess); + rb.PushEnum(state); +} + +void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(device.value()->GetCurrentState()); +} + +void IUser::GetNpadId(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(device.value()->GetNpadId()); +} + +void IUser::GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(device.value()->GetApplicationAreaSize()); } -NFP_User::~NFP_User() = default; +void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFP, "called"); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(availability_change_event->GetReadableEvent()); +} + +void IUser::RecreateApplicationArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + const auto access_id{rp.Pop<u32>()}; + const auto data{ctx.ReadBuffer()}; + LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle, + access_id, data.size()); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->RecreateApplicationArea(access_id, data); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +std::optional<std::shared_ptr<NfpDevice>> IUser::GetNfpDevice(u64 handle) { + for (auto& device : devices) { + if (device->GetHandle() == handle) { + return device; + } + } + return std::nullopt; +} } // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_user.h index 7f3c124f6..68c60ae82 100644 --- a/src/core/hle/service/nfp/nfp_user.h +++ b/src/core/hle/service/nfp/nfp_user.h @@ -1,17 +1,54 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/nfp/nfp.h" +#include "core/hle/service/nfp/nfp_types.h" namespace Service::NFP { +class NfpDevice; -class NFP_User final : public Module::Interface { +class IUser final : public ServiceFramework<IUser> { public: - explicit NFP_User(std::shared_ptr<Module> module_, Core::System& system_); - ~NFP_User() override; + explicit IUser(Core::System& system_); + +private: + void Initialize(Kernel::HLERequestContext& ctx); + void Finalize(Kernel::HLERequestContext& ctx); + void ListDevices(Kernel::HLERequestContext& ctx); + void StartDetection(Kernel::HLERequestContext& ctx); + void StopDetection(Kernel::HLERequestContext& ctx); + void Mount(Kernel::HLERequestContext& ctx); + void Unmount(Kernel::HLERequestContext& ctx); + void OpenApplicationArea(Kernel::HLERequestContext& ctx); + void GetApplicationArea(Kernel::HLERequestContext& ctx); + void SetApplicationArea(Kernel::HLERequestContext& ctx); + void Flush(Kernel::HLERequestContext& ctx); + void Restore(Kernel::HLERequestContext& ctx); + void CreateApplicationArea(Kernel::HLERequestContext& ctx); + void GetTagInfo(Kernel::HLERequestContext& ctx); + void GetRegisterInfo(Kernel::HLERequestContext& ctx); + void GetCommonInfo(Kernel::HLERequestContext& ctx); + void GetModelInfo(Kernel::HLERequestContext& ctx); + void AttachActivateEvent(Kernel::HLERequestContext& ctx); + void AttachDeactivateEvent(Kernel::HLERequestContext& ctx); + void GetState(Kernel::HLERequestContext& ctx); + void GetDeviceState(Kernel::HLERequestContext& ctx); + void GetNpadId(Kernel::HLERequestContext& ctx); + void GetApplicationAreaSize(Kernel::HLERequestContext& ctx); + void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx); + void RecreateApplicationArea(Kernel::HLERequestContext& ctx); + + std::optional<std::shared_ptr<NfpDevice>> GetNfpDevice(u64 handle); + + KernelHelpers::ServiceContext service_context; + + std::array<std::shared_ptr<NfpDevice>, 10> devices{}; + + State state{State::NonInitialized}; + Kernel::KEvent* availability_change_event; }; } // namespace Service::NFP diff --git a/src/core/hle/service/ngct/ngct.cpp b/src/core/hle/service/ngct/ngct.cpp index 8ec7d5266..8af8a835d 100644 --- a/src/core/hle/service/ngct/ngct.cpp +++ b/src/core/hle/service/ngct/ngct.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/string_util.h" #include "core/core.h" diff --git a/src/core/hle/service/ngct/ngct.h b/src/core/hle/service/ngct/ngct.h index 1f2a47b78..370bd4a25 100644 --- a/src/core/hle/service/ngct/ngct.h +++ b/src/core/hle/service/ngct/ngct.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index a253dd066..e3ef06481 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -1,14 +1,11 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/kernel.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/nifm/nifm.h" -#include "core/hle/service/service.h" namespace { @@ -20,8 +17,8 @@ namespace { } // Anonymous namespace -#include "core/network/network.h" -#include "core/network/network_interface.h" +#include "core/internal_network/network.h" +#include "core/internal_network/network_interface.h" namespace Service::NIFM { @@ -32,6 +29,19 @@ enum class RequestState : u32 { Connected = 3, }; +enum class InternetConnectionType : u8 { + WiFi = 1, + Ethernet = 2, +}; + +enum class InternetConnectionStatus : u8 { + ConnectingUnknown1, + ConnectingUnknown2, + ConnectingUnknown3, + ConnectingUnknown4, + Connected, +}; + struct IpAddressSetting { bool is_automatic{}; Network::IPv4Address current_address{}; @@ -260,135 +270,45 @@ public: } }; -class IGeneralService final : public ServiceFramework<IGeneralService> { -public: - explicit IGeneralService(Core::System& system_); - -private: - void GetClientId(Kernel::HLERequestContext& ctx) { - static constexpr u32 client_id = 1; - LOG_WARNING(Service_NIFM, "(STUBBED) called"); +void IGeneralService::GetClientId(Kernel::HLERequestContext& ctx) { + static constexpr u32 client_id = 1; + LOG_WARNING(Service_NIFM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid - } - void CreateScanRequest(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NIFM, "called"); + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid +} - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; +void IGeneralService::CreateScanRequest(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NIFM, "called"); - rb.Push(ResultSuccess); - rb.PushIpcInterface<IScanRequest>(system); - } - void CreateRequest(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NIFM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IScanRequest>(system); +} - rb.Push(ResultSuccess); - rb.PushIpcInterface<IRequest>(system); - } - void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); +void IGeneralService::CreateRequest(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NIFM, "called"); - const auto net_iface = Network::GetSelectedNetworkInterface(); - - const SfNetworkProfileData network_profile_data = [&net_iface] { - if (!net_iface) { - return SfNetworkProfileData{}; - } - - return SfNetworkProfileData{ - .ip_setting_data{ - .ip_address_setting{ - .is_automatic{true}, - .current_address{Network::TranslateIPv4(net_iface->ip_address)}, - .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)}, - .gateway{Network::TranslateIPv4(net_iface->gateway)}, - }, - .dns_setting{ - .is_automatic{true}, - .primary_dns{1, 1, 1, 1}, - .secondary_dns{1, 0, 0, 1}, - }, - .proxy_setting{ - .enabled{false}, - .port{}, - .proxy_server{}, - .automatic_auth_enabled{}, - .user{}, - .password{}, - }, - .mtu{1500}, - }, - .uuid{0xdeadbeef, 0xdeadbeef}, - .network_name{"yuzu Network"}, - .wireless_setting_data{ - .ssid_length{12}, - .ssid{"yuzu Network"}, - .passphrase{"yuzupassword"}, - }, - }; - }(); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - ctx.WriteBuffer(network_profile_data); + rb.Push(ResultSuccess); + rb.PushIpcInterface<IRequest>(system); +} - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); +void IGeneralService::GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); + const auto net_iface = Network::GetSelectedNetworkInterface(); - auto ipv4 = Network::GetHostIPv4Address(); - if (!ipv4) { - LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0"); - ipv4.emplace(Network::IPv4Address{0, 0, 0, 0}); + SfNetworkProfileData network_profile_data = [&net_iface] { + if (!net_iface) { + return SfNetworkProfileData{}; } - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushRaw(*ipv4); - } - void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NIFM, "called"); - - ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, - "SfNetworkProfileData is not the correct size"); - u128 uuid{}; - auto buffer = ctx.ReadBuffer(); - std::memcpy(&uuid, buffer.data() + 8, sizeof(u128)); - - IPC::ResponseBuilder rb{ctx, 6, 0, 1}; - - rb.Push(ResultSuccess); - rb.PushIpcInterface<INetworkProfile>(system); - rb.PushRaw<u128>(uuid); - } - void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); - - struct IpConfigInfo { - IpAddressSetting ip_address_setting{}; - DnsSetting dns_setting{}; - }; - static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), - "IpConfigInfo has incorrect size."); - - const auto net_iface = Network::GetSelectedNetworkInterface(); - - const IpConfigInfo ip_config_info = [&net_iface] { - if (!net_iface) { - return IpConfigInfo{}; - } - - return IpConfigInfo{ + return SfNetworkProfileData{ + .ip_setting_data{ .ip_address_setting{ .is_automatic{true}, .current_address{Network::TranslateIPv4(net_iface->ip_address)}, @@ -400,46 +320,178 @@ private: .primary_dns{1, 1, 1, 1}, .secondary_dns{1, 0, 0, 1}, }, - }; - }(); + .proxy_setting{ + .enabled{false}, + .port{}, + .proxy_server{}, + .automatic_auth_enabled{}, + .user{}, + .password{}, + }, + .mtu{1500}, + }, + .uuid{0xdeadbeef, 0xdeadbeef}, + .network_name{"yuzu Network"}, + .wireless_setting_data{ + .ssid_length{12}, + .ssid{"yuzu Network"}, + .passphrase{"yuzupassword"}, + }, + }; + }(); - IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; - rb.Push(ResultSuccess); - rb.PushRaw<IpConfigInfo>(ip_config_info); + // When we're connected to a room, spoof the hosts IP address + if (auto room_member = network.GetRoomMember().lock()) { + if (room_member->IsConnected()) { + network_profile_data.ip_setting_data.ip_address_setting.current_address = + room_member->GetFakeIpAddress(); + } } - void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u8>(0); + ctx.WriteBuffer(network_profile_data); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IGeneralService::RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IGeneralService::GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + + auto ipv4 = Network::GetHostIPv4Address(); + if (!ipv4) { + LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0"); + ipv4.emplace(Network::IPv4Address{0, 0, 0, 0}); } - void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - if (Network::GetHostIPv4Address().has_value()) { - rb.Push<u8>(1); - } else { - rb.Push<u8>(0); + // When we're connected to a room, spoof the hosts IP address + if (auto room_member = network.GetRoomMember().lock()) { + if (room_member->IsConnected()) { + ipv4 = room_member->GetFakeIpAddress(); } } - void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - if (Network::GetHostIPv4Address().has_value()) { - rb.Push<u8>(1); - } else { - rb.Push<u8>(0); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushRaw(*ipv4); +} + +void IGeneralService::CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NIFM, "called"); + + ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, "SfNetworkProfileData is not the correct size"); + u128 uuid{}; + auto buffer = ctx.ReadBuffer(); + std::memcpy(&uuid, buffer.data() + 8, sizeof(u128)); + + IPC::ResponseBuilder rb{ctx, 6, 0, 1}; + + rb.Push(ResultSuccess); + rb.PushIpcInterface<INetworkProfile>(system); + rb.PushRaw<u128>(uuid); +} + +void IGeneralService::GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + + struct IpConfigInfo { + IpAddressSetting ip_address_setting{}; + DnsSetting dns_setting{}; + }; + static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), + "IpConfigInfo has incorrect size."); + + const auto net_iface = Network::GetSelectedNetworkInterface(); + + IpConfigInfo ip_config_info = [&net_iface] { + if (!net_iface) { + return IpConfigInfo{}; + } + + return IpConfigInfo{ + .ip_address_setting{ + .is_automatic{true}, + .current_address{Network::TranslateIPv4(net_iface->ip_address)}, + .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)}, + .gateway{Network::TranslateIPv4(net_iface->gateway)}, + }, + .dns_setting{ + .is_automatic{true}, + .primary_dns{1, 1, 1, 1}, + .secondary_dns{1, 0, 0, 1}, + }, + }; + }(); + + // When we're connected to a room, spoof the hosts IP address + if (auto room_member = network.GetRoomMember().lock()) { + if (room_member->IsConnected()) { + ip_config_info.ip_address_setting.current_address = room_member->GetFakeIpAddress(); } } -}; + + IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; + rb.Push(ResultSuccess); + rb.PushRaw<IpConfigInfo>(ip_config_info); +} + +void IGeneralService::IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u8>(1); +} + +void IGeneralService::GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + + struct Output { + InternetConnectionType type{InternetConnectionType::WiFi}; + u8 wifi_strength{3}; + InternetConnectionStatus state{InternetConnectionStatus::Connected}; + }; + static_assert(sizeof(Output) == 0x3, "Output has incorrect size."); + + constexpr Output out{}; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushRaw(out); +} + +void IGeneralService::IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + if (Network::GetHostIPv4Address().has_value()) { + rb.Push<u8>(1); + } else { + rb.Push<u8>(0); + } +} + +void IGeneralService::IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { + LOG_ERROR(Service_NIFM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + if (Network::GetHostIPv4Address().has_value()) { + rb.Push<u8>(1); + } else { + rb.Push<u8>(0); + } +} IGeneralService::IGeneralService(Core::System& system_) - : ServiceFramework{system_, "IGeneralService"} { + : ServiceFramework{system_, "IGeneralService"}, network{system_.GetRoomNetwork()} { // clang-format off static const FunctionInfo functions[] = { {1, &IGeneralService::GetClientId, "GetClientId"}, @@ -458,7 +510,7 @@ IGeneralService::IGeneralService(Core::System& system_) {15, &IGeneralService::GetCurrentIpConfigInfo, "GetCurrentIpConfigInfo"}, {16, nullptr, "SetWirelessCommunicationEnabled"}, {17, &IGeneralService::IsWirelessCommunicationEnabled, "IsWirelessCommunicationEnabled"}, - {18, nullptr, "GetInternetConnectionStatus"}, + {18, &IGeneralService::GetInternetConnectionStatus, "GetInternetConnectionStatus"}, {19, nullptr, "SetEthernetCommunicationEnabled"}, {20, &IGeneralService::IsEthernetCommunicationEnabled, "IsEthernetCommunicationEnabled"}, {21, &IGeneralService::IsAnyInternetRequestAccepted, "IsAnyInternetRequestAccepted"}, @@ -490,6 +542,8 @@ IGeneralService::IGeneralService(Core::System& system_) RegisterHandlers(functions); } +IGeneralService::~IGeneralService() = default; + class NetworkInterface final : public ServiceFramework<NetworkInterface> { public: explicit NetworkInterface(const char* name, Core::System& system_) diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h index c3dd4f386..48161be28 100644 --- a/src/core/hle/service/nifm/nifm.h +++ b/src/core/hle/service/nifm/nifm.h @@ -1,9 +1,13 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include "core/hle/service/service.h" +#include "network/network.h" +#include "network/room.h" +#include "network/room_member.h" + namespace Core { class System; } @@ -17,4 +21,26 @@ namespace Service::NIFM { /// Registers all NIFM services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); +class IGeneralService final : public ServiceFramework<IGeneralService> { +public: + explicit IGeneralService(Core::System& system_); + ~IGeneralService() override; + +private: + void GetClientId(Kernel::HLERequestContext& ctx); + void CreateScanRequest(Kernel::HLERequestContext& ctx); + void CreateRequest(Kernel::HLERequestContext& ctx); + void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx); + void RemoveNetworkProfile(Kernel::HLERequestContext& ctx); + void GetCurrentIpAddress(Kernel::HLERequestContext& ctx); + void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx); + void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx); + void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx); + void GetInternetConnectionStatus(Kernel::HLERequestContext& ctx); + void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx); + void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx); + + Network::RoomNetwork& network; +}; + } // namespace Service::NIFM diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index 4fc23a958..b2bb7426d 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <chrono> #include <ctime> diff --git a/src/core/hle/service/nim/nim.h b/src/core/hle/service/nim/nim.h index 571153fe6..8f6ff28e8 100644 --- a/src/core/hle/service/nim/nim.h +++ b/src/core/hle/service/nim/nim.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/npns/npns.cpp b/src/core/hle/service/npns/npns.cpp index 32533cd94..8133711c2 100644 --- a/src/core/hle/service/npns/npns.cpp +++ b/src/core/hle/service/npns/npns.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/npns/npns.h b/src/core/hle/service/npns/npns.h index 3b7596b6b..84e6ec437 100644 --- a/src/core/hle/service/npns/npns.h +++ b/src/core/hle/service/npns/npns.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/ns/errors.h b/src/core/hle/service/ns/errors.h index f4aea8a65..8a7621798 100644 --- a/src/core/hle/service/ns/errors.h +++ b/src/core/hle/service/ns/errors.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,5 +7,5 @@ namespace Service::NS { -constexpr ResultCode ERR_APPLICATION_LANGUAGE_NOT_FOUND{ErrorModule::NS, 300}; +constexpr Result ERR_APPLICATION_LANGUAGE_NOT_FOUND{ErrorModule::NS, 300}; }
\ No newline at end of file diff --git a/src/core/hle/service/ns/iplatform_service_manager.cpp b/src/core/hle/service/ns/iplatform_service_manager.cpp new file mode 100644 index 000000000..fd047ff26 --- /dev/null +++ b/src/core/hle/service/ns/iplatform_service_manager.cpp @@ -0,0 +1,299 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <algorithm> +#include <cstring> +#include <vector> + +#include "common/assert.h" +#include "common/common_types.h" +#include "common/logging/log.h" +#include "common/swap.h" +#include "core/core.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/registered_cache.h" +#include "core/file_sys/romfs.h" +#include "core/file_sys/system_archive/system_archive.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/physical_memory.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/ns/iplatform_service_manager.h" + +namespace Service::NS { + +struct FontRegion { + u32 offset; + u32 size; +}; + +// The below data is specific to shared font data dumped from Switch on f/w 2.2 +// Virtual address and offsets/sizes likely will vary by dump +[[maybe_unused]] constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL}; +constexpr u32 EXPECTED_RESULT{0x7f9a0218}; // What we expect the decrypted bfttf first 4 bytes to be +constexpr u32 EXPECTED_MAGIC{0x36f81a1e}; // What we expect the encrypted bfttf first 4 bytes to be +constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000}; +constexpr FontRegion EMPTY_REGION{0, 0}; + +enum class LoadState : u32 { + Loading = 0, + Done = 1, +}; + +static void DecryptSharedFont(const std::vector<u32>& input, Kernel::PhysicalMemory& output, + std::size_t& offset) { + ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE, + "Shared fonts exceeds 17mb!"); + ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number"); + + const u32 KEY = input[0] ^ EXPECTED_RESULT; // Derive key using an inverse xor + std::vector<u32> transformed_font(input.size()); + // TODO(ogniK): Figure out a better way to do this + std::transform(input.begin(), input.end(), transformed_font.begin(), + [&KEY](u32 font_data) { return Common::swap32(font_data ^ KEY); }); + transformed_font[1] = Common::swap32(transformed_font[1]) ^ KEY; // "re-encrypt" the size + std::memcpy(output.data() + offset, transformed_font.data(), + transformed_font.size() * sizeof(u32)); + offset += transformed_font.size() * sizeof(u32); +} + +void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output) { + ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number"); + + if (input.size() < 2) { + LOG_ERROR(Service_NS, "Input font is empty"); + return; + } + + const u32 KEY = input[0] ^ EXPECTED_RESULT; // Derive key using an inverse xor + std::vector<u32> transformed_font(input.size()); + // TODO(ogniK): Figure out a better way to do this + std::transform(input.begin(), input.end(), transformed_font.begin(), + [&KEY](u32 font_data) { return Common::swap32(font_data ^ KEY); }); + std::memcpy(output.data(), transformed_font.data() + 2, + (transformed_font.size() - 2) * sizeof(u32)); +} + +void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, + std::size_t& offset) { + ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE, + "Shared fonts exceeds 17mb!"); + + const auto key = Common::swap32(EXPECTED_RESULT ^ EXPECTED_MAGIC); + std::vector<u32> transformed_font(input.size() + 2); + transformed_font[0] = Common::swap32(EXPECTED_MAGIC); + transformed_font[1] = Common::swap32(static_cast<u32>(input.size() * sizeof(u32))) ^ key; + std::transform(input.begin(), input.end(), transformed_font.begin() + 2, + [key](u32 in) { return in ^ key; }); + std::memcpy(output.data() + offset, transformed_font.data(), + transformed_font.size() * sizeof(u32)); + offset += transformed_font.size() * sizeof(u32); +} + +// Helper function to make BuildSharedFontsRawRegions a bit nicer +static u32 GetU32Swapped(const u8* data) { + u32 value; + std::memcpy(&value, data, sizeof(value)); + return Common::swap32(value); +} + +struct IPlatformServiceManager::Impl { + const FontRegion& GetSharedFontRegion(std::size_t index) const { + if (index >= shared_font_regions.size() || shared_font_regions.empty()) { + // No font fallback + return EMPTY_REGION; + } + return shared_font_regions.at(index); + } + + void BuildSharedFontsRawRegions(const Kernel::PhysicalMemory& input) { + // As we can derive the xor key we can just populate the offsets + // based on the shared memory dump + unsigned cur_offset = 0; + + for (std::size_t i = 0; i < SHARED_FONTS.size(); i++) { + // Out of shared fonts/invalid font + if (GetU32Swapped(input.data() + cur_offset) != EXPECTED_RESULT) { + break; + } + + // Derive key withing inverse xor + const u32 KEY = GetU32Swapped(input.data() + cur_offset) ^ EXPECTED_MAGIC; + const u32 SIZE = GetU32Swapped(input.data() + cur_offset + 4) ^ KEY; + shared_font_regions.push_back(FontRegion{cur_offset + 8, SIZE}); + cur_offset += SIZE + 8; + } + } + + /// Backing memory for the shared font data + std::shared_ptr<Kernel::PhysicalMemory> shared_font; + + // Automatically populated based on shared_fonts dump or system archives. + std::vector<FontRegion> shared_font_regions; +}; + +IPlatformServiceManager::IPlatformServiceManager(Core::System& system_, const char* service_name_) + : ServiceFramework{system_, service_name_}, impl{std::make_unique<Impl>()} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IPlatformServiceManager::RequestLoad, "RequestLoad"}, + {1, &IPlatformServiceManager::GetLoadState, "GetLoadState"}, + {2, &IPlatformServiceManager::GetSize, "GetSize"}, + {3, &IPlatformServiceManager::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"}, + {4, &IPlatformServiceManager::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, + {5, &IPlatformServiceManager::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"}, + {6, nullptr, "GetSharedFontInOrderOfPriorityForSystem"}, + {100, nullptr, "RequestApplicationFunctionAuthorization"}, + {101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"}, + {102, nullptr, "RequestApplicationFunctionAuthorizationByApplicationId"}, + {103, nullptr, "RefreshApplicationFunctionBlackListDebugRecord"}, + {104, nullptr, "RequestApplicationFunctionAuthorizationByProgramId"}, + {105, nullptr, "GetFunctionBlackListSystemVersionToAuthorize"}, + {106, nullptr, "GetFunctionBlackListVersion"}, + {1000, nullptr, "LoadNgWordDataForPlatformRegionChina"}, + {1001, nullptr, "GetNgWordDataSizeForPlatformRegionChina"}, + }; + // clang-format on + RegisterHandlers(functions); + + auto& fsc = system.GetFileSystemController(); + + // Attempt to load shared font data from disk + const auto* nand = fsc.GetSystemNANDContents(); + std::size_t offset = 0; + // Rebuild shared fonts from data ncas or synthesize + + impl->shared_font = std::make_shared<Kernel::PhysicalMemory>(SHARED_FONT_MEM_SIZE); + for (auto font : SHARED_FONTS) { + FileSys::VirtualFile romfs; + const auto nca = + nand->GetEntry(static_cast<u64>(font.first), FileSys::ContentRecordType::Data); + if (nca) { + romfs = nca->GetRomFS(); + } + + if (!romfs) { + romfs = FileSys::SystemArchive::SynthesizeSystemArchive(static_cast<u64>(font.first)); + } + + if (!romfs) { + LOG_ERROR(Service_NS, "Failed to find or synthesize {:016X}! Skipping", font.first); + continue; + } + + const auto extracted_romfs = FileSys::ExtractRomFS(romfs); + if (!extracted_romfs) { + LOG_ERROR(Service_NS, "Failed to extract RomFS for {:016X}! Skipping", font.first); + continue; + } + const auto font_fp = extracted_romfs->GetFile(font.second); + if (!font_fp) { + LOG_ERROR(Service_NS, "{:016X} has no file \"{}\"! Skipping", font.first, font.second); + continue; + } + std::vector<u32> font_data_u32(font_fp->GetSize() / sizeof(u32)); + font_fp->ReadBytes<u32>(font_data_u32.data(), font_fp->GetSize()); + // We need to be BigEndian as u32s for the xor encryption + std::transform(font_data_u32.begin(), font_data_u32.end(), font_data_u32.begin(), + Common::swap32); + // Font offset and size do not account for the header + const FontRegion region{static_cast<u32>(offset + 8), + static_cast<u32>((font_data_u32.size() * sizeof(u32)) - 8)}; + DecryptSharedFont(font_data_u32, *impl->shared_font, offset); + impl->shared_font_regions.push_back(region); + } +} + +IPlatformServiceManager::~IPlatformServiceManager() = default; + +void IPlatformServiceManager::RequestLoad(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u32 shared_font_type{rp.Pop<u32>()}; + // Games don't call this so all fonts should be loaded + LOG_DEBUG(Service_NS, "called, shared_font_type={}", shared_font_type); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IPlatformServiceManager::GetLoadState(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u32 font_id{rp.Pop<u32>()}; + LOG_DEBUG(Service_NS, "called, font_id={}", font_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u32>(static_cast<u32>(LoadState::Done)); +} + +void IPlatformServiceManager::GetSize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u32 font_id{rp.Pop<u32>()}; + LOG_DEBUG(Service_NS, "called, font_id={}", font_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u32>(impl->GetSharedFontRegion(font_id).size); +} + +void IPlatformServiceManager::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u32 font_id{rp.Pop<u32>()}; + LOG_DEBUG(Service_NS, "called, font_id={}", font_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u32>(impl->GetSharedFontRegion(font_id).offset); +} + +void IPlatformServiceManager::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { + // Map backing memory for the font data + LOG_DEBUG(Service_NS, "called"); + + // Create shared font memory object + std::memcpy(kernel.GetFontSharedMem().GetPointer(), impl->shared_font->data(), + impl->shared_font->size()); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(&kernel.GetFontSharedMem()); +} + +void IPlatformServiceManager::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 language_code{rp.Pop<u64>()}; // TODO(ogniK): Find out what this is used for + LOG_DEBUG(Service_NS, "called, language_code={:X}", language_code); + + IPC::ResponseBuilder rb{ctx, 4}; + std::vector<u32> font_codes; + std::vector<u32> font_offsets; + std::vector<u32> font_sizes; + + // TODO(ogniK): Have actual priority order + for (std::size_t i = 0; i < impl->shared_font_regions.size(); i++) { + font_codes.push_back(static_cast<u32>(i)); + auto region = impl->GetSharedFontRegion(i); + font_offsets.push_back(region.offset); + font_sizes.push_back(region.size); + } + + // Resize buffers if game requests smaller size output. + font_codes.resize( + std::min<std::size_t>(font_codes.size(), ctx.GetWriteBufferSize(0) / sizeof(u32))); + font_offsets.resize( + std::min<std::size_t>(font_offsets.size(), ctx.GetWriteBufferSize(1) / sizeof(u32))); + font_sizes.resize( + std::min<std::size_t>(font_sizes.size(), ctx.GetWriteBufferSize(2) / sizeof(u32))); + + ctx.WriteBuffer(font_codes, 0); + ctx.WriteBuffer(font_offsets, 1); + ctx.WriteBuffer(font_sizes, 2); + + rb.Push(ResultSuccess); + rb.Push<u8>(static_cast<u8>(LoadState::Done)); // Fonts Loaded + rb.Push<u32>(static_cast<u32>(font_codes.size())); +} + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/iplatform_service_manager.h b/src/core/hle/service/ns/iplatform_service_manager.h new file mode 100644 index 000000000..ed6eda89f --- /dev/null +++ b/src/core/hle/service/ns/iplatform_service_manager.h @@ -0,0 +1,58 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> +#include <vector> +#include "core/hle/service/service.h" + +namespace Service { + +namespace FileSystem { +class FileSystemController; +} // namespace FileSystem + +namespace NS { + +enum class FontArchives : u64 { + Extension = 0x0100000000000810, + Standard = 0x0100000000000811, + Korean = 0x0100000000000812, + ChineseTraditional = 0x0100000000000813, + ChineseSimple = 0x0100000000000814, +}; + +constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{ + std::make_pair(FontArchives::Standard, "nintendo_udsg-r_std_003.bfttf"), + std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_org_zh-cn_003.bfttf"), + std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_ext_zh-cn_003.bfttf"), + std::make_pair(FontArchives::ChineseTraditional, "nintendo_udjxh-db_zh-tw_003.bfttf"), + std::make_pair(FontArchives::Korean, "nintendo_udsg-r_ko_003.bfttf"), + std::make_pair(FontArchives::Extension, "nintendo_ext_003.bfttf"), + std::make_pair(FontArchives::Extension, "nintendo_ext2_003.bfttf"), +}; + +void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output); +void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, std::size_t& offset); + +class IPlatformServiceManager final : public ServiceFramework<IPlatformServiceManager> { +public: + explicit IPlatformServiceManager(Core::System& system_, const char* service_name_); + ~IPlatformServiceManager() override; + +private: + void RequestLoad(Kernel::HLERequestContext& ctx); + void GetLoadState(Kernel::HLERequestContext& ctx); + void GetSize(Kernel::HLERequestContext& ctx); + void GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx); + void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx); + void GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx); + + struct Impl; + std::unique_ptr<Impl> impl; +}; + +} // namespace NS + +} // namespace Service diff --git a/src/core/hle/service/ns/language.cpp b/src/core/hle/service/ns/language.cpp index e01c6be47..036a1e9b7 100644 --- a/src/core/hle/service/ns/language.cpp +++ b/src/core/hle/service/ns/language.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/ns/language.h" #include "core/hle/service/set/set.h" diff --git a/src/core/hle/service/ns/language.h b/src/core/hle/service/ns/language.h index 2cc8e4806..ab6b71029 100644 --- a/src/core/hle/service/ns/language.h +++ b/src/core/hle/service/ns/language.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 5eaad0474..f7318c3cb 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "common/settings.h" @@ -10,10 +9,10 @@ #include "core/file_sys/vfs.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/ns/errors.h" +#include "core/hle/service/ns/iplatform_service_manager.h" #include "core/hle/service/ns/language.h" #include "core/hle/service/ns/ns.h" #include "core/hle/service/ns/pdm_qry.h" -#include "core/hle/service/ns/pl_u.h" #include "core/hle/service/set/set.h" namespace Service::NS { @@ -765,7 +764,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system std::make_shared<PDM_QRY>(system)->InstallAsService(service_manager); - std::make_shared<PL_U>(system)->InstallAsService(service_manager); + std::make_shared<IPlatformServiceManager>(system, "pl:s")->InstallAsService(service_manager); + std::make_shared<IPlatformServiceManager>(system, "pl:u")->InstallAsService(service_manager); } } // namespace Service::NS diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h index 43540b0fb..4dc191518 100644 --- a/src/core/hle/service/ns/ns.h +++ b/src/core/hle/service/ns/ns.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/ns/pdm_qry.cpp b/src/core/hle/service/ns/pdm_qry.cpp index 36ce46353..aac8f573f 100644 --- a/src/core/hle/service/ns/pdm_qry.cpp +++ b/src/core/hle/service/ns/pdm_qry.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> @@ -9,7 +8,6 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/service/ns/pdm_qry.h" #include "core/hle/service/service.h" -#include "core/hle/service/sm/sm.h" namespace Service::NS { diff --git a/src/core/hle/service/ns/pdm_qry.h b/src/core/hle/service/ns/pdm_qry.h index 516136314..abcc3bef3 100644 --- a/src/core/hle/service/ns/pdm_qry.h +++ b/src/core/hle/service/ns/pdm_qry.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp deleted file mode 100644 index 74cc45f1e..000000000 --- a/src/core/hle/service/ns/pl_u.cpp +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> -#include <cstring> -#include <vector> - -#include "common/assert.h" -#include "common/common_types.h" -#include "common/logging/log.h" -#include "common/swap.h" -#include "core/core.h" -#include "core/file_sys/content_archive.h" -#include "core/file_sys/nca_metadata.h" -#include "core/file_sys/registered_cache.h" -#include "core/file_sys/romfs.h" -#include "core/file_sys/system_archive/system_archive.h" -#include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/k_shared_memory.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/physical_memory.h" -#include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/ns/pl_u.h" - -namespace Service::NS { - -struct FontRegion { - u32 offset; - u32 size; -}; - -// The below data is specific to shared font data dumped from Switch on f/w 2.2 -// Virtual address and offsets/sizes likely will vary by dump -[[maybe_unused]] constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL}; -constexpr u32 EXPECTED_RESULT{0x7f9a0218}; // What we expect the decrypted bfttf first 4 bytes to be -constexpr u32 EXPECTED_MAGIC{0x36f81a1e}; // What we expect the encrypted bfttf first 4 bytes to be -constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000}; -constexpr FontRegion EMPTY_REGION{0, 0}; - -enum class LoadState : u32 { - Loading = 0, - Done = 1, -}; - -static void DecryptSharedFont(const std::vector<u32>& input, Kernel::PhysicalMemory& output, - std::size_t& offset) { - ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE, - "Shared fonts exceeds 17mb!"); - ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number"); - - const u32 KEY = input[0] ^ EXPECTED_RESULT; // Derive key using an inverse xor - std::vector<u32> transformed_font(input.size()); - // TODO(ogniK): Figure out a better way to do this - std::transform(input.begin(), input.end(), transformed_font.begin(), - [&KEY](u32 font_data) { return Common::swap32(font_data ^ KEY); }); - transformed_font[1] = Common::swap32(transformed_font[1]) ^ KEY; // "re-encrypt" the size - std::memcpy(output.data() + offset, transformed_font.data(), - transformed_font.size() * sizeof(u32)); - offset += transformed_font.size() * sizeof(u32); -} - -void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output) { - ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number"); - - if (input.size() < 2) { - LOG_ERROR(Service_NS, "Input font is empty"); - return; - } - - const u32 KEY = input[0] ^ EXPECTED_RESULT; // Derive key using an inverse xor - std::vector<u32> transformed_font(input.size()); - // TODO(ogniK): Figure out a better way to do this - std::transform(input.begin(), input.end(), transformed_font.begin(), - [&KEY](u32 font_data) { return Common::swap32(font_data ^ KEY); }); - std::memcpy(output.data(), transformed_font.data() + 2, - (transformed_font.size() - 2) * sizeof(u32)); -} - -void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, - std::size_t& offset) { - ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE, - "Shared fonts exceeds 17mb!"); - - const auto key = Common::swap32(EXPECTED_RESULT ^ EXPECTED_MAGIC); - std::vector<u32> transformed_font(input.size() + 2); - transformed_font[0] = Common::swap32(EXPECTED_MAGIC); - transformed_font[1] = Common::swap32(static_cast<u32>(input.size() * sizeof(u32))) ^ key; - std::transform(input.begin(), input.end(), transformed_font.begin() + 2, - [key](u32 in) { return in ^ key; }); - std::memcpy(output.data() + offset, transformed_font.data(), - transformed_font.size() * sizeof(u32)); - offset += transformed_font.size() * sizeof(u32); -} - -// Helper function to make BuildSharedFontsRawRegions a bit nicer -static u32 GetU32Swapped(const u8* data) { - u32 value; - std::memcpy(&value, data, sizeof(value)); - return Common::swap32(value); -} - -struct PL_U::Impl { - const FontRegion& GetSharedFontRegion(std::size_t index) const { - if (index >= shared_font_regions.size() || shared_font_regions.empty()) { - // No font fallback - return EMPTY_REGION; - } - return shared_font_regions.at(index); - } - - void BuildSharedFontsRawRegions(const Kernel::PhysicalMemory& input) { - // As we can derive the xor key we can just populate the offsets - // based on the shared memory dump - unsigned cur_offset = 0; - - for (std::size_t i = 0; i < SHARED_FONTS.size(); i++) { - // Out of shared fonts/invalid font - if (GetU32Swapped(input.data() + cur_offset) != EXPECTED_RESULT) { - break; - } - - // Derive key withing inverse xor - const u32 KEY = GetU32Swapped(input.data() + cur_offset) ^ EXPECTED_MAGIC; - const u32 SIZE = GetU32Swapped(input.data() + cur_offset + 4) ^ KEY; - shared_font_regions.push_back(FontRegion{cur_offset + 8, SIZE}); - cur_offset += SIZE + 8; - } - } - - /// Backing memory for the shared font data - std::shared_ptr<Kernel::PhysicalMemory> shared_font; - - // Automatically populated based on shared_fonts dump or system archives. - std::vector<FontRegion> shared_font_regions; -}; - -PL_U::PL_U(Core::System& system_) - : ServiceFramework{system_, "pl:u"}, impl{std::make_unique<Impl>()} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &PL_U::RequestLoad, "RequestLoad"}, - {1, &PL_U::GetLoadState, "GetLoadState"}, - {2, &PL_U::GetSize, "GetSize"}, - {3, &PL_U::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"}, - {4, &PL_U::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, - {5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"}, - {6, nullptr, "GetSharedFontInOrderOfPriorityForSystem"}, - {100, nullptr, "RequestApplicationFunctionAuthorization"}, - {101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"}, - {102, nullptr, "RequestApplicationFunctionAuthorizationByApplicationId"}, - {103, nullptr, "RefreshApplicationFunctionBlackListDebugRecord"}, - {104, nullptr, "RequestApplicationFunctionAuthorizationByProgramId"}, - {105, nullptr, "GetFunctionBlackListSystemVersionToAuthorize"}, - {106, nullptr, "GetFunctionBlackListVersion"}, - {1000, nullptr, "LoadNgWordDataForPlatformRegionChina"}, - {1001, nullptr, "GetNgWordDataSizeForPlatformRegionChina"}, - }; - // clang-format on - RegisterHandlers(functions); - - auto& fsc = system.GetFileSystemController(); - - // Attempt to load shared font data from disk - const auto* nand = fsc.GetSystemNANDContents(); - std::size_t offset = 0; - // Rebuild shared fonts from data ncas or synthesize - - impl->shared_font = std::make_shared<Kernel::PhysicalMemory>(SHARED_FONT_MEM_SIZE); - for (auto font : SHARED_FONTS) { - FileSys::VirtualFile romfs; - const auto nca = - nand->GetEntry(static_cast<u64>(font.first), FileSys::ContentRecordType::Data); - if (nca) { - romfs = nca->GetRomFS(); - } - - if (!romfs) { - romfs = FileSys::SystemArchive::SynthesizeSystemArchive(static_cast<u64>(font.first)); - } - - if (!romfs) { - LOG_ERROR(Service_NS, "Failed to find or synthesize {:016X}! Skipping", font.first); - continue; - } - - const auto extracted_romfs = FileSys::ExtractRomFS(romfs); - if (!extracted_romfs) { - LOG_ERROR(Service_NS, "Failed to extract RomFS for {:016X}! Skipping", font.first); - continue; - } - const auto font_fp = extracted_romfs->GetFile(font.second); - if (!font_fp) { - LOG_ERROR(Service_NS, "{:016X} has no file \"{}\"! Skipping", font.first, font.second); - continue; - } - std::vector<u32> font_data_u32(font_fp->GetSize() / sizeof(u32)); - font_fp->ReadBytes<u32>(font_data_u32.data(), font_fp->GetSize()); - // We need to be BigEndian as u32s for the xor encryption - std::transform(font_data_u32.begin(), font_data_u32.end(), font_data_u32.begin(), - Common::swap32); - // Font offset and size do not account for the header - const FontRegion region{static_cast<u32>(offset + 8), - static_cast<u32>((font_data_u32.size() * sizeof(u32)) - 8)}; - DecryptSharedFont(font_data_u32, *impl->shared_font, offset); - impl->shared_font_regions.push_back(region); - } -} - -PL_U::~PL_U() = default; - -void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u32 shared_font_type{rp.Pop<u32>()}; - // Games don't call this so all fonts should be loaded - LOG_DEBUG(Service_NS, "called, shared_font_type={}", shared_font_type); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u32 font_id{rp.Pop<u32>()}; - LOG_DEBUG(Service_NS, "called, font_id={}", font_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(static_cast<u32>(LoadState::Done)); -} - -void PL_U::GetSize(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u32 font_id{rp.Pop<u32>()}; - LOG_DEBUG(Service_NS, "called, font_id={}", font_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(impl->GetSharedFontRegion(font_id).size); -} - -void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u32 font_id{rp.Pop<u32>()}; - LOG_DEBUG(Service_NS, "called, font_id={}", font_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(impl->GetSharedFontRegion(font_id).offset); -} - -void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { - // Map backing memory for the font data - LOG_DEBUG(Service_NS, "called"); - - // Create shared font memory object - std::memcpy(kernel.GetFontSharedMem().GetPointer(), impl->shared_font->data(), - impl->shared_font->size()); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(&kernel.GetFontSharedMem()); -} - -void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 language_code{rp.Pop<u64>()}; // TODO(ogniK): Find out what this is used for - LOG_DEBUG(Service_NS, "called, language_code={:X}", language_code); - - IPC::ResponseBuilder rb{ctx, 4}; - std::vector<u32> font_codes; - std::vector<u32> font_offsets; - std::vector<u32> font_sizes; - - // TODO(ogniK): Have actual priority order - for (std::size_t i = 0; i < impl->shared_font_regions.size(); i++) { - font_codes.push_back(static_cast<u32>(i)); - auto region = impl->GetSharedFontRegion(i); - font_offsets.push_back(region.offset); - font_sizes.push_back(region.size); - } - - // Resize buffers if game requests smaller size output. - font_codes.resize( - std::min<std::size_t>(font_codes.size(), ctx.GetWriteBufferSize(0) / sizeof(u32))); - font_offsets.resize( - std::min<std::size_t>(font_offsets.size(), ctx.GetWriteBufferSize(1) / sizeof(u32))); - font_sizes.resize( - std::min<std::size_t>(font_sizes.size(), ctx.GetWriteBufferSize(2) / sizeof(u32))); - - ctx.WriteBuffer(font_codes, 0); - ctx.WriteBuffer(font_offsets, 1); - ctx.WriteBuffer(font_sizes, 2); - - rb.Push(ResultSuccess); - rb.Push<u8>(static_cast<u8>(LoadState::Done)); // Fonts Loaded - rb.Push<u32>(static_cast<u32>(font_codes.size())); -} - -} // namespace Service::NS diff --git a/src/core/hle/service/ns/pl_u.h b/src/core/hle/service/ns/pl_u.h deleted file mode 100644 index f920c7f69..000000000 --- a/src/core/hle/service/ns/pl_u.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <memory> -#include <vector> -#include "core/hle/service/service.h" - -namespace Service { - -namespace FileSystem { -class FileSystemController; -} // namespace FileSystem - -namespace NS { - -enum class FontArchives : u64 { - Extension = 0x0100000000000810, - Standard = 0x0100000000000811, - Korean = 0x0100000000000812, - ChineseTraditional = 0x0100000000000813, - ChineseSimple = 0x0100000000000814, -}; - -constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{ - std::make_pair(FontArchives::Standard, "nintendo_udsg-r_std_003.bfttf"), - std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_org_zh-cn_003.bfttf"), - std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_ext_zh-cn_003.bfttf"), - std::make_pair(FontArchives::ChineseTraditional, "nintendo_udjxh-db_zh-tw_003.bfttf"), - std::make_pair(FontArchives::Korean, "nintendo_udsg-r_ko_003.bfttf"), - std::make_pair(FontArchives::Extension, "nintendo_ext_003.bfttf"), - std::make_pair(FontArchives::Extension, "nintendo_ext2_003.bfttf"), -}; - -void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output); -void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, std::size_t& offset); - -class PL_U final : public ServiceFramework<PL_U> { -public: - explicit PL_U(Core::System& system_); - ~PL_U() override; - -private: - void RequestLoad(Kernel::HLERequestContext& ctx); - void GetLoadState(Kernel::HLERequestContext& ctx); - void GetSize(Kernel::HLERequestContext& ctx); - void GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx); - void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx); - void GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx); - - struct Impl; - std::unique_ptr<Impl> impl; -}; - -} // namespace NS - -} // namespace Service diff --git a/src/core/hle/service/nvdrv/core/container.cpp b/src/core/hle/service/nvdrv/core/container.cpp new file mode 100644 index 000000000..37ca24f5d --- /dev/null +++ b/src/core/hle/service/nvdrv/core/container.cpp @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" +#include "video_core/host1x/host1x.h" + +namespace Service::Nvidia::NvCore { + +struct ContainerImpl { + explicit ContainerImpl(Tegra::Host1x::Host1x& host1x_) + : file{host1x_}, manager{host1x_}, device_file_data{} {} + NvMap file; + SyncpointManager manager; + Container::Host1xDeviceFileData device_file_data; +}; + +Container::Container(Tegra::Host1x::Host1x& host1x_) { + impl = std::make_unique<ContainerImpl>(host1x_); +} + +Container::~Container() = default; + +NvMap& Container::GetNvMapFile() { + return impl->file; +} + +const NvMap& Container::GetNvMapFile() const { + return impl->file; +} + +Container::Host1xDeviceFileData& Container::Host1xDeviceFile() { + return impl->device_file_data; +} + +const Container::Host1xDeviceFileData& Container::Host1xDeviceFile() const { + return impl->device_file_data; +} + +SyncpointManager& Container::GetSyncpointManager() { + return impl->manager; +} + +const SyncpointManager& Container::GetSyncpointManager() const { + return impl->manager; +} + +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/container.h b/src/core/hle/service/nvdrv/core/container.h new file mode 100644 index 000000000..b4b63ac90 --- /dev/null +++ b/src/core/hle/service/nvdrv/core/container.h @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <deque> +#include <memory> +#include <unordered_map> + +#include "core/hle/service/nvdrv/nvdata.h" + +namespace Tegra::Host1x { +class Host1x; +} // namespace Tegra::Host1x + +namespace Service::Nvidia::NvCore { + +class NvMap; +class SyncpointManager; + +struct ContainerImpl; + +class Container { +public: + explicit Container(Tegra::Host1x::Host1x& host1x); + ~Container(); + + NvMap& GetNvMapFile(); + + const NvMap& GetNvMapFile() const; + + SyncpointManager& GetSyncpointManager(); + + const SyncpointManager& GetSyncpointManager() const; + + struct Host1xDeviceFileData { + std::unordered_map<DeviceFD, u32> fd_to_id{}; + std::deque<u32> syncpts_accumulated{}; + u32 nvdec_next_id{}; + u32 vic_next_id{}; + }; + + Host1xDeviceFileData& Host1xDeviceFile(); + + const Host1xDeviceFileData& Host1xDeviceFile() const; + +private: + std::unique_ptr<ContainerImpl> impl; +}; + +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp new file mode 100644 index 000000000..fbd8a74a5 --- /dev/null +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp @@ -0,0 +1,272 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/hle/service/nvdrv/core/nvmap.h" +#include "core/memory.h" +#include "video_core/host1x/host1x.h" + +using Core::Memory::YUZU_PAGESIZE; + +namespace Service::Nvidia::NvCore { +NvMap::Handle::Handle(u64 size_, Id id_) + : size(size_), aligned_size(size), orig_size(size), id(id_) { + flags.raw = 0; +} + +NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress) { + std::scoped_lock lock(mutex); + + // Handles cannot be allocated twice + if (allocated) { + return NvResult::AccessDenied; + } + + flags = pFlags; + kind = pKind; + align = pAlign < YUZU_PAGESIZE ? YUZU_PAGESIZE : pAlign; + + // This flag is only applicable for handles with an address passed + if (pAddress) { + flags.keep_uncached_after_free.Assign(0); + } else { + LOG_CRITICAL(Service_NVDRV, + "Mapping nvmap handles without a CPU side address is unimplemented!"); + } + + size = Common::AlignUp(size, YUZU_PAGESIZE); + aligned_size = Common::AlignUp(size, align); + address = pAddress; + allocated = true; + + return NvResult::Success; +} + +NvResult NvMap::Handle::Duplicate(bool internal_session) { + std::scoped_lock lock(mutex); + // Unallocated handles cannot be duplicated as duplication requires memory accounting (in HOS) + if (!allocated) [[unlikely]] { + return NvResult::BadValue; + } + + // If we internally use FromId the duplication tracking of handles won't work accurately due to + // us not implementing per-process handle refs. + if (internal_session) { + internal_dupes++; + } else { + dupes++; + } + + return NvResult::Success; +} + +NvMap::NvMap(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} {} + +void NvMap::AddHandle(std::shared_ptr<Handle> handle_description) { + std::scoped_lock lock(handles_lock); + + handles.emplace(handle_description->id, std::move(handle_description)); +} + +void NvMap::UnmapHandle(Handle& handle_description) { + // Remove pending unmap queue entry if needed + if (handle_description.unmap_queue_entry) { + unmap_queue.erase(*handle_description.unmap_queue_entry); + handle_description.unmap_queue_entry.reset(); + } + + // Free and unmap the handle from the SMMU + host1x.MemoryManager().Unmap(static_cast<GPUVAddr>(handle_description.pin_virt_address), + handle_description.aligned_size); + host1x.Allocator().Free(handle_description.pin_virt_address, + static_cast<u32>(handle_description.aligned_size)); + handle_description.pin_virt_address = 0; +} + +bool NvMap::TryRemoveHandle(const Handle& handle_description) { + // No dupes left, we can remove from handle map + if (handle_description.dupes == 0 && handle_description.internal_dupes == 0) { + std::scoped_lock lock(handles_lock); + + auto it{handles.find(handle_description.id)}; + if (it != handles.end()) { + handles.erase(it); + } + + return true; + } else { + return false; + } +} + +NvResult NvMap::CreateHandle(u64 size, std::shared_ptr<NvMap::Handle>& result_out) { + if (!size) [[unlikely]] { + return NvResult::BadValue; + } + + u32 id{next_handle_id.fetch_add(HandleIdIncrement, std::memory_order_relaxed)}; + auto handle_description{std::make_shared<Handle>(size, id)}; + AddHandle(handle_description); + + result_out = handle_description; + return NvResult::Success; +} + +std::shared_ptr<NvMap::Handle> NvMap::GetHandle(Handle::Id handle) { + std::scoped_lock lock(handles_lock); + try { + return handles.at(handle); + } catch (std::out_of_range&) { + return nullptr; + } +} + +VAddr NvMap::GetHandleAddress(Handle::Id handle) { + std::scoped_lock lock(handles_lock); + try { + return handles.at(handle)->address; + } catch (std::out_of_range&) { + return 0; + } +} + +u32 NvMap::PinHandle(NvMap::Handle::Id handle) { + auto handle_description{GetHandle(handle)}; + if (!handle_description) [[unlikely]] { + return 0; + } + + std::scoped_lock lock(handle_description->mutex); + if (!handle_description->pins) { + // If we're in the unmap queue we can just remove ourselves and return since we're already + // mapped + { + // Lock now to prevent our queue entry from being removed for allocation in-between the + // following check and erase + std::scoped_lock queueLock(unmap_queue_lock); + if (handle_description->unmap_queue_entry) { + unmap_queue.erase(*handle_description->unmap_queue_entry); + handle_description->unmap_queue_entry.reset(); + + handle_description->pins++; + return handle_description->pin_virt_address; + } + } + + // If not then allocate some space and map it + u32 address{}; + auto& smmu_allocator = host1x.Allocator(); + auto& smmu_memory_manager = host1x.MemoryManager(); + while (!(address = + smmu_allocator.Allocate(static_cast<u32>(handle_description->aligned_size)))) { + // Free handles until the allocation succeeds + std::scoped_lock queueLock(unmap_queue_lock); + if (auto freeHandleDesc{unmap_queue.front()}) { + // Handles in the unmap queue are guaranteed not to be pinned so don't bother + // checking if they are before unmapping + std::scoped_lock freeLock(freeHandleDesc->mutex); + if (handle_description->pin_virt_address) + UnmapHandle(*freeHandleDesc); + } else { + LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!"); + } + } + + smmu_memory_manager.Map(static_cast<GPUVAddr>(address), handle_description->address, + handle_description->aligned_size); + handle_description->pin_virt_address = address; + } + + handle_description->pins++; + return handle_description->pin_virt_address; +} + +void NvMap::UnpinHandle(Handle::Id handle) { + auto handle_description{GetHandle(handle)}; + if (!handle_description) { + return; + } + + std::scoped_lock lock(handle_description->mutex); + if (--handle_description->pins < 0) { + LOG_WARNING(Service_NVDRV, "Pin count imbalance detected!"); + } else if (!handle_description->pins) { + 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) { + auto handle_description{GetHandle(handle)}; + if (!handle_description) { + LOG_CRITICAL(Service_NVDRV, "Unregistered handle!"); + return; + } + + auto result = handle_description->Duplicate(internal_session); + if (result != NvResult::Success) { + LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!"); + } +} + +std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool internal_session) { + std::weak_ptr<Handle> hWeak{GetHandle(handle)}; + FreeInfo freeInfo; + + // 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 (--handle_description->internal_dupes < 0) + LOG_WARNING(Service_NVDRV, "Internal duplicate count imbalance detected!"); + } else { + if (--handle_description->dupes < 0) { + LOG_WARNING(Service_NVDRV, "User duplicate count imbalance detected!"); + } else if (handle_description->dupes == 0) { + // Force unmap the handle + if (handle_description->pin_virt_address) { + std::scoped_lock queueLock(unmap_queue_lock); + UnmapHandle(*handle_description); + } + + handle_description->pins = 0; + } + } + + // 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 + if (TryRemoveHandle(*handle_description)) { + LOG_DEBUG(Service_NVDRV, "Removed nvmap handle: {}", handle); + } else { + LOG_DEBUG(Service_NVDRV, + "Tried to free nvmap handle: {} but didn't as it still has duplicates", + handle); + } + + freeInfo = { + .address = handle_description->address, + .size = handle_description->size, + .was_uncached = handle_description->flags.map_uncached.Value() != 0, + }; + } else { + return std::nullopt; + } + + // Handle hasn't been freed from memory, set address to 0 to mark that the handle wasn't freed + if (!hWeak.expired()) { + LOG_DEBUG(Service_NVDRV, "nvmap handle: {} wasn't freed as it is still in use", handle); + freeInfo.address = 0; + } + + return freeInfo; +} + +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h new file mode 100644 index 000000000..b9dd3801f --- /dev/null +++ b/src/core/hle/service/nvdrv/core/nvmap.h @@ -0,0 +1,175 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <atomic> +#include <list> +#include <memory> +#include <mutex> +#include <optional> +#include <unordered_map> +#include <assert.h> + +#include "common/bit_field.h" +#include "common/common_types.h" +#include "core/hle/service/nvdrv/nvdata.h" + +namespace Tegra { + +namespace Host1x { +class Host1x; +} // namespace Host1x + +} // namespace Tegra + +namespace Service::Nvidia::NvCore { +/** + * @brief The nvmap core class holds the global state for nvmap and provides methods to manage + * handles + */ +class NvMap { +public: + /** + * @brief A handle to a contiguous block of memory in an application's address space + */ + struct Handle { + std::mutex mutex; + + 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 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 + + 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 + + using Id = u32; + Id id; //!< A globally unique identifier for this handle + + s32 pins{}; + u32 pin_virt_address{}; + std::optional<typename std::list<std::shared_ptr<Handle>>::iterator> unmap_queue_entry{}; + + union Flags { + u32 raw; + 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<4, 1, u32> _unk0_; //!< Passed to IOVMM for pins + } flags{}; + static_assert(sizeof(Flags) == sizeof(u32)); + + u64 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 + bool allocated{}; //!< If the handle has been allocated with `Alloc` + + u64 dma_map_addr{}; //! remove me after implementing pinning. + + Handle(u64 size, Id id); + + /** + * @brief Sets up the handle with the given memory config, can allocate memory from the tmem + * if a 0 address is passed + */ + [[nodiscard]] NvResult Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress); + + /** + * @brief Increases the dupe counter of the handle for the given session + */ + [[nodiscard]] NvResult Duplicate(bool internal_session); + + /** + * @brief Obtains a pointer to the handle's memory and marks the handle it as having been + * mapped + */ + u8* GetPointer() { + if (!address) { + return nullptr; + } + + is_shared_mem_mapped = true; + return reinterpret_cast<u8*>(address); + } + }; + + /** + * @brief Encapsulates the result of a FreeHandle operation + */ + struct FreeInfo { + u64 address; //!< Address the handle referred to before deletion + u64 size; //!< Page-aligned handle size + bool was_uncached; //!< If the handle was allocated as uncached + }; + + explicit NvMap(Tegra::Host1x::Host1x& host1x); + + /** + * @brief Creates an unallocated handle of the given size + */ + [[nodiscard]] NvResult CreateHandle(u64 size, std::shared_ptr<NvMap::Handle>& result_out); + + std::shared_ptr<Handle> GetHandle(Handle::Id handle); + + VAddr GetHandleAddress(Handle::Id handle); + + /** + * @brief Maps a handle into the SMMU address space + * @note This operation is refcounted, the number of calls to this must eventually match the + * number of calls to `UnpinHandle` + * @return The SMMU virtual address that the handle has been mapped to + */ + u32 PinHandle(Handle::Id handle); + + /** + * @brief When this has been called an equal number of times to `PinHandle` for the supplied + * handle it will be added to a list of handles to be freed when necessary + */ + void UnpinHandle(Handle::Id handle); + + /** + * @brief Tries to duplicate a handle + */ + void DuplicateHandle(Handle::Id handle, bool internal_session = false); + + /** + * @brief Tries to free a handle and remove a single dupe + * @note If a handle has no dupes left and has no other users a FreeInfo struct will be returned + * describing the prior state of the handle + */ + std::optional<FreeInfo> FreeHandle(Handle::Id handle, bool internal_session); + +private: + std::list<std::shared_ptr<Handle>> unmap_queue{}; + std::mutex unmap_queue_lock{}; //!< Protects access to `unmap_queue` + + std::unordered_map<Handle::Id, std::shared_ptr<Handle>> + handles{}; //!< Main owning map of 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 + std::atomic<u32> next_handle_id{HandleIdIncrement}; + Tegra::Host1x::Host1x& host1x; + + void AddHandle(std::shared_ptr<Handle> handle); + + /** + * @brief Unmaps and frees the SMMU memory region a handle is mapped to + * @note Both `unmap_queue_lock` and `handle_description.mutex` MUST be locked when calling this + */ + void UnmapHandle(Handle& handle_description); + + /** + * @brief Removes a handle from the map taking its dupes into account + * @note handle_description.mutex MUST be locked when calling this + * @return If the handle was removed from the map + */ + bool TryRemoveHandle(const Handle& handle_description); +}; +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp new file mode 100644 index 000000000..eda2041a0 --- /dev/null +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp @@ -0,0 +1,121 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/assert.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" +#include "video_core/host1x/host1x.h" + +namespace Service::Nvidia::NvCore { + +SyncpointManager::SyncpointManager(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} { + constexpr u32 VBlank0SyncpointId{26}; + constexpr u32 VBlank1SyncpointId{27}; + + // Reserve both vblank syncpoints as client managed as they use Continuous Mode + // Refer to section 14.3.5.3 of the TRM for more information on Continuous Mode + // https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/8f74a72394efb871cb3f886a3de2998cd7ff2990/drivers/gpu/host1x/drm/dc.c#L660 + ReserveSyncpoint(VBlank0SyncpointId, true); + ReserveSyncpoint(VBlank1SyncpointId, true); + + for (u32 syncpoint_id : channel_syncpoints) { + if (syncpoint_id) { + ReserveSyncpoint(syncpoint_id, false); + } + } +} + +SyncpointManager::~SyncpointManager() = default; + +u32 SyncpointManager::ReserveSyncpoint(u32 id, bool client_managed) { + if (syncpoints.at(id).reserved) { + ASSERT_MSG(false, "Requested syncpoint is in use"); + return 0; + } + + syncpoints.at(id).reserved = true; + syncpoints.at(id).interface_managed = client_managed; + + return id; +} + +u32 SyncpointManager::FindFreeSyncpoint() { + for (u32 i{1}; i < syncpoints.size(); i++) { + if (!syncpoints[i].reserved) { + return i; + } + } + ASSERT_MSG(false, "Failed to find a free syncpoint!"); + return 0; +} + +u32 SyncpointManager::AllocateSyncpoint(bool client_managed) { + std::lock_guard lock(reservation_lock); + return ReserveSyncpoint(FindFreeSyncpoint(), client_managed); +} + +void SyncpointManager::FreeSyncpoint(u32 id) { + std::lock_guard lock(reservation_lock); + ASSERT(syncpoints.at(id).reserved); + syncpoints.at(id).reserved = false; +} + +bool SyncpointManager::IsSyncpointAllocated(u32 id) { + return (id <= SyncpointCount) && syncpoints[id].reserved; +} + +bool SyncpointManager::HasSyncpointExpired(u32 id, u32 threshold) const { + const SyncpointInfo& syncpoint{syncpoints.at(id)}; + + if (!syncpoint.reserved) { + ASSERT(false); + return 0; + } + + // If the interface manages counters then we don't keep track of the maximum value as it handles + // sanity checking the values then + if (syncpoint.interface_managed) { + return static_cast<s32>(syncpoint.counter_min - threshold) >= 0; + } else { + return (syncpoint.counter_max - threshold) >= (syncpoint.counter_min - threshold); + } +} + +u32 SyncpointManager::IncrementSyncpointMaxExt(u32 id, u32 amount) { + if (!syncpoints.at(id).reserved) { + ASSERT(false); + return 0; + } + + return syncpoints.at(id).counter_max += amount; +} + +u32 SyncpointManager::ReadSyncpointMinValue(u32 id) { + if (!syncpoints.at(id).reserved) { + ASSERT(false); + return 0; + } + + return syncpoints.at(id).counter_min; +} + +u32 SyncpointManager::UpdateMin(u32 id) { + if (!syncpoints.at(id).reserved) { + ASSERT(false); + return 0; + } + + syncpoints.at(id).counter_min = host1x.GetSyncpointManager().GetHostSyncpointValue(id); + return syncpoints.at(id).counter_min; +} + +NvFence SyncpointManager::GetSyncpointFence(u32 id) { + if (!syncpoints.at(id).reserved) { + ASSERT(false); + return NvFence{}; + } + + return {.id = static_cast<s32>(id), .value = syncpoints.at(id).counter_max}; +} + +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.h b/src/core/hle/service/nvdrv/core/syncpoint_manager.h new file mode 100644 index 000000000..b76ef9032 --- /dev/null +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.h @@ -0,0 +1,134 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <array> +#include <atomic> +#include <mutex> + +#include "common/common_types.h" +#include "core/hle/service/nvdrv/nvdata.h" + +namespace Tegra::Host1x { +class Host1x; +} // namespace Tegra::Host1x + +namespace Service::Nvidia::NvCore { + +enum class ChannelType : u32 { + MsEnc = 0, + VIC = 1, + GPU = 2, + NvDec = 3, + Display = 4, + NvJpg = 5, + TSec = 6, + Max = 7 +}; + +/** + * @brief SyncpointManager handles allocating and accessing host1x syncpoints, these are cached + * versions of the HW syncpoints which are intermittently synced + * @note Refer to Chapter 14 of the Tegra X1 TRM for an exhaustive overview of them + * @url https://http.download.nvidia.com/tegra-public-appnotes/host1x.html + * @url + * https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/jetson-tx1/drivers/video/tegra/host/nvhost_syncpt.c + */ +class SyncpointManager final { +public: + explicit SyncpointManager(Tegra::Host1x::Host1x& host1x); + ~SyncpointManager(); + + /** + * @brief Checks if the given syncpoint is both allocated and below the number of HW syncpoints + */ + bool IsSyncpointAllocated(u32 id); + + /** + * @brief Finds a free syncpoint and reserves it + * @return The ID of the reserved syncpoint + */ + u32 AllocateSyncpoint(bool client_managed); + + /** + * @url + * https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/8f74a72394efb871cb3f886a3de2998cd7ff2990/drivers/gpu/host1x/syncpt.c#L259 + */ + bool HasSyncpointExpired(u32 id, u32 threshold) const; + + bool IsFenceSignalled(NvFence fence) const { + return HasSyncpointExpired(fence.id, fence.value); + } + + /** + * @brief Atomically increments the maximum value of a syncpoint by the given amount + * @return The new max value of the syncpoint + */ + u32 IncrementSyncpointMaxExt(u32 id, u32 amount); + + /** + * @return The minimum value of the syncpoint + */ + u32 ReadSyncpointMinValue(u32 id); + + /** + * @brief Synchronises the minimum value of the syncpoint to with the GPU + * @return The new minimum value of the syncpoint + */ + u32 UpdateMin(u32 id); + + /** + * @brief Frees the usage of a syncpoint. + */ + void FreeSyncpoint(u32 id); + + /** + * @return A fence that will be signalled once this syncpoint hits its maximum value + */ + NvFence GetSyncpointFence(u32 id); + + static constexpr std::array<u32, static_cast<u32>(ChannelType::Max)> channel_syncpoints{ + 0x0, // `MsEnc` is unimplemented + 0xC, // `VIC` + 0x0, // `GPU` syncpoints are allocated per-channel instead + 0x36, // `NvDec` + 0x0, // `Display` is unimplemented + 0x37, // `NvJpg` + 0x0, // `TSec` is unimplemented + }; //!< Maps each channel ID to a constant syncpoint + +private: + /** + * @note reservation_lock should be locked when calling this + */ + u32 ReserveSyncpoint(u32 id, bool client_managed); + + /** + * @return The ID of the first free syncpoint + */ + u32 FindFreeSyncpoint(); + + struct SyncpointInfo { + std::atomic<u32> counter_min; //!< The least value the syncpoint can be (The value it was + //!< when it was last synchronized with host1x) + std::atomic<u32> counter_max; //!< The maximum value the syncpoint can reach according to + //!< the current usage + bool interface_managed; //!< If the syncpoint is managed by a host1x client interface, a + //!< client interface is a HW block that can handle host1x + //!< transactions on behalf of a host1x client (Which would + //!< otherwise need to be manually synced using PIO which is + //!< synchronous and requires direct cooperation of the CPU) + bool reserved; //!< If the syncpoint is reserved or not, not to be confused with a reserved + //!< value + }; + + constexpr static std::size_t SyncpointCount{192}; + std::array<SyncpointInfo, SyncpointCount> syncpoints{}; + std::mutex reservation_lock; + + Tegra::Host1x::Host1x& host1x; +}; + +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h index 3d874243a..204b0e757 100644 --- a/src/core/hle/service/nvdrv/devices/nvdevice.h +++ b/src/core/hle/service/nvdrv/devices/nvdevice.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -12,6 +11,10 @@ namespace Core { class System; } +namespace Kernel { +class KEvent; +} + namespace Service::Nvidia::Devices { /// Represents an abstract nvidia device node. It is to be subclassed by concrete device nodes to @@ -65,6 +68,10 @@ public: */ virtual void OnClose(DeviceFD fd) = 0; + virtual Kernel::KEvent* QueryEvent(u32 event_id) { + return nullptr; + } + protected: Core::System& system; }; diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 68f1e9060..4122fc98d 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -1,20 +1,20 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" #include "core/core_timing.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" -#include "core/hle/service/nvdrv/devices/nvmap.h" #include "core/perf_stats.h" #include "video_core/gpu.h" namespace Service::Nvidia::Devices { -nvdisp_disp0::nvdisp_disp0(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_) - : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)} {} +nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core) + : nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {} nvdisp_disp0::~nvdisp_disp0() = default; NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, @@ -38,23 +38,27 @@ NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& void nvdisp_disp0::OnOpen(DeviceFD fd) {} void nvdisp_disp0::OnClose(DeviceFD fd) {} -void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, - u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform, - const Common::Rectangle<int>& crop_rect) { - const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle); +void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, + u32 height, u32 stride, android::BufferTransformFlags transform, + const Common::Rectangle<int>& crop_rect, + std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences) { + const VAddr addr = nvmap.GetHandleAddress(buffer_handle); LOG_TRACE(Service, "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", addr, offset, width, height, stride, format); - const auto pixel_format = static_cast<Tegra::FramebufferConfig::PixelFormat>(format); - const auto transform_flags = static_cast<Tegra::FramebufferConfig::TransformFlags>(transform); - const Tegra::FramebufferConfig framebuffer{addr, offset, width, height, - stride, pixel_format, transform_flags, crop_rect}; + const Tegra::FramebufferConfig framebuffer{addr, offset, width, height, + stride, format, transform, crop_rect}; + system.GPU().RequestSwapBuffers(&framebuffer, fences, num_fences); system.GetPerfStats().EndSystemFrame(); - system.GPU().SwapBuffers(&framebuffer); system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); system.GetPerfStats().BeginSystemFrame(); } +Kernel::KEvent* nvdisp_disp0::QueryEvent(u32 event_id) { + LOG_CRITICAL(Service_NVDRV, "Unknown DISP Event {}", event_id); + return nullptr; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index de01e1d5f..04217ab12 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -9,7 +8,13 @@ #include "common/common_types.h" #include "common/math_util.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" -#include "core/hle/service/nvflinger/buffer_queue.h" +#include "core/hle/service/nvflinger/buffer_transform_flags.h" +#include "core/hle/service/nvflinger/pixel_format.h" + +namespace Service::Nvidia::NvCore { +class Container; +class NvMap; +} // namespace Service::Nvidia::NvCore namespace Service::Nvidia::Devices { @@ -17,7 +22,7 @@ class nvmap; class nvdisp_disp0 final : public nvdevice { public: - explicit nvdisp_disp0(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_); + explicit nvdisp_disp0(Core::System& system_, NvCore::Container& core); ~nvdisp_disp0() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, @@ -31,12 +36,16 @@ public: void OnClose(DeviceFD fd) override; /// Performs a screen flip, drawing the buffer pointed to by the handle. - void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, - NVFlinger::BufferQueue::BufferTransformFlags transform, - const Common::Rectangle<int>& crop_rect); + void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height, + u32 stride, android::BufferTransformFlags transform, + const Common::Rectangle<int>& crop_rect, + std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences); + + Kernel::KEvent* QueryEvent(u32 event_id) override; private: - std::shared_ptr<nvmap> nvmap_dev; + NvCore::Container& container; + NvCore::NvMap& nvmap; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 85170cdb3..6411dbf43 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -1,22 +1,30 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #include <cstring> #include <utility> +#include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" -#include "core/hle/service/nvdrv/devices/nvmap.h" +#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" +#include "core/hle/service/nvdrv/nvdrv.h" +#include "video_core/control/channel_state.h" +#include "video_core/gpu.h" #include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" namespace Service::Nvidia::Devices { -nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_) - : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)} {} +nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, Module& module_, NvCore::Container& core) + : nvdevice{system_}, module{module_}, container{core}, nvmap{core.GetNvMapFile()}, vm{}, + gmmu{} {} + nvhost_as_gpu::~nvhost_as_gpu() = default; NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, @@ -83,12 +91,52 @@ NvResult nvhost_as_gpu::AllocAsEx(const std::vector<u8>& input, std::vector<u8>& IoctlAllocAsEx params{}; std::memcpy(¶ms, input.data(), input.size()); - LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size); - if (params.big_page_size == 0) { - params.big_page_size = DEFAULT_BIG_PAGE_SIZE; + LOG_DEBUG(Service_NVDRV, "called, big_page_size=0x{:X}", params.big_page_size); + + std::scoped_lock lock(mutex); + + if (vm.initialised) { + ASSERT_MSG(false, "Cannot initialise an address space twice!"); + return NvResult::InvalidState; + } + + if (params.big_page_size) { + if (!std::has_single_bit(params.big_page_size)) { + LOG_ERROR(Service_NVDRV, "Non power-of-2 big page size: 0x{:X}!", params.big_page_size); + return NvResult::BadValue; + } + + if ((params.big_page_size & VM::SUPPORTED_BIG_PAGE_SIZES) == 0) { + LOG_ERROR(Service_NVDRV, "Unsupported big page size: 0x{:X}!", params.big_page_size); + return NvResult::BadValue; + } + + vm.big_page_size = params.big_page_size; + vm.big_page_size_bits = static_cast<u32>(std::countr_zero(params.big_page_size)); + + vm.va_range_start = params.big_page_size << VM::VA_START_SHIFT; + } + + // If this is unspecified then default values should be used + if (params.va_range_start) { + vm.va_range_start = params.va_range_start; + vm.va_range_split = params.va_range_split; + vm.va_range_end = params.va_range_end; } - big_page_size = params.big_page_size; + const auto start_pages{static_cast<u32>(vm.va_range_start >> VM::PAGE_SIZE_BITS)}; + const auto end_pages{static_cast<u32>(vm.va_range_split >> VM::PAGE_SIZE_BITS)}; + vm.small_page_allocator = std::make_shared<VM::Allocator>(start_pages, end_pages); + + const auto start_big_pages{static_cast<u32>(vm.va_range_split >> vm.big_page_size_bits)}; + const auto end_big_pages{ + static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)}; + vm.big_page_allocator = std::make_unique<VM::Allocator>(start_big_pages, end_big_pages); + + gmmu = std::make_shared<Tegra::MemoryManager>(system, 40, vm.big_page_size_bits, + VM::PAGE_SIZE_BITS); + system.GPU().InitAddressSpace(*gmmu); + vm.initialised = true; return NvResult::Success; } @@ -100,21 +148,76 @@ NvResult nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector< LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, params.page_size, params.flags); - const auto size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)}; - if ((params.flags & AddressSpaceFlags::FixedOffset) != AddressSpaceFlags::None) { - params.offset = *system.GPU().MemoryManager().AllocateFixed(params.offset, size); + std::scoped_lock lock(mutex); + + if (!vm.initialised) { + return NvResult::BadValue; + } + + if (params.page_size != VM::YUZU_PAGESIZE && params.page_size != vm.big_page_size) { + return NvResult::BadValue; + } + + if (params.page_size != vm.big_page_size && + ((params.flags & MappingFlags::Sparse) != MappingFlags::None)) { + UNIMPLEMENTED_MSG("Sparse small pages are not implemented!"); + return NvResult::NotImplemented; + } + + const u32 page_size_bits{params.page_size == VM::YUZU_PAGESIZE ? VM::PAGE_SIZE_BITS + : vm.big_page_size_bits}; + + auto& allocator{params.page_size == VM::YUZU_PAGESIZE ? *vm.small_page_allocator + : *vm.big_page_allocator}; + + if ((params.flags & MappingFlags::Fixed) != MappingFlags::None) { + allocator.AllocateFixed(static_cast<u32>(params.offset >> page_size_bits), params.pages); } else { - params.offset = system.GPU().MemoryManager().Allocate(size, params.align); + params.offset = static_cast<u64>(allocator.Allocate(params.pages)) << page_size_bits; + if (!params.offset) { + ASSERT_MSG(false, "Failed to allocate free space in the GPU AS!"); + return NvResult::InsufficientMemory; + } } - auto result = NvResult::Success; - if (!params.offset) { - LOG_CRITICAL(Service_NVDRV, "allocation failed for size {}", size); - result = NvResult::InsufficientMemory; + u64 size{static_cast<u64>(params.pages) * params.page_size}; + + if ((params.flags & MappingFlags::Sparse) != MappingFlags::None) { + gmmu->MapSparse(params.offset, size); } + allocation_map[params.offset] = { + .size = size, + .mappings{}, + .page_size = params.page_size, + .sparse = (params.flags & MappingFlags::Sparse) != MappingFlags::None, + .big_pages = params.page_size != VM::YUZU_PAGESIZE, + }; + std::memcpy(output.data(), ¶ms, output.size()); - return result; + return NvResult::Success; +} + +void nvhost_as_gpu::FreeMappingLocked(u64 offset) { + auto mapping{mapping_map.at(offset)}; + + if (!mapping->fixed) { + auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; + u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; + + allocator.Free(static_cast<u32>(mapping->offset >> page_size_bits), + static_cast<u32>(mapping->size >> page_size_bits)); + } + + // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state + // Only FreeSpace can unmap them fully + if (mapping->sparse_alloc) { + gmmu->MapSparse(offset, mapping->size, mapping->big_page); + } else { + gmmu->Unmap(offset, mapping->size); + } + + mapping_map.erase(offset); } NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) { @@ -124,8 +227,40 @@ NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset, params.pages, params.page_size); - system.GPU().MemoryManager().Unmap(params.offset, - static_cast<std::size_t>(params.pages) * params.page_size); + std::scoped_lock lock(mutex); + + if (!vm.initialised) { + return NvResult::BadValue; + } + + try { + auto allocation{allocation_map[params.offset]}; + + if (allocation.page_size != params.page_size || + allocation.size != (static_cast<u64>(params.pages) * params.page_size)) { + return NvResult::BadValue; + } + + for (const auto& mapping : allocation.mappings) { + FreeMappingLocked(mapping->offset); + } + + // Unset sparse flag if required + if (allocation.sparse) { + gmmu->Unmap(params.offset, allocation.size); + } + + auto& allocator{params.page_size == VM::YUZU_PAGESIZE ? *vm.small_page_allocator + : *vm.big_page_allocator}; + u32 page_size_bits{params.page_size == VM::YUZU_PAGESIZE ? VM::PAGE_SIZE_BITS + : vm.big_page_size_bits}; + + allocator.Free(static_cast<u32>(params.offset >> page_size_bits), + static_cast<u32>(allocation.size >> page_size_bits)); + allocation_map.erase(params.offset); + } catch (const std::out_of_range&) { + return NvResult::BadValue; + } std::memcpy(output.data(), ¶ms, output.size()); return NvResult::Success; @@ -136,35 +271,52 @@ NvResult nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& out LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries); - auto result = NvResult::Success; std::vector<IoctlRemapEntry> entries(num_entries); std::memcpy(entries.data(), input.data(), input.size()); + std::scoped_lock lock(mutex); + + if (!vm.initialised) { + return NvResult::BadValue; + } + for (const auto& entry : entries) { - LOG_DEBUG(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", - entry.offset, entry.nvmap_handle, entry.pages); + GPUVAddr virtual_address{static_cast<u64>(entry.as_offset_big_pages) + << vm.big_page_size_bits}; + u64 size{static_cast<u64>(entry.big_pages) << vm.big_page_size_bits}; - const auto object{nvmap_dev->GetObject(entry.nvmap_handle)}; - if (!object) { - LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle); - result = NvResult::InvalidState; - break; + auto alloc{allocation_map.upper_bound(virtual_address)}; + + if (alloc-- == allocation_map.begin() || + (virtual_address - alloc->first) + size > alloc->second.size) { + LOG_WARNING(Service_NVDRV, "Cannot remap into an unallocated region!"); + return NvResult::BadValue; } - const auto offset{static_cast<GPUVAddr>(entry.offset) << 0x10}; - const auto size{static_cast<u64>(entry.pages) << 0x10}; - const auto map_offset{static_cast<u64>(entry.map_offset) << 0x10}; - const auto addr{system.GPU().MemoryManager().Map(object->addr + map_offset, offset, size)}; + if (!alloc->second.sparse) { + LOG_WARNING(Service_NVDRV, "Cannot remap a non-sparse mapping!"); + return NvResult::BadValue; + } - if (!addr) { - LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!"); - result = NvResult::InvalidState; - break; + const bool use_big_pages = alloc->second.big_pages; + if (!entry.handle) { + gmmu->MapSparse(virtual_address, size, use_big_pages); + } else { + auto handle{nvmap.GetHandle(entry.handle)}; + if (!handle) { + return NvResult::BadValue; + } + + VAddr cpu_address{static_cast<VAddr>( + handle->address + + (static_cast<u64>(entry.handle_offset_big_pages) << vm.big_page_size_bits))}; + + gmmu->Map(virtual_address, cpu_address, size, use_big_pages); } } std::memcpy(output.data(), entries.data(), output.size()); - return result; + return NvResult::Success; } NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { @@ -174,79 +326,98 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8 LOG_DEBUG(Service_NVDRV, "called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}" ", offset={}", - params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size, + params.flags, params.handle, params.buffer_offset, params.mapping_size, params.offset); - const auto object{nvmap_dev->GetObject(params.nvmap_handle)}; - if (!object) { - LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle); - std::memcpy(output.data(), ¶ms, output.size()); - return NvResult::InvalidState; - } - - // The real nvservices doesn't make a distinction between handles and ids, and - // object can only have one handle and it will be the same as its id. Assert that this is the - // case to prevent unexpected behavior. - ASSERT(object->id == params.nvmap_handle); - auto& gpu = system.GPU(); + std::scoped_lock lock(mutex); - u64 page_size{params.page_size}; - if (!page_size) { - page_size = object->align; + if (!vm.initialised) { + return NvResult::BadValue; } - if ((params.flags & AddressSpaceFlags::Remap) != AddressSpaceFlags::None) { - if (const auto buffer_map{FindBufferMap(params.offset)}; buffer_map) { - const auto cpu_addr{static_cast<VAddr>(buffer_map->CpuAddr() + params.buffer_offset)}; - const auto gpu_addr{static_cast<GPUVAddr>(params.offset + params.buffer_offset)}; + // Remaps a subregion of an existing mapping to a different PA + if ((params.flags & MappingFlags::Remap) != MappingFlags::None) { + try { + auto mapping{mapping_map.at(params.offset)}; - if (!gpu.MemoryManager().Map(cpu_addr, gpu_addr, params.mapping_size)) { - LOG_CRITICAL(Service_NVDRV, - "remap failed, flags={:X}, nvmap_handle={:X}, buffer_offset={}, " - "mapping_size = {}, offset={}", - params.flags, params.nvmap_handle, params.buffer_offset, - params.mapping_size, params.offset); - - std::memcpy(output.data(), ¶ms, output.size()); - return NvResult::InvalidState; + if (mapping->size < params.mapping_size) { + LOG_WARNING(Service_NVDRV, + "Cannot remap a partially mapped GPU address space region: 0x{:X}", + params.offset); + return NvResult::BadValue; } - std::memcpy(output.data(), ¶ms, output.size()); - return NvResult::Success; - } else { - LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset); + u64 gpu_address{static_cast<u64>(params.offset + params.buffer_offset)}; + VAddr cpu_address{mapping->ptr + params.buffer_offset}; + + gmmu->Map(gpu_address, cpu_address, params.mapping_size, mapping->big_page); - std::memcpy(output.data(), ¶ms, output.size()); - return NvResult::InvalidState; + return NvResult::Success; + } catch (const std::out_of_range&) { + LOG_WARNING(Service_NVDRV, "Cannot remap an unmapped GPU address space region: 0x{:X}", + params.offset); + return NvResult::BadValue; } } - // We can only map objects that have already been assigned a CPU address. - ASSERT(object->status == nvmap::Object::Status::Allocated); - - const auto physical_address{object->addr + params.buffer_offset}; - u64 size{params.mapping_size}; - if (!size) { - size = object->size; + auto handle{nvmap.GetHandle(params.handle)}; + if (!handle) { + return NvResult::BadValue; } - const bool is_alloc{(params.flags & AddressSpaceFlags::FixedOffset) == AddressSpaceFlags::None}; - if (is_alloc) { - params.offset = gpu.MemoryManager().MapAllocate(physical_address, size, page_size); - } else { - params.offset = gpu.MemoryManager().Map(physical_address, params.offset, size); - } + VAddr cpu_address{static_cast<VAddr>(handle->address + params.buffer_offset)}; + u64 size{params.mapping_size ? params.mapping_size : handle->orig_size}; + + bool big_page{[&]() { + if (Common::IsAligned(handle->align, vm.big_page_size)) { + return true; + } else if (Common::IsAligned(handle->align, VM::YUZU_PAGESIZE)) { + return false; + } else { + ASSERT(false); + return false; + } + }()}; + + if ((params.flags & MappingFlags::Fixed) != MappingFlags::None) { + auto alloc{allocation_map.upper_bound(params.offset)}; - auto result = NvResult::Success; - if (!params.offset) { - LOG_CRITICAL(Service_NVDRV, "failed to map size={}", size); - result = NvResult::InvalidState; + if (alloc-- == allocation_map.begin() || + (params.offset - alloc->first) + size > alloc->second.size) { + ASSERT_MSG(false, "Cannot perform a fixed mapping into an unallocated region!"); + return NvResult::BadValue; + } + + const bool use_big_pages = alloc->second.big_pages && big_page; + gmmu->Map(params.offset, cpu_address, size, use_big_pages); + + auto mapping{std::make_shared<Mapping>(cpu_address, params.offset, size, true, + use_big_pages, alloc->second.sparse)}; + alloc->second.mappings.push_back(mapping); + mapping_map[params.offset] = mapping; } else { - AddBufferMap(params.offset, size, physical_address, is_alloc); + + auto& allocator{big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; + u32 page_size{big_page ? vm.big_page_size : VM::YUZU_PAGESIZE}; + u32 page_size_bits{big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; + + params.offset = static_cast<u64>(allocator.Allocate( + static_cast<u32>(Common::AlignUp(size, page_size) >> page_size_bits))) + << page_size_bits; + if (!params.offset) { + ASSERT_MSG(false, "Failed to allocate free space in the GPU AS!"); + return NvResult::InsufficientMemory; + } + + gmmu->Map(params.offset, cpu_address, Common::AlignUp(size, page_size), big_page); + + auto mapping{ + std::make_shared<Mapping>(cpu_address, params.offset, size, false, big_page, false)}; + mapping_map[params.offset] = mapping; } std::memcpy(output.data(), ¶ms, output.size()); - return result; + return NvResult::Success; } NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { @@ -255,47 +426,82 @@ NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8 LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); - if (const auto size{RemoveBufferMap(params.offset)}; size) { - system.GPU().MemoryManager().Unmap(params.offset, *size); - } else { - LOG_ERROR(Service_NVDRV, "invalid offset=0x{:X}", params.offset); + std::scoped_lock lock(mutex); + + if (!vm.initialised) { + return NvResult::BadValue; + } + + try { + auto mapping{mapping_map.at(params.offset)}; + + if (!mapping->fixed) { + auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; + u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; + + allocator.Free(static_cast<u32>(mapping->offset >> page_size_bits), + static_cast<u32>(mapping->size >> page_size_bits)); + } + + // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state + // Only FreeSpace can unmap them fully + if (mapping->sparse_alloc) { + gmmu->MapSparse(params.offset, mapping->size, mapping->big_page); + } else { + gmmu->Unmap(params.offset, mapping->size); + } + + mapping_map.erase(params.offset); + } catch (const std::out_of_range&) { + LOG_WARNING(Service_NVDRV, "Couldn't find region to unmap at 0x{:X}", params.offset); } - std::memcpy(output.data(), ¶ms, output.size()); return NvResult::Success; } NvResult nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { IoctlBindChannel params{}; std::memcpy(¶ms, input.data(), input.size()); - LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}", params.fd); + LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); - channel = params.fd; + auto gpu_channel_device = module.GetDevice<nvhost_gpu>(params.fd); + gpu_channel_device->channel_state->memory_manager = gmmu; return NvResult::Success; } +void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) { + params.buf_size = 2 * sizeof(VaRegion); + + params.regions = std::array<VaRegion, 2>{ + VaRegion{ + .offset = vm.small_page_allocator->GetVAStart() << VM::PAGE_SIZE_BITS, + .page_size = VM::YUZU_PAGESIZE, + ._pad0_{}, + .pages = vm.small_page_allocator->GetVALimit() - vm.small_page_allocator->GetVAStart(), + }, + VaRegion{ + .offset = vm.big_page_allocator->GetVAStart() << vm.big_page_size_bits, + .page_size = vm.big_page_size, + ._pad0_{}, + .pages = vm.big_page_allocator->GetVALimit() - vm.big_page_allocator->GetVAStart(), + }, + }; +} + NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) { IoctlGetVaRegions params{}; std::memcpy(¶ms, input.data(), input.size()); - LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr, - params.buf_size); - - params.buf_size = 0x30; + LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, + params.buf_size); - params.small = IoctlVaRegion{ - .offset = 0x04000000, - .page_size = DEFAULT_SMALL_PAGE_SIZE, - .pages = 0x3fbfff, - }; + std::scoped_lock lock(mutex); - params.big = IoctlVaRegion{ - .offset = 0x04000000, - .page_size = big_page_size, - .pages = 0x1bffff, - }; + if (!vm.initialised) { + return NvResult::BadValue; + } - // TODO(ogniK): This probably can stay stubbed but should add support way way later + GetVARegionsImpl(params); std::memcpy(output.data(), ¶ms, output.size()); return NvResult::Success; @@ -306,62 +512,27 @@ NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u IoctlGetVaRegions params{}; std::memcpy(¶ms, input.data(), input.size()); - LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr, - params.buf_size); - - params.buf_size = 0x30; + LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, + params.buf_size); - params.small = IoctlVaRegion{ - .offset = 0x04000000, - .page_size = 0x1000, - .pages = 0x3fbfff, - }; + std::scoped_lock lock(mutex); - params.big = IoctlVaRegion{ - .offset = 0x04000000, - .page_size = big_page_size, - .pages = 0x1bffff, - }; + if (!vm.initialised) { + return NvResult::BadValue; + } - // TODO(ogniK): This probably can stay stubbed but should add support way way later + GetVARegionsImpl(params); std::memcpy(output.data(), ¶ms, output.size()); - std::memcpy(inline_output.data(), ¶ms.small, sizeof(IoctlVaRegion)); - std::memcpy(inline_output.data() + sizeof(IoctlVaRegion), ¶ms.big, sizeof(IoctlVaRegion)); + std::memcpy(inline_output.data(), ¶ms.regions[0], sizeof(VaRegion)); + std::memcpy(inline_output.data() + sizeof(VaRegion), ¶ms.regions[1], sizeof(VaRegion)); return NvResult::Success; } -std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const { - const auto end{buffer_mappings.upper_bound(gpu_addr)}; - for (auto iter{buffer_mappings.begin()}; iter != end; ++iter) { - if (gpu_addr >= iter->second.StartAddr() && gpu_addr < iter->second.EndAddr()) { - return iter->second; - } - } - - return std::nullopt; -} - -void nvhost_as_gpu::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, - bool is_allocated) { - buffer_mappings[gpu_addr] = {gpu_addr, size, cpu_addr, is_allocated}; -} - -std::optional<std::size_t> nvhost_as_gpu::RemoveBufferMap(GPUVAddr gpu_addr) { - if (const auto iter{buffer_mappings.find(gpu_addr)}; iter != buffer_mappings.end()) { - std::size_t size{}; - - if (iter->second.IsAllocated()) { - size = iter->second.Size(); - } - - buffer_mappings.erase(iter); - - return size; - } - - return std::nullopt; +Kernel::KEvent* nvhost_as_gpu::QueryEvent(u32 event_id) { + LOG_CRITICAL(Service_NVDRV, "Unknown AS GPU Event {}", event_id); + return nullptr; } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index 24e3151cb..86fe71c75 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -1,36 +1,50 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once +#include <bit> +#include <list> #include <map> #include <memory> +#include <mutex> #include <optional> #include <vector> +#include "common/address_space.h" #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" -namespace Service::Nvidia::Devices { +namespace Tegra { +class MemoryManager; +} // namespace Tegra + +namespace Service::Nvidia { +class Module; +} -constexpr u32 DEFAULT_BIG_PAGE_SIZE = 1 << 16; -constexpr u32 DEFAULT_SMALL_PAGE_SIZE = 1 << 12; +namespace Service::Nvidia::NvCore { +class Container; +class NvMap; +} // namespace Service::Nvidia::NvCore -class nvmap; +namespace Service::Nvidia::Devices { -enum class AddressSpaceFlags : u32 { - None = 0x0, - FixedOffset = 0x1, - Remap = 0x100, +enum class MappingFlags : u32 { + None = 0, + Fixed = 1 << 0, + Sparse = 1 << 1, + Remap = 1 << 8, }; -DECLARE_ENUM_FLAG_OPERATORS(AddressSpaceFlags); +DECLARE_ENUM_FLAG_OPERATORS(MappingFlags); class nvhost_as_gpu final : public nvdevice { public: - explicit nvhost_as_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_); + explicit nvhost_as_gpu(Core::System& system_, Module& module, NvCore::Container& core); ~nvhost_as_gpu() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, @@ -43,46 +57,17 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; -private: - class BufferMap final { - public: - constexpr BufferMap() = default; - - constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_) - : start_addr{start_addr_}, end_addr{start_addr_ + size_} {} - - constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_, VAddr cpu_addr_, - bool is_allocated_) - : start_addr{start_addr_}, end_addr{start_addr_ + size_}, cpu_addr{cpu_addr_}, - is_allocated{is_allocated_} {} - - constexpr VAddr StartAddr() const { - return start_addr; - } - - constexpr VAddr EndAddr() const { - return end_addr; - } - - constexpr std::size_t Size() const { - return end_addr - start_addr; - } - - constexpr VAddr CpuAddr() const { - return cpu_addr; - } - - constexpr bool IsAllocated() const { - return is_allocated; - } - - private: - GPUVAddr start_addr{}; - GPUVAddr end_addr{}; - VAddr cpu_addr{}; - bool is_allocated{}; + Kernel::KEvent* QueryEvent(u32 event_id) override; + + struct VaRegion { + u64 offset; + u32 page_size; + u32 _pad0_; + u64 pages; }; + static_assert(sizeof(VaRegion) == 0x18); +private: struct IoctlAllocAsEx { u32_le flags{}; // usually passes 1 s32_le as_fd{}; // ignored; passes 0 @@ -97,7 +82,7 @@ private: struct IoctlAllocSpace { u32_le pages{}; u32_le page_size{}; - AddressSpaceFlags flags{}; + MappingFlags flags{}; INSERT_PADDING_WORDS(1); union { u64_le offset; @@ -114,19 +99,19 @@ private: static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size"); struct IoctlRemapEntry { - u16_le flags{}; - u16_le kind{}; - u32_le nvmap_handle{}; - u32_le map_offset{}; - u32_le offset{}; - u32_le pages{}; + u16 flags; + u16 kind; + NvCore::NvMap::Handle::Id handle; + u32 handle_offset_big_pages; + u32 as_offset_big_pages; + u32 big_pages; }; static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size"); struct IoctlMapBufferEx { - AddressSpaceFlags flags{}; // bit0: fixed_offset, bit2: cacheable - u32_le kind{}; // -1 is default - u32_le nvmap_handle{}; + MappingFlags flags{}; // bit0: fixed_offset, bit2: cacheable + u32_le kind{}; // -1 is default + NvCore::NvMap::Handle::Id handle; u32_le page_size{}; // 0 means don't care s64_le buffer_offset{}; u64_le mapping_size{}; @@ -144,27 +129,15 @@ private: }; static_assert(sizeof(IoctlBindChannel) == 4, "IoctlBindChannel is incorrect size"); - struct IoctlVaRegion { - u64_le offset{}; - u32_le page_size{}; - INSERT_PADDING_WORDS(1); - u64_le pages{}; - }; - static_assert(sizeof(IoctlVaRegion) == 24, "IoctlVaRegion is incorrect size"); - struct IoctlGetVaRegions { u64_le buf_addr{}; // (contained output user ptr on linux, ignored) u32_le buf_size{}; // forced to 2*sizeof(struct va_region) u32_le reserved{}; - IoctlVaRegion small{}; - IoctlVaRegion big{}; + std::array<VaRegion, 2> regions{}; }; - static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2, + static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2, "IoctlGetVaRegions is incorrect size"); - s32 channel{}; - u32 big_page_size{DEFAULT_BIG_PAGE_SIZE}; - NvResult AllocAsEx(const std::vector<u8>& input, std::vector<u8>& output); NvResult AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output); NvResult Remap(const std::vector<u8>& input, std::vector<u8>& output); @@ -173,18 +146,75 @@ private: NvResult FreeSpace(const std::vector<u8>& input, std::vector<u8>& output); NvResult BindChannel(const std::vector<u8>& input, std::vector<u8>& output); + void GetVARegionsImpl(IoctlGetVaRegions& params); NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output); NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output, std::vector<u8>& inline_output); - std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const; - void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated); - std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr); + void FreeMappingLocked(u64 offset); + + Module& module; + + NvCore::Container& container; + NvCore::NvMap& nvmap; - std::shared_ptr<nvmap> nvmap_dev; + struct Mapping { + VAddr ptr; + u64 offset; + u64 size; + bool fixed; + bool big_page; // Only valid if fixed == false + bool sparse_alloc; + + Mapping(VAddr ptr_, u64 offset_, u64 size_, bool fixed_, bool big_page_, bool sparse_alloc_) + : ptr(ptr_), offset(offset_), size(size_), fixed(fixed_), big_page(big_page_), + sparse_alloc(sparse_alloc_) {} + }; + + struct Allocation { + u64 size; + std::list<std::shared_ptr<Mapping>> mappings; + u32 page_size; + bool sparse; + bool big_pages; + }; - // This is expected to be ordered, therefore we must use a map, not unordered_map - std::map<GPUVAddr, BufferMap> buffer_mappings; + std::map<u64, std::shared_ptr<Mapping>> + mapping_map; //!< This maps the base addresses of mapped buffers to their total sizes and + //!< mapping type, this is needed as what was originally a single buffer may + //!< have been split into multiple GPU side buffers with the remap flag. + std::map<u64, Allocation> allocation_map; //!< Holds allocations created by AllocSpace from + //!< which fixed buffers can be mapped into + std::mutex mutex; //!< Locks all AS operations + + struct VM { + static constexpr u32 YUZU_PAGESIZE{0x1000}; + static constexpr u32 PAGE_SIZE_BITS{std::countr_zero(YUZU_PAGESIZE)}; + + static constexpr u32 SUPPORTED_BIG_PAGE_SIZES{0x30000}; + static constexpr u32 DEFAULT_BIG_PAGE_SIZE{0x20000}; + u32 big_page_size{DEFAULT_BIG_PAGE_SIZE}; + u32 big_page_size_bits{std::countr_zero(DEFAULT_BIG_PAGE_SIZE)}; + + static constexpr u32 VA_START_SHIFT{10}; + static constexpr u64 DEFAULT_VA_SPLIT{1ULL << 34}; + static constexpr u64 DEFAULT_VA_RANGE{1ULL << 37}; + u64 va_range_start{DEFAULT_BIG_PAGE_SIZE << VA_START_SHIFT}; + u64 va_range_split{DEFAULT_VA_SPLIT}; + u64 va_range_end{DEFAULT_VA_RANGE}; + + using Allocator = Common::FlatAllocator<u32, 0, 32>; + + std::unique_ptr<Allocator> big_page_allocator; + std::shared_ptr<Allocator> + small_page_allocator; //! Shared as this is also used by nvhost::GpuChannel + + bool initialised{}; + } vm; + std::shared_ptr<Tegra::MemoryManager> gmmu; + + // s32 channel{}; + // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE}; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index f9b82b504..5bee4a3d3 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -1,25 +1,39 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later +#include <bit> #include <cstdlib> #include <cstring> +#include <fmt/format.h> #include "common/assert.h" #include "common/logging/log.h" +#include "common/scope_exit.h" #include "core/core.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_writable_event.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" #include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" namespace Service::Nvidia::Devices { nvhost_ctrl::nvhost_ctrl(Core::System& system_, EventInterface& events_interface_, - SyncpointManager& syncpoint_manager_) - : nvdevice{system_}, events_interface{events_interface_}, syncpoint_manager{ - syncpoint_manager_} {} -nvhost_ctrl::~nvhost_ctrl() = default; + NvCore::Container& core_) + : nvdevice{system_}, events_interface{events_interface_}, core{core_}, + syncpoint_manager{core_.GetSyncpointManager()} {} + +nvhost_ctrl::~nvhost_ctrl() { + for (auto& event : events) { + if (!event.registered) { + continue; + } + events_interface.FreeEvent(event.kevent); + } +} NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { @@ -31,13 +45,15 @@ NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& case 0x1c: return IocCtrlClearEventWait(input, output); case 0x1d: - return IocCtrlEventWait(input, output, false); - case 0x1e: return IocCtrlEventWait(input, output, true); + case 0x1e: + return IocCtrlEventWait(input, output, false); case 0x1f: return IocCtrlEventRegister(input, output); case 0x20: return IocCtrlEventUnregister(input, output); + case 0x21: + return IocCtrlEventUnregisterBatch(input, output); } break; default: @@ -61,6 +77,7 @@ NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& } void nvhost_ctrl::OnOpen(DeviceFD fd) {} + void nvhost_ctrl::OnClose(DeviceFD fd) {} NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { @@ -72,116 +89,167 @@ NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector } NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, - bool is_async) { + bool is_allocation) { IocCtrlEventWaitParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}", - params.syncpt_id, params.threshold, params.timeout, is_async); + LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_allocation={}", + params.fence.id, params.fence.value, params.timeout, is_allocation); - if (params.syncpt_id >= MaxSyncPoints) { - return NvResult::BadParameter; - } + bool must_unmark_fail = !is_allocation; + const u32 event_id = params.value.raw; + SCOPE_EXIT({ + std::memcpy(output.data(), ¶ms, sizeof(params)); + if (must_unmark_fail) { + events[event_id].fails = 0; + } + }); - u32 event_id = params.value & 0x00FF; + const u32 fence_id = static_cast<u32>(params.fence.id); - if (event_id >= MaxNvEvents) { - std::memcpy(output.data(), ¶ms, sizeof(params)); + if (fence_id >= MaxSyncPoints) { return NvResult::BadParameter; } - if (syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) { - params.value = syncpoint_manager.GetSyncpointMin(params.syncpt_id); - std::memcpy(output.data(), ¶ms, sizeof(params)); - events_interface.failed[event_id] = false; + if (params.fence.value == 0) { + if (!syncpoint_manager.IsSyncpointAllocated(params.fence.id)) { + LOG_WARNING(Service_NVDRV, + "Unallocated syncpt_id={}, threshold={}, timeout={}, is_allocation={}", + params.fence.id, params.fence.value, params.timeout, is_allocation); + } else { + params.value.raw = syncpoint_manager.ReadSyncpointMinValue(fence_id); + } return NvResult::Success; } - if (const auto new_value = syncpoint_manager.RefreshSyncpoint(params.syncpt_id); - syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) { - params.value = new_value; - std::memcpy(output.data(), ¶ms, sizeof(params)); - events_interface.failed[event_id] = false; + if (syncpoint_manager.IsFenceSignalled(params.fence)) { + params.value.raw = syncpoint_manager.ReadSyncpointMinValue(fence_id); return NvResult::Success; } - auto& event = events_interface.events[event_id]; - auto& gpu = system.GPU(); - - // This is mostly to take into account unimplemented features. As synced - // gpu is always synced. - if (!gpu.IsAsync()) { - event.event->GetWritableEvent().Signal(); - return NvResult::Success; - } - const u32 current_syncpoint_value = event.fence.value; - const s32 diff = current_syncpoint_value - params.threshold; - if (diff >= 0) { - event.event->GetWritableEvent().Signal(); - params.value = current_syncpoint_value; - std::memcpy(output.data(), ¶ms, sizeof(params)); - events_interface.failed[event_id] = false; + if (const auto new_value = syncpoint_manager.UpdateMin(fence_id); + syncpoint_manager.IsFenceSignalled(params.fence)) { + params.value.raw = new_value; return NvResult::Success; } - const u32 target_value = current_syncpoint_value - diff; - if (!is_async) { - params.value = 0; + auto& host1x_syncpoint_manager = system.Host1x().GetSyncpointManager(); + const u32 target_value = params.fence.value; + + auto lock = NvEventsLock(); + + u32 slot = [&]() { + if (is_allocation) { + params.value.raw = 0; + return FindFreeNvEvent(fence_id); + } else { + return params.value.raw; + } + }(); + + must_unmark_fail = false; + + const auto check_failing = [&]() { + if (events[slot].fails > 2) { + { + auto lk = system.StallProcesses(); + host1x_syncpoint_manager.WaitHost(fence_id, target_value); + system.UnstallProcesses(); + } + params.value.raw = target_value; + return true; + } + return false; + }; + + if (slot >= MaxNvEvents) { + return NvResult::BadParameter; } if (params.timeout == 0) { - std::memcpy(output.data(), ¶ms, sizeof(params)); + if (check_failing()) { + events[slot].fails = 0; + return NvResult::Success; + } return NvResult::Timeout; } - EventState status = events_interface.status[event_id]; - const bool bad_parameter = status != EventState::Free && status != EventState::Registered; - if (bad_parameter) { - std::memcpy(output.data(), ¶ms, sizeof(params)); + auto& event = events[slot]; + + if (!event.registered) { return NvResult::BadParameter; } - events_interface.SetEventStatus(event_id, EventState::Waiting); - events_interface.assigned_syncpt[event_id] = params.syncpt_id; - events_interface.assigned_value[event_id] = target_value; - if (is_async) { - params.value = params.syncpt_id << 4; - } else { - params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000; - } - params.value |= event_id; - event.event->GetWritableEvent().Clear(); - if (events_interface.failed[event_id]) { - { - auto lk = system.StallCPU(); - gpu.WaitFence(params.syncpt_id, target_value); - system.UnstallCPU(); - } - std::memcpy(output.data(), ¶ms, sizeof(params)); - events_interface.failed[event_id] = false; + + if (event.IsBeingUsed()) { + return NvResult::BadParameter; + } + + if (check_failing()) { + event.fails = 0; return NvResult::Success; } - gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); - std::memcpy(output.data(), ¶ms, sizeof(params)); + + params.value.raw = 0; + + event.status.store(EventState::Waiting, std::memory_order_release); + event.assigned_syncpt = fence_id; + event.assigned_value = target_value; + if (is_allocation) { + params.value.syncpoint_id_for_allocation.Assign(static_cast<u16>(fence_id)); + params.value.event_allocated.Assign(1); + } else { + params.value.syncpoint_id.Assign(fence_id); + } + params.value.raw |= slot; + + event.wait_handle = + host1x_syncpoint_manager.RegisterHostAction(fence_id, target_value, [this, slot]() { + auto& event_ = events[slot]; + if (event_.status.exchange(EventState::Signalling, std::memory_order_acq_rel) == + EventState::Waiting) { + event_.kevent->GetWritableEvent().Signal(); + } + event_.status.store(EventState::Signalled, std::memory_order_release); + }); return NvResult::Timeout; } +NvResult nvhost_ctrl::FreeEvent(u32 slot) { + if (slot >= MaxNvEvents) { + return NvResult::BadParameter; + } + + auto& event = events[slot]; + + if (!event.registered) { + return NvResult::Success; + } + + if (event.IsBeingUsed()) { + return NvResult::Busy; + } + + FreeNvEvent(slot); + return NvResult::Success; +} + NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) { IocCtrlEventRegisterParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); - const u32 event_id = params.user_event_id & 0x00FF; + const u32 event_id = params.user_event_id; LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); if (event_id >= MaxNvEvents) { return NvResult::BadParameter; } - if (events_interface.registered[event_id]) { - const auto event_state = events_interface.status[event_id]; - if (event_state != EventState::Free) { - LOG_WARNING(Service_NVDRV, "Event already registered! Unregistering previous event"); - events_interface.UnregisterEvent(event_id); - } else { - return NvResult::BadParameter; + + auto lock = NvEventsLock(); + + if (events[event_id].registered) { + const auto result = FreeEvent(event_id); + if (result != NvResult::Success) { + return result; } } - events_interface.RegisterEvent(event_id); + CreateNvEvent(event_id); return NvResult::Success; } @@ -191,34 +259,142 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::memcpy(¶ms, input.data(), sizeof(params)); const u32 event_id = params.user_event_id & 0x00FF; LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); - if (event_id >= MaxNvEvents) { - return NvResult::BadParameter; - } - if (!events_interface.registered[event_id]) { - return NvResult::BadParameter; + + auto lock = NvEventsLock(); + return FreeEvent(event_id); +} + +NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(const std::vector<u8>& input, + std::vector<u8>& output) { + IocCtrlEventUnregisterBatchParams params{}; + std::memcpy(¶ms, input.data(), sizeof(params)); + u64 event_mask = params.user_events; + LOG_DEBUG(Service_NVDRV, " called, event_mask: {:X}", event_mask); + + auto lock = NvEventsLock(); + while (event_mask != 0) { + const u64 event_id = std::countr_zero(event_mask); + event_mask &= ~(1ULL << event_id); + const auto result = FreeEvent(static_cast<u32>(event_id)); + if (result != NvResult::Success) { + return result; + } } - events_interface.UnregisterEvent(event_id); return NvResult::Success; } NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) { - IocCtrlEventSignalParams params{}; + IocCtrlEventClearParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); - u32 event_id = params.event_id & 0x00FF; - LOG_WARNING(Service_NVDRV, "cleared event wait on, event_id: {:X}", event_id); + u32 event_id = params.event_id.slot; + LOG_DEBUG(Service_NVDRV, "called, event_id: {:X}", event_id); if (event_id >= MaxNvEvents) { return NvResult::BadParameter; } - if (events_interface.status[event_id] == EventState::Waiting) { - events_interface.LiberateEvent(event_id); - } - events_interface.failed[event_id] = true; - syncpoint_manager.RefreshSyncpoint(events_interface.events[event_id].fence.id); + auto lock = NvEventsLock(); + + auto& event = events[event_id]; + if (event.status.exchange(EventState::Cancelling, std::memory_order_acq_rel) == + EventState::Waiting) { + auto& host1x_syncpoint_manager = system.Host1x().GetSyncpointManager(); + host1x_syncpoint_manager.DeregisterHostAction(event.assigned_syncpt, event.wait_handle); + syncpoint_manager.UpdateMin(event.assigned_syncpt); + event.wait_handle = {}; + } + event.fails++; + event.status.store(EventState::Cancelled, std::memory_order_release); + event.kevent->GetWritableEvent().Clear(); return NvResult::Success; } +Kernel::KEvent* nvhost_ctrl::QueryEvent(u32 event_id) { + const auto desired_event = SyncpointEventValue{.raw = event_id}; + + const bool allocated = desired_event.event_allocated.Value() != 0; + const u32 slot{allocated ? desired_event.partial_slot.Value() + : static_cast<u32>(desired_event.slot)}; + if (slot >= MaxNvEvents) { + ASSERT(false); + return nullptr; + } + + const u32 syncpoint_id{allocated ? desired_event.syncpoint_id_for_allocation.Value() + : desired_event.syncpoint_id.Value()}; + + auto lock = NvEventsLock(); + + auto& event = events[slot]; + if (event.registered && event.assigned_syncpt == syncpoint_id) { + ASSERT(event.kevent); + return event.kevent; + } + // Is this possible in hardware? + ASSERT_MSG(false, "Slot:{}, SyncpointID:{}, requested", slot, syncpoint_id); + return nullptr; +} + +std::unique_lock<std::mutex> nvhost_ctrl::NvEventsLock() { + return std::unique_lock<std::mutex>(events_mutex); +} + +void nvhost_ctrl::CreateNvEvent(u32 event_id) { + auto& event = events[event_id]; + ASSERT(!event.kevent); + ASSERT(!event.registered); + ASSERT(!event.IsBeingUsed()); + event.kevent = events_interface.CreateEvent(fmt::format("NVCTRL::NvEvent_{}", event_id)); + event.status = EventState::Available; + event.registered = true; + const u64 mask = 1ULL << event_id; + event.fails = 0; + events_mask |= mask; + event.assigned_syncpt = 0; +} + +void nvhost_ctrl::FreeNvEvent(u32 event_id) { + auto& event = events[event_id]; + ASSERT(event.kevent); + ASSERT(event.registered); + ASSERT(!event.IsBeingUsed()); + events_interface.FreeEvent(event.kevent); + event.kevent = nullptr; + event.status = EventState::Available; + event.registered = false; + const u64 mask = ~(1ULL << event_id); + events_mask &= mask; +} + +u32 nvhost_ctrl::FindFreeNvEvent(u32 syncpoint_id) { + u32 slot{MaxNvEvents}; + u32 free_slot{MaxNvEvents}; + for (u32 i = 0; i < MaxNvEvents; i++) { + auto& event = events[i]; + if (event.registered) { + if (!event.IsBeingUsed()) { + slot = i; + if (event.assigned_syncpt == syncpoint_id) { + return slot; + } + } + } else if (free_slot == MaxNvEvents) { + free_slot = i; + } + } + if (free_slot < MaxNvEvents) { + CreateNvEvent(free_slot); + return free_slot; + } + + if (slot < MaxNvEvents) { + return slot; + } + + LOG_CRITICAL(Service_NVDRV, "Failed to allocate an event"); + return 0; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index cdf03887d..0b56d7070 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -1,21 +1,28 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once #include <array> #include <vector> +#include "common/bit_field.h" #include "common/common_types.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" #include "core/hle/service/nvdrv/nvdrv.h" +#include "video_core/host1x/syncpoint_manager.h" + +namespace Service::Nvidia::NvCore { +class Container; +class SyncpointManager; +} // namespace Service::Nvidia::NvCore namespace Service::Nvidia::Devices { class nvhost_ctrl final : public nvdevice { public: explicit nvhost_ctrl(Core::System& system_, EventInterface& events_interface_, - SyncpointManager& syncpoint_manager_); + NvCore::Container& core); ~nvhost_ctrl() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, @@ -28,7 +35,70 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; + Kernel::KEvent* QueryEvent(u32 event_id) override; + + union SyncpointEventValue { + u32 raw; + + union { + BitField<0, 4, u32> partial_slot; + BitField<4, 28, u32> syncpoint_id; + }; + + struct { + u16 slot; + union { + BitField<0, 12, u16> syncpoint_id_for_allocation; + BitField<12, 1, u16> event_allocated; + }; + }; + }; + static_assert(sizeof(SyncpointEventValue) == sizeof(u32)); + private: + struct InternalEvent { + // Mask representing registered events + + // Each kernel event associated to an NV event + Kernel::KEvent* kevent{}; + // The status of the current NVEvent + std::atomic<EventState> status{}; + + // Tells the NVEvent that it has failed. + u32 fails{}; + // When an NVEvent is waiting on GPU interrupt, this is the sync_point + // associated with it. + u32 assigned_syncpt{}; + // This is the value of the GPU interrupt for which the NVEvent is waiting + // for. + u32 assigned_value{}; + + // Tells if an NVEvent is registered or not + bool registered{}; + + // Used for waiting on a syncpoint & canceling it. + Tegra::Host1x::SyncpointManager::ActionHandle wait_handle{}; + + bool IsBeingUsed() const { + const auto current_status = status.load(std::memory_order_acquire); + return current_status == EventState::Waiting || + current_status == EventState::Cancelling || + current_status == EventState::Signalling; + } + }; + + std::unique_lock<std::mutex> NvEventsLock(); + + void CreateNvEvent(u32 event_id); + + void FreeNvEvent(u32 event_id); + + u32 FindFreeNvEvent(u32 syncpoint_id); + + std::array<InternalEvent, MaxNvEvents> events{}; + std::mutex events_mutex; + u64 events_mask{}; + struct IocSyncptReadParams { u32_le id{}; u32_le value{}; @@ -84,27 +154,18 @@ private: }; static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size"); - struct IocCtrlEventSignalParams { - u32_le event_id{}; + struct IocCtrlEventClearParams { + SyncpointEventValue event_id{}; }; - static_assert(sizeof(IocCtrlEventSignalParams) == 4, - "IocCtrlEventSignalParams is incorrect size"); + static_assert(sizeof(IocCtrlEventClearParams) == 4, + "IocCtrlEventClearParams is incorrect size"); struct IocCtrlEventWaitParams { - u32_le syncpt_id{}; - u32_le threshold{}; - s32_le timeout{}; - u32_le value{}; - }; - static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size"); - - struct IocCtrlEventWaitAsyncParams { - u32_le syncpt_id{}; - u32_le threshold{}; + NvFence fence{}; u32_le timeout{}; - u32_le value{}; + SyncpointEventValue value{}; }; - static_assert(sizeof(IocCtrlEventWaitAsyncParams) == 16, + static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitAsyncParams is incorrect size"); struct IocCtrlEventRegisterParams { @@ -119,19 +180,25 @@ private: static_assert(sizeof(IocCtrlEventUnregisterParams) == 4, "IocCtrlEventUnregisterParams is incorrect size"); - struct IocCtrlEventKill { + struct IocCtrlEventUnregisterBatchParams { u64_le user_events{}; }; - static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size"); + static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8, + "IocCtrlEventKill is incorrect size"); NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); - NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async); + NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, + bool is_allocation); NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output); NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output); + NvResult IocCtrlEventUnregisterBatch(const std::vector<u8>& input, std::vector<u8>& output); NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output); + NvResult FreeEvent(u32 slot); + EventInterface& events_interface; - SyncpointManager& syncpoint_manager; + NvCore::Container& core; + NvCore::SyncpointManager& syncpoint_manager; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 05b4e2151..ced57dfe6 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include "common/assert.h" @@ -8,11 +7,19 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" +#include "core/hle/service/nvdrv/nvdrv.h" namespace Service::Nvidia::Devices { -nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_) : nvdevice{system_} {} -nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; +nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_) + : nvdevice{system_}, events_interface{events_interface_} { + error_notifier_event = events_interface.CreateEvent("CtrlGpuErrorNotifier"); + unknown_event = events_interface.CreateEvent("CtrlGpuUknownEvent"); +} +nvhost_ctrl_gpu::~nvhost_ctrl_gpu() { + events_interface.FreeEvent(error_notifier_event); + events_interface.FreeEvent(unknown_event); +} NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { @@ -287,4 +294,17 @@ NvResult nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u return NvResult::Success; } +Kernel::KEvent* nvhost_ctrl_gpu::QueryEvent(u32 event_id) { + switch (event_id) { + case 1: + return error_notifier_event; + case 2: + return unknown_event; + default: { + LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id); + } + } + return nullptr; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index 898d00a17..1e8f254e2 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -1,19 +1,24 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include <vector> + +#include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" +namespace Service::Nvidia { +class EventInterface; +} + namespace Service::Nvidia::Devices { class nvhost_ctrl_gpu final : public nvdevice { public: - explicit nvhost_ctrl_gpu(Core::System& system_); + explicit nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_); ~nvhost_ctrl_gpu() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, @@ -26,6 +31,8 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; + Kernel::KEvent* QueryEvent(u32 event_id) override; + private: struct IoctlGpuCharacteristics { u32_le arch; // 0x120 (NVGPU_GPU_ARCH_GM200) @@ -159,6 +166,12 @@ private: NvResult ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output); NvResult FlushL2(const std::vector<u8>& input, std::vector<u8>& output); NvResult GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output); + + EventInterface& events_interface; + + // Events + Kernel::KEvent* error_notifier_event; + Kernel::KEvent* unknown_event; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 0a043e386..45a759fa8 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -1,34 +1,50 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" -#include "core/hle/service/nvdrv/syncpoint_manager.h" +#include "core/hle/service/nvdrv/nvdrv.h" #include "core/memory.h" +#include "video_core/control/channel_state.h" +#include "video_core/engines/puller.h" #include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" namespace Service::Nvidia::Devices { namespace { -Tegra::CommandHeader BuildFenceAction(Tegra::GPU::FenceOperation op, u32 syncpoint_id) { - Tegra::GPU::FenceAction result{}; +Tegra::CommandHeader BuildFenceAction(Tegra::Engines::Puller::FenceOperation op, u32 syncpoint_id) { + Tegra::Engines::Puller::FenceAction result{}; result.op.Assign(op); result.syncpoint_id.Assign(syncpoint_id); return {result.raw}; } } // namespace -nvhost_gpu::nvhost_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, - SyncpointManager& syncpoint_manager_) - : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, syncpoint_manager{syncpoint_manager_} { - channel_fence.id = syncpoint_manager_.AllocateSyncpoint(); - channel_fence.value = system_.GPU().GetSyncpointValue(channel_fence.id); +nvhost_gpu::nvhost_gpu(Core::System& system_, EventInterface& events_interface_, + NvCore::Container& core_) + : nvdevice{system_}, events_interface{events_interface_}, core{core_}, + syncpoint_manager{core_.GetSyncpointManager()}, nvmap{core.GetNvMapFile()}, + channel_state{system.GPU().AllocateChannel()} { + channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false); + sm_exception_breakpoint_int_report_event = + events_interface.CreateEvent("GpuChannelSMExceptionBreakpointInt"); + sm_exception_breakpoint_pause_report_event = + events_interface.CreateEvent("GpuChannelSMExceptionBreakpointPause"); + error_notifier_event = events_interface.CreateEvent("GpuChannelErrorNotifier"); } -nvhost_gpu::~nvhost_gpu() = default; +nvhost_gpu::~nvhost_gpu() { + events_interface.FreeEvent(sm_exception_breakpoint_int_report_event); + events_interface.FreeEvent(sm_exception_breakpoint_pause_report_event); + events_interface.FreeEvent(error_notifier_event); + syncpoint_manager.FreeSyncpoint(channel_syncpoint); +} NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { @@ -168,9 +184,14 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8 params.num_entries, params.flags, params.unk0, params.unk1, params.unk2, params.unk3); - channel_fence.value = system.GPU().GetSyncpointValue(channel_fence.id); + if (channel_state->initialized) { + LOG_CRITICAL(Service_NVDRV, "Already allocated!"); + return NvResult::AlreadyAllocated; + } + + system.GPU().InitChannel(*channel_state); - params.fence_out = channel_fence; + params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint); std::memcpy(output.data(), ¶ms, output.size()); return NvResult::Success; @@ -187,40 +208,39 @@ NvResult nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::ve return NvResult::Success; } -static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) { +static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) { return { - Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, + Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1, Tegra::SubmissionMode::Increasing), {fence.value}, - Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1, + Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1, Tegra::SubmissionMode::Increasing), - BuildFenceAction(Tegra::GPU::FenceOperation::Acquire, fence.id), + BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Acquire, fence.id), }; } -static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence, u32 add_increment) { +static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(NvFence fence) { std::vector<Tegra::CommandHeader> result{ - Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, + Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1, Tegra::SubmissionMode::Increasing), {}}; - for (u32 count = 0; count < add_increment; ++count) { - result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1, + for (u32 count = 0; count < 2; ++count) { + result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1, Tegra::SubmissionMode::Increasing)); - result.emplace_back(BuildFenceAction(Tegra::GPU::FenceOperation::Increment, fence.id)); + result.emplace_back( + BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Increment, fence.id)); } return result; } -static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(Fence fence, - u32 add_increment) { +static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(NvFence fence) { std::vector<Tegra::CommandHeader> result{ - Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1, + Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForIdle, 1, Tegra::SubmissionMode::Increasing), {}}; - const std::vector<Tegra::CommandHeader> increment{ - BuildIncrementCommandList(fence, add_increment)}; + const std::vector<Tegra::CommandHeader> increment{BuildIncrementCommandList(fence)}; result.insert(result.end(), increment.begin(), increment.end()); @@ -234,33 +254,41 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8> auto& gpu = system.GPU(); - params.fence_out.id = channel_fence.id; + std::scoped_lock lock(channel_mutex); - if (params.flags.add_wait.Value() && - !syncpoint_manager.IsSyncpointExpired(params.fence_out.id, params.fence_out.value)) { - gpu.PushGPUEntries(Tegra::CommandList{BuildWaitCommandList(params.fence_out)}); - } + const auto bind_id = channel_state->bind_id; - if (params.flags.add_increment.Value() || params.flags.increment.Value()) { - const u32 increment_value = params.flags.increment.Value() ? params.fence_out.value : 0; - params.fence_out.value = syncpoint_manager.IncreaseSyncpoint( - params.fence_out.id, params.AddIncrementValue() + increment_value); - } else { - params.fence_out.value = syncpoint_manager.GetSyncpointMax(params.fence_out.id); + auto& flags = params.flags; + + if (flags.fence_wait.Value()) { + if (flags.increment_value.Value()) { + return NvResult::BadParameter; + } + + if (!syncpoint_manager.IsFenceSignalled(params.fence)) { + gpu.PushGPUEntries(bind_id, Tegra::CommandList{BuildWaitCommandList(params.fence)}); + } } - gpu.PushGPUEntries(std::move(entries)); + params.fence.id = channel_syncpoint; + + u32 increment{(flags.fence_increment.Value() != 0 ? 2 : 0) + + (flags.increment_value.Value() != 0 ? params.fence.value : 0)}; + params.fence.value = syncpoint_manager.IncrementSyncpointMaxExt(channel_syncpoint, increment); + gpu.PushGPUEntries(bind_id, std::move(entries)); - if (params.flags.add_increment.Value()) { - if (params.flags.suppress_wfi) { - gpu.PushGPUEntries(Tegra::CommandList{ - BuildIncrementCommandList(params.fence_out, params.AddIncrementValue())}); + if (flags.fence_increment.Value()) { + if (flags.suppress_wfi.Value()) { + gpu.PushGPUEntries(bind_id, + Tegra::CommandList{BuildIncrementCommandList(params.fence)}); } else { - gpu.PushGPUEntries(Tegra::CommandList{ - BuildIncrementWithWfiCommandList(params.fence_out, params.AddIncrementValue())}); + gpu.PushGPUEntries(bind_id, + Tegra::CommandList{BuildIncrementWithWfiCommandList(params.fence)}); } } + flags.raw = 0; + std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmitGpfifo)); return NvResult::Success; } @@ -328,4 +356,19 @@ NvResult nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vect return NvResult::Success; } +Kernel::KEvent* nvhost_gpu::QueryEvent(u32 event_id) { + switch (event_id) { + case 1: + return sm_exception_breakpoint_int_report_event; + case 2: + return sm_exception_breakpoint_pause_report_event; + case 3: + return error_notifier_event; + default: { + LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id); + } + } + return nullptr; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index f27a82bff..1e4ecd55b 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -1,29 +1,43 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include <memory> #include <vector> #include "common/bit_field.h" +#include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" #include "core/hle/service/nvdrv/nvdata.h" #include "video_core/dma_pusher.h" +namespace Tegra { +namespace Control { +struct ChannelState; +} +} // namespace Tegra + namespace Service::Nvidia { + +namespace NvCore { +class Container; +class NvMap; class SyncpointManager; -} +} // namespace NvCore + +class EventInterface; +} // namespace Service::Nvidia namespace Service::Nvidia::Devices { +class nvhost_as_gpu; class nvmap; class nvhost_gpu final : public nvdevice { public: - explicit nvhost_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, - SyncpointManager& syncpoint_manager_); + explicit nvhost_gpu(Core::System& system_, EventInterface& events_interface_, + NvCore::Container& core); ~nvhost_gpu() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, @@ -36,7 +50,10 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; + Kernel::KEvent* QueryEvent(u32 event_id) override; + private: + friend class nvhost_as_gpu; enum class CtxObjects : u32_le { Ctx2D = 0x902D, Ctx3D = 0xB197, @@ -108,7 +125,7 @@ private: static_assert(sizeof(IoctlGetErrorNotification) == 16, "IoctlGetErrorNotification is incorrect size"); - static_assert(sizeof(Fence) == 8, "Fence is incorrect size"); + static_assert(sizeof(NvFence) == 8, "Fence is incorrect size"); struct IoctlAllocGpfifoEx { u32_le num_entries{}; @@ -126,7 +143,7 @@ private: u32_le num_entries{}; // in u32_le flags{}; // in u32_le unk0{}; // in (1 works) - Fence fence_out{}; // out + NvFence fence_out{}; // out u32_le unk1{}; // in u32_le unk2{}; // in u32_le unk3{}; // in @@ -146,19 +163,15 @@ private: u32_le num_entries{}; // number of fence objects being submitted union { u32_le raw; - BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list - BitField<1, 1, u32_le> add_increment; // append an increment to the list - BitField<2, 1, u32_le> new_hw_format; // mostly ignored - BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt - BitField<8, 1, u32_le> increment; // increment the returned fence + BitField<0, 1, u32_le> fence_wait; // append a wait sync_point to the list + BitField<1, 1, u32_le> fence_increment; // append an increment to the list + BitField<2, 1, u32_le> new_hw_format; // mostly ignored + BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt + BitField<8, 1, u32_le> increment_value; // increment the returned fence } flags; - Fence fence_out{}; // returned new fence object for others to wait on - - u32 AddIncrementValue() const { - return flags.add_increment.Value() << 1; - } + NvFence fence{}; // returned new fence object for others to wait on }; - static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence), + static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(NvFence), "IoctlSubmitGpfifo is incorrect size"); struct IoctlGetWaitbase { @@ -191,9 +204,18 @@ private: NvResult ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); NvResult ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output); - std::shared_ptr<nvmap> nvmap_dev; - SyncpointManager& syncpoint_manager; - Fence channel_fence; + EventInterface& events_interface; + NvCore::Container& core; + NvCore::SyncpointManager& syncpoint_manager; + NvCore::NvMap& nvmap; + std::shared_ptr<Tegra::Control::ChannelState> channel_state; + u32 channel_syncpoint; + std::mutex channel_mutex; + + // Events + Kernel::KEvent* sm_exception_breakpoint_int_report_event; + Kernel::KEvent* sm_exception_breakpoint_pause_report_event; + Kernel::KEvent* error_notifier_event; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index 8314d1ec2..1703f9cc3 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -1,18 +1,18 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#include "audio_core/audio_core.h" #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" #include "video_core/renderer_base.h" namespace Service::Nvidia::Devices { -nvhost_nvdec::nvhost_nvdec(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, - SyncpointManager& syncpoint_manager_) - : nvhost_nvdec_common{system_, std::move(nvmap_dev_), syncpoint_manager_} {} +nvhost_nvdec::nvhost_nvdec(Core::System& system_, NvCore::Container& core_) + : nvhost_nvdec_common{system_, core_, NvCore::ChannelType::NvDec} {} nvhost_nvdec::~nvhost_nvdec() = default; NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, @@ -21,8 +21,9 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& case 0x0: switch (command.cmd) { case 0x1: { - if (!fd_to_id.contains(fd)) { - fd_to_id[fd] = next_id++; + auto& host1x_file = core.Host1xDeviceFile(); + if (!host1x_file.fd_to_id.contains(fd)) { + host1x_file.fd_to_id[fd] = host1x_file.nvdec_next_id++; } return Submit(fd, input, output); } @@ -66,14 +67,19 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& return NvResult::NotImplemented; } -void nvhost_nvdec::OnOpen(DeviceFD fd) {} +void nvhost_nvdec::OnOpen(DeviceFD fd) { + LOG_INFO(Service_NVDRV, "NVDEC video stream started"); + system.AudioCore().SetNVDECActive(true); +} void nvhost_nvdec::OnClose(DeviceFD fd) { LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); - const auto iter = fd_to_id.find(fd); - if (iter != fd_to_id.end()) { + auto& host1x_file = core.Host1xDeviceFile(); + const auto iter = host1x_file.fd_to_id.find(fd); + if (iter != host1x_file.fd_to_id.end()) { system.GPU().ClearCdmaInstance(iter->second); } + system.AudioCore().SetNVDECActive(false); } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index a507c4d0a..c1b4e53e8 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -11,8 +10,7 @@ namespace Service::Nvidia::Devices { class nvhost_nvdec final : public nvhost_nvdec_common { public: - explicit nvhost_nvdec(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, - SyncpointManager& syncpoint_manager_); + explicit nvhost_nvdec(Core::System& system_, NvCore::Container& core); ~nvhost_nvdec() override; NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, @@ -24,9 +22,6 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; - -private: - u32 next_id{}; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index 8a05f0668..99eede702 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <cstring> @@ -9,10 +8,12 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h" -#include "core/hle/service/nvdrv/devices/nvmap.h" -#include "core/hle/service/nvdrv/syncpoint_manager.h" #include "core/memory.h" +#include "video_core/host1x/host1x.h" #include "video_core/memory_manager.h" #include "video_core/renderer_base.h" @@ -45,10 +46,22 @@ std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::s } } // Anonymous namespace -nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, - SyncpointManager& syncpoint_manager_) - : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, syncpoint_manager{syncpoint_manager_} {} -nvhost_nvdec_common::~nvhost_nvdec_common() = default; +nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, NvCore::Container& core_, + NvCore::ChannelType channel_type_) + : nvdevice{system_}, core{core_}, syncpoint_manager{core.GetSyncpointManager()}, + nvmap{core.GetNvMapFile()}, channel_type{channel_type_} { + auto& syncpts_accumulated = core.Host1xDeviceFile().syncpts_accumulated; + if (syncpts_accumulated.empty()) { + channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false); + } else { + channel_syncpoint = syncpts_accumulated.front(); + syncpts_accumulated.pop_front(); + } +} + +nvhost_nvdec_common::~nvhost_nvdec_common() { + core.Host1xDeviceFile().syncpts_accumulated.push_back(channel_syncpoint); +} NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) { IoctlSetNvmapFD params{}; @@ -85,16 +98,16 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector<u8>& input, for (std::size_t i = 0; i < syncpt_increments.size(); i++) { const SyncptIncr& syncpt_incr = syncpt_increments[i]; fence_thresholds[i] = - syncpoint_manager.IncreaseSyncpoint(syncpt_incr.id, syncpt_incr.increments); + syncpoint_manager.IncrementSyncpointMaxExt(syncpt_incr.id, syncpt_incr.increments); } } for (const auto& cmd_buffer : command_buffers) { - const auto object = nvmap_dev->GetObject(cmd_buffer.memory_id); + const auto object = nvmap.GetHandle(cmd_buffer.memory_id); ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;); Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count); - system.Memory().ReadBlock(object->addr + cmd_buffer.offset, cmdlist.data(), + system.Memory().ReadBlock(object->address + cmd_buffer.offset, cmdlist.data(), cmdlist.size() * sizeof(u32)); - gpu.PushCommandBuffer(fd_to_id[fd], cmdlist); + gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist); } std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit)); // Some games expect command_buffers to be written back @@ -113,10 +126,8 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::ve std::memcpy(¶ms, input.data(), sizeof(IoctlGetSyncpoint)); LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); - if (device_syncpoints[params.param] == 0 && system.GPU().UseNvdec()) { - device_syncpoints[params.param] = syncpoint_manager.AllocateSyncpoint(); - } - params.value = device_syncpoints[params.param]; + // const u32 id{NvCore::SyncpointManager::channel_syncpoints[static_cast<u32>(channel_type)]}; + params.value = channel_syncpoint; std::memcpy(output.data(), ¶ms, sizeof(IoctlGetSyncpoint)); return NvResult::Success; @@ -124,6 +135,7 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::ve NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { IoctlGetWaitbase params{}; + LOG_CRITICAL(Service_NVDRV, "called WAITBASE"); std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase)); params.value = 0; // Seems to be hard coded at 0 std::memcpy(output.data(), ¶ms, sizeof(IoctlGetWaitbase)); @@ -137,28 +149,8 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer)); - auto& gpu = system.GPU(); - for (auto& cmd_buffer : cmd_buffer_handles) { - auto object{nvmap_dev->GetObject(cmd_buffer.map_handle)}; - if (!object) { - LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmd_buffer.map_handle); - std::memcpy(output.data(), ¶ms, output.size()); - return NvResult::InvalidState; - } - if (object->dma_map_addr == 0) { - // NVDEC and VIC memory is in the 32-bit address space - // MapAllocate32 will attempt to map a lower 32-bit value in the shared gpu memory space - const GPUVAddr low_addr = gpu.MemoryManager().MapAllocate32(object->addr, object->size); - object->dma_map_addr = static_cast<u32>(low_addr); - // Ensure that the dma_map_addr is indeed in the lower 32-bit address space. - ASSERT(object->dma_map_addr == low_addr); - } - if (!object->dma_map_addr) { - LOG_ERROR(Service_NVDRV, "failed to map size={}", object->size); - } else { - cmd_buffer.map_address = object->dma_map_addr; - } + cmd_buffer.map_address = nvmap.PinHandle(cmd_buffer.map_handle); } std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBuffer)); std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(), @@ -168,11 +160,16 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto } NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { - // This is intntionally stubbed. - // Skip unmapping buffers here, as to not break the continuity of the VP9 reference frame - // addresses, and risk invalidating data before the async GPU thread is done with it + IoctlMapBuffer params{}; + std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer)); + std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); + + SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer)); + for (auto& cmd_buffer : cmd_buffer_handles) { + nvmap.UnpinHandle(cmd_buffer.map_handle); + } + std::memset(output.data(), 0, output.size()); - LOG_DEBUG(Service_NVDRV, "(STUBBED) called"); return NvResult::Success; } @@ -183,4 +180,9 @@ NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input, return NvResult::Success; } +Kernel::KEvent* nvhost_nvdec_common::QueryEvent(u32 event_id) { + LOG_CRITICAL(Service_NVDRV, "Unknown HOSTX1 Event {}", event_id); + return nullptr; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index e28c54df6..fe76100c8 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h @@ -1,24 +1,28 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include <deque> #include <vector> #include "common/common_types.h" #include "common/swap.h" +#include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" namespace Service::Nvidia { -class SyncpointManager; + +namespace NvCore { +class Container; +class NvMap; +} // namespace NvCore namespace Devices { -class nvmap; class nvhost_nvdec_common : public nvdevice { public: - explicit nvhost_nvdec_common(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, - SyncpointManager& syncpoint_manager_); + explicit nvhost_nvdec_common(Core::System& system_, NvCore::Container& core, + NvCore::ChannelType channel_type); ~nvhost_nvdec_common() override; protected: @@ -111,11 +115,15 @@ protected: NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output); NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output); - std::unordered_map<DeviceFD, u32> fd_to_id{}; + Kernel::KEvent* QueryEvent(u32 event_id) override; + + u32 channel_syncpoint; s32_le nvmap_fd{}; u32_le submit_timeout{}; - std::shared_ptr<nvmap> nvmap_dev; - SyncpointManager& syncpoint_manager; + NvCore::Container& core; + NvCore::SyncpointManager& syncpoint_manager; + NvCore::NvMap& nvmap; + NvCore::ChannelType channel_type; std::array<u32, MaxSyncPoints> device_syncpoints{}; }; }; // namespace Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp index c2be3cea7..bdbc2f9e1 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h index 6045e5cbd..440e7d371 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index 76b39806f..73f97136e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp @@ -1,17 +1,17 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/devices/nvhost_vic.h" #include "video_core/renderer_base.h" namespace Service::Nvidia::Devices { -nvhost_vic::nvhost_vic(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, - SyncpointManager& syncpoint_manager_) - : nvhost_nvdec_common{system_, std::move(nvmap_dev_), syncpoint_manager_} {} + +nvhost_vic::nvhost_vic(Core::System& system_, NvCore::Container& core_) + : nvhost_nvdec_common{system_, core_, NvCore::ChannelType::VIC} {} nvhost_vic::~nvhost_vic() = default; @@ -20,11 +20,13 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& i switch (command.group) { case 0x0: switch (command.cmd) { - case 0x1: - if (!fd_to_id.contains(fd)) { - fd_to_id[fd] = next_id++; + case 0x1: { + auto& host1x_file = core.Host1xDeviceFile(); + if (!host1x_file.fd_to_id.contains(fd)) { + host1x_file.fd_to_id[fd] = host1x_file.vic_next_id++; } return Submit(fd, input, output); + } case 0x2: return GetSyncpoint(input, output); case 0x3: @@ -68,8 +70,9 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& i void nvhost_vic::OnOpen(DeviceFD fd) {} void nvhost_vic::OnClose(DeviceFD fd) { - const auto iter = fd_to_id.find(fd); - if (iter != fd_to_id.end()) { + auto& host1x_file = core.Host1xDeviceFile(); + const auto iter = host1x_file.fd_to_id.find(fd); + if (iter != host1x_file.fd_to_id.end()) { system.GPU().ClearCdmaInstance(iter->second); } } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index c9732c037..f164caafb 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -10,8 +9,7 @@ namespace Service::Nvidia::Devices { class nvhost_vic final : public nvhost_nvdec_common { public: - explicit nvhost_vic(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, - SyncpointManager& syncpoint_manager_); + explicit nvhost_vic(Core::System& system_, NvCore::Container& core); ~nvhost_vic(); NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, @@ -23,8 +21,5 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; - -private: - u32 next_id{}; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index dc59b4494..ddf273b5e 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -1,21 +1,27 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> +#include <bit> #include <cstring> +#include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" +#include "core/core.h" +#include "core/hle/kernel/k_page_table.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvmap.h" +#include "core/memory.h" + +using Core::Memory::YUZU_PAGESIZE; namespace Service::Nvidia::Devices { -nvmap::nvmap(Core::System& system_) : nvdevice{system_} { - // Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to - // represent this. - CreateObject(0); -} +nvmap::nvmap(Core::System& system_, NvCore::Container& container_) + : nvdevice{system_}, container{container_}, file{container.GetNvMapFile()} {} nvmap::~nvmap() = default; @@ -63,39 +69,21 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, void nvmap::OnOpen(DeviceFD fd) {} void nvmap::OnClose(DeviceFD fd) {} -VAddr nvmap::GetObjectAddress(u32 handle) const { - auto object = GetObject(handle); - ASSERT(object); - ASSERT(object->status == Object::Status::Allocated); - return object->addr; -} - -u32 nvmap::CreateObject(u32 size) { - // Create a new nvmap object and obtain a handle to it. - auto object = std::make_shared<Object>(); - object->id = next_id++; - object->size = size; - object->status = Object::Status::Created; - object->refcount = 1; - - const u32 handle = next_handle++; - - handles.insert_or_assign(handle, std::move(object)); - - return handle; -} - NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { IocCreateParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size); - - if (!params.size) { - LOG_ERROR(Service_NVDRV, "Size is 0"); - return NvResult::BadValue; + LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size); + + std::shared_ptr<NvCore::NvMap::Handle> handle_description{}; + auto result = + file.CreateHandle(Common::AlignUp(params.size, YUZU_PAGESIZE), handle_description); + if (result != NvResult::Success) { + LOG_CRITICAL(Service_NVDRV, "Failed to create Object"); + return result; } - - params.handle = CreateObject(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: 0x{:X}", handle_description->id, params.size); std::memcpy(output.data(), ¶ms, sizeof(params)); return NvResult::Success; @@ -104,63 +92,68 @@ NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { IocAllocParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr); + LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address); if (!params.handle) { - LOG_ERROR(Service_NVDRV, "Handle is 0"); + LOG_CRITICAL(Service_NVDRV, "Handle is 0"); return NvResult::BadValue; } if ((params.align - 1) & params.align) { - LOG_ERROR(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align); + LOG_CRITICAL(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align); return NvResult::BadValue; } - const u32 min_alignment = 0x1000; - if (params.align < min_alignment) { - params.align = min_alignment; + // Force page size alignment at a minimum + if (params.align < YUZU_PAGESIZE) { + params.align = YUZU_PAGESIZE; } - auto object = GetObject(params.handle); - if (!object) { - LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); + auto handle_description{file.GetHandle(params.handle)}; + if (!handle_description) { + LOG_CRITICAL(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); return NvResult::BadValue; } - if (object->status == Object::Status::Allocated) { - LOG_ERROR(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle); + if (handle_description->allocated) { + LOG_CRITICAL(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle); return NvResult::InsufficientMemory; } - object->flags = params.flags; - object->align = params.align; - object->kind = params.kind; - object->addr = params.addr; - object->status = Object::Status::Allocated; - + const auto result = + handle_description->Alloc(params.flags, params.align, params.kind, params.address); + if (result != NvResult::Success) { + LOG_CRITICAL(Service_NVDRV, "Object failed to allocate, handle={:08X}", params.handle); + return result; + } + ASSERT(system.CurrentProcess() + ->PageTable() + .LockForDeviceAddressSpace(handle_description->address, handle_description->size) + .IsSuccess()); std::memcpy(output.data(), ¶ms, sizeof(params)); - return NvResult::Success; + return result; } NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) { IocGetIdParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service_NVDRV, "called"); + LOG_DEBUG(Service_NVDRV, "called"); + // See the comment in FromId for extra info on this function if (!params.handle) { - LOG_ERROR(Service_NVDRV, "Handle is zero"); + LOG_CRITICAL(Service_NVDRV, "Error!"); return NvResult::BadValue; } - auto object = GetObject(params.handle); - if (!object) { - LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); - return NvResult::BadValue; + auto handle_description{file.GetHandle(params.handle)}; + if (!handle_description) { + LOG_CRITICAL(Service_NVDRV, "Error!"); + return NvResult::AccessDenied; // This will always return EPERM irrespective of if the + // handle exists or not } - params.id = object->id; - + params.id = handle_description->id; std::memcpy(output.data(), ¶ms, sizeof(params)); return NvResult::Success; } @@ -169,26 +162,29 @@ NvResult nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) IocFromIdParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + LOG_DEBUG(Service_NVDRV, "called, id:{}", params.id); - auto itr = std::find_if(handles.begin(), handles.end(), - [&](const auto& entry) { return entry.second->id == params.id; }); - if (itr == handles.end()) { - LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); + // Handles and IDs are always the same value in nvmap however IDs can be used globally given the + // right permissions. + // Since we don't plan on ever supporting multiprocess we can skip implementing handle refs and + // so this function just does simple validation and passes through the handle id. + if (!params.id) { + LOG_CRITICAL(Service_NVDRV, "Zero Id is invalid!"); return NvResult::BadValue; } - auto& object = itr->second; - if (object->status != Object::Status::Allocated) { - LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle); + auto handle_description{file.GetHandle(params.id)}; + if (!handle_description) { + LOG_CRITICAL(Service_NVDRV, "Unregistered handle!"); return NvResult::BadValue; } - itr->second->refcount++; - - // Return the existing handle instead of creating a new one. - params.handle = itr->first; - + auto result = handle_description->Duplicate(false); + if (result != NvResult::Success) { + LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!"); + return result; + } + params.handle = handle_description->id; std::memcpy(output.data(), ¶ms, sizeof(params)); return NvResult::Success; } @@ -199,35 +195,43 @@ NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) IocParamParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param); + LOG_DEBUG(Service_NVDRV, "called type={}", params.param); - auto object = GetObject(params.handle); - if (!object) { - LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); + if (!params.handle) { + LOG_CRITICAL(Service_NVDRV, "Invalid handle!"); return NvResult::BadValue; } - if (object->status != Object::Status::Allocated) { - LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle); + auto handle_description{file.GetHandle(params.handle)}; + if (!handle_description) { + LOG_CRITICAL(Service_NVDRV, "Not registered handle!"); return NvResult::BadValue; } - switch (static_cast<ParamTypes>(params.param)) { - case ParamTypes::Size: - params.result = object->size; + switch (params.param) { + case HandleParameterType::Size: + params.result = static_cast<u32_le>(handle_description->orig_size); + break; + case HandleParameterType::Alignment: + params.result = static_cast<u32_le>(handle_description->align); break; - case ParamTypes::Alignment: - params.result = object->align; + case HandleParameterType::Base: + params.result = static_cast<u32_le>(-22); // posix EINVAL break; - case ParamTypes::Heap: - // TODO(Subv): Seems to be a hardcoded value? - params.result = 0x40000000; + case HandleParameterType::Heap: + if (handle_description->allocated) + params.result = 0x40000000; + else + params.result = 0; break; - case ParamTypes::Kind: - params.result = object->kind; + case HandleParameterType::Kind: + params.result = handle_description->kind; + break; + case HandleParameterType::IsSharedMemMapped: + params.result = handle_description->is_shared_mem_mapped; break; default: - UNIMPLEMENTED(); + return NvResult::BadValue; } std::memcpy(output.data(), ¶ms, sizeof(params)); @@ -235,46 +239,29 @@ NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) } NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { - // TODO(Subv): These flags are unconfirmed. - enum FreeFlags { - Freed = 0, - NotFreedYet = 1, - }; - IocFreeParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + LOG_DEBUG(Service_NVDRV, "called"); - auto itr = handles.find(params.handle); - if (itr == handles.end()) { - LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); - return NvResult::BadValue; - } - if (!itr->second->refcount) { - LOG_ERROR( - Service_NVDRV, - "There is no references to this object. The object is already freed. handle={:08X}", - params.handle); - return NvResult::BadValue; + if (!params.handle) { + LOG_CRITICAL(Service_NVDRV, "Handle null freed?"); + return NvResult::Success; } - itr->second->refcount--; - - params.size = itr->second->size; - - if (itr->second->refcount == 0) { - params.flags = Freed; - // The address of the nvmap is written to the output if we're finally freeing it, otherwise - // 0 is written. - params.address = itr->second->addr; + if (auto freeInfo{file.FreeHandle(params.handle, false)}) { + ASSERT(system.CurrentProcess() + ->PageTable() + .UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size) + .IsSuccess()); + params.address = freeInfo->address; + params.size = static_cast<u32>(freeInfo->size); + params.flags.raw = 0; + params.flags.map_uncached.Assign(freeInfo->was_uncached); } else { - params.flags = NotFreedYet; - params.address = 0; + // This is possible when there's internel dups or other duplicates. } - handles.erase(params.handle); - std::memcpy(output.data(), ¶ms, sizeof(params)); return NvResult::Success; } diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index d90b69e5a..e9bfd0358 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -10,15 +9,23 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" +#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" +namespace Service::Nvidia::NvCore { +class Container; +} // namespace Service::Nvidia::NvCore + namespace Service::Nvidia::Devices { class nvmap final : public nvdevice { public: - explicit nvmap(Core::System& system_); + explicit nvmap(Core::System& system_, NvCore::Container& container); ~nvmap() override; + nvmap(const nvmap&) = delete; + nvmap& operator=(const nvmap&) = delete; + NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, @@ -29,31 +36,15 @@ public: void OnOpen(DeviceFD fd) override; void OnClose(DeviceFD fd) override; - /// Returns the allocated address of an nvmap object given its handle. - VAddr GetObjectAddress(u32 handle) const; - - /// Represents an nvmap object. - struct Object { - enum class Status { Created, Allocated }; - u32 id; - u32 size; - u32 flags; - u32 align; - u8 kind; - VAddr addr; - Status status; - u32 refcount; - u32 dma_map_addr; + enum class HandleParameterType : u32_le { + Size = 1, + Alignment = 2, + Base = 3, + Heap = 4, + Kind = 5, + IsSharedMemMapped = 6 }; - std::shared_ptr<Object> GetObject(u32 handle) const { - auto itr = handles.find(handle); - if (itr != handles.end()) { - return itr->second; - } - return {}; - } - private: /// Id to use for the next handle that is created. u32 next_handle = 0; @@ -61,9 +52,6 @@ private: /// Id to use for the next object that is created. u32 next_id = 0; - /// Mapping of currently allocated handles to the objects they represent. - std::unordered_map<u32, std::shared_ptr<Object>> handles; - struct IocCreateParams { // Input u32_le size{}; @@ -84,11 +72,11 @@ private: // Input u32_le handle{}; u32_le heap_mask{}; - u32_le flags{}; + NvCore::NvMap::Handle::Flags flags{}; u32_le align{}; u8 kind{}; INSERT_PADDING_BYTES(7); - u64_le addr{}; + u64_le address{}; }; static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size"); @@ -97,14 +85,14 @@ private: INSERT_PADDING_BYTES(4); u64_le address{}; u32_le size{}; - u32_le flags{}; + NvCore::NvMap::Handle::Flags flags{}; }; static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size"); struct IocParamParams { // Input u32_le handle{}; - u32_le param{}; + HandleParameterType param{}; // Output u32_le result{}; }; @@ -118,14 +106,15 @@ private: }; static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); - u32 CreateObject(u32 size); - NvResult IocCreate(const std::vector<u8>& input, std::vector<u8>& output); NvResult IocAlloc(const std::vector<u8>& input, std::vector<u8>& output); NvResult IocGetId(const std::vector<u8>& input, std::vector<u8>& output); NvResult IocFromId(const std::vector<u8>& input, std::vector<u8>& output); NvResult IocParam(const std::vector<u8>& input, std::vector<u8>& output); NvResult IocFree(const std::vector<u8>& input, std::vector<u8>& output); + + NvCore::Container& container; + NvCore::NvMap& file; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h index 5ab221fc1..0e2f47075 100644 --- a/src/core/hle/service/nvdrv/nvdata.h +++ b/src/core/hle/service/nvdrv/nvdata.h @@ -1,6 +1,6 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once @@ -16,17 +16,11 @@ using DeviceFD = s32; constexpr DeviceFD INVALID_NVDRV_FD = -1; -struct Fence { +struct NvFence { s32 id; u32 value; }; - -static_assert(sizeof(Fence) == 8, "Fence has wrong size"); - -struct MultiFence { - u32 num_fences; - std::array<Fence, 4> fences; -}; +static_assert(sizeof(NvFence) == 8, "NvFence has wrong size"); enum class NvResult : u32 { Success = 0x0, @@ -85,11 +79,15 @@ enum class NvResult : u32 { ModuleNotPresent = 0xA000E, }; +// obtained from +// https://github.com/skyline-emu/skyline/blob/nvdec-dev/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost/ctrl.h#L47 enum class EventState { - Free = 0, - Registered = 1, - Waiting = 2, - Busy = 3, + Available = 0, + Waiting = 1, + Cancelling = 2, + Signalling = 3, + Signalled = 4, + Cancelled = 5, }; union Ioctl { diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index aa7e47cbf..5e7b7468f 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -1,6 +1,6 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #include <utility> @@ -9,6 +9,7 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_writable_event.h" +#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" @@ -16,17 +17,31 @@ #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" #include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" +#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h" #include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h" #include "core/hle/service/nvdrv/devices/nvhost_vic.h" #include "core/hle/service/nvdrv/devices/nvmap.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvdrv/nvdrv_interface.h" #include "core/hle/service/nvdrv/nvmemp.h" -#include "core/hle/service/nvdrv/syncpoint_manager.h" #include "core/hle/service/nvflinger/nvflinger.h" +#include "video_core/gpu.h" namespace Service::Nvidia { +EventInterface::EventInterface(Module& module_) : module{module_}, guard{}, on_signal{} {} + +EventInterface::~EventInterface() = default; + +Kernel::KEvent* EventInterface::CreateEvent(std::string name) { + Kernel::KEvent* new_event = module.service_context.CreateEvent(std::move(name)); + return new_event; +} + +void EventInterface::FreeEvent(Kernel::KEvent* event) { + module.service_context.CloseEvent(event); +} + void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, Core::System& system) { auto module_ = std::make_shared<Module>(system); @@ -39,34 +54,54 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger } Module::Module(Core::System& system) - : syncpoint_manager{system.GPU()}, service_context{system, "nvdrv"} { - for (u32 i = 0; i < MaxNvEvents; i++) { - events_interface.events[i].event = - service_context.CreateEvent(fmt::format("NVDRV::NvEvent_{}", i)); - events_interface.status[i] = EventState::Free; - events_interface.registered[i] = false; - } - auto nvmap_dev = std::make_shared<Devices::nvmap>(system); - devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev); - devices["/dev/nvhost-gpu"] = - std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev, syncpoint_manager); - devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(system); - devices["/dev/nvmap"] = nvmap_dev; - devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev); - devices["/dev/nvhost-ctrl"] = - std::make_shared<Devices::nvhost_ctrl>(system, events_interface, syncpoint_manager); - devices["/dev/nvhost-nvdec"] = - std::make_shared<Devices::nvhost_nvdec>(system, nvmap_dev, syncpoint_manager); - devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system); - devices["/dev/nvhost-vic"] = - std::make_shared<Devices::nvhost_vic>(system, nvmap_dev, syncpoint_manager); + : service_context{system, "nvdrv"}, events_interface{*this}, container{system.Host1x()} { + builders["/dev/nvhost-as-gpu"] = [this, &system](DeviceFD fd) { + std::shared_ptr<Devices::nvdevice> device = + std::make_shared<Devices::nvhost_as_gpu>(system, *this, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-gpu"] = [this, &system](DeviceFD fd) { + std::shared_ptr<Devices::nvdevice> device = + std::make_shared<Devices::nvhost_gpu>(system, events_interface, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-ctrl-gpu"] = [this, &system](DeviceFD fd) { + std::shared_ptr<Devices::nvdevice> device = + std::make_shared<Devices::nvhost_ctrl_gpu>(system, events_interface); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvmap"] = [this, &system](DeviceFD fd) { + std::shared_ptr<Devices::nvdevice> device = + std::make_shared<Devices::nvmap>(system, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvdisp_disp0"] = [this, &system](DeviceFD fd) { + std::shared_ptr<Devices::nvdevice> device = + std::make_shared<Devices::nvdisp_disp0>(system, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-ctrl"] = [this, &system](DeviceFD fd) { + std::shared_ptr<Devices::nvdevice> device = + std::make_shared<Devices::nvhost_ctrl>(system, events_interface, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-nvdec"] = [this, &system](DeviceFD fd) { + std::shared_ptr<Devices::nvdevice> device = + std::make_shared<Devices::nvhost_nvdec>(system, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-nvjpg"] = [this, &system](DeviceFD fd) { + std::shared_ptr<Devices::nvdevice> device = std::make_shared<Devices::nvhost_nvjpg>(system); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-vic"] = [this, &system](DeviceFD fd) { + std::shared_ptr<Devices::nvdevice> device = + std::make_shared<Devices::nvhost_vic>(system, container); + return open_files.emplace(fd, device).first; + }; } -Module::~Module() { - for (u32 i = 0; i < MaxNvEvents; i++) { - service_context.CloseEvent(events_interface.events[i].event); - } -} +Module::~Module() {} NvResult Module::VerifyFD(DeviceFD fd) const { if (fd < 0) { @@ -83,18 +118,18 @@ NvResult Module::VerifyFD(DeviceFD fd) const { } DeviceFD Module::Open(const std::string& device_name) { - if (devices.find(device_name) == devices.end()) { + auto it = builders.find(device_name); + if (it == builders.end()) { LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name); return INVALID_NVDRV_FD; } - auto device = devices[device_name]; const DeviceFD fd = next_fd++; + auto& builder = it->second; + auto device = builder(fd)->second; device->OnOpen(fd); - open_files[fd] = std::move(device); - return fd; } @@ -169,22 +204,24 @@ NvResult Module::Close(DeviceFD fd) { return NvResult::Success; } -void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) { - for (u32 i = 0; i < MaxNvEvents; i++) { - if (events_interface.assigned_syncpt[i] == syncpoint_id && - events_interface.assigned_value[i] == value) { - events_interface.LiberateEvent(i); - events_interface.events[i].event->GetWritableEvent().Signal(); - } +NvResult Module::QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event) { + if (fd < 0) { + LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); + return NvResult::InvalidState; } -} -Kernel::KReadableEvent& Module::GetEvent(const u32 event_id) { - return events_interface.events[event_id].event->GetReadableEvent(); -} + const auto itr = open_files.find(fd); -Kernel::KWritableEvent& Module::GetEventWriteable(const u32 event_id) { - return events_interface.events[event_id].event->GetWritableEvent(); + if (itr == open_files.end()) { + LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd); + return NvResult::NotImplemented; + } + + event = itr->second->QueryEvent(event_id); + if (!event) { + return NvResult::BadParameter; + } + return NvResult::Success; } } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index a5af5b785..146d046a9 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -1,17 +1,21 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once +#include <functional> +#include <list> #include <memory> +#include <string> #include <unordered_map> #include <vector> #include "common/common_types.h" #include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/nvdata.h" -#include "core/hle/service/nvdrv/syncpoint_manager.h" +#include "core/hle/service/nvflinger/ui/fence.h" #include "core/hle/service/service.h" namespace Core { @@ -28,81 +32,31 @@ class NVFlinger; namespace Service::Nvidia { +namespace NvCore { +class Container; class SyncpointManager; +} // namespace NvCore namespace Devices { class nvdevice; -} +class nvhost_ctrl; +} // namespace Devices -/// Represents an Nvidia event -struct NvEvent { - Kernel::KEvent* event{}; - Fence fence{}; -}; +class Module; -struct EventInterface { - // Mask representing currently busy events - u64 events_mask{}; - // Each kernel event associated to an NV event - std::array<NvEvent, MaxNvEvents> events; - // The status of the current NVEvent - std::array<EventState, MaxNvEvents> status{}; - // Tells if an NVEvent is registered or not - std::array<bool, MaxNvEvents> registered{}; - // Tells the NVEvent that it has failed. - std::array<bool, MaxNvEvents> failed{}; - // When an NVEvent is waiting on GPU interrupt, this is the sync_point - // associated with it. - std::array<u32, MaxNvEvents> assigned_syncpt{}; - // This is the value of the GPU interrupt for which the NVEvent is waiting - // for. - std::array<u32, MaxNvEvents> assigned_value{}; - // Constant to denote an unasigned syncpoint. - static constexpr u32 unassigned_syncpt = 0xFFFFFFFF; - std::optional<u32> GetFreeEvent() const { - u64 mask = events_mask; - for (u32 i = 0; i < MaxNvEvents; i++) { - const bool is_free = (mask & 0x1) == 0; - if (is_free) { - if (status[i] == EventState::Registered || status[i] == EventState::Free) { - return {i}; - } - } - mask = mask >> 1; - } - return std::nullopt; - } - void SetEventStatus(const u32 event_id, EventState new_status) { - EventState old_status = status[event_id]; - if (old_status == new_status) { - return; - } - status[event_id] = new_status; - if (new_status == EventState::Registered) { - registered[event_id] = true; - } - if (new_status == EventState::Waiting || new_status == EventState::Busy) { - events_mask |= (1ULL << event_id); - } - } - void RegisterEvent(const u32 event_id) { - registered[event_id] = true; - if (status[event_id] == EventState::Free) { - status[event_id] = EventState::Registered; - } - } - void UnregisterEvent(const u32 event_id) { - registered[event_id] = false; - if (status[event_id] == EventState::Registered) { - status[event_id] = EventState::Free; - } - } - void LiberateEvent(const u32 event_id) { - status[event_id] = registered[event_id] ? EventState::Registered : EventState::Free; - events_mask &= ~(1ULL << event_id); - assigned_syncpt[event_id] = unassigned_syncpt; - assigned_value[event_id] = 0; - } +class EventInterface { +public: + explicit EventInterface(Module& module_); + ~EventInterface(); + + Kernel::KEvent* CreateEvent(std::string name); + + void FreeEvent(Kernel::KEvent* event); + +private: + Module& module; + std::mutex guard; + std::list<Devices::nvhost_ctrl*> on_signal; }; class Module final { @@ -112,9 +66,9 @@ public: /// Returns a pointer to one of the available devices, identified by its name. template <typename T> - std::shared_ptr<T> GetDevice(const std::string& name) { - auto itr = devices.find(name); - if (itr == devices.end()) + std::shared_ptr<T> GetDevice(DeviceFD fd) { + auto itr = open_files.find(fd); + if (itr == open_files.end()) return nullptr; return std::static_pointer_cast<T>(itr->second); } @@ -137,28 +91,27 @@ public: /// Closes a device file descriptor and returns operation success. NvResult Close(DeviceFD fd); - void SignalSyncpt(const u32 syncpoint_id, const u32 value); - - Kernel::KReadableEvent& GetEvent(u32 event_id); - - Kernel::KWritableEvent& GetEventWriteable(u32 event_id); + NvResult QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event); private: - /// Manages syncpoints on the host - SyncpointManager syncpoint_manager; + friend class EventInterface; + friend class Service::NVFlinger::NVFlinger; /// Id to use for the next open file descriptor. DeviceFD next_fd = 1; + using FilesContainerType = std::unordered_map<DeviceFD, std::shared_ptr<Devices::nvdevice>>; /// Mapping of file descriptors to the devices they reference. - std::unordered_map<DeviceFD, std::shared_ptr<Devices::nvdevice>> open_files; + FilesContainerType open_files; - /// Mapping of device node names to their implementation. - std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices; + KernelHelpers::ServiceContext service_context; EventInterface events_interface; - KernelHelpers::ServiceContext service_context; + /// Manages syncpoints on the host + NvCore::Container container; + + std::unordered_map<std::string, std::function<FilesContainerType::iterator(DeviceFD)>> builders; }; /// Registers all NVDRV services with the specified service manager. diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp index c16babe14..edbdfee43 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp +++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp @@ -1,11 +1,12 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors +// SPDX-License-Identifier: GPL-3.0-or-later #include <cinttypes> #include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_readable_event.h" #include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/nvdrv.h" @@ -13,10 +14,6 @@ namespace Service::Nvidia { -void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) { - nvdrv->SignalSyncpt(syncpoint_id, value); -} - void NVDRV::Open(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NVDRV, "called"); IPC::ResponseBuilder rb{ctx, 4}; @@ -26,7 +23,7 @@ void NVDRV::Open(Kernel::HLERequestContext& ctx) { rb.Push<DeviceFD>(0); rb.PushEnum(NvResult::NotInitialized); - LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + LOG_ERROR(Service_NVDRV, "NvServices is not initialized!"); return; } @@ -61,7 +58,7 @@ void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) { if (!is_initialized) { ServiceError(ctx, NvResult::NotInitialized); - LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + LOG_ERROR(Service_NVDRV, "NvServices is not initialized!"); return; } @@ -87,7 +84,7 @@ void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) { if (!is_initialized) { ServiceError(ctx, NvResult::NotInitialized); - LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + LOG_ERROR(Service_NVDRV, "NvServices is not initialized!"); return; } @@ -114,7 +111,7 @@ void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) { if (!is_initialized) { ServiceError(ctx, NvResult::NotInitialized); - LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + LOG_ERROR(Service_NVDRV, "NvServices is not initialized!"); return; } @@ -139,7 +136,7 @@ void NVDRV::Close(Kernel::HLERequestContext& ctx) { if (!is_initialized) { ServiceError(ctx, NvResult::NotInitialized); - LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + LOG_ERROR(Service_NVDRV, "NvServices is not initialized!"); return; } @@ -165,33 +162,28 @@ void NVDRV::Initialize(Kernel::HLERequestContext& ctx) { void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto fd = rp.Pop<DeviceFD>(); - const auto event_id = rp.Pop<u32>() & 0x00FF; - LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id); + const auto event_id = rp.Pop<u32>(); if (!is_initialized) { ServiceError(ctx, NvResult::NotInitialized); - LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + LOG_ERROR(Service_NVDRV, "NvServices is not initialized!"); return; } - const auto nv_result = nvdrv->VerifyFD(fd); - if (nv_result != NvResult::Success) { - LOG_ERROR(Service_NVDRV, "Invalid FD specified DeviceFD={}!", fd); - ServiceError(ctx, nv_result); - return; - } + Kernel::KEvent* event = nullptr; + NvResult result = nvdrv->QueryEvent(fd, event_id, event); - if (event_id < MaxNvEvents) { + if (result == NvResult::Success) { IPC::ResponseBuilder rb{ctx, 3, 1}; rb.Push(ResultSuccess); - auto& event = nvdrv->GetEvent(event_id); - event.Clear(); - rb.PushCopyObjects(event); + auto& readable_event = event->GetReadableEvent(); + rb.PushCopyObjects(readable_event); rb.PushEnum(NvResult::Success); } else { + LOG_ERROR(Service_NVDRV, "Invalid event request!"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.PushEnum(NvResult::BadParameter); + rb.PushEnum(result); } } @@ -230,7 +222,7 @@ void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) { } NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name) - : ServiceFramework{system_, name}, nvdrv{std::move(nvdrv_)} { + : ServiceFramework{system_, name, ServiceThreadType::CreateNew}, nvdrv{std::move(nvdrv_)} { static const FunctionInfo functions[] = { {0, &NVDRV::Open, "Open"}, {1, &NVDRV::Ioctl1, "Ioctl"}, diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.h b/src/core/hle/service/nvdrv/nvdrv_interface.h index 0e764c53f..cd58a4f35 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.h +++ b/src/core/hle/service/nvdrv/nvdrv_interface.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -19,8 +18,6 @@ public: explicit NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name); ~NVDRV() override; - void SignalGPUInterruptSyncpt(u32 syncpoint_id, u32 value); - private: void Open(Kernel::HLERequestContext& ctx); void Ioctl1(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/nvdrv/nvmemp.cpp b/src/core/hle/service/nvdrv/nvmemp.cpp index 331c02243..e433580b1 100644 --- a/src/core/hle/service/nvdrv/nvmemp.cpp +++ b/src/core/hle/service/nvdrv/nvmemp.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "common/logging/log.h" diff --git a/src/core/hle/service/nvdrv/nvmemp.h b/src/core/hle/service/nvdrv/nvmemp.h index 724c27ef9..3d4276327 100644 --- a/src/core/hle/service/nvdrv/nvmemp.h +++ b/src/core/hle/service/nvdrv/nvmemp.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/syncpoint_manager.cpp deleted file mode 100644 index 3b6f55526..000000000 --- a/src/core/hle/service/nvdrv/syncpoint_manager.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/assert.h" -#include "core/hle/service/nvdrv/syncpoint_manager.h" -#include "video_core/gpu.h" - -namespace Service::Nvidia { - -SyncpointManager::SyncpointManager(Tegra::GPU& gpu_) : gpu{gpu_} {} - -SyncpointManager::~SyncpointManager() = default; - -u32 SyncpointManager::RefreshSyncpoint(u32 syncpoint_id) { - syncpoints[syncpoint_id].min = gpu.GetSyncpointValue(syncpoint_id); - return GetSyncpointMin(syncpoint_id); -} - -u32 SyncpointManager::AllocateSyncpoint() { - for (u32 syncpoint_id = 1; syncpoint_id < MaxSyncPoints; syncpoint_id++) { - if (!syncpoints[syncpoint_id].is_allocated) { - syncpoints[syncpoint_id].is_allocated = true; - return syncpoint_id; - } - } - UNREACHABLE_MSG("No more available syncpoints!"); - return {}; -} - -u32 SyncpointManager::IncreaseSyncpoint(u32 syncpoint_id, u32 value) { - for (u32 index = 0; index < value; ++index) { - syncpoints[syncpoint_id].max.fetch_add(1, std::memory_order_relaxed); - } - - return GetSyncpointMax(syncpoint_id); -} - -} // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.h b/src/core/hle/service/nvdrv/syncpoint_manager.h deleted file mode 100644 index 99f286474..000000000 --- a/src/core/hle/service/nvdrv/syncpoint_manager.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <array> -#include <atomic> - -#include "common/common_types.h" -#include "core/hle/service/nvdrv/nvdata.h" - -namespace Tegra { -class GPU; -} - -namespace Service::Nvidia { - -class SyncpointManager final { -public: - explicit SyncpointManager(Tegra::GPU& gpu_); - ~SyncpointManager(); - - /** - * Returns true if the specified syncpoint is expired for the given value. - * @param syncpoint_id Syncpoint ID to check. - * @param value Value to check against the specified syncpoint. - * @returns True if the specified syncpoint is expired for the given value, otherwise False. - */ - bool IsSyncpointExpired(u32 syncpoint_id, u32 value) const { - return (GetSyncpointMax(syncpoint_id) - value) >= (GetSyncpointMin(syncpoint_id) - value); - } - - /** - * Gets the lower bound for the specified syncpoint. - * @param syncpoint_id Syncpoint ID to get the lower bound for. - * @returns The lower bound for the specified syncpoint. - */ - u32 GetSyncpointMin(u32 syncpoint_id) const { - return syncpoints.at(syncpoint_id).min.load(std::memory_order_relaxed); - } - - /** - * Gets the uper bound for the specified syncpoint. - * @param syncpoint_id Syncpoint ID to get the upper bound for. - * @returns The upper bound for the specified syncpoint. - */ - u32 GetSyncpointMax(u32 syncpoint_id) const { - return syncpoints.at(syncpoint_id).max.load(std::memory_order_relaxed); - } - - /** - * Refreshes the minimum value for the specified syncpoint. - * @param syncpoint_id Syncpoint ID to be refreshed. - * @returns The new syncpoint minimum value. - */ - u32 RefreshSyncpoint(u32 syncpoint_id); - - /** - * Allocates a new syncoint. - * @returns The syncpoint ID for the newly allocated syncpoint. - */ - u32 AllocateSyncpoint(); - - /** - * Increases the maximum value for the specified syncpoint. - * @param syncpoint_id Syncpoint ID to be increased. - * @param value Value to increase the specified syncpoint by. - * @returns The new syncpoint maximum value. - */ - u32 IncreaseSyncpoint(u32 syncpoint_id, u32 value); - -private: - struct Syncpoint { - std::atomic<u32> min; - std::atomic<u32> max; - std::atomic<bool> is_allocated; - }; - - std::array<Syncpoint, MaxSyncPoints> syncpoints{}; - - Tegra::GPU& gpu; -}; - -} // namespace Service::Nvidia diff --git a/src/core/hle/service/nvflinger/binder.h b/src/core/hle/service/nvflinger/binder.h new file mode 100644 index 000000000..157333ff8 --- /dev/null +++ b/src/core/hle/service/nvflinger/binder.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/binder/IBinder.h + +#pragma once + +#include "common/common_types.h" + +namespace Kernel { +class HLERequestContext; +class KReadableEvent; +} // namespace Kernel + +namespace Service::android { + +enum class TransactionId { + RequestBuffer = 1, + SetBufferCount = 2, + DequeueBuffer = 3, + DetachBuffer = 4, + DetachNextBuffer = 5, + AttachBuffer = 6, + QueueBuffer = 7, + CancelBuffer = 8, + Query = 9, + Connect = 10, + Disconnect = 11, + AllocateBuffers = 13, + SetPreallocatedBuffer = 14, + GetBufferHistory = 17, +}; + +class IBinder { +public: + virtual ~IBinder() = default; + virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, + u32 flags) = 0; + virtual Kernel::KReadableEvent& GetNativeHandle() = 0; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_item.h b/src/core/hle/service/nvflinger/buffer_item.h new file mode 100644 index 000000000..f73dec4f1 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_item.h @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItem.h + +#pragma once + +#include <memory> + +#include "common/common_types.h" +#include "common/math_util.h" +#include "core/hle/service/nvflinger/ui/fence.h" +#include "core/hle/service/nvflinger/window.h" + +namespace Service::android { + +class GraphicBuffer; + +class BufferItem final { +public: + constexpr BufferItem() = default; + + std::shared_ptr<GraphicBuffer> graphic_buffer; + Fence fence; + Common::Rectangle<s32> crop; + NativeWindowTransform transform{}; + u32 scaling_mode{}; + s64 timestamp{}; + bool is_auto_timestamp{}; + u64 frame_number{}; + + // The default value for buf, used to indicate this doesn't correspond to a slot. + static constexpr s32 INVALID_BUFFER_SLOT = -1; + union { + s32 slot{INVALID_BUFFER_SLOT}; + s32 buf; + }; + + bool is_droppable{}; + bool acquire_called{}; + bool transform_to_display_inverse{}; + s32 swap_interval{}; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_item_consumer.cpp b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp new file mode 100644 index 000000000..6d2c92a2c --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferItemConsumer.cpp + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/hle/service/nvflinger/buffer_item.h" +#include "core/hle/service/nvflinger/buffer_item_consumer.h" +#include "core/hle/service/nvflinger/buffer_queue_consumer.h" + +namespace Service::android { + +BufferItemConsumer::BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer_) + : ConsumerBase{std::move(consumer_)} {} + +Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when, + bool wait_for_fence) { + if (!item) { + return Status::BadValue; + } + + std::scoped_lock lock{mutex}; + + if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) { + if (status != Status::NoBufferAvailable) { + LOG_ERROR(Service_NVFlinger, "Failed to acquire buffer: {}", status); + } + return status; + } + + if (wait_for_fence) { + UNIMPLEMENTED(); + } + + item->graphic_buffer = slots[item->slot].graphic_buffer; + + return Status::NoError; +} + +Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) { + std::scoped_lock lock{mutex}; + + if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence); + status != Status::NoError) { + LOG_ERROR(Service_NVFlinger, "Failed to add fence: {}", status); + } + + if (const auto status = ReleaseBufferLocked(item.buf, item.graphic_buffer); + status != Status::NoError) { + LOG_WARNING(Service_NVFlinger, "Failed to release buffer: {}", status); + return status; + } + + return Status::NoError; +} + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_item_consumer.h b/src/core/hle/service/nvflinger/buffer_item_consumer.h new file mode 100644 index 000000000..69046233d --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_item_consumer.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItemConsumer.h + +#pragma once + +#include <chrono> +#include <memory> + +#include "common/common_types.h" +#include "core/hle/service/nvflinger/consumer_base.h" +#include "core/hle/service/nvflinger/status.h" + +namespace Service::android { + +class BufferItem; + +class BufferItemConsumer final : public ConsumerBase { +public: + explicit BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer); + Status AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when, + bool wait_for_fence = true); + Status ReleaseBuffer(const BufferItem& item, Fence& release_fence); +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp deleted file mode 100644 index 5fead6d1b..000000000 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> - -#include "common/assert.h" -#include "common/logging/log.h" -#include "core/core.h" -#include "core/hle/kernel/k_writable_event.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/nvflinger/buffer_queue.h" - -namespace Service::NVFlinger { - -BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_, - KernelHelpers::ServiceContext& service_context_) - : id(id_), layer_id(layer_id_), service_context{service_context_} { - buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent"); -} - -BufferQueue::~BufferQueue() { - service_context.CloseEvent(buffer_wait_event); -} - -void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { - ASSERT(slot < buffer_slots); - LOG_WARNING(Service, "Adding graphics buffer {}", slot); - - { - std::unique_lock lock{free_buffers_mutex}; - free_buffers.push_back(slot); - } - free_buffers_condition.notify_one(); - - buffers[slot] = { - .slot = slot, - .status = Buffer::Status::Free, - .igbp_buffer = igbp_buffer, - .transform = {}, - .crop_rect = {}, - .swap_interval = 0, - .multi_fence = {}, - }; - - buffer_wait_event->GetWritableEvent().Signal(); -} - -std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width, - u32 height) { - // Wait for first request before trying to dequeue - { - std::unique_lock lock{free_buffers_mutex}; - free_buffers_condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; }); - } - - if (!is_connect) { - // Buffer was disconnected while the thread was blocked, this is most likely due to - // emulation being stopped - return std::nullopt; - } - - std::unique_lock lock{free_buffers_mutex}; - - auto f_itr = free_buffers.begin(); - auto slot = buffers.size(); - - while (f_itr != free_buffers.end()) { - const Buffer& buffer = buffers[*f_itr]; - if (buffer.status == Buffer::Status::Free && buffer.igbp_buffer.width == width && - buffer.igbp_buffer.height == height) { - slot = *f_itr; - free_buffers.erase(f_itr); - break; - } - ++f_itr; - } - if (slot == buffers.size()) { - return std::nullopt; - } - buffers[slot].status = Buffer::Status::Dequeued; - return {{buffers[slot].slot, &buffers[slot].multi_fence}}; -} - -const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const { - ASSERT(slot < buffers.size()); - ASSERT(buffers[slot].status == Buffer::Status::Dequeued); - ASSERT(buffers[slot].slot == slot); - - return buffers[slot].igbp_buffer; -} - -void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform, - const Common::Rectangle<int>& crop_rect, u32 swap_interval, - Service::Nvidia::MultiFence& multi_fence) { - ASSERT(slot < buffers.size()); - ASSERT(buffers[slot].status == Buffer::Status::Dequeued); - ASSERT(buffers[slot].slot == slot); - - buffers[slot].status = Buffer::Status::Queued; - buffers[slot].transform = transform; - buffers[slot].crop_rect = crop_rect; - buffers[slot].swap_interval = swap_interval; - buffers[slot].multi_fence = multi_fence; - std::unique_lock lock{queue_sequence_mutex}; - queue_sequence.push_back(slot); -} - -void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence) { - ASSERT(slot < buffers.size()); - ASSERT(buffers[slot].status != Buffer::Status::Free); - ASSERT(buffers[slot].slot == slot); - - buffers[slot].status = Buffer::Status::Free; - buffers[slot].multi_fence = multi_fence; - buffers[slot].swap_interval = 0; - - { - std::unique_lock lock{free_buffers_mutex}; - free_buffers.push_back(slot); - } - free_buffers_condition.notify_one(); - - buffer_wait_event->GetWritableEvent().Signal(); -} - -std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() { - std::unique_lock lock{queue_sequence_mutex}; - std::size_t buffer_slot = buffers.size(); - // Iterate to find a queued buffer matching the requested slot. - while (buffer_slot == buffers.size() && !queue_sequence.empty()) { - const auto slot = static_cast<std::size_t>(queue_sequence.front()); - ASSERT(slot < buffers.size()); - if (buffers[slot].status == Buffer::Status::Queued) { - ASSERT(buffers[slot].slot == slot); - buffer_slot = slot; - } - queue_sequence.pop_front(); - } - if (buffer_slot == buffers.size()) { - return std::nullopt; - } - buffers[buffer_slot].status = Buffer::Status::Acquired; - return {{buffers[buffer_slot]}}; -} - -void BufferQueue::ReleaseBuffer(u32 slot) { - ASSERT(slot < buffers.size()); - ASSERT(buffers[slot].status == Buffer::Status::Acquired); - ASSERT(buffers[slot].slot == slot); - - buffers[slot].status = Buffer::Status::Free; - { - std::unique_lock lock{free_buffers_mutex}; - free_buffers.push_back(slot); - } - free_buffers_condition.notify_one(); - - buffer_wait_event->GetWritableEvent().Signal(); -} - -void BufferQueue::Connect() { - std::unique_lock lock{queue_sequence_mutex}; - queue_sequence.clear(); - is_connect = true; -} - -void BufferQueue::Disconnect() { - buffers.fill({}); - { - std::unique_lock lock{queue_sequence_mutex}; - queue_sequence.clear(); - } - buffer_wait_event->GetWritableEvent().Signal(); - is_connect = false; - free_buffers_condition.notify_one(); -} - -u32 BufferQueue::Query(QueryType type) { - LOG_WARNING(Service, "(STUBBED) called type={}", type); - - switch (type) { - case QueryType::NativeWindowFormat: - return static_cast<u32>(PixelFormat::RGBA8888); - case QueryType::NativeWindowWidth: - case QueryType::NativeWindowHeight: - break; - case QueryType::NativeWindowMinUndequeuedBuffers: - return 0; - case QueryType::NativeWindowConsumerUsageBits: - return 0; - } - UNIMPLEMENTED_MSG("Unimplemented query type={}", type); - return 0; -} - -Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() { - return buffer_wait_event->GetWritableEvent(); -} - -Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() { - return buffer_wait_event->GetReadableEvent(); -} - -} // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h deleted file mode 100644 index f2a579133..000000000 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <condition_variable> -#include <list> -#include <mutex> -#include <optional> - -#include "common/common_funcs.h" -#include "common/math_util.h" -#include "common/swap.h" -#include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/service/nvdrv/nvdata.h" - -namespace Kernel { -class KernelCore; -class KEvent; -class KReadableEvent; -class KWritableEvent; -} // namespace Kernel - -namespace Service::KernelHelpers { -class ServiceContext; -} // namespace Service::KernelHelpers - -namespace Service::NVFlinger { - -constexpr u32 buffer_slots = 0x40; -struct IGBPBuffer { - u32_le magic; - u32_le width; - u32_le height; - u32_le stride; - u32_le format; - u32_le usage; - INSERT_PADDING_WORDS(1); - u32_le index; - INSERT_PADDING_WORDS(3); - u32_le gpu_buffer_id; - INSERT_PADDING_WORDS(6); - u32_le external_format; - INSERT_PADDING_WORDS(10); - u32_le nvmap_handle; - u32_le offset; - INSERT_PADDING_WORDS(60); -}; - -static_assert(sizeof(IGBPBuffer) == 0x16C, "IGBPBuffer has wrong size"); - -class BufferQueue final { -public: - enum class QueryType { - NativeWindowWidth = 0, - NativeWindowHeight = 1, - NativeWindowFormat = 2, - /// The minimum number of buffers that must remain un-dequeued after a buffer has been - /// queued - NativeWindowMinUndequeuedBuffers = 3, - /// The consumer gralloc usage bits currently set by the consumer - NativeWindowConsumerUsageBits = 10, - }; - - explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_, - KernelHelpers::ServiceContext& service_context_); - ~BufferQueue(); - - enum class BufferTransformFlags : u32 { - /// No transform flags are set - Unset = 0x00, - /// Flip source image horizontally (around the vertical axis) - FlipH = 0x01, - /// Flip source image vertically (around the horizontal axis) - FlipV = 0x02, - /// Rotate source image 90 degrees clockwise - Rotate90 = 0x04, - /// Rotate source image 180 degrees - Rotate180 = 0x03, - /// Rotate source image 270 degrees clockwise - Rotate270 = 0x07, - }; - - enum class PixelFormat : u32 { - RGBA8888 = 1, - RGBX8888 = 2, - RGB888 = 3, - RGB565 = 4, - BGRA8888 = 5, - RGBA5551 = 6, - RRGBA4444 = 7, - }; - - struct Buffer { - enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 }; - - u32 slot; - Status status = Status::Free; - IGBPBuffer igbp_buffer; - BufferTransformFlags transform; - Common::Rectangle<int> crop_rect; - u32 swap_interval; - Service::Nvidia::MultiFence multi_fence; - }; - - void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer); - std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> DequeueBuffer(u32 width, - u32 height); - const IGBPBuffer& RequestBuffer(u32 slot) const; - void QueueBuffer(u32 slot, BufferTransformFlags transform, - const Common::Rectangle<int>& crop_rect, u32 swap_interval, - Service::Nvidia::MultiFence& multi_fence); - void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence); - std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer(); - void ReleaseBuffer(u32 slot); - void Connect(); - void Disconnect(); - u32 Query(QueryType type); - - u32 GetId() const { - return id; - } - - bool IsConnected() const { - return is_connect; - } - - Kernel::KWritableEvent& GetWritableBufferWaitEvent(); - - Kernel::KReadableEvent& GetBufferWaitEvent(); - -private: - BufferQueue(const BufferQueue&) = delete; - - u32 id{}; - u64 layer_id{}; - std::atomic_bool is_connect{}; - - std::list<u32> free_buffers; - std::array<Buffer, buffer_slots> buffers; - std::list<u32> queue_sequence; - Kernel::KEvent* buffer_wait_event{}; - - std::mutex free_buffers_mutex; - std::condition_variable free_buffers_condition; - - std::mutex queue_sequence_mutex; - - KernelHelpers::ServiceContext& service_context; -}; - -} // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp new file mode 100644 index 000000000..1ce67c771 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp @@ -0,0 +1,213 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp + +#include "common/logging/log.h" +#include "core/hle/service/nvdrv/core/nvmap.h" +#include "core/hle/service/nvflinger/buffer_item.h" +#include "core/hle/service/nvflinger/buffer_queue_consumer.h" +#include "core/hle/service/nvflinger/buffer_queue_core.h" +#include "core/hle/service/nvflinger/producer_listener.h" +#include "core/hle/service/nvflinger/ui/graphic_buffer.h" + +namespace Service::android { + +BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_, + Service::Nvidia::NvCore::NvMap& nvmap_) + : core{std::move(core_)}, slots{core->slots}, nvmap(nvmap_) {} + +BufferQueueConsumer::~BufferQueueConsumer() = default; + +Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer, + std::chrono::nanoseconds expected_present) { + std::scoped_lock lock{core->mutex}; + + // Check that the consumer doesn't currently have the maximum number of buffers acquired. + const s32 num_acquired_buffers{ + static_cast<s32>(std::count_if(slots.begin(), slots.end(), [](const auto& slot) { + return slot.buffer_state == BufferState::Acquired; + }))}; + + if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) { + LOG_ERROR(Service_NVFlinger, "max acquired buffer count reached: {} (max {})", + num_acquired_buffers, core->max_acquired_buffer_count); + return Status::InvalidOperation; + } + + // Check if the queue is empty. + if (core->queue.empty()) { + return Status::NoBufferAvailable; + } + + auto front(core->queue.begin()); + + // If expected_present is specified, we may not want to return a buffer yet. + if (expected_present.count() != 0) { + constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second + + // The expected_present argument indicates when the buffer is expected to be presented + // on-screen. + while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) { + const auto& buffer_item{core->queue[1]}; + + // If entry[1] is timely, drop entry[0] (and repeat). + const auto desired_present = buffer_item.timestamp; + if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC || + desired_present > expected_present.count()) { + // This buffer is set to display in the near future, or desired_present is garbage. + LOG_DEBUG(Service_NVFlinger, "nodrop desire={} expect={}", desired_present, + expected_present.count()); + break; + } + + LOG_DEBUG(Service_NVFlinger, "drop desire={} expect={} size={}", desired_present, + expected_present.count(), core->queue.size()); + + if (core->StillTracking(*front)) { + // Front buffer is still in mSlots, so mark the slot as free + slots[front->slot].buffer_state = BufferState::Free; + } + + core->queue.erase(front); + front = core->queue.begin(); + } + + // See if the front buffer is ready to be acquired. + const auto desired_present = front->timestamp; + if (desired_present > expected_present.count() && + desired_present < expected_present.count() + MAX_REASONABLE_NSEC) { + LOG_DEBUG(Service_NVFlinger, "defer desire={} expect={}", desired_present, + expected_present.count()); + return Status::PresentLater; + } + + LOG_DEBUG(Service_NVFlinger, "accept desire={} expect={}", desired_present, + expected_present.count()); + } + + const auto slot = front->slot; + *out_buffer = *front; + + LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot); + + // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to + // avoid unnecessarily remapping this buffer on the consumer side. + if (out_buffer->acquire_called) { + out_buffer->graphic_buffer = nullptr; + } + + core->queue.erase(front); + + // We might have freed a slot while dropping old buffers, or the producer may be blocked + // waiting for the number of buffers in the queue to decrease. + core->SignalDequeueCondition(); + + return Status::NoError; +} + +Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence) { + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + LOG_ERROR(Service_NVFlinger, "slot {} out of range", slot); + return Status::BadValue; + } + + std::shared_ptr<IProducerListener> listener; + { + std::scoped_lock lock{core->mutex}; + + // If the frame number has changed because the buffer has been reallocated, we can ignore + // this ReleaseBuffer for the old buffer. + if (frame_number != slots[slot].frame_number) { + return Status::StaleBufferSlot; + } + + // Make sure this buffer hasn't been queued while acquired by the consumer. + auto current(core->queue.begin()); + while (current != core->queue.end()) { + if (current->slot == slot) { + LOG_ERROR(Service_NVFlinger, "buffer slot {} pending release is currently queued", + slot); + return Status::BadValue; + } + ++current; + } + + slots[slot].buffer_state = BufferState::Free; + + nvmap.FreeHandle(slots[slot].graphic_buffer->BufferId(), true); + + listener = core->connected_producer_listener; + + LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot); + + core->SignalDequeueCondition(); + } + + // Call back without lock held + if (listener != nullptr) { + listener->OnBufferReleased(); + } + + return Status::NoError; +} + +Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_listener, + bool controlled_by_app) { + if (consumer_listener == nullptr) { + LOG_ERROR(Service_NVFlinger, "consumer_listener may not be nullptr"); + return Status::BadValue; + } + + LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app); + + std::scoped_lock lock{core->mutex}; + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + core->consumer_listener = consumer_listener; + core->consumer_controlled_by_app = controlled_by_app; + + return Status::NoError; +} + +Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) { + if (out_slot_mask == nullptr) { + LOG_ERROR(Service_NVFlinger, "out_slot_mask may not be nullptr"); + return Status::BadValue; + } + + std::scoped_lock lock{core->mutex}; + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + u64 mask = 0; + for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + if (!slots[s].acquire_called) { + mask |= (1ULL << s); + } + } + + // Remove from the mask queued buffers for which acquire has been called, since the consumer + // will not receive their buffer addresses and so must retain their cached information + auto current(core->queue.begin()); + while (current != core->queue.end()) { + if (current->acquire_called) { + mask &= ~(1ULL << current->slot); + } + ++current; + } + + LOG_DEBUG(Service_NVFlinger, "returning mask {}", mask); + *out_slot_mask = mask; + return Status::NoError; +} + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.h b/src/core/hle/service/nvflinger/buffer_queue_consumer.h new file mode 100644 index 000000000..4ec06ca13 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueConsumer.h + +#pragma once + +#include <chrono> +#include <memory> + +#include "common/common_types.h" +#include "core/hle/service/nvflinger/buffer_queue_defs.h" +#include "core/hle/service/nvflinger/status.h" + +namespace Service::Nvidia::NvCore { +class NvMap; +} // namespace Service::Nvidia::NvCore + +namespace Service::android { + +class BufferItem; +class BufferQueueCore; +class IConsumerListener; + +class BufferQueueConsumer final { +public: + explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_, + Service::Nvidia::NvCore::NvMap& nvmap_); + ~BufferQueueConsumer(); + + Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present); + Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence); + Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app); + Status GetReleasedBuffers(u64* out_slot_mask); + +private: + std::shared_ptr<BufferQueueCore> core; + BufferQueueDefs::SlotsType& slots; + Service::Nvidia::NvCore::NvMap& nvmap; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp new file mode 100644 index 000000000..ea4a14ea4 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueCore.cpp + +#include "common/assert.h" + +#include "core/hle/service/nvflinger/buffer_queue_core.h" + +namespace Service::android { + +BufferQueueCore::BufferQueueCore() = default; + +BufferQueueCore::~BufferQueueCore() = default; + +void BufferQueueCore::NotifyShutdown() { + std::scoped_lock lock{mutex}; + + is_shutting_down = true; + + SignalDequeueCondition(); +} + +void BufferQueueCore::SignalDequeueCondition() { + dequeue_condition.notify_all(); +} + +bool BufferQueueCore::WaitForDequeueCondition() { + if (is_shutting_down) { + return false; + } + + dequeue_condition.wait(mutex); + + return true; +} + +s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const { + // If DequeueBuffer is allowed to error out, we don't have to add an extra buffer. + if (!use_async_buffer) { + return max_acquired_buffer_count; + } + + if (dequeue_buffer_cannot_block || async) { + return max_acquired_buffer_count + 1; + } + + return max_acquired_buffer_count; +} + +s32 BufferQueueCore::GetMinMaxBufferCountLocked(bool async) const { + return GetMinUndequeuedBufferCountLocked(async) + 1; +} + +s32 BufferQueueCore::GetMaxBufferCountLocked(bool async) const { + const auto min_buffer_count = GetMinMaxBufferCountLocked(async); + auto max_buffer_count = std::max(default_max_buffer_count, min_buffer_count); + + if (override_max_buffer_count != 0) { + ASSERT(override_max_buffer_count >= min_buffer_count); + max_buffer_count = override_max_buffer_count; + } + + // Any buffers that are dequeued by the producer or sitting in the queue waiting to be consumed + // need to have their slots preserved. + for (s32 slot = max_buffer_count; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { + const auto state = slots[slot].buffer_state; + if (state == BufferState::Queued || state == BufferState::Dequeued) { + max_buffer_count = slot + 1; + } + } + + return max_buffer_count; +} + +s32 BufferQueueCore::GetPreallocatedBufferCountLocked() const { + return static_cast<s32>(std::count_if(slots.begin(), slots.end(), + [](const auto& slot) { return slot.is_preallocated; })); +} + +void BufferQueueCore::FreeBufferLocked(s32 slot) { + LOG_DEBUG(Service_NVFlinger, "slot {}", slot); + + slots[slot].graphic_buffer.reset(); + + slots[slot].buffer_state = BufferState::Free; + slots[slot].frame_number = UINT32_MAX; + slots[slot].acquire_called = false; + slots[slot].fence = Fence::NoFence(); +} + +void BufferQueueCore::FreeAllBuffersLocked() { + buffer_has_been_queued = false; + + for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { + FreeBufferLocked(slot); + } +} + +bool BufferQueueCore::StillTracking(const BufferItem& item) const { + const BufferSlot& slot = slots[item.slot]; + + return (slot.graphic_buffer != nullptr) && (item.graphic_buffer == slot.graphic_buffer); +} + +void BufferQueueCore::WaitWhileAllocatingLocked() const { + while (is_allocating) { + is_allocating_condition.wait(mutex); + } +} + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.h b/src/core/hle/service/nvflinger/buffer_queue_core.h new file mode 100644 index 000000000..ca6baefaf --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_core.h @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueCore.h + +#pragma once + +#include <condition_variable> +#include <list> +#include <memory> +#include <mutex> +#include <set> +#include <vector> + +#include "core/hle/service/nvflinger/buffer_item.h" +#include "core/hle/service/nvflinger/buffer_queue_defs.h" +#include "core/hle/service/nvflinger/pixel_format.h" +#include "core/hle/service/nvflinger/status.h" +#include "core/hle/service/nvflinger/window.h" + +namespace Service::android { + +class IConsumerListener; +class IProducerListener; + +class BufferQueueCore final { + friend class BufferQueueProducer; + friend class BufferQueueConsumer; + +public: + static constexpr s32 INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT; + + BufferQueueCore(); + ~BufferQueueCore(); + + void NotifyShutdown(); + +private: + void SignalDequeueCondition(); + bool WaitForDequeueCondition(); + + s32 GetMinUndequeuedBufferCountLocked(bool async) const; + s32 GetMinMaxBufferCountLocked(bool async) const; + s32 GetMaxBufferCountLocked(bool async) const; + s32 GetPreallocatedBufferCountLocked() const; + void FreeBufferLocked(s32 slot); + void FreeAllBuffersLocked(); + bool StillTracking(const BufferItem& item) const; + void WaitWhileAllocatingLocked() const; + +private: + mutable std::mutex mutex; + bool is_abandoned{}; + bool consumer_controlled_by_app{}; + std::shared_ptr<IConsumerListener> consumer_listener; + u32 consumer_usage_bit{}; + NativeWindowApi connected_api{NativeWindowApi::NoConnectedApi}; + std::shared_ptr<IProducerListener> connected_producer_listener; + BufferQueueDefs::SlotsType slots{}; + std::vector<BufferItem> queue; + s32 override_max_buffer_count{}; + mutable std::condition_variable_any dequeue_condition; + const bool use_async_buffer{}; // This is always disabled on HOS + bool dequeue_buffer_cannot_block{}; + PixelFormat default_buffer_format{PixelFormat::Rgba8888}; + u32 default_width{1}; + u32 default_height{1}; + s32 default_max_buffer_count{2}; + const s32 max_acquired_buffer_count{}; // This is always zero on HOS + bool buffer_has_been_queued{}; + u64 frame_counter{}; + u32 transform_hint{}; + bool is_allocating{}; + mutable std::condition_variable_any is_allocating_condition; + bool is_shutting_down{}; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_defs.h b/src/core/hle/service/nvflinger/buffer_queue_defs.h new file mode 100644 index 000000000..334445213 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_defs.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueDefs.h + +#pragma once + +#include <array> + +#include "common/common_types.h" +#include "core/hle/service/nvflinger/buffer_slot.h" + +namespace Service::android::BufferQueueDefs { + +// BufferQueue will keep track of at most this value of buffers. +constexpr s32 NUM_BUFFER_SLOTS = 64; + +using SlotsType = std::array<BufferSlot, NUM_BUFFER_SLOTS>; + +} // namespace Service::android::BufferQueueDefs diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp new file mode 100644 index 000000000..d4ab23a10 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp @@ -0,0 +1,927 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueProducer.cpp + +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "core/core.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/kernel/k_writable_event.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nvdrv/core/nvmap.h" +#include "core/hle/service/nvflinger/buffer_queue_core.h" +#include "core/hle/service/nvflinger/buffer_queue_producer.h" +#include "core/hle/service/nvflinger/consumer_listener.h" +#include "core/hle/service/nvflinger/parcel.h" +#include "core/hle/service/nvflinger/ui/graphic_buffer.h" +#include "core/hle/service/nvflinger/window.h" +#include "core/hle/service/vi/vi.h" + +namespace Service::android { + +BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, + std::shared_ptr<BufferQueueCore> buffer_queue_core_, + Service::Nvidia::NvCore::NvMap& nvmap_) + : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots), + nvmap(nvmap_) { + buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent"); +} + +BufferQueueProducer::~BufferQueueProducer() { + service_context.CloseEvent(buffer_wait_event); +} + +Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf) { + LOG_DEBUG(Service_NVFlinger, "slot {}", slot); + + std::scoped_lock lock{core->mutex}; + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot, + BufferQueueDefs::NUM_BUFFER_SLOTS); + return Status::BadValue; + } else if (slots[slot].buffer_state != BufferState::Dequeued) { + LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot, + slots[slot].buffer_state); + return Status::BadValue; + } + + slots[slot].request_buffer_called = true; + *buf = slots[slot].graphic_buffer; + + return Status::NoError; +} + +Status BufferQueueProducer::SetBufferCount(s32 buffer_count) { + LOG_DEBUG(Service_NVFlinger, "count = {}", buffer_count); + + std::shared_ptr<IConsumerListener> listener; + { + std::scoped_lock lock{core->mutex}; + core->WaitWhileAllocatingLocked(); + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + if (buffer_count > BufferQueueDefs::NUM_BUFFER_SLOTS) { + LOG_ERROR(Service_NVFlinger, "buffer_count {} too large (max {})", buffer_count, + BufferQueueDefs::NUM_BUFFER_SLOTS); + return Status::BadValue; + } + + // There must be no dequeued buffers when changing the buffer count. + for (s32 s{}; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + if (slots[s].buffer_state == BufferState::Dequeued) { + LOG_ERROR(Service_NVFlinger, "buffer owned by producer"); + return Status::BadValue; + } + } + + if (buffer_count == 0) { + core->override_max_buffer_count = 0; + core->SignalDequeueCondition(); + return Status::NoError; + } + + const s32 min_buffer_slots = core->GetMinMaxBufferCountLocked(false); + if (buffer_count < min_buffer_slots) { + LOG_ERROR(Service_NVFlinger, "requested buffer count {} is less than minimum {}", + buffer_count, min_buffer_slots); + return Status::BadValue; + } + + // Here we are guaranteed that the producer doesn't have any dequeued buffers and will + // release all of its buffer references. + if (core->GetPreallocatedBufferCountLocked() <= 0) { + core->FreeAllBuffersLocked(); + } + + core->override_max_buffer_count = buffer_count; + core->SignalDequeueCondition(); + buffer_wait_event->GetWritableEvent().Signal(); + listener = core->consumer_listener; + } + + // Call back without lock held + if (listener != nullptr) { + listener->OnBuffersReleased(); + } + + return Status::NoError; +} + +Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, + Status* return_flags) const { + bool try_again = true; + + while (try_again) { + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + const s32 max_buffer_count = core->GetMaxBufferCountLocked(async); + if (async && core->override_max_buffer_count) { + if (core->override_max_buffer_count < max_buffer_count) { + LOG_ERROR(Service_NVFlinger, "async mode is invalid with buffer count override"); + return Status::BadValue; + } + } + + // Free up any buffers that are in slots beyond the max buffer count + for (s32 s = max_buffer_count; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + ASSERT(slots[s].buffer_state == BufferState::Free); + if (slots[s].graphic_buffer != nullptr) { + core->FreeBufferLocked(s); + *return_flags |= Status::ReleaseAllBuffers; + } + } + + // Look for a free buffer to give to the client + *found = BufferQueueCore::INVALID_BUFFER_SLOT; + s32 dequeued_count{}; + s32 acquired_count{}; + for (s32 s{}; s < max_buffer_count; ++s) { + switch (slots[s].buffer_state) { + case BufferState::Dequeued: + ++dequeued_count; + break; + case BufferState::Acquired: + ++acquired_count; + break; + case BufferState::Free: + // We return the oldest of the free buffers to avoid stalling the producer if + // possible, since the consumer may still have pending reads of in-flight buffers + if (*found == BufferQueueCore::INVALID_BUFFER_SLOT || + slots[s].frame_number < slots[*found].frame_number) { + *found = s; + } + break; + default: + break; + } + } + + // Producers are not allowed to dequeue more than one buffer if they did not set a buffer + // count + if (!core->override_max_buffer_count && dequeued_count) { + LOG_ERROR(Service_NVFlinger, + "can't dequeue multiple buffers without setting the buffer count"); + return Status::InvalidOperation; + } + + // See whether a buffer has been queued since the last SetBufferCount so we know whether to + // perform the min undequeued buffers check below + if (core->buffer_has_been_queued) { + // Make sure the producer is not trying to dequeue more buffers than allowed + const s32 new_undequeued_count = max_buffer_count - (dequeued_count + 1); + const s32 min_undequeued_count = core->GetMinUndequeuedBufferCountLocked(async); + if (new_undequeued_count < min_undequeued_count) { + LOG_ERROR(Service_NVFlinger, + "min undequeued buffer count({}) exceeded (dequeued={} undequeued={})", + min_undequeued_count, dequeued_count, new_undequeued_count); + return Status::InvalidOperation; + } + } + + // If we disconnect and reconnect quickly, we can be in a state where our slots are empty + // but we have many buffers in the queue. This can cause us to run out of memory if we + // outrun the consumer. Wait here if it looks like we have too many buffers queued up. + const bool too_many_buffers = core->queue.size() > static_cast<size_t>(max_buffer_count); + if (too_many_buffers) { + LOG_ERROR(Service_NVFlinger, "queue size is {}, waiting", core->queue.size()); + } + + // If no buffer is found, or if the queue has too many buffers outstanding, wait for a + // buffer to be acquired or released, or for the max buffer count to change. + try_again = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) || too_many_buffers; + if (try_again) { + // Return an error if we're in non-blocking mode (producer and consumer are controlled + // by the application). + if (core->dequeue_buffer_cannot_block && + (acquired_count <= core->max_acquired_buffer_count)) { + return Status::WouldBlock; + } + + if (!core->WaitForDequeueCondition()) { + // We are no longer running + return Status::NoError; + } + } + } + + return Status::NoError; +} + +Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool async, u32 width, + u32 height, PixelFormat format, u32 usage) { + LOG_DEBUG(Service_NVFlinger, "async={} w={} h={} format={}, usage={}", async ? "true" : "false", + width, height, format, usage); + + if ((width != 0 && height == 0) || (width == 0 && height != 0)) { + LOG_ERROR(Service_NVFlinger, "invalid size: w={} h={}", width, height); + return Status::BadValue; + } + + Status return_flags = Status::NoError; + bool attached_by_consumer = false; + { + std::scoped_lock lock{core->mutex}; + core->WaitWhileAllocatingLocked(); + + if (format == PixelFormat::NoFormat) { + format = core->default_buffer_format; + } + + // Enable the usage bits the consumer requested + usage |= core->consumer_usage_bit; + + s32 found{}; + Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags); + if (status != Status::NoError) { + return status; + } + + // This should not happen + if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { + LOG_ERROR(Service_NVFlinger, "no available buffer slots"); + return Status::Busy; + } + + *out_slot = found; + + attached_by_consumer = slots[found].attached_by_consumer; + + const bool use_default_size = !width && !height; + if (use_default_size) { + width = core->default_width; + height = core->default_height; + } + + slots[found].buffer_state = BufferState::Dequeued; + + const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer); + if ((buffer == nullptr) || (buffer->Width() != width) || (buffer->Height() != height) || + (buffer->Format() != format) || ((buffer->Usage() & usage) != usage)) { + slots[found].acquire_called = false; + slots[found].graphic_buffer = nullptr; + slots[found].request_buffer_called = false; + slots[found].fence = Fence::NoFence(); + + return_flags |= Status::BufferNeedsReallocation; + } + + *out_fence = slots[found].fence; + slots[found].fence = Fence::NoFence(); + } + + if ((return_flags & Status::BufferNeedsReallocation) != Status::None) { + LOG_DEBUG(Service_NVFlinger, "allocating a new buffer for slot {}", *out_slot); + + auto graphic_buffer = std::make_shared<GraphicBuffer>(width, height, format, usage); + if (graphic_buffer == nullptr) { + LOG_ERROR(Service_NVFlinger, "creating GraphicBuffer failed"); + return Status::NoMemory; + } + + { + std::scoped_lock lock{core->mutex}; + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + slots[*out_slot].frame_number = UINT32_MAX; + slots[*out_slot].graphic_buffer = graphic_buffer; + } + } + + if (attached_by_consumer) { + return_flags |= Status::BufferNeedsReallocation; + } + + LOG_DEBUG(Service_NVFlinger, "returning slot={} frame={}, flags={}", *out_slot, + slots[*out_slot].frame_number, return_flags); + + return return_flags; +} + +Status BufferQueueProducer::DetachBuffer(s32 slot) { + LOG_DEBUG(Service_NVFlinger, "slot {}", slot); + + std::scoped_lock lock{core->mutex}; + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + LOG_ERROR(Service_NVFlinger, "slot {} out of range [0, {})", slot, + BufferQueueDefs::NUM_BUFFER_SLOTS); + return Status::BadValue; + } else if (slots[slot].buffer_state != BufferState::Dequeued) { + LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot, + slots[slot].buffer_state); + return Status::BadValue; + } else if (!slots[slot].request_buffer_called) { + LOG_ERROR(Service_NVFlinger, "buffer in slot {} has not been requested", slot); + return Status::BadValue; + } + + core->FreeBufferLocked(slot); + core->SignalDequeueCondition(); + + return Status::NoError; +} + +Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer, + Fence* out_fence) { + if (out_buffer == nullptr) { + LOG_ERROR(Service_NVFlinger, "out_buffer must not be nullptr"); + return Status::BadValue; + } else if (out_fence == nullptr) { + LOG_ERROR(Service_NVFlinger, "out_fence must not be nullptr"); + return Status::BadValue; + } + + std::scoped_lock lock{core->mutex}; + core->WaitWhileAllocatingLocked(); + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + // Find the oldest valid slot + int found = BufferQueueCore::INVALID_BUFFER_SLOT; + for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + if (slots[s].buffer_state == BufferState::Free && slots[s].graphic_buffer != nullptr) { + if (found == BufferQueueCore::INVALID_BUFFER_SLOT || + slots[s].frame_number < slots[found].frame_number) { + found = s; + } + } + } + + if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { + return Status::NoMemory; + } + + LOG_DEBUG(Service_NVFlinger, "Detached slot {}", found); + + *out_buffer = slots[found].graphic_buffer; + *out_fence = slots[found].fence; + + core->FreeBufferLocked(found); + + return Status::NoError; +} + +Status BufferQueueProducer::AttachBuffer(s32* out_slot, + const std::shared_ptr<GraphicBuffer>& buffer) { + if (out_slot == nullptr) { + LOG_ERROR(Service_NVFlinger, "out_slot must not be nullptr"); + return Status::BadValue; + } else if (buffer == nullptr) { + LOG_ERROR(Service_NVFlinger, "Cannot attach nullptr buffer"); + return Status::BadValue; + } + + std::scoped_lock lock{core->mutex}; + core->WaitWhileAllocatingLocked(); + + Status return_flags = Status::NoError; + s32 found{}; + + const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags); + if (status != Status::NoError) { + return status; + } + + // This should not happen + if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { + LOG_ERROR(Service_NVFlinger, "No available buffer slots"); + return Status::Busy; + } + + *out_slot = found; + + LOG_DEBUG(Service_NVFlinger, "Returning slot {} flags={}", *out_slot, return_flags); + + slots[*out_slot].graphic_buffer = buffer; + slots[*out_slot].buffer_state = BufferState::Dequeued; + slots[*out_slot].fence = Fence::NoFence(); + slots[*out_slot].request_buffer_called = true; + + return return_flags; +} + +Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, + QueueBufferOutput* output) { + s64 timestamp{}; + bool is_auto_timestamp{}; + Common::Rectangle<s32> crop; + NativeWindowScalingMode scaling_mode{}; + NativeWindowTransform transform; + u32 sticky_transform_{}; + bool async{}; + s32 swap_interval{}; + Fence fence{}; + + input.Deflate(×tamp, &is_auto_timestamp, &crop, &scaling_mode, &transform, + &sticky_transform_, &async, &swap_interval, &fence); + + switch (scaling_mode) { + case NativeWindowScalingMode::Freeze: + case NativeWindowScalingMode::ScaleToWindow: + case NativeWindowScalingMode::ScaleCrop: + case NativeWindowScalingMode::NoScaleCrop: + break; + default: + LOG_ERROR(Service_NVFlinger, "unknown scaling mode {}", scaling_mode); + return Status::BadValue; + } + + std::shared_ptr<IConsumerListener> frame_available_listener; + std::shared_ptr<IConsumerListener> frame_replaced_listener; + s32 callback_ticket{}; + BufferItem item; + + { + std::scoped_lock lock{core->mutex}; + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + const s32 max_buffer_count = core->GetMaxBufferCountLocked(async); + if (async && core->override_max_buffer_count) { + if (core->override_max_buffer_count < max_buffer_count) { + LOG_ERROR(Service_NVFlinger, "async mode is invalid with " + "buffer count override"); + return Status::BadValue; + } + } + + if (slot < 0 || slot >= max_buffer_count) { + LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot, + max_buffer_count); + return Status::BadValue; + } else if (slots[slot].buffer_state != BufferState::Dequeued) { + LOG_ERROR(Service_NVFlinger, + "slot {} is not owned by the producer " + "(state = {})", + slot, slots[slot].buffer_state); + return Status::BadValue; + } else if (!slots[slot].request_buffer_called) { + LOG_ERROR(Service_NVFlinger, + "slot {} was queued without requesting " + "a buffer", + slot); + return Status::BadValue; + } + + LOG_DEBUG(Service_NVFlinger, + "slot={} frame={} time={} crop=[{},{},{},{}] transform={} scale={}", slot, + core->frame_counter + 1, timestamp, crop.Left(), crop.Top(), crop.Right(), + crop.Bottom(), transform, scaling_mode); + + const std::shared_ptr<GraphicBuffer>& graphic_buffer(slots[slot].graphic_buffer); + Common::Rectangle<s32> buffer_rect(graphic_buffer->Width(), graphic_buffer->Height()); + Common::Rectangle<s32> cropped_rect; + [[maybe_unused]] const bool unused = crop.Intersect(buffer_rect, &cropped_rect); + + if (cropped_rect != crop) { + LOG_ERROR(Service_NVFlinger, "crop rect is not contained within the buffer in slot {}", + slot); + return Status::BadValue; + } + + slots[slot].fence = fence; + slots[slot].buffer_state = BufferState::Queued; + ++core->frame_counter; + slots[slot].frame_number = core->frame_counter; + + item.acquire_called = slots[slot].acquire_called; + item.graphic_buffer = slots[slot].graphic_buffer; + item.crop = crop; + item.transform = transform & ~NativeWindowTransform::InverseDisplay; + item.transform_to_display_inverse = + (transform & NativeWindowTransform::InverseDisplay) != NativeWindowTransform::None; + item.scaling_mode = static_cast<u32>(scaling_mode); + item.timestamp = timestamp; + item.is_auto_timestamp = is_auto_timestamp; + item.frame_number = core->frame_counter; + item.slot = slot; + item.fence = fence; + item.is_droppable = core->dequeue_buffer_cannot_block || async; + item.swap_interval = swap_interval; + + nvmap.DuplicateHandle(item.graphic_buffer->BufferId(), true); + + sticky_transform = sticky_transform_; + + if (core->queue.empty()) { + // When the queue is empty, we can simply queue this buffer + core->queue.push_back(item); + frame_available_listener = core->consumer_listener; + } else { + // When the queue is not empty, we need to look at the front buffer + // state to see if we need to replace it + auto front(core->queue.begin()); + + if (front->is_droppable) { + // If the front queued buffer is still being tracked, we first + // mark it as freed + if (core->StillTracking(*front)) { + slots[front->slot].buffer_state = BufferState::Free; + // Reset the frame number of the freed buffer so that it is the first in line to + // be dequeued again + slots[front->slot].frame_number = 0; + } + // Overwrite the droppable buffer with the incoming one + *front = item; + frame_replaced_listener = core->consumer_listener; + } else { + core->queue.push_back(item); + frame_available_listener = core->consumer_listener; + } + } + + core->buffer_has_been_queued = true; + core->SignalDequeueCondition(); + output->Inflate(core->default_width, core->default_height, core->transform_hint, + static_cast<u32>(core->queue.size())); + + // Take a ticket for the callback functions + callback_ticket = next_callback_ticket++; + } + + // Don't send the GraphicBuffer through the callback, and don't send the slot number, since the + // consumer shouldn't need it + item.graphic_buffer.reset(); + item.slot = BufferItem::INVALID_BUFFER_SLOT; + + // Call back without the main BufferQueue lock held, but with the callback lock held so we can + // ensure that callbacks occur in order + { + std::scoped_lock lock{callback_mutex}; + while (callback_ticket != current_callback_ticket) { + callback_condition.wait(callback_mutex); + } + + if (frame_available_listener != nullptr) { + frame_available_listener->OnFrameAvailable(item); + } else if (frame_replaced_listener != nullptr) { + frame_replaced_listener->OnFrameReplaced(item); + } + + ++current_callback_ticket; + callback_condition.notify_all(); + } + + return Status::NoError; +} + +void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) { + LOG_DEBUG(Service_NVFlinger, "slot {}", slot); + + std::scoped_lock lock{core->mutex}; + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return; + } + + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot, + BufferQueueDefs::NUM_BUFFER_SLOTS); + return; + } else if (slots[slot].buffer_state != BufferState::Dequeued) { + LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot, + slots[slot].buffer_state); + return; + } + + slots[slot].buffer_state = BufferState::Free; + slots[slot].frame_number = 0; + slots[slot].fence = fence; + + core->SignalDequeueCondition(); + buffer_wait_event->GetWritableEvent().Signal(); +} + +Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) { + std::scoped_lock lock{core->mutex}; + + if (out_value == nullptr) { + LOG_ERROR(Service_NVFlinger, "outValue was nullptr"); + return Status::BadValue; + } + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + u32 value{}; + switch (what) { + case NativeWindow::Width: + value = core->default_width; + break; + case NativeWindow::Height: + value = core->default_height; + break; + case NativeWindow::Format: + value = static_cast<u32>(core->default_buffer_format); + break; + case NativeWindow::MinUndequeedBuffers: + value = core->GetMinUndequeuedBufferCountLocked(false); + break; + case NativeWindow::StickyTransform: + value = sticky_transform; + break; + case NativeWindow::ConsumerRunningBehind: + value = (core->queue.size() > 1); + break; + case NativeWindow::ConsumerUsageBits: + value = core->consumer_usage_bit; + break; + default: + ASSERT(false); + return Status::BadValue; + } + + LOG_DEBUG(Service_NVFlinger, "what = {}, value = {}", what, value); + + *out_value = static_cast<s32>(value); + + return Status::NoError; +} + +Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& listener, + NativeWindowApi api, bool producer_controlled_by_app, + QueueBufferOutput* output) { + std::scoped_lock lock{core->mutex}; + + LOG_DEBUG(Service_NVFlinger, "api = {} producer_controlled_by_app = {}", api, + producer_controlled_by_app); + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + if (core->consumer_listener == nullptr) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has no consumer"); + return Status::NoInit; + } + + if (output == nullptr) { + LOG_ERROR(Service_NVFlinger, "output was nullptr"); + return Status::BadValue; + } + + if (core->connected_api != NativeWindowApi::NoConnectedApi) { + LOG_ERROR(Service_NVFlinger, "already connected (cur = {} req = {})", core->connected_api, + api); + return Status::BadValue; + } + + Status status = Status::NoError; + switch (api) { + case NativeWindowApi::Egl: + case NativeWindowApi::Cpu: + case NativeWindowApi::Media: + case NativeWindowApi::Camera: + core->connected_api = api; + output->Inflate(core->default_width, core->default_height, core->transform_hint, + static_cast<u32>(core->queue.size())); + core->connected_producer_listener = listener; + break; + default: + LOG_ERROR(Service_NVFlinger, "unknown api = {}", api); + status = Status::BadValue; + break; + } + + core->buffer_has_been_queued = false; + core->dequeue_buffer_cannot_block = + core->consumer_controlled_by_app && producer_controlled_by_app; + + return status; +} + +Status BufferQueueProducer::Disconnect(NativeWindowApi api) { + LOG_DEBUG(Service_NVFlinger, "api = {}", api); + + Status status = Status::NoError; + std::shared_ptr<IConsumerListener> listener; + + { + std::scoped_lock lock{core->mutex}; + + core->WaitWhileAllocatingLocked(); + + if (core->is_abandoned) { + // Disconnecting after the surface has been abandoned is a no-op. + return Status::NoError; + } + + switch (api) { + case NativeWindowApi::Egl: + case NativeWindowApi::Cpu: + case NativeWindowApi::Media: + case NativeWindowApi::Camera: + if (core->connected_api == api) { + core->FreeAllBuffersLocked(); + core->connected_producer_listener = nullptr; + core->connected_api = NativeWindowApi::NoConnectedApi; + core->SignalDequeueCondition(); + buffer_wait_event->GetWritableEvent().Signal(); + listener = core->consumer_listener; + } else { + LOG_ERROR(Service_NVFlinger, "still connected to another api (cur = {} req = {})", + core->connected_api, api); + status = Status::BadValue; + } + break; + default: + LOG_ERROR(Service_NVFlinger, "unknown api = {}", api); + status = Status::BadValue; + break; + } + } + + // Call back without lock held + if (listener != nullptr) { + listener->OnBuffersReleased(); + } + + return status; +} + +Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, + const std::shared_ptr<GraphicBuffer>& buffer) { + LOG_DEBUG(Service_NVFlinger, "slot {}", slot); + + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + return Status::BadValue; + } + + std::scoped_lock lock{core->mutex}; + + slots[slot] = {}; + slots[slot].graphic_buffer = buffer; + slots[slot].frame_number = 0; + + // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for + // this to be called with an empty buffer, Naruto Ultimate Ninja Storm is a game that does this. + if (buffer) { + slots[slot].is_preallocated = true; + + core->override_max_buffer_count = core->GetPreallocatedBufferCountLocked(); + core->default_width = buffer->Width(); + core->default_height = buffer->Height(); + core->default_buffer_format = buffer->Format(); + } + + core->SignalDequeueCondition(); + buffer_wait_event->GetWritableEvent().Signal(); + + return Status::NoError; +} + +void BufferQueueProducer::Transact(Kernel::HLERequestContext& ctx, TransactionId code, u32 flags) { + Status status{Status::NoError}; + Parcel parcel_in{ctx.ReadBuffer()}; + Parcel parcel_out{}; + + switch (code) { + case TransactionId::Connect: { + const auto enable_listener = parcel_in.Read<bool>(); + const auto api = parcel_in.Read<NativeWindowApi>(); + const auto producer_controlled_by_app = parcel_in.Read<bool>(); + + UNIMPLEMENTED_IF_MSG(enable_listener, "Listener is unimplemented!"); + + std::shared_ptr<IProducerListener> listener; + QueueBufferOutput output{}; + + status = Connect(listener, api, producer_controlled_by_app, &output); + + parcel_out.Write(output); + break; + } + case TransactionId::SetPreallocatedBuffer: { + const auto slot = parcel_in.Read<s32>(); + const auto buffer = parcel_in.ReadObject<GraphicBuffer>(); + + status = SetPreallocatedBuffer(slot, buffer); + break; + } + case TransactionId::DequeueBuffer: { + const auto is_async = parcel_in.Read<bool>(); + const auto width = parcel_in.Read<u32>(); + const auto height = parcel_in.Read<u32>(); + const auto pixel_format = parcel_in.Read<PixelFormat>(); + const auto usage = parcel_in.Read<u32>(); + + s32 slot{}; + Fence fence{}; + + status = DequeueBuffer(&slot, &fence, is_async, width, height, pixel_format, usage); + + parcel_out.Write(slot); + parcel_out.WriteObject(&fence); + break; + } + case TransactionId::RequestBuffer: { + const auto slot = parcel_in.Read<s32>(); + + std::shared_ptr<GraphicBuffer> buf; + + status = RequestBuffer(slot, &buf); + + parcel_out.WriteObject(buf); + break; + } + case TransactionId::QueueBuffer: { + const auto slot = parcel_in.Read<s32>(); + + QueueBufferInput input{parcel_in}; + QueueBufferOutput output; + + status = QueueBuffer(slot, input, &output); + + parcel_out.Write(output); + break; + } + case TransactionId::Query: { + const auto what = parcel_in.Read<NativeWindow>(); + + s32 value{}; + + status = Query(what, &value); + + parcel_out.Write(value); + break; + } + case TransactionId::CancelBuffer: { + const auto slot = parcel_in.Read<s32>(); + const auto fence = parcel_in.ReadFlattened<Fence>(); + + CancelBuffer(slot, fence); + break; + } + case TransactionId::Disconnect: { + const auto api = parcel_in.Read<NativeWindowApi>(); + + status = Disconnect(api); + break; + } + case TransactionId::DetachBuffer: { + const auto slot = parcel_in.Read<s32>(); + + status = DetachBuffer(slot); + break; + } + case TransactionId::SetBufferCount: { + const auto buffer_count = parcel_in.Read<s32>(); + + status = SetBufferCount(buffer_count); + break; + } + case TransactionId::GetBufferHistory: + LOG_WARNING(Service_NVFlinger, "(STUBBED) called, transaction=GetBufferHistory"); + break; + default: + ASSERT_MSG(false, "Unimplemented TransactionId {}", code); + break; + } + + parcel_out.Write(status); + + ctx.WriteBuffer(parcel_out.Serialize()); +} + +Kernel::KReadableEvent& BufferQueueProducer::GetNativeHandle() { + return buffer_wait_event->GetReadableEvent(); +} + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h new file mode 100644 index 000000000..0ba03a568 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueProducer.h + +#pragma once + +#include <condition_variable> +#include <memory> +#include <mutex> + +#include "common/common_funcs.h" +#include "core/hle/service/nvdrv/nvdata.h" +#include "core/hle/service/nvflinger/binder.h" +#include "core/hle/service/nvflinger/buffer_queue_defs.h" +#include "core/hle/service/nvflinger/buffer_slot.h" +#include "core/hle/service/nvflinger/graphic_buffer_producer.h" +#include "core/hle/service/nvflinger/pixel_format.h" +#include "core/hle/service/nvflinger/status.h" +#include "core/hle/service/nvflinger/window.h" + +namespace Kernel { +class KernelCore; +class KEvent; +class KReadableEvent; +class KWritableEvent; +} // namespace Kernel + +namespace Service::KernelHelpers { +class ServiceContext; +} // namespace Service::KernelHelpers + +namespace Service::Nvidia::NvCore { +class NvMap; +} // namespace Service::Nvidia::NvCore + +namespace Service::android { + +class BufferQueueCore; +class IProducerListener; + +class BufferQueueProducer final : public IBinder { +public: + explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, + std::shared_ptr<BufferQueueCore> buffer_queue_core_, + Service::Nvidia::NvCore::NvMap& nvmap_); + ~BufferQueueProducer(); + + void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) override; + + Kernel::KReadableEvent& GetNativeHandle() override; + +public: + Status RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf); + Status SetBufferCount(s32 buffer_count); + Status DequeueBuffer(s32* out_slot, android::Fence* out_fence, bool async, u32 width, + u32 height, PixelFormat format, u32 usage); + Status DetachBuffer(s32 slot); + Status DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer, Fence* out_fence); + Status AttachBuffer(s32* outSlot, const std::shared_ptr<GraphicBuffer>& buffer); + Status QueueBuffer(s32 slot, const QueueBufferInput& input, QueueBufferOutput* output); + void CancelBuffer(s32 slot, const Fence& fence); + Status Query(NativeWindow what, s32* out_value); + Status Connect(const std::shared_ptr<IProducerListener>& listener, NativeWindowApi api, + bool producer_controlled_by_app, QueueBufferOutput* output); + + Status Disconnect(NativeWindowApi api); + Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<GraphicBuffer>& buffer); + +private: + BufferQueueProducer(const BufferQueueProducer&) = delete; + + Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags) const; + + Kernel::KEvent* buffer_wait_event{}; + Service::KernelHelpers::ServiceContext& service_context; + + std::shared_ptr<BufferQueueCore> core; + BufferQueueDefs::SlotsType& slots; + u32 sticky_transform{}; + std::mutex callback_mutex; + s32 next_callback_ticket{}; + s32 current_callback_ticket{}; + std::condition_variable_any callback_condition; + + Service::Nvidia::NvCore::NvMap& nvmap; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_slot.h b/src/core/hle/service/nvflinger/buffer_slot.h new file mode 100644 index 000000000..0cd0e9964 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_slot.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferSlot.h + +#pragma once + +#include <memory> + +#include "common/common_types.h" +#include "core/hle/service/nvflinger/ui/fence.h" + +namespace Service::android { + +class GraphicBuffer; + +enum class BufferState : u32 { + Free = 0, + Dequeued = 1, + Queued = 2, + Acquired = 3, +}; + +struct BufferSlot final { + constexpr BufferSlot() = default; + + std::shared_ptr<GraphicBuffer> graphic_buffer; + BufferState buffer_state{BufferState::Free}; + bool request_buffer_called{}; + u64 frame_number{}; + Fence fence; + bool acquire_called{}; + bool attached_by_consumer{}; + bool is_preallocated{}; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_transform_flags.h b/src/core/hle/service/nvflinger/buffer_transform_flags.h new file mode 100644 index 000000000..67aa5dad6 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_transform_flags.h @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace Service::android { + +enum class BufferTransformFlags : u32 { + /// No transform flags are set + Unset = 0x00, + /// Flip source image horizontally (around the vertical axis) + FlipH = 0x01, + /// Flip source image vertically (around the horizontal axis) + FlipV = 0x02, + /// Rotate source image 90 degrees clockwise + Rotate90 = 0x04, + /// Rotate source image 180 degrees + Rotate180 = 0x03, + /// Rotate source image 270 degrees clockwise + Rotate270 = 0x07, +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/consumer_base.cpp b/src/core/hle/service/nvflinger/consumer_base.cpp new file mode 100644 index 000000000..5b9995854 --- /dev/null +++ b/src/core/hle/service/nvflinger/consumer_base.cpp @@ -0,0 +1,133 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/ConsumerBase.cpp + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/hle/service/nvflinger/buffer_item.h" +#include "core/hle/service/nvflinger/buffer_queue_consumer.h" +#include "core/hle/service/nvflinger/buffer_queue_core.h" +#include "core/hle/service/nvflinger/consumer_base.h" +#include "core/hle/service/nvflinger/ui/graphic_buffer.h" + +namespace Service::android { + +ConsumerBase::ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_) + : consumer{std::move(consumer_)} {} + +ConsumerBase::~ConsumerBase() { + std::scoped_lock lock{mutex}; + + ASSERT_MSG(is_abandoned, "consumer is not abandoned!"); +} + +void ConsumerBase::Connect(bool controlled_by_app) { + consumer->Connect(shared_from_this(), controlled_by_app); +} + +void ConsumerBase::FreeBufferLocked(s32 slot_index) { + LOG_DEBUG(Service_NVFlinger, "slot_index={}", slot_index); + + slots[slot_index].graphic_buffer = nullptr; + slots[slot_index].fence = Fence::NoFence(); + slots[slot_index].frame_number = 0; +} + +void ConsumerBase::OnFrameAvailable(const BufferItem& item) { + LOG_DEBUG(Service_NVFlinger, "called"); +} + +void ConsumerBase::OnFrameReplaced(const BufferItem& item) { + LOG_DEBUG(Service_NVFlinger, "called"); +} + +void ConsumerBase::OnBuffersReleased() { + std::scoped_lock lock{mutex}; + + LOG_DEBUG(Service_NVFlinger, "called"); + + if (is_abandoned) { + // Nothing to do if we're already abandoned. + return; + } + + u64 mask = 0; + consumer->GetReleasedBuffers(&mask); + for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) { + if (mask & (1ULL << i)) { + FreeBufferLocked(i); + } + } +} + +void ConsumerBase::OnSidebandStreamChanged() {} + +Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when) { + Status err = consumer->AcquireBuffer(item, present_when); + if (err != Status::NoError) { + return err; + } + + if (item->graphic_buffer != nullptr) { + slots[item->slot].graphic_buffer = item->graphic_buffer; + } + + slots[item->slot].frame_number = item->frame_number; + slots[item->slot].fence = item->fence; + + LOG_DEBUG(Service_NVFlinger, "slot={}", item->slot); + + return Status::NoError; +} + +Status ConsumerBase::AddReleaseFenceLocked(s32 slot, + const std::shared_ptr<GraphicBuffer> graphic_buffer, + const Fence& fence) { + LOG_DEBUG(Service_NVFlinger, "slot={}", slot); + + // If consumer no longer tracks this graphic_buffer, we can safely + // drop this fence, as it will never be received by the producer. + + if (!StillTracking(slot, graphic_buffer)) { + return Status::NoError; + } + + slots[slot].fence = fence; + + return Status::NoError; +} + +Status ConsumerBase::ReleaseBufferLocked(s32 slot, + const std::shared_ptr<GraphicBuffer> graphic_buffer) { + // If consumer no longer tracks this graphic_buffer (we received a new + // buffer on the same slot), the buffer producer is definitely no longer + // tracking it. + + if (!StillTracking(slot, graphic_buffer)) { + return Status::NoError; + } + + LOG_DEBUG(Service_NVFlinger, "slot={}", slot); + Status err = consumer->ReleaseBuffer(slot, slots[slot].frame_number, slots[slot].fence); + if (err == Status::StaleBufferSlot) { + FreeBufferLocked(slot); + } + + slots[slot].fence = Fence::NoFence(); + + return err; +} + +bool ConsumerBase::StillTracking(s32 slot, + const std::shared_ptr<GraphicBuffer> graphic_buffer) const { + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + return false; + } + + return (slots[slot].graphic_buffer != nullptr && + slots[slot].graphic_buffer->Handle() == graphic_buffer->Handle()); +} + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/consumer_base.h b/src/core/hle/service/nvflinger/consumer_base.h new file mode 100644 index 000000000..90ba07f45 --- /dev/null +++ b/src/core/hle/service/nvflinger/consumer_base.h @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/ConsumerBase.h + +#pragma once + +#include <array> +#include <chrono> +#include <memory> +#include <mutex> + +#include "common/common_types.h" +#include "core/hle/service/nvflinger/buffer_queue_defs.h" +#include "core/hle/service/nvflinger/consumer_listener.h" +#include "core/hle/service/nvflinger/status.h" + +namespace Service::android { + +class BufferItem; +class BufferQueueConsumer; + +class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> { +public: + void Connect(bool controlled_by_app); + +protected: + explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_); + virtual ~ConsumerBase(); + + virtual void OnFrameAvailable(const BufferItem& item) override; + virtual void OnFrameReplaced(const BufferItem& item) override; + virtual void OnBuffersReleased() override; + virtual void OnSidebandStreamChanged() override; + + void FreeBufferLocked(s32 slot_index); + Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when); + Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer); + bool StillTracking(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer) const; + Status AddReleaseFenceLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer, + const Fence& fence); + + struct Slot final { + std::shared_ptr<GraphicBuffer> graphic_buffer; + Fence fence; + u64 frame_number{}; + }; + +protected: + std::array<Slot, BufferQueueDefs::NUM_BUFFER_SLOTS> slots; + + bool is_abandoned{}; + + std::unique_ptr<BufferQueueConsumer> consumer; + + mutable std::mutex mutex; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/consumer_listener.h b/src/core/hle/service/nvflinger/consumer_listener.h new file mode 100644 index 000000000..74a193988 --- /dev/null +++ b/src/core/hle/service/nvflinger/consumer_listener.h @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IConsumerListener.h + +#pragma once + +namespace Service::android { + +class BufferItem; + +/// ConsumerListener is the interface through which the BufferQueue notifies the consumer of events +/// that the consumer may wish to react to. +class IConsumerListener { +public: + IConsumerListener() = default; + virtual ~IConsumerListener() = default; + + virtual void OnFrameAvailable(const BufferItem& item) = 0; + virtual void OnFrameReplaced(const BufferItem& item) = 0; + virtual void OnBuffersReleased() = 0; + virtual void OnSidebandStreamChanged() = 0; +}; + +}; // namespace Service::android diff --git a/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp b/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp new file mode 100644 index 000000000..4043c91f1 --- /dev/null +++ b/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/IGraphicBufferProducer.cpp + +#include "core/hle/service/nvflinger/graphic_buffer_producer.h" +#include "core/hle/service/nvflinger/parcel.h" + +namespace Service::android { + +QueueBufferInput::QueueBufferInput(Parcel& parcel) { + parcel.ReadFlattened(*this); +} + +QueueBufferOutput::QueueBufferOutput() = default; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/graphic_buffer_producer.h b/src/core/hle/service/nvflinger/graphic_buffer_producer.h new file mode 100644 index 000000000..6ea327bbe --- /dev/null +++ b/src/core/hle/service/nvflinger/graphic_buffer_producer.h @@ -0,0 +1,76 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IGraphicBufferProducer.h + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/math_util.h" +#include "core/hle/service/nvflinger/ui/fence.h" +#include "core/hle/service/nvflinger/window.h" + +namespace Service::android { + +class Parcel; + +#pragma pack(push, 1) +struct QueueBufferInput final { + explicit QueueBufferInput(Parcel& parcel); + + void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle<s32>* crop_, + NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_, + u32* sticky_transform_, bool* async_, s32* swap_interval_, Fence* fence_) const { + *timestamp_ = timestamp; + *is_auto_timestamp_ = static_cast<bool>(is_auto_timestamp); + *crop_ = crop; + *scaling_mode_ = scaling_mode; + *transform_ = transform; + *sticky_transform_ = sticky_transform; + *async_ = static_cast<bool>(async); + *swap_interval_ = swap_interval; + *fence_ = fence; + } + +private: + s64 timestamp{}; + s32 is_auto_timestamp{}; + Common::Rectangle<s32> crop{}; + NativeWindowScalingMode scaling_mode{}; + NativeWindowTransform transform{}; + u32 sticky_transform{}; + s32 async{}; + s32 swap_interval{}; + Fence fence{}; +}; +#pragma pack(pop) +static_assert(sizeof(QueueBufferInput) == 84, "QueueBufferInput has wrong size"); + +struct QueueBufferOutput final { + QueueBufferOutput(); + + void Deflate(u32* width_, u32* height_, u32* transform_hint_, u32* num_pending_buffers_) const { + *width_ = width; + *height_ = height; + *transform_hint_ = transform_hint; + *num_pending_buffers_ = num_pending_buffers; + } + + void Inflate(u32 width_, u32 height_, u32 transform_hint_, u32 num_pending_buffers_) { + width = width_; + height = height_; + transform_hint = transform_hint_; + num_pending_buffers = num_pending_buffers_; + } + +private: + u32 width{}; + u32 height{}; + u32 transform_hint{}; + u32 num_pending_buffers{}; +}; +static_assert(sizeof(QueueBufferOutput) == 16, "QueueBufferOutput has wrong size"); + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp b/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp new file mode 100644 index 000000000..dc9b2a9ec --- /dev/null +++ b/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include <mutex> + +#include "common/common_types.h" +#include "core/hle/service/nvflinger/hos_binder_driver_server.h" + +namespace Service::NVFlinger { + +HosBinderDriverServer::HosBinderDriverServer(Core::System& system_) + : service_context(system_, "HosBinderDriverServer") {} + +HosBinderDriverServer::~HosBinderDriverServer() {} + +u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr<android::IBinder>&& binder) { + std::scoped_lock lk{lock}; + + last_id++; + + producers[last_id] = std::move(binder); + + return last_id; +} + +android::IBinder* HosBinderDriverServer::TryGetProducer(u64 id) { + std::scoped_lock lk{lock}; + + if (auto search = producers.find(id); search != producers.end()) { + return search->second.get(); + } + + return {}; +} + +} // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/hos_binder_driver_server.h b/src/core/hle/service/nvflinger/hos_binder_driver_server.h new file mode 100644 index 000000000..8fddc1206 --- /dev/null +++ b/src/core/hle/service/nvflinger/hos_binder_driver_server.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <memory> +#include <mutex> +#include <unordered_map> + +#include "common/common_types.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nvflinger/binder.h" + +namespace Core { +class System; +} + +namespace Service::NVFlinger { + +class HosBinderDriverServer final { +public: + explicit HosBinderDriverServer(Core::System& system_); + ~HosBinderDriverServer(); + + u64 RegisterProducer(std::unique_ptr<android::IBinder>&& binder); + + android::IBinder* TryGetProducer(u64 id); + +private: + KernelHelpers::ServiceContext service_context; + + std::unordered_map<u64, std::unique_ptr<android::IBinder>> producers; + std::mutex lock; + u64 last_id{}; +}; + +} // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 01e69de30..aa14d2cbc 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #include <algorithm> #include <optional> @@ -16,11 +15,17 @@ #include "core/hle/kernel/k_readable_event.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvdrv/nvdrv.h" -#include "core/hle/service/nvflinger/buffer_queue.h" +#include "core/hle/service/nvflinger/buffer_item_consumer.h" +#include "core/hle/service/nvflinger/buffer_queue_core.h" +#include "core/hle/service/nvflinger/hos_binder_driver_server.h" #include "core/hle/service/nvflinger/nvflinger.h" +#include "core/hle/service/nvflinger/ui/graphic_buffer.h" #include "core/hle/service/vi/display/vi_display.h" #include "core/hle/service/vi/layer/vi_layer.h" +#include "core/hle/service/vi/vi_results.h" #include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" +#include "video_core/host1x/syncpoint_manager.h" namespace Service::NVFlinger { @@ -28,7 +33,7 @@ constexpr auto frame_ns = std::chrono::nanoseconds{1000000000 / 60}; void NVFlinger::SplitVSync(std::stop_token stop_token) { system.RegisterHostThread(); - std::string name = "yuzu:VSyncThread"; + std::string name = "VSyncThread"; MicroProfileOnThreadCreate(name.c_str()); // Cleanup @@ -36,69 +41,87 @@ void NVFlinger::SplitVSync(std::stop_token stop_token) { Common::SetCurrentThreadName(name.c_str()); Common::SetCurrentThreadPriority(Common::ThreadPriority::High); - s64 delay = 0; + while (!stop_token.stop_requested()) { + vsync_signal.wait(false); + vsync_signal.store(false); + guard->lock(); - const s64 time_start = system.CoreTiming().GetGlobalTimeNs().count(); + Compose(); - const auto ticks = GetNextTicks(); - const s64 time_end = system.CoreTiming().GetGlobalTimeNs().count(); - const s64 time_passed = time_end - time_start; - const s64 next_time = std::max<s64>(0, ticks - time_passed - delay); + guard->unlock(); - if (next_time > 0) { - std::this_thread::sleep_for(std::chrono::nanoseconds{next_time}); - } - delay = (system.CoreTiming().GetGlobalTimeNs().count() - time_end) - next_time; } } -NVFlinger::NVFlinger(Core::System& system_) - : system(system_), service_context(system_, "nvflinger") { - displays.emplace_back(0, "Default", service_context, system); - displays.emplace_back(1, "External", service_context, system); - displays.emplace_back(2, "Edid", service_context, system); - displays.emplace_back(3, "Internal", service_context, system); - displays.emplace_back(4, "Null", service_context, system); +NVFlinger::NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_) + : system(system_), service_context(system_, "nvflinger"), + hos_binder_driver_server(hos_binder_driver_server_) { + displays.emplace_back(0, "Default", hos_binder_driver_server, service_context, system); + displays.emplace_back(1, "External", hos_binder_driver_server, service_context, system); + displays.emplace_back(2, "Edid", hos_binder_driver_server, service_context, system); + displays.emplace_back(3, "Internal", hos_binder_driver_server, service_context, system); + displays.emplace_back(4, "Null", hos_binder_driver_server, service_context, system); guard = std::make_shared<std::mutex>(); // Schedule the screen composition events - composition_event = Core::Timing::CreateEvent( - "ScreenComposition", [this](std::uintptr_t, std::chrono::nanoseconds ns_late) { + multi_composition_event = Core::Timing::CreateEvent( + "ScreenComposition", + [this](std::uintptr_t, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { + vsync_signal.store(true); + vsync_signal.notify_all(); + return std::chrono::nanoseconds(GetNextTicks()); + }); + + single_composition_event = Core::Timing::CreateEvent( + "ScreenComposition", + [this](std::uintptr_t, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { const auto lock_guard = Lock(); Compose(); - const auto ticks = std::chrono::nanoseconds{GetNextTicks()}; - const auto ticks_delta = ticks - ns_late; - const auto future_ns = std::max(std::chrono::nanoseconds::zero(), ticks_delta); - - this->system.CoreTiming().ScheduleEvent(future_ns, composition_event); + return std::chrono::nanoseconds(GetNextTicks()); }); if (system.IsMulticore()) { + system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, multi_composition_event); vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); }); } else { - system.CoreTiming().ScheduleEvent(frame_ns, composition_event); + system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, single_composition_event); } } NVFlinger::~NVFlinger() { - for (auto& buffer_queue : buffer_queues) { - buffer_queue->Disconnect(); + if (system.IsMulticore()) { + system.CoreTiming().UnscheduleEvent(multi_composition_event, {}); + vsync_thread.request_stop(); + vsync_signal.store(true); + vsync_signal.notify_all(); + } else { + system.CoreTiming().UnscheduleEvent(single_composition_event, {}); } - if (!system.IsMulticore()) { - system.CoreTiming().UnscheduleEvent(composition_event, 0); + + for (auto& display : displays) { + for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { + display.GetLayer(layer).Core().NotifyShutdown(); + } + } + + if (nvdrv) { + nvdrv->Close(disp_fd); } } void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { nvdrv = std::move(instance); + disp_fd = nvdrv->Open("/dev/nvdisp_disp0"); } std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { const auto lock_guard = Lock(); - LOG_DEBUG(Service, "Opening \"{}\" display", name); + LOG_DEBUG(Service_NVFlinger, "Opening \"{}\" display", name); const auto itr = std::find_if(displays.begin(), displays.end(), @@ -125,10 +148,8 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { } void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { - const u32 buffer_queue_id = next_buffer_queue_id++; - buffer_queues.emplace_back( - std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id, service_context)); - display.CreateLayer(layer_id, *buffer_queues.back()); + const auto buffer_id = next_buffer_queue_id++; + display.CreateLayer(layer_id, buffer_id, nvdrv->container); } void NVFlinger::CloseLayer(u64 layer_id) { @@ -147,30 +168,18 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) { return std::nullopt; } - return layer->GetBufferQueue().GetId(); + return layer->GetBinderId(); } -Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) { +ResultVal<Kernel::KReadableEvent*> NVFlinger::FindVsyncEvent(u64 display_id) { const auto lock_guard = Lock(); auto* const display = FindDisplay(display_id); if (display == nullptr) { - return nullptr; + return VI::ResultNotFound; } - return &display->GetVSyncEvent(); -} - -BufferQueue* NVFlinger::FindBufferQueue(u32 id) { - const auto lock_guard = Lock(); - const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(), - [id](const auto& queue) { return queue->GetId() == id; }); - - if (itr == buffer_queues.end()) { - return nullptr; - } - - return itr->get(); + return display->GetVSyncEvent(); } VI::Display* NVFlinger::FindDisplay(u64 display_id) { @@ -227,7 +236,7 @@ VI::Layer* NVFlinger::FindOrCreateLayer(u64 display_id, u64 layer_id) { auto* layer = display->FindLayer(layer_id); if (layer == nullptr) { - LOG_DEBUG(Service, "Layer at id {} not found. Trying to create it.", layer_id); + LOG_DEBUG(Service_NVFlinger, "Layer at id {} not found. Trying to create it.", layer_id); CreateLayerAtId(*display, layer_id); return display->FindLayer(layer_id); } @@ -246,44 +255,43 @@ void NVFlinger::Compose() { // TODO(Subv): Support more than 1 layer. VI::Layer& layer = display.GetLayer(0); - auto& buffer_queue = layer.GetBufferQueue(); - // Search for a queued buffer and acquire it - auto buffer = buffer_queue.AcquireBuffer(); + android::BufferItem buffer{}; + const auto status = layer.GetConsumer().AcquireBuffer(&buffer, {}, false); - if (!buffer) { + if (status != android::Status::NoError) { continue; } - const auto& igbp_buffer = buffer->get().igbp_buffer; + const auto& igbp_buffer = *buffer.graphic_buffer; if (!system.IsPoweredOn()) { return; // We are likely shutting down } - auto& gpu = system.GPU(); - const auto& multi_fence = buffer->get().multi_fence; - guard->unlock(); - for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) { - const auto& fence = multi_fence.fences[fence_id]; - gpu.WaitFence(fence.id, fence.value); - } - guard->lock(); - - MicroProfileFlip(); - // Now send the buffer to the GPU for drawing. // TODO(Subv): Support more than just disp0. The display device selection is probably based // on which display we're drawing (Default, Internal, External, etc) - auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0"); + auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd); ASSERT(nvdisp); - nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.external_format, - igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, - buffer->get().transform, buffer->get().crop_rect); + guard->unlock(); + Common::Rectangle<int> crop_rect{ + static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()), + static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())}; - swap_interval = buffer->get().swap_interval; - buffer_queue.ReleaseBuffer(buffer->get().slot); + nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(), + igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(), + static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect, + buffer.fence.fences, buffer.fence.num_fences); + + MicroProfileFlip(); + guard->lock(); + + swap_interval = buffer.swap_interval; + + auto fence = android::Fence::NoFence(); + layer.GetConsumer().ReleaseBuffer(buffer, fence); } } @@ -291,9 +299,21 @@ s64 NVFlinger::GetNextTicks() const { static constexpr s64 max_hertz = 120LL; const auto& settings = Settings::values; - const bool unlocked_fps = settings.disable_fps_limit.GetValue(); - const s64 fps_cap = unlocked_fps ? static_cast<s64>(settings.fps_cap.GetValue()) : 1; - return (1000000000 * (1LL << swap_interval)) / (max_hertz * fps_cap); + auto speed_scale = 1.f; + if (settings.use_multi_core.GetValue()) { + if (settings.use_speed_limit.GetValue()) { + // Scales the speed based on speed_limit setting on MC. SC is handled by + // SpeedLimiter::DoSpeedLimiting. + speed_scale = 100.f / settings.speed_limit.GetValue(); + } else { + // Run at unlocked framerate. + speed_scale = 0.01f; + } + } + + const auto next_ticks = ((1000000000 * (1LL << swap_interval)) / max_hertz); + + return static_cast<s64>(speed_scale * static_cast<float>(next_ticks)); } } // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 7935cf773..b62615de2 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once @@ -12,6 +11,7 @@ #include <vector> #include "common/common_types.h" +#include "core/hle/result.h" #include "core/hle/service/kernel_helpers.h" namespace Common { @@ -37,13 +37,16 @@ class Display; class Layer; } // namespace Service::VI -namespace Service::NVFlinger { +namespace Service::android { +class BufferQueueCore; +class BufferQueueProducer; +} // namespace Service::android -class BufferQueue; +namespace Service::NVFlinger { class NVFlinger final { public: - explicit NVFlinger(Core::System& system_); + explicit NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_); ~NVFlinger(); /// Sets the NVDrv module instance to use to send buffers to the GPU. @@ -69,11 +72,9 @@ public: /// Gets the vsync event for the specified display. /// - /// If an invalid display ID is provided, then nullptr is returned. - [[nodiscard]] Kernel::KReadableEvent* FindVsyncEvent(u64 display_id); - - /// Obtains a buffer queue identified by the ID. - [[nodiscard]] BufferQueue* FindBufferQueue(u32 id); + /// If an invalid display ID is provided, then VI::ResultNotFound is returned. + /// If the vsync event has already been retrieved, then VI::ResultPermissionDenied is returned. + [[nodiscard]] ResultVal<Kernel::KReadableEvent*> FindVsyncEvent(u64 display_id); /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when /// finished. @@ -82,6 +83,12 @@ public: [[nodiscard]] s64 GetNextTicks() const; private: + struct Layer { + std::unique_ptr<android::BufferQueueCore> core; + std::unique_ptr<android::BufferQueueProducer> producer; + }; + +private: [[nodiscard]] std::unique_lock<std::mutex> Lock() const { return std::unique_lock{*guard}; } @@ -109,9 +116,9 @@ private: void SplitVSync(std::stop_token stop_token); std::shared_ptr<Nvidia::Module> nvdrv; + s32 disp_fd; std::list<VI::Display> displays; - std::vector<std::unique_ptr<BufferQueue>> buffer_queues; /// Id to use for the next layer that is created, this counter is shared among all displays. u64 next_layer_id = 1; @@ -122,15 +129,20 @@ private: u32 swap_interval = 1; /// Event that handles screen composition. - std::shared_ptr<Core::Timing::EventType> composition_event; + std::shared_ptr<Core::Timing::EventType> multi_composition_event; + std::shared_ptr<Core::Timing::EventType> single_composition_event; std::shared_ptr<std::mutex> guard; Core::System& system; + std::atomic<bool> vsync_signal; + std::jthread vsync_thread; KernelHelpers::ServiceContext service_context; + + HosBinderDriverServer& hos_binder_driver_server; }; } // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/parcel.h b/src/core/hle/service/nvflinger/parcel.h new file mode 100644 index 000000000..f3fa2587d --- /dev/null +++ b/src/core/hle/service/nvflinger/parcel.h @@ -0,0 +1,172 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <memory> +#include <vector> + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/common_types.h" + +namespace Service::android { + +class Parcel final { +public: + static constexpr std::size_t DefaultBufferSize = 0x40; + + Parcel() : buffer(DefaultBufferSize) {} + + template <typename T> + explicit Parcel(const T& out_data) : buffer(DefaultBufferSize) { + Write(out_data); + } + + explicit Parcel(std::vector<u8> in_data) : buffer(std::move(in_data)) { + DeserializeHeader(); + [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); + } + + template <typename T> + void Read(T& val) { + static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); + ASSERT(read_index + sizeof(T) <= buffer.size()); + + std::memcpy(&val, buffer.data() + read_index, sizeof(T)); + read_index += sizeof(T); + read_index = Common::AlignUp(read_index, 4); + } + + template <typename T> + T Read() { + T val; + Read(val); + return val; + } + + template <typename T> + void ReadFlattened(T& val) { + const auto flattened_size = Read<s64>(); + ASSERT(sizeof(T) == flattened_size); + Read(val); + } + + template <typename T> + T ReadFlattened() { + T val; + ReadFlattened(val); + return val; + } + + template <typename T> + T ReadUnaligned() { + static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); + ASSERT(read_index + sizeof(T) <= buffer.size()); + + T val; + std::memcpy(&val, buffer.data() + read_index, sizeof(T)); + read_index += sizeof(T); + return val; + } + + template <typename T> + const std::shared_ptr<T> ReadObject() { + static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); + + const auto is_valid{Read<bool>()}; + + if (is_valid) { + auto result = std::make_shared<T>(); + ReadFlattened(*result); + return result; + } + + return {}; + } + + std::u16string ReadInterfaceToken() { + [[maybe_unused]] const u32 unknown = Read<u32>(); + const u32 length = Read<u32>(); + + std::u16string token; + token.reserve(length + 1); + + for (u32 ch = 0; ch < length + 1; ++ch) { + token.push_back(ReadUnaligned<u16>()); + } + + read_index = Common::AlignUp(read_index, 4); + + return token; + } + + template <typename T> + void Write(const T& val) { + static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); + + if (buffer.size() < write_index + sizeof(T)) { + buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize); + } + + std::memcpy(buffer.data() + write_index, &val, sizeof(T)); + write_index += sizeof(T); + write_index = Common::AlignUp(write_index, 4); + } + + template <typename T> + void WriteObject(const T* ptr) { + static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); + + if (!ptr) { + Write<u32>(0); + return; + } + + Write<u32>(1); + Write<s64>(sizeof(T)); + Write(*ptr); + } + + template <typename T> + void WriteObject(const std::shared_ptr<T> ptr) { + WriteObject(ptr.get()); + } + + void DeserializeHeader() { + ASSERT(buffer.size() > sizeof(Header)); + + Header header{}; + std::memcpy(&header, buffer.data(), sizeof(Header)); + + read_index = header.data_offset; + } + + std::vector<u8> Serialize() const { + ASSERT(read_index == 0); + + Header header{}; + header.data_size = static_cast<u32>(write_index - sizeof(Header)); + header.data_offset = sizeof(Header); + header.objects_size = 4; + header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size); + std::memcpy(buffer.data(), &header, sizeof(Header)); + + return buffer; + } + +private: + struct Header { + u32 data_size; + u32 data_offset; + u32 objects_size; + u32 objects_offset; + }; + static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size"); + + mutable std::vector<u8> buffer; + std::size_t read_index = 0; + std::size_t write_index = sizeof(Header); +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/pixel_format.h b/src/core/hle/service/nvflinger/pixel_format.h new file mode 100644 index 000000000..f77d0acfb --- /dev/null +++ b/src/core/hle/service/nvflinger/pixel_format.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace Service::android { + +enum class PixelFormat : u32 { + NoFormat = 0, + Rgba8888 = 1, + Rgbx8888 = 2, + Rgb888 = 3, + Rgb565 = 4, + Bgra8888 = 5, + Rgba5551 = 6, + Rgba4444 = 7, +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/producer_listener.h b/src/core/hle/service/nvflinger/producer_listener.h new file mode 100644 index 000000000..1c4d5db0e --- /dev/null +++ b/src/core/hle/service/nvflinger/producer_listener.h @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IProducerListener.h + +#pragma once + +namespace Service::android { + +class IProducerListener { +public: + virtual void OnBufferReleased() = 0; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/status.h b/src/core/hle/service/nvflinger/status.h new file mode 100644 index 000000000..7af166c40 --- /dev/null +++ b/src/core/hle/service/nvflinger/status.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace Service::android { + +enum class Status : s32 { + None = 0, + NoError = 0, + StaleBufferSlot = 1, + NoBufferAvailable = 2, + PresentLater = 3, + WouldBlock = -11, + NoMemory = -12, + Busy = -16, + NoInit = -19, + BadValue = -22, + InvalidOperation = -37, + BufferNeedsReallocation = 1, + ReleaseAllBuffers = 2, +}; +DECLARE_ENUM_FLAG_OPERATORS(Status); + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/ui/fence.h b/src/core/hle/service/nvflinger/ui/fence.h new file mode 100644 index 000000000..536e8156d --- /dev/null +++ b/src/core/hle/service/nvflinger/ui/fence.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/Fence.h + +#pragma once + +#include <array> + +#include "common/common_types.h" +#include "core/hle/service/nvdrv/nvdata.h" + +namespace Service::android { + +class Fence { +public: + constexpr Fence() = default; + + static constexpr Fence NoFence() { + Fence fence; + fence.fences[0].id = -1; + return fence; + } + +public: + u32 num_fences{}; + std::array<Service::Nvidia::NvFence, 4> fences{}; +}; +static_assert(sizeof(Fence) == 36, "Fence has wrong size"); + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/ui/graphic_buffer.h b/src/core/hle/service/nvflinger/ui/graphic_buffer.h new file mode 100644 index 000000000..9a27f8f02 --- /dev/null +++ b/src/core/hle/service/nvflinger/ui/graphic_buffer.h @@ -0,0 +1,100 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2007 The Android Open Source Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Parts of this implementation were based on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/GraphicBuffer.h + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/service/nvflinger/pixel_format.h" + +namespace Service::android { + +class GraphicBuffer final { +public: + constexpr GraphicBuffer() = default; + + constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_) + : width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_}, + usage{static_cast<s32>(usage_)} {} + + constexpr u32 Width() const { + return static_cast<u32>(width); + } + + constexpr u32 Height() const { + return static_cast<u32>(height); + } + + constexpr u32 Stride() const { + return static_cast<u32>(stride); + } + + constexpr u32 Usage() const { + return static_cast<u32>(usage); + } + + constexpr PixelFormat Format() const { + return format; + } + + constexpr u32 BufferId() const { + return buffer_id; + } + + constexpr PixelFormat ExternalFormat() const { + return external_format; + } + + constexpr u32 Handle() const { + return handle; + } + + constexpr u32 Offset() const { + return offset; + } + + constexpr bool NeedsReallocation(u32 width_, u32 height_, PixelFormat format_, + u32 usage_) const { + if (static_cast<s32>(width_) != width) { + return true; + } + + if (static_cast<s32>(height_) != height) { + return true; + } + + if (format_ != format) { + return true; + } + + if ((static_cast<u32>(usage) & usage_) != usage_) { + return true; + } + + return false; + } + +private: + u32 magic{}; + s32 width{}; + s32 height{}; + s32 stride{}; + PixelFormat format{}; + s32 usage{}; + INSERT_PADDING_WORDS(1); + u32 index{}; + INSERT_PADDING_WORDS(3); + u32 buffer_id{}; + INSERT_PADDING_WORDS(6); + PixelFormat external_format{}; + INSERT_PADDING_WORDS(10); + u32 handle{}; + u32 offset{}; + INSERT_PADDING_WORDS(60); +}; +static_assert(sizeof(GraphicBuffer) == 0x16C, "GraphicBuffer has wrong size"); + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/window.h b/src/core/hle/service/nvflinger/window.h new file mode 100644 index 000000000..61cca5b01 --- /dev/null +++ b/src/core/hle/service/nvflinger/window.h @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace Service::android { + +/// Attributes queryable with Query +enum class NativeWindow : s32 { + Width = 0, + Height = 1, + Format = 2, + MinUndequeedBuffers = 3, + QueuesToWindowComposer = 4, + ConcreteType = 5, + DefaultWidth = 6, + DefaultHeight = 7, + TransformHint = 8, + ConsumerRunningBehind = 9, + ConsumerUsageBits = 10, + StickyTransform = 11, + DefaultDataSpace = 12, + BufferAge = 13, +}; + +/// Parameter for Connect/Disconnect +enum class NativeWindowApi : s32 { + NoConnectedApi = 0, + Egl = 1, + Cpu = 2, + Media = 3, + Camera = 4, +}; + +/// Scaling mode parameter for QueueBuffer +enum class NativeWindowScalingMode : s32 { + Freeze = 0, + ScaleToWindow = 1, + ScaleCrop = 2, + NoScaleCrop = 3, +}; + +/// Transform parameter for QueueBuffer +enum class NativeWindowTransform : u32 { + None = 0x0, + InverseDisplay = 0x08, +}; +DECLARE_ENUM_FLAG_OPERATORS(NativeWindowTransform); + +} // namespace Service::android diff --git a/src/core/hle/service/olsc/olsc.cpp b/src/core/hle/service/olsc/olsc.cpp index 39a8031a5..530e1be3b 100644 --- a/src/core/hle/service/olsc/olsc.cpp +++ b/src/core/hle/service/olsc/olsc.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/ipc_helpers.h" #include "core/hle/service/olsc/olsc.h" diff --git a/src/core/hle/service/olsc/olsc.h b/src/core/hle/service/olsc/olsc.h index 24f24ca6b..1522d8d32 100644 --- a/src/core/hle/service/olsc/olsc.h +++ b/src/core/hle/service/olsc/olsc.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/pcie/pcie.cpp b/src/core/hle/service/pcie/pcie.cpp index 9bc851591..79501b9f9 100644 --- a/src/core/hle/service/pcie/pcie.cpp +++ b/src/core/hle/service/pcie/pcie.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/pcie/pcie.h b/src/core/hle/service/pcie/pcie.h index e5709a72f..cebfd9042 100644 --- a/src/core/hle/service/pcie/pcie.h +++ b/src/core/hle/service/pcie/pcie.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/pctl/pctl.cpp b/src/core/hle/service/pctl/pctl.cpp index 908e0a1e3..3f47bf094 100644 --- a/src/core/hle/service/pctl/pctl.cpp +++ b/src/core/hle/service/pctl/pctl.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/pctl/pctl.h" diff --git a/src/core/hle/service/pctl/pctl.h b/src/core/hle/service/pctl/pctl.h index 1d28900b2..87f93161e 100644 --- a/src/core/hle/service/pctl/pctl.h +++ b/src/core/hle/service/pctl/pctl.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/pctl/pctl_module.cpp b/src/core/hle/service/pctl/pctl_module.cpp index 240776101..2a123b42d 100644 --- a/src/core/hle/service/pctl/pctl_module.cpp +++ b/src/core/hle/service/pctl/pctl_module.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/core.h" @@ -14,10 +13,10 @@ namespace Service::PCTL { namespace Error { -constexpr ResultCode ResultNoFreeCommunication{ErrorModule::PCTL, 101}; -constexpr ResultCode ResultStereoVisionRestricted{ErrorModule::PCTL, 104}; -constexpr ResultCode ResultNoCapability{ErrorModule::PCTL, 131}; -constexpr ResultCode ResultNoRestrictionEnabled{ErrorModule::PCTL, 181}; +constexpr Result ResultNoFreeCommunication{ErrorModule::PCTL, 101}; +constexpr Result ResultStereoVisionRestricted{ErrorModule::PCTL, 104}; +constexpr Result ResultNoCapability{ErrorModule::PCTL, 131}; +constexpr Result ResultNoRestrictionEnabled{ErrorModule::PCTL, 181}; } // namespace Error diff --git a/src/core/hle/service/pctl/pctl_module.h b/src/core/hle/service/pctl/pctl_module.h index f25c5c557..6f584530d 100644 --- a/src/core/hle/service/pctl/pctl_module.h +++ b/src/core/hle/service/pctl/pctl_module.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/pcv/pcv.cpp b/src/core/hle/service/pcv/pcv.cpp index 68b2c4178..f7a497a14 100644 --- a/src/core/hle/service/pcv/pcv.cpp +++ b/src/core/hle/service/pcv/pcv.cpp @@ -1,9 +1,9 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> +#include "core/hle/ipc_helpers.h" #include "core/hle/service/pcv/pcv.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" @@ -78,10 +78,102 @@ public: } }; +class IClkrstSession final : public ServiceFramework<IClkrstSession> { +public: + explicit IClkrstSession(Core::System& system_, DeviceCode deivce_code_) + : ServiceFramework{system_, "IClkrstSession"}, deivce_code(deivce_code_) { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "SetClockEnabled"}, + {1, nullptr, "SetClockDisabled"}, + {2, nullptr, "SetResetAsserted"}, + {3, nullptr, "SetResetDeasserted"}, + {4, nullptr, "SetPowerEnabled"}, + {5, nullptr, "SetPowerDisabled"}, + {6, nullptr, "GetState"}, + {7, &IClkrstSession::SetClockRate, "SetClockRate"}, + {8, &IClkrstSession::GetClockRate, "GetClockRate"}, + {9, nullptr, "SetMinVClockRate"}, + {10, nullptr, "GetPossibleClockRates"}, + {11, nullptr, "GetDvfsTable"}, + }; + // clang-format on + RegisterHandlers(functions); + } + +private: + void SetClockRate(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + clock_rate = rp.Pop<u32>(); + LOG_DEBUG(Service_PCV, "(STUBBED) called, clock_rate={}", clock_rate); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void GetClockRate(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PCV, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u32>(clock_rate); + } + + DeviceCode deivce_code; + u32 clock_rate{}; +}; + +class CLKRST final : public ServiceFramework<CLKRST> { +public: + explicit CLKRST(Core::System& system_, const char* name) : ServiceFramework{system_, name} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &CLKRST::OpenSession, "OpenSession"}, + {1, nullptr, "GetTemperatureThresholds"}, + {2, nullptr, "SetTemperature"}, + {3, nullptr, "GetModuleStateTable"}, + {4, nullptr, "GetModuleStateTableEvent"}, + {5, nullptr, "GetModuleStateTableMaxCount"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void OpenSession(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_code = static_cast<DeviceCode>(rp.Pop<u32>()); + const auto unkonwn_input = rp.Pop<u32>(); + + LOG_DEBUG(Service_PCV, "called, device_code={}, input={}", device_code, unkonwn_input); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IClkrstSession>(system, device_code); + } +}; + +class CLKRST_A final : public ServiceFramework<CLKRST_A> { +public: + explicit CLKRST_A(Core::System& system_) : ServiceFramework{system_, "clkrst:a"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "ReleaseControl"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { std::make_shared<PCV>(system)->InstallAsService(sm); std::make_shared<PCV_ARB>(system)->InstallAsService(sm); std::make_shared<PCV_IMM>(system)->InstallAsService(sm); + std::make_shared<CLKRST>(system, "clkrst")->InstallAsService(sm); + std::make_shared<CLKRST>(system, "clkrst:i")->InstallAsService(sm); + std::make_shared<CLKRST_A>(system)->InstallAsService(sm); } } // namespace Service::PCV diff --git a/src/core/hle/service/pcv/pcv.h b/src/core/hle/service/pcv/pcv.h index c61a0b591..6b26b6fa7 100644 --- a/src/core/hle/service/pcv/pcv.h +++ b/src/core/hle/service/pcv/pcv.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -14,6 +13,97 @@ class ServiceManager; namespace Service::PCV { +enum class DeviceCode : u32 { + Cpu = 0x40000001, + Gpu = 0x40000002, + I2s1 = 0x40000003, + I2s2 = 0x40000004, + I2s3 = 0x40000005, + Pwm = 0x40000006, + I2c1 = 0x02000001, + I2c2 = 0x02000002, + I2c3 = 0x02000003, + I2c4 = 0x02000004, + I2c5 = 0x02000005, + I2c6 = 0x02000006, + Spi1 = 0x07000000, + Spi2 = 0x07000001, + Spi3 = 0x07000002, + Spi4 = 0x07000003, + Disp1 = 0x40000011, + Disp2 = 0x40000012, + Isp = 0x40000013, + Vi = 0x40000014, + Sdmmc1 = 0x40000015, + Sdmmc2 = 0x40000016, + Sdmmc3 = 0x40000017, + Sdmmc4 = 0x40000018, + Owr = 0x40000019, + Csite = 0x4000001A, + Tsec = 0x4000001B, + Mselect = 0x4000001C, + Hda2codec2x = 0x4000001D, + Actmon = 0x4000001E, + I2cSlow = 0x4000001F, + Sor1 = 0x40000020, + Sata = 0x40000021, + Hda = 0x40000022, + XusbCoreHostSrc = 0x40000023, + XusbFalconSrc = 0x40000024, + XusbFsSrc = 0x40000025, + XusbCoreDevSrc = 0x40000026, + XusbSsSrc = 0x40000027, + UartA = 0x03000001, + UartB = 0x35000405, + UartC = 0x3500040F, + UartD = 0x37000001, + Host1x = 0x4000002C, + Entropy = 0x4000002D, + SocTherm = 0x4000002E, + Vic = 0x4000002F, + Nvenc = 0x40000030, + Nvjpg = 0x40000031, + Nvdec = 0x40000032, + Qspi = 0x40000033, + ViI2c = 0x40000034, + Tsecb = 0x40000035, + Ape = 0x40000036, + AudioDsp = 0x40000037, + AudioUart = 0x40000038, + Emc = 0x40000039, + Plle = 0x4000003A, + PlleHwSeq = 0x4000003B, + Dsi = 0x4000003C, + Maud = 0x4000003D, + Dpaux1 = 0x4000003E, + MipiCal = 0x4000003F, + UartFstMipiCal = 0x40000040, + Osc = 0x40000041, + SysBus = 0x40000042, + SorSafe = 0x40000043, + XusbSs = 0x40000044, + XusbHost = 0x40000045, + XusbDevice = 0x40000046, + Extperiph1 = 0x40000047, + Ahub = 0x40000048, + Hda2hdmicodec = 0x40000049, + Gpuaux = 0x4000004A, + UsbD = 0x4000004B, + Usb2 = 0x4000004C, + Pcie = 0x4000004D, + Afi = 0x4000004E, + PciExClk = 0x4000004F, + PExUsbPhy = 0x40000050, + XUsbPadCtl = 0x40000051, + Apbdma = 0x40000052, + Usb2TrkClk = 0x40000053, + XUsbIoPll = 0x40000054, + XUsbIoPllHwSeq = 0x40000055, + Cec = 0x40000056, + Extperiph2 = 0x40000057, + OscClk = 0x40000080 +}; + void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); } // namespace Service::PCV diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp index 057666021..b10e86c8f 100644 --- a/src/core/hle/service/pm/pm.cpp +++ b/src/core/hle/service/pm/pm.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" #include "core/hle/ipc_helpers.h" @@ -13,12 +12,12 @@ namespace Service::PM { namespace { -constexpr ResultCode ResultProcessNotFound{ErrorModule::PM, 1}; -[[maybe_unused]] constexpr ResultCode ResultAlreadyStarted{ErrorModule::PM, 2}; -[[maybe_unused]] constexpr ResultCode ResultNotTerminated{ErrorModule::PM, 3}; -[[maybe_unused]] constexpr ResultCode ResultDebugHookInUse{ErrorModule::PM, 4}; -[[maybe_unused]] constexpr ResultCode ResultApplicationRunning{ErrorModule::PM, 5}; -[[maybe_unused]] constexpr ResultCode ResultInvalidSize{ErrorModule::PM, 6}; +constexpr Result ResultProcessNotFound{ErrorModule::PM, 1}; +[[maybe_unused]] constexpr Result ResultAlreadyStarted{ErrorModule::PM, 2}; +[[maybe_unused]] constexpr Result ResultNotTerminated{ErrorModule::PM, 3}; +[[maybe_unused]] constexpr Result ResultDebugHookInUse{ErrorModule::PM, 4}; +[[maybe_unused]] constexpr Result ResultApplicationRunning{ErrorModule::PM, 5}; +[[maybe_unused]] constexpr Result ResultInvalidSize{ErrorModule::PM, 6}; constexpr u64 NO_PROCESS_FOUND_PID{0}; diff --git a/src/core/hle/service/pm/pm.h b/src/core/hle/service/pm/pm.h index 852e7050c..060103928 100644 --- a/src/core/hle/service/pm/pm.h +++ b/src/core/hle/service/pm/pm.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index 5c8a44688..78f897d3e 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/hex_util.h" #include "common/logging/log.h" diff --git a/src/core/hle/service/prepo/prepo.h b/src/core/hle/service/prepo/prepo.h index 395b57ead..37ea5afad 100644 --- a/src/core/hle/service/prepo/prepo.h +++ b/src/core/hle/service/prepo/prepo.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/psc/psc.cpp b/src/core/hle/service/psc/psc.cpp index f8ea02b58..3a9412cf5 100644 --- a/src/core/hle/service/psc/psc.cpp +++ b/src/core/hle/service/psc/psc.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/psc/psc.h b/src/core/hle/service/psc/psc.h index 89344f32d..d248372c2 100644 --- a/src/core/hle/service/psc/psc.h +++ b/src/core/hle/service/psc/psc.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp index 5d248f671..2c31e9485 100644 --- a/src/core/hle/service/ptm/psm.cpp +++ b/src/core/hle/service/ptm/psm.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> @@ -10,10 +9,8 @@ #include "core/hle/kernel/k_event.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/ptm/psm.h" -#include "core/hle/service/service.h" -#include "core/hle/service/sm/sm.h" -namespace Service::PSM { +namespace Service::PTM { class IPsmSession final : public ServiceFramework<IPsmSession> { public: @@ -58,7 +55,7 @@ public: private: void BindStateChangeEvent(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PSM, "called"); + LOG_DEBUG(Service_PTM, "called"); should_signal = true; @@ -68,7 +65,7 @@ private: } void UnbindStateChangeEvent(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PSM, "called"); + LOG_DEBUG(Service_PTM, "called"); should_signal = false; @@ -79,7 +76,7 @@ private: void SetChargerTypeChangeEventEnabled(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto state = rp.Pop<bool>(); - LOG_DEBUG(Service_PSM, "called, state={}", state); + LOG_DEBUG(Service_PTM, "called, state={}", state); should_signal_charger_type = state; @@ -90,7 +87,7 @@ private: void SetPowerSupplyChangeEventEnabled(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto state = rp.Pop<bool>(); - LOG_DEBUG(Service_PSM, "called, state={}", state); + LOG_DEBUG(Service_PTM, "called, state={}", state); should_signal_power_supply = state; @@ -101,7 +98,7 @@ private: void SetBatteryVoltageStateChangeEventEnabled(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto state = rp.Pop<bool>(); - LOG_DEBUG(Service_PSM, "called, state={}", state); + LOG_DEBUG(Service_PTM, "called, state={}", state); should_signal_battery_voltage = state; @@ -118,76 +115,58 @@ private: Kernel::KEvent* state_change_event; }; -class PSM final : public ServiceFramework<PSM> { -public: - explicit PSM(Core::System& system_) : ServiceFramework{system_, "psm"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"}, - {1, &PSM::GetChargerType, "GetChargerType"}, - {2, nullptr, "EnableBatteryCharging"}, - {3, nullptr, "DisableBatteryCharging"}, - {4, nullptr, "IsBatteryChargingEnabled"}, - {5, nullptr, "AcquireControllerPowerSupply"}, - {6, nullptr, "ReleaseControllerPowerSupply"}, - {7, &PSM::OpenSession, "OpenSession"}, - {8, nullptr, "EnableEnoughPowerChargeEmulation"}, - {9, nullptr, "DisableEnoughPowerChargeEmulation"}, - {10, nullptr, "EnableFastBatteryCharging"}, - {11, nullptr, "DisableFastBatteryCharging"}, - {12, nullptr, "GetBatteryVoltageState"}, - {13, nullptr, "GetRawBatteryChargePercentage"}, - {14, nullptr, "IsEnoughPowerSupplied"}, - {15, nullptr, "GetBatteryAgePercentage"}, - {16, nullptr, "GetBatteryChargeInfoEvent"}, - {17, nullptr, "GetBatteryChargeInfoFields"}, - {18, nullptr, "GetBatteryChargeCalibratedEvent"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - - ~PSM() override = default; - -private: - void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PSM, "called"); +PSM::PSM(Core::System& system_) : ServiceFramework{system_, "psm"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"}, + {1, &PSM::GetChargerType, "GetChargerType"}, + {2, nullptr, "EnableBatteryCharging"}, + {3, nullptr, "DisableBatteryCharging"}, + {4, nullptr, "IsBatteryChargingEnabled"}, + {5, nullptr, "AcquireControllerPowerSupply"}, + {6, nullptr, "ReleaseControllerPowerSupply"}, + {7, &PSM::OpenSession, "OpenSession"}, + {8, nullptr, "EnableEnoughPowerChargeEmulation"}, + {9, nullptr, "DisableEnoughPowerChargeEmulation"}, + {10, nullptr, "EnableFastBatteryCharging"}, + {11, nullptr, "DisableFastBatteryCharging"}, + {12, nullptr, "GetBatteryVoltageState"}, + {13, nullptr, "GetRawBatteryChargePercentage"}, + {14, nullptr, "IsEnoughPowerSupplied"}, + {15, nullptr, "GetBatteryAgePercentage"}, + {16, nullptr, "GetBatteryChargeInfoEvent"}, + {17, nullptr, "GetBatteryChargeInfoFields"}, + {18, nullptr, "GetBatteryChargeCalibratedEvent"}, + }; + // clang-format on - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(battery_charge_percentage); - } + RegisterHandlers(functions); +} - void GetChargerType(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PSM, "called"); +PSM::~PSM() = default; - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(charger_type); - } +void PSM::GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PTM, "called"); - void OpenSession(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PSM, "called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u32>(battery_charge_percentage); +} - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IPsmSession>(system); - } +void PSM::GetChargerType(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PTM, "called"); - enum class ChargerType : u32 { - Unplugged = 0, - RegularCharger = 1, - LowPowerCharger = 2, - Unknown = 3, - }; + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(charger_type); +} - u32 battery_charge_percentage{100}; // 100% - ChargerType charger_type{ChargerType::RegularCharger}; -}; +void PSM::OpenSession(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PTM, "called"); -void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { - std::make_shared<PSM>(system)->InstallAsService(sm); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IPsmSession>(system); } -} // namespace Service::PSM +} // namespace Service::PTM diff --git a/src/core/hle/service/ptm/psm.h b/src/core/hle/service/ptm/psm.h index 2930ce26a..f674ba8bc 100644 --- a/src/core/hle/service/ptm/psm.h +++ b/src/core/hle/service/ptm/psm.h @@ -1,19 +1,31 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -namespace Core { -class System; -} +#include "core/hle/service/service.h" -namespace Service::SM { -class ServiceManager; -} +namespace Service::PTM { -namespace Service::PSM { +class PSM final : public ServiceFramework<PSM> { +public: + explicit PSM(Core::System& system_); + ~PSM() override; -void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); +private: + enum class ChargerType : u32 { + Unplugged = 0, + RegularCharger = 1, + LowPowerCharger = 2, + Unknown = 3, + }; -} // namespace Service::PSM + void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx); + void GetChargerType(Kernel::HLERequestContext& ctx); + void OpenSession(Kernel::HLERequestContext& ctx); + + u32 battery_charge_percentage{100}; + ChargerType charger_type{ChargerType::RegularCharger}; +}; + +} // namespace Service::PTM diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp new file mode 100644 index 000000000..4bea995c6 --- /dev/null +++ b/src/core/hle/service/ptm/ptm.cpp @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include <memory> + +#include "core/core.h" +#include "core/hle/service/ptm/psm.h" +#include "core/hle/service/ptm/ptm.h" +#include "core/hle/service/ptm/ts.h" + +namespace Service::PTM { + +void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { + std::make_shared<PSM>(system)->InstallAsService(sm); + std::make_shared<TS>(system)->InstallAsService(sm); +} + +} // namespace Service::PTM diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h new file mode 100644 index 000000000..06224a24e --- /dev/null +++ b/src/core/hle/service/ptm/ptm.h @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +namespace Core { +class System; +} + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::PTM { + +void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); + +} // namespace Service::PTM diff --git a/src/core/hle/service/ptm/ts.cpp b/src/core/hle/service/ptm/ts.cpp new file mode 100644 index 000000000..65c3f135f --- /dev/null +++ b/src/core/hle/service/ptm/ts.cpp @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include <memory> + +#include "core/core.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/ptm/ts.h" + +namespace Service::PTM { + +TS::TS(Core::System& system_) : ServiceFramework{system_, "ts"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetTemperatureRange"}, + {1, &TS::GetTemperature, "GetTemperature"}, + {2, nullptr, "SetMeasurementMode"}, + {3, nullptr, "GetTemperatureMilliC"}, + {4, nullptr, "OpenSession"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +TS::~TS() = default; + +void TS::GetTemperature(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto location{rp.PopEnum<Location>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called. location={}", location); + + const s32 temperature = location == Location::Internal ? 35 : 20; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(temperature); +} + +} // namespace Service::PTM diff --git a/src/core/hle/service/ptm/ts.h b/src/core/hle/service/ptm/ts.h new file mode 100644 index 000000000..39a734ef7 --- /dev/null +++ b/src/core/hle/service/ptm/ts.h @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/service/service.h" + +namespace Service::PTM { + +class TS final : public ServiceFramework<TS> { +public: + explicit TS(Core::System& system_); + ~TS() override; + +private: + enum class Location : u8 { + Internal, + External, + }; + + void GetTemperature(Kernel::HLERequestContext& ctx); +}; + +} // namespace Service::PTM diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index f54e6fe56..dadaf897f 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <fmt/format.h> #include "common/assert.h" @@ -32,6 +31,7 @@ #include "core/hle/service/glue/glue.h" #include "core/hle/service/grc/grc.h" #include "core/hle/service/hid/hid.h" +#include "core/hle/service/jit/jit.h" #include "core/hle/service/lbl/lbl.h" #include "core/hle/service/ldn/ldn.h" #include "core/hle/service/ldr/ldr.h" @@ -39,6 +39,7 @@ #include "core/hle/service/mig/mig.h" #include "core/hle/service/mii/mii.h" #include "core/hle/service/mm/mm_u.h" +#include "core/hle/service/mnpp/mnpp_app.h" #include "core/hle/service/ncm/ncm.h" #include "core/hle/service/nfc/nfc.h" #include "core/hle/service/nfp/nfp.h" @@ -48,6 +49,7 @@ #include "core/hle/service/npns/npns.h" #include "core/hle/service/ns/ns.h" #include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvflinger/hos_binder_driver_server.h" #include "core/hle/service/nvflinger/nvflinger.h" #include "core/hle/service/olsc/olsc.h" #include "core/hle/service/pcie/pcie.h" @@ -56,7 +58,7 @@ #include "core/hle/service/pm/pm.h" #include "core/hle/service/prepo/prepo.h" #include "core/hle/service/psc/psc.h" -#include "core/hle/service/ptm/psm.h" +#include "core/hle/service/ptm/ptm.h" #include "core/hle/service/service.h" #include "core/hle/service/set/settings.h" #include "core/hle/service/sm/sm.h" @@ -89,8 +91,9 @@ namespace Service { } ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_, - u32 max_sessions_, InvokerFn* handler_invoker_) - : SessionRequestHandler(system_.Kernel(), service_name_), system{system_}, + ServiceThreadType thread_type, u32 max_sessions_, + InvokerFn* handler_invoker_) + : SessionRequestHandler(system_.Kernel(), service_name_, thread_type), system{system_}, service_name{service_name_}, max_sessions{max_sessions_}, handler_invoker{handler_invoker_} {} ServiceFrameworkBase::~ServiceFrameworkBase() { @@ -187,17 +190,20 @@ void ServiceFrameworkBase::InvokeRequestTipc(Kernel::HLERequestContext& ctx) { handler_invoker(this, info->handler_callback, ctx); } -ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session, - Kernel::HLERequestContext& ctx) { +Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session, + Kernel::HLERequestContext& ctx) { const auto guard = LockService(); + Result result = ResultSuccess; + switch (ctx.GetCommandType()) { case IPC::CommandType::Close: case IPC::CommandType::TIPC_Close: { session.Close(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); - return IPC::ERR_REMOTE_PROCESS_DEAD; + result = IPC::ERR_REMOTE_PROCESS_DEAD; + break; } case IPC::CommandType::ControlWithContext: case IPC::CommandType::Control: { @@ -224,12 +230,13 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& sessi ctx.WriteToOutgoingCommandBuffer(ctx.GetThread()); } - return ResultSuccess; + return result; } /// Initialize Services Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) - : nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system)} { + : hos_binder_driver_server{std::make_unique<NVFlinger::HosBinderDriverServer>(system)}, + nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system, *hos_binder_driver_server)} { // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it // here and pass it into the respective InstallInterfaces functions. @@ -258,6 +265,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system Glue::InstallInterfaces(system); GRC::InstallInterfaces(*sm, system); HID::InstallInterfaces(*sm, system); + JIT::InstallInterfaces(*sm, system); LBL::InstallInterfaces(*sm, system); LDN::InstallInterfaces(*sm, system); LDR::InstallInterfaces(*sm, system); @@ -265,6 +273,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system Migration::InstallInterfaces(*sm, system); Mii::InstallInterfaces(*sm, system); MM::InstallInterfaces(*sm, system); + MNPP::InstallInterfaces(*sm, system); NCM::InstallInterfaces(*sm, system); NFC::InstallInterfaces(*sm, system); NFP::InstallInterfaces(*sm, system); @@ -281,14 +290,14 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system PlayReport::InstallInterfaces(*sm, system); PM::InstallInterfaces(system); PSC::InstallInterfaces(*sm, system); - PSM::InstallInterfaces(*sm, system); + PTM::InstallInterfaces(*sm, system); Set::InstallInterfaces(*sm, system); Sockets::InstallInterfaces(*sm, system); SPL::InstallInterfaces(*sm, system); SSL::InstallInterfaces(*sm, system); Time::InstallInterfaces(system); USB::InstallInterfaces(*sm, system); - VI::InstallInterfaces(*sm, system, *nv_flinger); + VI::InstallInterfaces(*sm, system, *nv_flinger, *hos_binder_driver_server); WLAN::InstallInterfaces(*sm, system); } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index c9d6b879d..5bf197c51 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -9,7 +8,6 @@ #include <string> #include <boost/container/flat_map.hpp> #include "common/common_types.h" -#include "common/spin_lock.h" #include "core/hle/kernel/hle_ipc.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -33,8 +31,9 @@ class FileSystemController; } namespace NVFlinger { +class HosBinderDriverServer; class NVFlinger; -} +} // namespace NVFlinger namespace SM { class ServiceManager; @@ -80,8 +79,8 @@ public: Kernel::KClientPort& CreatePort(); /// Handles a synchronization request for the service. - ResultCode HandleSyncRequest(Kernel::KServerSession& session, - Kernel::HLERequestContext& context) override; + Result HandleSyncRequest(Kernel::KServerSession& session, + Kernel::HLERequestContext& context) override; protected: /// Member-function pointer type of SyncRequest handlers. @@ -89,7 +88,7 @@ protected: using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&); /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread. - [[nodiscard]] std::scoped_lock<Common::SpinLock> LockService() { + [[nodiscard]] std::scoped_lock<std::mutex> LockService() { return std::scoped_lock{lock_service}; } @@ -113,7 +112,8 @@ private: Kernel::HLERequestContext& ctx); explicit ServiceFrameworkBase(Core::System& system_, const char* service_name_, - u32 max_sessions_, InvokerFn* handler_invoker_); + ServiceThreadType thread_type, u32 max_sessions_, + InvokerFn* handler_invoker_); ~ServiceFrameworkBase() override; void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n); @@ -133,7 +133,7 @@ private: boost::container::flat_map<u32, FunctionInfoBase> handlers_tipc; /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread. - Common::SpinLock lock_service; + std::mutex lock_service; }; /** @@ -175,14 +175,17 @@ protected: /** * Initializes the handler with no functions installed. * - * @param system_ The system context to construct this service under. + * @param system_ The system context to construct this service under. * @param service_name_ Name of the service. - * @param max_sessions_ Maximum number of sessions that can be - * connected to this service at the same time. + * @param thread_type Specifies the thread type for this service. If this is set to CreateNew, + * it creates a new thread for it, otherwise this uses the default thread. + * @param max_sessions_ Maximum number of sessions that can be connected to this service at the + * same time. */ explicit ServiceFramework(Core::System& system_, const char* service_name_, + ServiceThreadType thread_type = ServiceThreadType::Default, u32 max_sessions_ = ServerSessionCountMax) - : ServiceFrameworkBase(system_, service_name_, max_sessions_, Invoker) {} + : ServiceFrameworkBase(system_, service_name_, thread_type, max_sessions_, Invoker) {} /// Registers handlers in the service. template <std::size_t N> @@ -236,6 +239,7 @@ public: ~Services(); private: + std::unique_ptr<NVFlinger::HosBinderDriverServer> hos_binder_driver_server; std::unique_ptr<NVFlinger::NVFlinger> nv_flinger; }; diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index 8795eb6b7..f761c2da4 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <array> @@ -75,7 +74,7 @@ constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 18> language_to_la constexpr std::size_t PRE_4_0_0_MAX_ENTRIES = 0xF; constexpr std::size_t POST_4_0_0_MAX_ENTRIES = 0x40; -constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625}; +constexpr Result ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625}; void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t num_language_codes) { IPC::ResponseBuilder rb{ctx, 3}; diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h index acabebeaa..60cad3e6f 100644 --- a/src/core/hle/service/set/set.h +++ b/src/core/hle/service/set/set.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp index b2aa7bc0c..d2c0d536f 100644 --- a/src/core/hle/service/set/set_cal.cpp +++ b/src/core/hle/service/set/set_cal.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/set/set_cal.h" diff --git a/src/core/hle/service/set/set_cal.h b/src/core/hle/service/set/set_cal.h index a29fc3ddd..8f50278ed 100644 --- a/src/core/hle/service/set/set_cal.h +++ b/src/core/hle/service/set/set_cal.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/set/set_fd.cpp b/src/core/hle/service/set/set_fd.cpp index f04dc5047..278ef32e1 100644 --- a/src/core/hle/service/set/set_fd.cpp +++ b/src/core/hle/service/set/set_fd.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/set/set_fd.h" diff --git a/src/core/hle/service/set/set_fd.h b/src/core/hle/service/set/set_fd.h index c28cb301e..150a7cbce 100644 --- a/src/core/hle/service/set/set_fd.h +++ b/src/core/hle/service/set/set_fd.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index 38e6eae04..2a0b812c1 100644 --- a/src/core/hle/service/set/set_sys.cpp +++ b/src/core/hle/service/set/set_sys.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "common/logging/log.h" @@ -33,7 +32,7 @@ void GetFirmwareVersionImpl(Kernel::HLERequestContext& ctx, GetFirmwareVersionTy // consistence (currently reports as 5.1.0-0.0) const auto archive = FileSys::SystemArchive::SystemVersion(); - const auto early_exit_failure = [&ctx](std::string_view desc, ResultCode code) { + const auto early_exit_failure = [&ctx](std::string_view desc, Result code) { LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).", desc); IPC::ResponseBuilder rb{ctx, 2}; diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h index edb185a68..ac97772b7 100644 --- a/src/core/hle/service/set/set_sys.h +++ b/src/core/hle/service/set/set_sys.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/set/settings.cpp b/src/core/hle/service/set/settings.cpp index 212ebc427..4ebc2a0ec 100644 --- a/src/core/hle/service/set/settings.cpp +++ b/src/core/hle/service/set/settings.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/set/set.h" #include "core/hle/service/set/set_cal.h" diff --git a/src/core/hle/service/set/settings.h b/src/core/hle/service/set/settings.h index 7a6950dd0..6cd7d634c 100644 --- a/src/core/hle/service/set/settings.h +++ b/src/core/hle/service/set/settings.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index eaa172595..246c94623 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <tuple> #include "common/assert.h" @@ -18,10 +17,10 @@ namespace Service::SM { -constexpr ResultCode ERR_NOT_INITIALIZED(ErrorModule::SM, 2); -constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::SM, 4); -constexpr ResultCode ERR_INVALID_NAME(ErrorModule::SM, 6); -constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7); +constexpr Result ERR_NOT_INITIALIZED(ErrorModule::SM, 2); +constexpr Result ERR_ALREADY_REGISTERED(ErrorModule::SM, 4); +constexpr Result ERR_INVALID_NAME(ErrorModule::SM, 6); +constexpr Result ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7); ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {} ServiceManager::~ServiceManager() = default; @@ -30,7 +29,7 @@ void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { controller_interface->InvokeRequest(context); } -static ResultCode ValidateServiceName(const std::string& name) { +static Result ValidateServiceName(const std::string& name) { if (name.empty() || name.size() > 8) { LOG_ERROR(Service_SM, "Invalid service name! service={}", name); return ERR_INVALID_NAME; @@ -44,8 +43,8 @@ Kernel::KClientPort& ServiceManager::InterfaceFactory(ServiceManager& self, Core return self.sm_interface->CreatePort(); } -ResultCode ServiceManager::RegisterService(std::string name, u32 max_sessions, - Kernel::SessionRequestHandlerPtr handler) { +Result ServiceManager::RegisterService(std::string name, u32 max_sessions, + Kernel::SessionRequestHandlerPtr handler) { CASCADE_CODE(ValidateServiceName(name)); @@ -59,7 +58,7 @@ ResultCode ServiceManager::RegisterService(std::string name, u32 max_sessions, return ResultSuccess; } -ResultCode ServiceManager::UnregisterService(const std::string& name) { +Result ServiceManager::UnregisterService(const std::string& name) { CASCADE_CODE(ValidateServiceName(name)); const auto iter = registered_services.find(name); @@ -81,6 +80,8 @@ ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name } auto* port = Kernel::KPort::Create(kernel); + SCOPE_EXIT({ port->Close(); }); + port->Initialize(ServerSessionCountMax, false, name); auto handler = it->second; port->GetServerPort().SetSessionHandler(std::move(handler)); @@ -93,7 +94,7 @@ ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name * Inputs: * 0: 0x00000000 * Outputs: - * 0: ResultCode + * 0: Result */ void SM::Initialize(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_SM, "called"); @@ -151,7 +152,7 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext& auto& port = port_result.Unwrap(); SCOPE_EXIT({ port->GetClientPort().Close(); }); - server_ports.emplace_back(&port->GetServerPort()); + kernel.RegisterServerObject(&port->GetServerPort()); // Create a new session. Kernel::KClientSession* session{}; @@ -204,7 +205,7 @@ void SM::UnregisterService(Kernel::HLERequestContext& ctx) { } SM::SM(ServiceManager& service_manager_, Core::System& system_) - : ServiceFramework{system_, "sm:", 4}, + : ServiceFramework{system_, "sm:", ServiceThreadType::Default, 4}, service_manager{service_manager_}, kernel{system_.Kernel()} { RegisterHandlers({ {0, &SM::Initialize, "Initialize"}, @@ -222,10 +223,6 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_) }); } -SM::~SM() { - for (auto& server_port : server_ports) { - server_port->Close(); - } -} +SM::~SM() = default; } // namespace Service::SM diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 021eb51b4..878decc6f 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -22,7 +21,6 @@ class KClientPort; class KClientSession; class KernelCore; class KPort; -class KServerPort; class SessionRequestHandler; } // namespace Kernel @@ -48,7 +46,6 @@ private: ServiceManager& service_manager; bool is_initialized{}; Kernel::KernelCore& kernel; - std::vector<Kernel::KServerPort*> server_ports; }; class ServiceManager { @@ -58,9 +55,9 @@ public: explicit ServiceManager(Kernel::KernelCore& kernel_); ~ServiceManager(); - ResultCode RegisterService(std::string name, u32 max_sessions, - Kernel::SessionRequestHandlerPtr handler); - ResultCode UnregisterService(const std::string& name); + Result RegisterService(std::string name, u32 max_sessions, + Kernel::SessionRequestHandlerPtr handler); + Result UnregisterService(const std::string& name); ResultVal<Kernel::KPort*> GetServicePort(const std::string& name); template <Common::DerivedFrom<Kernel::SessionRequestHandler> T> diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp index 09f9ecee1..2a4bd64ab 100644 --- a/src/core/hle/service/sm/sm_controller.cpp +++ b/src/core/hle/service/sm/sm_controller.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "common/logging/log.h" @@ -34,7 +33,7 @@ void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) { // Create a session. Kernel::KClientSession* session{}; - const ResultCode result = parent_port.CreateSession(std::addressof(session), session_manager); + const Result result = parent_port.CreateSession(std::addressof(session), session_manager); if (result.IsError()) { LOG_CRITICAL(Service, "CreateSession failed with error 0x{:08X}", result.raw); IPC::ResponseBuilder rb{ctx, 2}; diff --git a/src/core/hle/service/sm/sm_controller.h b/src/core/hle/service/sm/sm_controller.h index 7494f898d..ed386f660 100644 --- a/src/core/hle/service/sm/sm_controller.h +++ b/src/core/hle/service/sm/sm_controller.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index f83272633..9e94a462f 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> #include <memory> @@ -10,13 +9,16 @@ #include <fmt/format.h> #include "common/microprofile.h" -#include "common/thread.h" +#include "common/socket_types.h" +#include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/service/sockets/bsd.h" #include "core/hle/service/sockets/sockets_translate.h" -#include "core/network/network.h" -#include "core/network/sockets.h" +#include "core/internal_network/network.h" +#include "core/internal_network/socket_proxy.h" +#include "core/internal_network/sockets.h" +#include "network/network.h" namespace Service::Sockets { @@ -474,7 +476,13 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco LOG_INFO(Service, "New socket fd={}", fd); - descriptor.socket = std::make_unique<Network::Socket>(); + auto room_member = room_network.GetRoomMember().lock(); + if (room_member && room_member->IsConnected()) { + descriptor.socket = std::make_unique<Network::ProxySocket>(room_network); + } else { + descriptor.socket = std::make_unique<Network::Socket>(); + } + descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol)); descriptor.is_connection_based = IsConnectionBased(type); @@ -569,9 +577,9 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) { new_descriptor.socket = std::move(result.socket); new_descriptor.is_connection_based = descriptor.is_connection_based; - ASSERT(write_buffer.size() == sizeof(SockAddrIn)); const SockAddrIn guest_addr_in = Translate(result.sockaddr_in); - std::memcpy(write_buffer.data(), &guest_addr_in, sizeof(guest_addr_in)); + const size_t length = std::min(sizeof(guest_addr_in), write_buffer.size()); + std::memcpy(write_buffer.data(), &guest_addr_in, length); return {new_fd, Errno::SUCCESS}; } @@ -650,7 +658,7 @@ std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) { ASSERT(arg == 0); return {descriptor.flags, Errno::SUCCESS}; case FcntlCmd::SETFL: { - const bool enable = (arg & FLAG_O_NONBLOCK) != 0; + const bool enable = (arg & Network::FLAG_O_NONBLOCK) != 0; const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable)); if (bsd_errno != Errno::SUCCESS) { return {-1, bsd_errno}; @@ -671,7 +679,7 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con return Errno::BADF; } - Network::Socket* const socket = file_descriptors[fd]->socket.get(); + Network::SocketBase* const socket = file_descriptors[fd]->socket.get(); if (optname == OptName::LINGER) { ASSERT(optlen == sizeof(Linger)); @@ -690,6 +698,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)); @@ -719,7 +730,27 @@ std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) if (!IsFileDescriptorValid(fd)) { return {-1, Errno::BADF}; } - return Translate(file_descriptors[fd]->socket->Recv(flags, message)); + + FileDescriptor& descriptor = *file_descriptors[fd]; + + // Apply flags + using Network::FLAG_MSG_DONTWAIT; + using Network::FLAG_O_NONBLOCK; + if ((flags & FLAG_MSG_DONTWAIT) != 0) { + flags &= ~FLAG_MSG_DONTWAIT; + if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { + descriptor.socket->SetNonBlock(true); + } + } + + const auto [ret, bsd_errno] = Translate(descriptor.socket->Recv(flags, message)); + + // Restore original state + if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { + descriptor.socket->SetNonBlock(false); + } + + return {ret, bsd_errno}; } std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, @@ -740,6 +771,8 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess } // Apply flags + using Network::FLAG_MSG_DONTWAIT; + using Network::FLAG_O_NONBLOCK; if ((flags & FLAG_MSG_DONTWAIT) != 0) { flags &= ~FLAG_MSG_DONTWAIT; if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { @@ -838,7 +871,19 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co rb.PushEnum(bsd_errno); } -BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} { +void BSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) { + for (auto& optional_descriptor : file_descriptors) { + if (!optional_descriptor.has_value()) { + continue; + } + FileDescriptor& descriptor = *optional_descriptor; + descriptor.socket.get()->HandleProxyPacket(packet); + } +} + +BSD::BSD(Core::System& system_, const char* name) + : ServiceFramework{system_, name, ServiceThreadType::CreateNew}, room_network{ + system_.GetRoomNetwork()} { // clang-format off static const FunctionInfo functions[] = { {0, &BSD::RegisterClient, "RegisterClient"}, @@ -879,9 +924,20 @@ BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, na // clang-format on RegisterHandlers(functions); + + if (auto room_member = room_network.GetRoomMember().lock()) { + proxy_packet_received = room_member->BindOnProxyPacketReceived( + [this](const Network::ProxyPacket& packet) { OnProxyPacketReceived(packet); }); + } else { + LOG_ERROR(Service, "Network isn't initialized"); + } } -BSD::~BSD() = default; +BSD::~BSD() { + if (auto room_member = room_network.GetRoomMember().lock()) { + room_member->Unbind(proxy_packet_received); + } +} BSDCFG::BSDCFG(Core::System& system_) : ServiceFramework{system_, "bsdcfg"} { // clang-format off diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index a387e50df..81e855e0f 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,16 +7,19 @@ #include <vector> #include "common/common_types.h" +#include "common/socket_types.h" #include "core/hle/service/service.h" #include "core/hle/service/sockets/sockets.h" +#include "network/network.h" namespace Core { class System; } namespace Network { +class SocketBase; class Socket; -} +} // namespace Network namespace Service::Sockets { @@ -31,7 +33,7 @@ private: static constexpr size_t MAX_FD = 128; struct FileDescriptor { - std::unique_ptr<Network::Socket> socket; + std::unique_ptr<Network::SocketBase> socket; s32 flags = 0; bool is_connection_based = false; }; @@ -166,6 +168,14 @@ private: void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept; std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors; + + Network::RoomNetwork& room_network; + + /// Callback to parse and handle a received wifi packet. + void OnProxyPacketReceived(const Network::ProxyPacket& packet); + + // Callback identifier for the OnProxyPacketReceived event. + Network::RoomMember::CallbackHandle<Network::ProxyPacket> proxy_packet_received; }; class BSDCFG final : public ServiceFramework<BSDCFG> { diff --git a/src/core/hle/service/sockets/ethc.cpp b/src/core/hle/service/sockets/ethc.cpp index 899a64c2f..c12ea999b 100644 --- a/src/core/hle/service/sockets/ethc.cpp +++ b/src/core/hle/service/sockets/ethc.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/sockets/ethc.h" diff --git a/src/core/hle/service/sockets/ethc.h b/src/core/hle/service/sockets/ethc.h index 71884182e..7c5759a96 100644 --- a/src/core/hle/service/sockets/ethc.h +++ b/src/core/hle/service/sockets/ethc.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/sockets/nsd.cpp b/src/core/hle/service/sockets/nsd.cpp index 1159debc5..6491a73be 100644 --- a/src/core/hle/service/sockets/nsd.cpp +++ b/src/core/hle/service/sockets/nsd.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/sockets/nsd.h" diff --git a/src/core/hle/service/sockets/nsd.h b/src/core/hle/service/sockets/nsd.h index becf93125..5cc12b855 100644 --- a/src/core/hle/service/sockets/nsd.h +++ b/src/core/hle/service/sockets/nsd.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp index fb6142c49..097c37d7a 100644 --- a/src/core/hle/service/sockets/sfdnsres.cpp +++ b/src/core/hle/service/sockets/sfdnsres.cpp @@ -1,9 +1,28 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#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 +40,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 +50,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 +196,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..96018ea77 100644 --- a/src/core/hle/service/sockets/sfdnsres.h +++ b/src/core/hle/service/sockets/sfdnsres.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -19,6 +18,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.cpp b/src/core/hle/service/sockets/sockets.cpp index 96f73bce3..8d3ba6f96 100644 --- a/src/core/hle/service/sockets/sockets.cpp +++ b/src/core/hle/service/sockets/sockets.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/sockets/bsd.h" #include "core/hle/service/sockets/ethc.h" diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h index 02dbbae40..31b7dad33 100644 --- a/src/core/hle/service/sockets/sockets.h +++ b/src/core/hle/service/sockets/sockets.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -23,7 +22,9 @@ enum class Errno : u32 { AGAIN = 11, INVAL = 22, MFILE = 24, + MSGSIZE = 90, NOTCONN = 107, + TIMEDOUT = 110, }; enum class Domain : u32 { @@ -46,6 +47,7 @@ enum class Protocol : u32 { enum class OptName : u32 { REUSEADDR = 0x4, + KEEPALIVE = 0x8, BROADCAST = 0x20, LINGER = 0x80, SNDBUF = 0x1001, @@ -96,10 +98,6 @@ struct Linger { u32 linger; }; -constexpr u32 FLAG_MSG_DONTWAIT = 0x80; - -constexpr u32 FLAG_O_NONBLOCK = 0x800; - /// Registers all Sockets services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp index ca61d72ca..023aa0486 100644 --- a/src/core/hle/service/sockets/sockets_translate.cpp +++ b/src/core/hle/service/sockets/sockets_translate.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <utility> @@ -8,7 +7,7 @@ #include "common/common_types.h" #include "core/hle/service/sockets/sockets.h" #include "core/hle/service/sockets/sockets_translate.h" -#include "core/network/network.h" +#include "core/internal_network/network.h" namespace Service::Sockets { @@ -26,6 +25,8 @@ Errno Translate(Network::Errno value) { return Errno::MFILE; case Network::Errno::NOTCONN: return Errno::NOTCONN; + case Network::Errno::TIMEDOUT: + return Errno::TIMEDOUT; default: UNIMPLEMENTED_MSG("Unimplemented errno={}", value); return Errno::SUCCESS; diff --git a/src/core/hle/service/sockets/sockets_translate.h b/src/core/hle/service/sockets/sockets_translate.h index 057d1ff22..c93291d3e 100644 --- a/src/core/hle/service/sockets/sockets_translate.h +++ b/src/core/hle/service/sockets/sockets_translate.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,7 +7,7 @@ #include "common/common_types.h" #include "core/hle/service/sockets/sockets.h" -#include "core/network/network.h" +#include "core/internal_network/network.h" namespace Service::Sockets { diff --git a/src/core/hle/service/spl/csrng.cpp b/src/core/hle/service/spl/csrng.cpp index 9c7f89475..ca121fb05 100644 --- a/src/core/hle/service/spl/csrng.cpp +++ b/src/core/hle/service/spl/csrng.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/spl/csrng.h" diff --git a/src/core/hle/service/spl/csrng.h b/src/core/hle/service/spl/csrng.h index 0d03cc6cb..b337a8281 100644 --- a/src/core/hle/service/spl/csrng.h +++ b/src/core/hle/service/spl/csrng.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/spl/spl.cpp b/src/core/hle/service/spl/spl.cpp index 20384042f..fde212186 100644 --- a/src/core/hle/service/spl/spl.cpp +++ b/src/core/hle/service/spl/spl.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/spl/spl.h" diff --git a/src/core/hle/service/spl/spl.h b/src/core/hle/service/spl/spl.h index 5599c0c01..7b63d8b1a 100644 --- a/src/core/hle/service/spl/spl.h +++ b/src/core/hle/service/spl/spl.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/spl/spl_module.cpp b/src/core/hle/service/spl/spl_module.cpp index 10f7d1461..64eae1ebf 100644 --- a/src/core/hle/service/spl/spl_module.cpp +++ b/src/core/hle/service/spl/spl_module.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <cstdlib> diff --git a/src/core/hle/service/spl/spl_module.h b/src/core/hle/service/spl/spl_module.h index 61630df80..4c9a3c618 100644 --- a/src/core/hle/service/spl/spl_module.h +++ b/src/core/hle/service/spl/spl_module.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/spl/spl_results.h b/src/core/hle/service/spl/spl_results.h index a07c61409..dd7ba11f3 100644 --- a/src/core/hle/service/spl/spl_results.h +++ b/src/core/hle/service/spl/spl_results.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -9,23 +8,23 @@ namespace Service::SPL { // Description 0 - 99 -constexpr ResultCode ResultSecureMonitorError{ErrorModule::SPL, 0}; -constexpr ResultCode ResultSecureMonitorNotImplemented{ErrorModule::SPL, 1}; -constexpr ResultCode ResultSecureMonitorInvalidArgument{ErrorModule::SPL, 2}; -constexpr ResultCode ResultSecureMonitorBusy{ErrorModule::SPL, 3}; -constexpr ResultCode ResultSecureMonitorNoAsyncOperation{ErrorModule::SPL, 4}; -constexpr ResultCode ResultSecureMonitorInvalidAsyncOperation{ErrorModule::SPL, 5}; -constexpr ResultCode ResultSecureMonitorNotPermitted{ErrorModule::SPL, 6}; -constexpr ResultCode ResultSecureMonitorNotInitialized{ErrorModule::SPL, 7}; +constexpr Result ResultSecureMonitorError{ErrorModule::SPL, 0}; +constexpr Result ResultSecureMonitorNotImplemented{ErrorModule::SPL, 1}; +constexpr Result ResultSecureMonitorInvalidArgument{ErrorModule::SPL, 2}; +constexpr Result ResultSecureMonitorBusy{ErrorModule::SPL, 3}; +constexpr Result ResultSecureMonitorNoAsyncOperation{ErrorModule::SPL, 4}; +constexpr Result ResultSecureMonitorInvalidAsyncOperation{ErrorModule::SPL, 5}; +constexpr Result ResultSecureMonitorNotPermitted{ErrorModule::SPL, 6}; +constexpr Result ResultSecureMonitorNotInitialized{ErrorModule::SPL, 7}; -constexpr ResultCode ResultInvalidSize{ErrorModule::SPL, 100}; -constexpr ResultCode ResultUnknownSecureMonitorError{ErrorModule::SPL, 101}; -constexpr ResultCode ResultDecryptionFailed{ErrorModule::SPL, 102}; +constexpr Result ResultInvalidSize{ErrorModule::SPL, 100}; +constexpr Result ResultUnknownSecureMonitorError{ErrorModule::SPL, 101}; +constexpr Result ResultDecryptionFailed{ErrorModule::SPL, 102}; -constexpr ResultCode ResultOutOfKeySlots{ErrorModule::SPL, 104}; -constexpr ResultCode ResultInvalidKeySlot{ErrorModule::SPL, 105}; -constexpr ResultCode ResultBootReasonAlreadySet{ErrorModule::SPL, 106}; -constexpr ResultCode ResultBootReasonNotSet{ErrorModule::SPL, 107}; -constexpr ResultCode ResultInvalidArgument{ErrorModule::SPL, 108}; +constexpr Result ResultOutOfKeySlots{ErrorModule::SPL, 104}; +constexpr Result ResultInvalidKeySlot{ErrorModule::SPL, 105}; +constexpr Result ResultBootReasonAlreadySet{ErrorModule::SPL, 106}; +constexpr Result ResultBootReasonNotSet{ErrorModule::SPL, 107}; +constexpr Result ResultInvalidArgument{ErrorModule::SPL, 108}; } // namespace Service::SPL diff --git a/src/core/hle/service/spl/spl_types.h b/src/core/hle/service/spl/spl_types.h index a654e7556..91f9cc032 100644 --- a/src/core/hle/service/spl/spl_types.h +++ b/src/core/hle/service/spl/spl_types.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp index a81a595ea..3735e0452 100644 --- a/src/core/hle/service/ssl/ssl.cpp +++ b/src/core/hle/service/ssl/ssl.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/ipc_helpers.h" #include "core/hle/service/service.h" diff --git a/src/core/hle/service/ssl/ssl.h b/src/core/hle/service/ssl/ssl.h index a3aa4b4b5..27b38a003 100644 --- a/src/core/hle/service/ssl/ssl.h +++ b/src/core/hle/service/ssl/ssl.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h index d0cacb80c..ef070f32f 100644 --- a/src/core/hle/service/time/clock_types.h +++ b/src/core/hle/service/time/clock_types.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -23,7 +22,7 @@ struct SteadyClockTimePoint { s64 time_point; Common::UUID clock_source_id; - ResultCode GetSpanBetween(SteadyClockTimePoint other, s64& span) const { + Result GetSpanBetween(SteadyClockTimePoint other, s64& span) const { span = 0; if (clock_source_id != other.clock_source_id) { @@ -93,9 +92,9 @@ struct ClockSnapshot { TimeType type; INSERT_PADDING_BYTES_NOINIT(0x2); - static ResultCode GetCurrentTime(s64& current_time, - const SteadyClockTimePoint& steady_clock_time_point, - const SystemClockContext& context) { + static Result GetCurrentTime(s64& current_time, + const SteadyClockTimePoint& steady_clock_time_point, + const SystemClockContext& context) { if (steady_clock_time_point.clock_source_id != context.steady_time_point.clock_source_id) { current_time = 0; return ERROR_TIME_MISMATCH; diff --git a/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h b/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h index 42893e3f6..0f928a5a5 100644 --- a/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h +++ b/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/ephemeral_network_system_clock_core.h b/src/core/hle/service/time/ephemeral_network_system_clock_core.h index d12cb5335..0a5f5aafb 100644 --- a/src/core/hle/service/time/ephemeral_network_system_clock_core.h +++ b/src/core/hle/service/time/ephemeral_network_system_clock_core.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/errors.h b/src/core/hle/service/time/errors.h index 8501a3e8c..6655d30e1 100644 --- a/src/core/hle/service/time/errors.h +++ b/src/core/hle/service/time/errors.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,15 +7,15 @@ namespace Service::Time { -constexpr ResultCode ERROR_PERMISSION_DENIED{ErrorModule::Time, 1}; -constexpr ResultCode ERROR_TIME_MISMATCH{ErrorModule::Time, 102}; -constexpr ResultCode ERROR_UNINITIALIZED_CLOCK{ErrorModule::Time, 103}; -constexpr ResultCode ERROR_TIME_NOT_FOUND{ErrorModule::Time, 200}; -constexpr ResultCode ERROR_OVERFLOW{ErrorModule::Time, 201}; -constexpr ResultCode ERROR_LOCATION_NAME_TOO_LONG{ErrorModule::Time, 801}; -constexpr ResultCode ERROR_OUT_OF_RANGE{ErrorModule::Time, 902}; -constexpr ResultCode ERROR_TIME_ZONE_CONVERSION_FAILED{ErrorModule::Time, 903}; -constexpr ResultCode ERROR_TIME_ZONE_NOT_FOUND{ErrorModule::Time, 989}; -constexpr ResultCode ERROR_NOT_IMPLEMENTED{ErrorModule::Time, 990}; +constexpr Result ERROR_PERMISSION_DENIED{ErrorModule::Time, 1}; +constexpr Result ERROR_TIME_MISMATCH{ErrorModule::Time, 102}; +constexpr Result ERROR_UNINITIALIZED_CLOCK{ErrorModule::Time, 103}; +constexpr Result ERROR_TIME_NOT_FOUND{ErrorModule::Time, 200}; +constexpr Result ERROR_OVERFLOW{ErrorModule::Time, 201}; +constexpr Result ERROR_LOCATION_NAME_TOO_LONG{ErrorModule::Time, 801}; +constexpr Result ERROR_OUT_OF_RANGE{ErrorModule::Time, 902}; +constexpr Result ERROR_TIME_ZONE_CONVERSION_FAILED{ErrorModule::Time, 903}; +constexpr Result ERROR_TIME_ZONE_NOT_FOUND{ErrorModule::Time, 989}; +constexpr Result ERROR_NOT_IMPLEMENTED{ErrorModule::Time, 990}; } // namespace Service::Time diff --git a/src/core/hle/service/time/local_system_clock_context_writer.h b/src/core/hle/service/time/local_system_clock_context_writer.h index ac6c7b4b1..1639ef2b9 100644 --- a/src/core/hle/service/time/local_system_clock_context_writer.h +++ b/src/core/hle/service/time/local_system_clock_context_writer.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -15,7 +14,7 @@ public: : SystemClockContextUpdateCallback{}, shared_memory{shared_memory_} {} protected: - ResultCode Update() override { + Result Update() override { shared_memory.UpdateLocalSystemClockContext(context); return ResultSuccess; } diff --git a/src/core/hle/service/time/network_system_clock_context_writer.h b/src/core/hle/service/time/network_system_clock_context_writer.h index a54fd7fe1..655e4c06d 100644 --- a/src/core/hle/service/time/network_system_clock_context_writer.h +++ b/src/core/hle/service/time/network_system_clock_context_writer.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -16,7 +15,7 @@ public: : SystemClockContextUpdateCallback{}, shared_memory{shared_memory_} {} protected: - ResultCode Update() override { + Result Update() override { shared_memory.UpdateNetworkSystemClockContext(context); return ResultSuccess; } diff --git a/src/core/hle/service/time/standard_local_system_clock_core.h b/src/core/hle/service/time/standard_local_system_clock_core.h index 6320c7af1..ae2ff1bfd 100644 --- a/src/core/hle/service/time/standard_local_system_clock_core.h +++ b/src/core/hle/service/time/standard_local_system_clock_core.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/standard_network_system_clock_core.h b/src/core/hle/service/time/standard_network_system_clock_core.h index 95923a27b..c1ec5252b 100644 --- a/src/core/hle/service/time/standard_network_system_clock_core.h +++ b/src/core/hle/service/time/standard_network_system_clock_core.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/standard_steady_clock_core.cpp b/src/core/hle/service/time/standard_steady_clock_core.cpp index a1ffdd524..3dbbb9850 100644 --- a/src/core/hle/service/time/standard_steady_clock_core.cpp +++ b/src/core/hle/service/time/standard_steady_clock_core.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" #include "core/core_timing.h" diff --git a/src/core/hle/service/time/standard_steady_clock_core.h b/src/core/hle/service/time/standard_steady_clock_core.h index f56f3fd95..036463b87 100644 --- a/src/core/hle/service/time/standard_steady_clock_core.h +++ b/src/core/hle/service/time/standard_steady_clock_core.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/standard_user_system_clock_core.cpp b/src/core/hle/service/time/standard_user_system_clock_core.cpp index e94220a44..b033757ed 100644 --- a/src/core/hle/service/time/standard_user_system_clock_core.cpp +++ b/src/core/hle/service/time/standard_user_system_clock_core.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "core/core.h" @@ -28,9 +27,9 @@ StandardUserSystemClockCore::~StandardUserSystemClockCore() { service_context.CloseEvent(auto_correction_event); } -ResultCode StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::System& system, - bool value) { - if (const ResultCode result{ApplyAutomaticCorrection(system, value)}; result != ResultSuccess) { +Result StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::System& system, + bool value) { + if (const Result result{ApplyAutomaticCorrection(system, value)}; result != ResultSuccess) { return result; } @@ -39,27 +38,27 @@ ResultCode StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::Syst return ResultSuccess; } -ResultCode StandardUserSystemClockCore::GetClockContext(Core::System& system, - SystemClockContext& ctx) const { - if (const ResultCode result{ApplyAutomaticCorrection(system, false)}; result != ResultSuccess) { +Result StandardUserSystemClockCore::GetClockContext(Core::System& system, + SystemClockContext& ctx) const { + if (const Result result{ApplyAutomaticCorrection(system, false)}; result != ResultSuccess) { return result; } return local_system_clock_core.GetClockContext(system, ctx); } -ResultCode StandardUserSystemClockCore::Flush(const SystemClockContext&) { - UNREACHABLE(); +Result StandardUserSystemClockCore::Flush(const SystemClockContext&) { + UNIMPLEMENTED(); return ERROR_NOT_IMPLEMENTED; } -ResultCode StandardUserSystemClockCore::SetClockContext(const SystemClockContext&) { - UNREACHABLE(); +Result StandardUserSystemClockCore::SetClockContext(const SystemClockContext&) { + UNIMPLEMENTED(); return ERROR_NOT_IMPLEMENTED; } -ResultCode StandardUserSystemClockCore::ApplyAutomaticCorrection(Core::System& system, - bool value) const { +Result StandardUserSystemClockCore::ApplyAutomaticCorrection(Core::System& system, + bool value) const { if (auto_correction_enabled == value) { return ResultSuccess; } @@ -69,7 +68,7 @@ ResultCode StandardUserSystemClockCore::ApplyAutomaticCorrection(Core::System& s } SystemClockContext ctx{}; - if (const ResultCode result{network_system_clock_core.GetClockContext(system, ctx)}; + if (const Result result{network_system_clock_core.GetClockContext(system, ctx)}; result != ResultSuccess) { return result; } diff --git a/src/core/hle/service/time/standard_user_system_clock_core.h b/src/core/hle/service/time/standard_user_system_clock_core.h index b7cb2b045..ee6e29487 100644 --- a/src/core/hle/service/time/standard_user_system_clock_core.h +++ b/src/core/hle/service/time/standard_user_system_clock_core.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -29,9 +28,9 @@ public: ~StandardUserSystemClockCore() override; - ResultCode SetAutomaticCorrectionEnabled(Core::System& system, bool value); + Result SetAutomaticCorrectionEnabled(Core::System& system, bool value); - ResultCode GetClockContext(Core::System& system, SystemClockContext& ctx) const override; + Result GetClockContext(Core::System& system, SystemClockContext& ctx) const override; bool IsAutomaticCorrectionEnabled() const { return auto_correction_enabled; @@ -42,11 +41,11 @@ public: } protected: - ResultCode Flush(const SystemClockContext&) override; + Result Flush(const SystemClockContext&) override; - ResultCode SetClockContext(const SystemClockContext&) override; + Result SetClockContext(const SystemClockContext&) override; - ResultCode ApplyAutomaticCorrection(Core::System& system, bool value) const; + Result ApplyAutomaticCorrection(Core::System& system, bool value) const; const SteadyClockTimePoint& GetAutomaticCorrectionUpdatedTime() const { return auto_correction_time; diff --git a/src/core/hle/service/time/steady_clock_core.h b/src/core/hle/service/time/steady_clock_core.h index 5ee2c0e0a..2867c351c 100644 --- a/src/core/hle/service/time/steady_clock_core.h +++ b/src/core/hle/service/time/steady_clock_core.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/system_clock_context_update_callback.cpp b/src/core/hle/service/time/system_clock_context_update_callback.cpp index f656fab1c..a649bed3a 100644 --- a/src/core/hle/service/time/system_clock_context_update_callback.cpp +++ b/src/core/hle/service/time/system_clock_context_update_callback.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/kernel/k_writable_event.h" #include "core/hle/service/time/errors.h" @@ -31,8 +30,8 @@ void SystemClockContextUpdateCallback::BroadcastOperationEvent() { } } -ResultCode SystemClockContextUpdateCallback::Update(const SystemClockContext& value) { - ResultCode result{ResultSuccess}; +Result SystemClockContextUpdateCallback::Update(const SystemClockContext& value) { + Result result{ResultSuccess}; if (NeedUpdate(value)) { context = value; @@ -48,7 +47,7 @@ ResultCode SystemClockContextUpdateCallback::Update(const SystemClockContext& va return result; } -ResultCode SystemClockContextUpdateCallback::Update() { +Result SystemClockContextUpdateCallback::Update() { return ResultSuccess; } diff --git a/src/core/hle/service/time/system_clock_context_update_callback.h b/src/core/hle/service/time/system_clock_context_update_callback.h index 6936397a5..9c6caf196 100644 --- a/src/core/hle/service/time/system_clock_context_update_callback.h +++ b/src/core/hle/service/time/system_clock_context_update_callback.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -29,10 +28,10 @@ public: void BroadcastOperationEvent(); - ResultCode Update(const SystemClockContext& value); + Result Update(const SystemClockContext& value); protected: - virtual ResultCode Update(); + virtual Result Update(); SystemClockContext context{}; diff --git a/src/core/hle/service/time/system_clock_core.cpp b/src/core/hle/service/time/system_clock_core.cpp index 5c2354cdd..da078241f 100644 --- a/src/core/hle/service/time/system_clock_core.cpp +++ b/src/core/hle/service/time/system_clock_core.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/time/steady_clock_core.h" #include "core/hle/service/time/system_clock_context_update_callback.h" @@ -15,13 +14,13 @@ SystemClockCore::SystemClockCore(SteadyClockCore& steady_clock_core_) SystemClockCore::~SystemClockCore() = default; -ResultCode SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time) const { +Result SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time) const { posix_time = 0; const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; SystemClockContext clock_context{}; - if (const ResultCode result{GetClockContext(system, clock_context)}; result != ResultSuccess) { + if (const Result result{GetClockContext(system, clock_context)}; result != ResultSuccess) { return result; } @@ -34,26 +33,26 @@ ResultCode SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time return ResultSuccess; } -ResultCode SystemClockCore::SetCurrentTime(Core::System& system, s64 posix_time) { +Result SystemClockCore::SetCurrentTime(Core::System& system, s64 posix_time) { const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; const SystemClockContext clock_context{posix_time - current_time_point.time_point, current_time_point}; - if (const ResultCode result{SetClockContext(clock_context)}; result != ResultSuccess) { + if (const Result result{SetClockContext(clock_context)}; result != ResultSuccess) { return result; } return Flush(clock_context); } -ResultCode SystemClockCore::Flush(const SystemClockContext& clock_context) { +Result SystemClockCore::Flush(const SystemClockContext& clock_context) { if (!system_clock_context_update_callback) { return ResultSuccess; } return system_clock_context_update_callback->Update(clock_context); } -ResultCode SystemClockCore::SetSystemClockContext(const SystemClockContext& clock_context) { - if (const ResultCode result{SetClockContext(clock_context)}; result != ResultSuccess) { +Result SystemClockCore::SetSystemClockContext(const SystemClockContext& clock_context) { + if (const Result result{SetClockContext(clock_context)}; result != ResultSuccess) { return result; } return Flush(clock_context); diff --git a/src/core/hle/service/time/system_clock_core.h b/src/core/hle/service/time/system_clock_core.h index b9237ad28..8cb34126f 100644 --- a/src/core/hle/service/time/system_clock_core.h +++ b/src/core/hle/service/time/system_clock_core.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -30,28 +29,28 @@ public: return steady_clock_core; } - ResultCode GetCurrentTime(Core::System& system, s64& posix_time) const; + Result GetCurrentTime(Core::System& system, s64& posix_time) const; - ResultCode SetCurrentTime(Core::System& system, s64 posix_time); + Result SetCurrentTime(Core::System& system, s64 posix_time); - virtual ResultCode GetClockContext([[maybe_unused]] Core::System& system, - SystemClockContext& value) const { + virtual Result GetClockContext([[maybe_unused]] Core::System& system, + SystemClockContext& value) const { value = context; return ResultSuccess; } - virtual ResultCode SetClockContext(const SystemClockContext& value) { + virtual Result SetClockContext(const SystemClockContext& value) { context = value; return ResultSuccess; } - virtual ResultCode Flush(const SystemClockContext& clock_context); + virtual Result Flush(const SystemClockContext& clock_context); void SetUpdateCallbackInstance(std::shared_ptr<SystemClockContextUpdateCallback> callback) { system_clock_context_update_callback = std::move(callback); } - ResultCode SetSystemClockContext(const SystemClockContext& context); + Result SetSystemClockContext(const SystemClockContext& context); bool IsInitialized() const { return is_initialized; diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.cpp b/src/core/hle/service/time/tick_based_steady_clock_core.cpp index 47d4ab980..27600413e 100644 --- a/src/core/hle/service/time/tick_based_steady_clock_core.cpp +++ b/src/core/hle/service/time/tick_based_steady_clock_core.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" #include "core/core_timing.h" diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.h b/src/core/hle/service/time/tick_based_steady_clock_core.h index 1a5a53fd7..491185dc3 100644 --- a/src/core/hle/service/time/tick_based_steady_clock_core.h +++ b/src/core/hle/service/time/tick_based_steady_clock_core.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 4d8823b5a..f77cdbb43 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/core.h" @@ -44,8 +43,7 @@ private: } s64 posix_time{}; - if (const ResultCode result{clock_core.GetCurrentTime(system, posix_time)}; - result.IsError()) { + if (const Result result{clock_core.GetCurrentTime(system, posix_time)}; result.IsError()) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); return; @@ -66,7 +64,7 @@ private: } Clock::SystemClockContext system_clock_context{}; - if (const ResultCode result{clock_core.GetClockContext(system, system_clock_context)}; + if (const Result result{clock_core.GetClockContext(system, system_clock_context)}; result.IsError()) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); @@ -117,7 +115,7 @@ private: Clock::SteadyClockCore& clock_core; }; -ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal( +Result Module::Interface::GetClockSnapshotFromSystemClockContextInternal( Kernel::KThread* thread, Clock::SystemClockContext user_context, Clock::SystemClockContext network_context, Clock::TimeType type, Clock::ClockSnapshot& clock_snapshot) { @@ -130,7 +128,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal( time_manager.GetStandardUserSystemClockCore().IsAutomaticCorrectionEnabled(); clock_snapshot.type = type; - if (const ResultCode result{ + if (const Result result{ time_manager.GetTimeZoneContentManager().GetTimeZoneManager().GetDeviceLocationName( clock_snapshot.location_name)}; result != ResultSuccess) { @@ -139,7 +137,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal( clock_snapshot.user_context = user_context; - if (const ResultCode result{Clock::ClockSnapshot::GetCurrentTime( + if (const Result result{Clock::ClockSnapshot::GetCurrentTime( clock_snapshot.user_time, clock_snapshot.steady_clock_time_point, clock_snapshot.user_context)}; result != ResultSuccess) { @@ -147,7 +145,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal( } TimeZone::CalendarInfo userCalendarInfo{}; - if (const ResultCode result{ + if (const Result result{ time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules( clock_snapshot.user_time, userCalendarInfo)}; result != ResultSuccess) { @@ -166,7 +164,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal( } TimeZone::CalendarInfo networkCalendarInfo{}; - if (const ResultCode result{ + if (const Result result{ time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules( clock_snapshot.network_time, networkCalendarInfo)}; result != ResultSuccess) { @@ -263,7 +261,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called, type={}", type); Clock::SystemClockContext user_context{}; - if (const ResultCode result{ + if (const Result result{ system.GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(system, user_context)}; result.IsError()) { @@ -273,7 +271,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { } Clock::SystemClockContext network_context{}; - if (const ResultCode result{ + if (const Result result{ system.GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext( system, network_context)}; result.IsError()) { @@ -283,7 +281,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { } Clock::ClockSnapshot clock_snapshot{}; - if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal( + if (const Result result{GetClockSnapshotFromSystemClockContextInternal( &ctx.GetThread(), user_context, network_context, type, clock_snapshot)}; result.IsError()) { IPC::ResponseBuilder rb{ctx, 2}; @@ -309,7 +307,7 @@ void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLEReques LOG_DEBUG(Service_Time, "called, type={}", type); Clock::ClockSnapshot clock_snapshot{}; - if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal( + if (const Result result{GetClockSnapshotFromSystemClockContextInternal( &ctx.GetThread(), user_context, network_context, type, clock_snapshot)}; result != ResultSuccess) { IPC::ResponseBuilder rb{ctx, 2}; @@ -366,7 +364,7 @@ void Module::Interface::CalculateSpanBetween(Kernel::HLERequestContext& ctx) { Clock::TimeSpanType time_span_type{}; s64 span{}; - if (const ResultCode result{snapshot_a.steady_clock_time_point.GetSpanBetween( + if (const Result result{snapshot_a.steady_clock_time_point.GetSpanBetween( snapshot_b.steady_clock_time_point, span)}; result != ResultSuccess) { if (snapshot_a.network_time && snapshot_b.network_time) { diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h index 30e2cd369..76a46cfc7 100644 --- a/src/core/hle/service/time/time.h +++ b/src/core/hle/service/time/time.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -37,7 +36,7 @@ public: void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx); private: - ResultCode GetClockSnapshotFromSystemClockContextInternal( + Result GetClockSnapshotFromSystemClockContextInternal( Kernel::KThread* thread, Clock::SystemClockContext user_context, Clock::SystemClockContext network_context, Clock::TimeType type, Clock::ClockSnapshot& cloc_snapshot); diff --git a/src/core/hle/service/time/time_interface.cpp b/src/core/hle/service/time/time_interface.cpp index bb7b6b5c1..0c53e98ee 100644 --- a/src/core/hle/service/time/time_interface.cpp +++ b/src/core/hle/service/time/time_interface.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/time/time_interface.h" diff --git a/src/core/hle/service/time/time_interface.h b/src/core/hle/service/time/time_interface.h index c41766f1a..ceeb0e5ef 100644 --- a/src/core/hle/service/time/time_interface.h +++ b/src/core/hle/service/time/time_interface.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp index 00f1ae8cf..28667710e 100644 --- a/src/core/hle/service/time/time_manager.cpp +++ b/src/core/hle/service/time/time_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <chrono> #include <ctime> @@ -112,7 +111,7 @@ struct TimeManager::Impl final { FileSys::VirtualFile& vfs_file) { if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule( location_name, vfs_file) != ResultSuccess) { - UNREACHABLE(); + ASSERT(false); return; } @@ -156,7 +155,7 @@ struct TimeManager::Impl final { } else { if (standard_local_system_clock_core.SetCurrentTime(system_, posix_time) != ResultSuccess) { - UNREACHABLE(); + ASSERT(false); return; } } @@ -171,7 +170,7 @@ struct TimeManager::Impl final { if (standard_network_system_clock_core.SetSystemClockContext(clock_context) != ResultSuccess) { - UNREACHABLE(); + ASSERT(false); return; } @@ -184,7 +183,7 @@ struct TimeManager::Impl final { Clock::SteadyClockTimePoint steady_clock_time_point) { if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled( system_, is_automatic_correction_enabled) != ResultSuccess) { - UNREACHABLE(); + ASSERT(false); return; } @@ -204,7 +203,7 @@ struct TimeManager::Impl final { if (GetStandardLocalSystemClockCore() .SetCurrentTime(system_, timespan.ToSeconds()) .IsError()) { - UNREACHABLE(); + ASSERT(false); return; } } diff --git a/src/core/hle/service/time/time_manager.h b/src/core/hle/service/time/time_manager.h index 2404067c0..4f046f266 100644 --- a/src/core/hle/service/time/time_manager.h +++ b/src/core/hle/service/time/time_manager.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp index ed9f75ed6..a3aa0e77f 100644 --- a/src/core/hle/service/time/time_sharedmemory.cpp +++ b/src/core/hle/service/time/time_sharedmemory.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" #include "core/core_timing.h" diff --git a/src/core/hle/service/time/time_sharedmemory.h b/src/core/hle/service/time/time_sharedmemory.h index 9307ea795..561685acd 100644 --- a/src/core/hle/service/time/time_sharedmemory.h +++ b/src/core/hle/service/time/time_sharedmemory.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp index c634b6abd..afbfe9715 100644 --- a/src/core/hle/service/time/time_zone_content_manager.cpp +++ b/src/core/hle/service/time/time_zone_content_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <sstream> @@ -91,10 +90,10 @@ void TimeZoneContentManager::Initialize(TimeManager& time_manager) { } } -ResultCode TimeZoneContentManager::LoadTimeZoneRule(TimeZoneRule& rules, - const std::string& location_name) const { +Result TimeZoneContentManager::LoadTimeZoneRule(TimeZoneRule& rules, + const std::string& location_name) const { FileSys::VirtualFile vfs_file; - if (const ResultCode result{GetTimeZoneInfoFile(location_name, vfs_file)}; + if (const Result result{GetTimeZoneInfoFile(location_name, vfs_file)}; result != ResultSuccess) { return result; } @@ -107,8 +106,8 @@ bool TimeZoneContentManager::IsLocationNameValid(const std::string& location_nam location_name_cache.end(); } -ResultCode TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& location_name, - FileSys::VirtualFile& vfs_file) const { +Result TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& location_name, + FileSys::VirtualFile& vfs_file) const { if (!IsLocationNameValid(location_name)) { return ERROR_TIME_NOT_FOUND; } diff --git a/src/core/hle/service/time/time_zone_content_manager.h b/src/core/hle/service/time/time_zone_content_manager.h index cfa601084..3d94b6428 100644 --- a/src/core/hle/service/time/time_zone_content_manager.h +++ b/src/core/hle/service/time/time_zone_content_manager.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -33,12 +32,12 @@ public: return time_zone_manager; } - ResultCode LoadTimeZoneRule(TimeZoneRule& rules, const std::string& location_name) const; + Result LoadTimeZoneRule(TimeZoneRule& rules, const std::string& location_name) const; private: bool IsLocationNameValid(const std::string& location_name) const; - ResultCode GetTimeZoneInfoFile(const std::string& location_name, - FileSys::VirtualFile& vfs_file) const; + Result GetTimeZoneInfoFile(const std::string& location_name, + FileSys::VirtualFile& vfs_file) const; Core::System& system; TimeZoneManager time_zone_manager; diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp index 2989cee5e..2aa675df9 100644 --- a/src/core/hle/service/time/time_zone_manager.cpp +++ b/src/core/hle/service/time/time_zone_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <climits> @@ -111,10 +110,9 @@ static constexpr s64 GetLeapDaysFromYear(s64 year) { } } -static constexpr int GetMonthLength(bool is_leap_year, int month) { - constexpr std::array<int, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - constexpr std::array<int, 12> month_lengths_leap{31, 29, 31, 30, 31, 30, - 31, 31, 30, 31, 30, 31}; +static constexpr s8 GetMonthLength(bool is_leap_year, int month) { + constexpr std::array<s8, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + constexpr std::array<s8, 12> month_lengths_leap{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; return is_leap_year ? month_lengths_leap[month] : month_lengths[month]; } @@ -281,7 +279,7 @@ static constexpr int TransitionTime(int year, Rule rule, int offset) { break; } default: - UNREACHABLE(); + ASSERT(false); } return value + rule.transition_time + offset; } @@ -668,8 +666,8 @@ static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFi return true; } -static ResultCode CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInternal& calendar_time, - CalendarAdditionalInfo& calendar_additional_info) { +static Result CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInternal& calendar_time, + CalendarAdditionalInfo& calendar_additional_info) { s64 year{epoch_year}; s64 time_days{time / seconds_per_day}; s64 remaining_seconds{time % seconds_per_day}; @@ -743,9 +741,9 @@ static ResultCode CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInter return ResultSuccess; } -static ResultCode ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time, - CalendarTimeInternal& calendar_time, - CalendarAdditionalInfo& calendar_additional_info) { +static Result ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time, + CalendarTimeInternal& calendar_time, + CalendarAdditionalInfo& calendar_additional_info) { if ((rules.go_ahead && time < rules.ats[0]) || (rules.go_back && time > rules.ats[rules.time_count - 1])) { s64 seconds{}; @@ -768,7 +766,7 @@ static ResultCode ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time, if (new_time < rules.ats[0] && new_time > rules.ats[rules.time_count - 1]) { return ERROR_TIME_NOT_FOUND; } - if (const ResultCode result{ + if (const Result result{ ToCalendarTimeInternal(rules, new_time, calendar_time, calendar_additional_info)}; result != ResultSuccess) { return result; @@ -799,8 +797,8 @@ static ResultCode ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time, tti_index = rules.types[low - 1]; } - if (const ResultCode result{CreateCalendarTime(time, rules.ttis[tti_index].gmt_offset, - calendar_time, calendar_additional_info)}; + if (const Result result{CreateCalendarTime(time, rules.ttis[tti_index].gmt_offset, + calendar_time, calendar_additional_info)}; result != ResultSuccess) { return result; } @@ -813,9 +811,9 @@ static ResultCode ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time, return ResultSuccess; } -static ResultCode ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) { +static Result ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) { CalendarTimeInternal calendar_time{}; - const ResultCode result{ + const Result result{ ToCalendarTimeInternal(rules, time, calendar_time, calendar.additional_info)}; calendar.time.year = static_cast<s16>(calendar_time.year); @@ -832,13 +830,13 @@ static ResultCode ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, Calend TimeZoneManager::TimeZoneManager() = default; TimeZoneManager::~TimeZoneManager() = default; -ResultCode TimeZoneManager::ToCalendarTime(const TimeZoneRule& rules, s64 time, - CalendarInfo& calendar) const { +Result TimeZoneManager::ToCalendarTime(const TimeZoneRule& rules, s64 time, + CalendarInfo& calendar) const { return ToCalendarTimeImpl(rules, time, calendar); } -ResultCode TimeZoneManager::SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name, - FileSys::VirtualFile& vfs_file) { +Result TimeZoneManager::SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name, + FileSys::VirtualFile& vfs_file) { TimeZoneRule rule{}; if (ParseTimeZoneBinary(rule, vfs_file)) { device_location_name = location_name; @@ -848,12 +846,12 @@ ResultCode TimeZoneManager::SetDeviceLocationNameWithTimeZoneRule(const std::str return ERROR_TIME_ZONE_CONVERSION_FAILED; } -ResultCode TimeZoneManager::SetUpdatedTime(const Clock::SteadyClockTimePoint& value) { +Result TimeZoneManager::SetUpdatedTime(const Clock::SteadyClockTimePoint& value) { time_zone_update_time_point = value; return ResultSuccess; } -ResultCode TimeZoneManager::ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const { +Result TimeZoneManager::ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const { if (is_initialized) { return ToCalendarTime(time_zone_rule, time, calendar); } else { @@ -861,16 +859,16 @@ ResultCode TimeZoneManager::ToCalendarTimeWithMyRules(s64 time, CalendarInfo& ca } } -ResultCode TimeZoneManager::ParseTimeZoneRuleBinary(TimeZoneRule& rules, - FileSys::VirtualFile& vfs_file) const { +Result TimeZoneManager::ParseTimeZoneRuleBinary(TimeZoneRule& rules, + FileSys::VirtualFile& vfs_file) const { if (!ParseTimeZoneBinary(rules, vfs_file)) { return ERROR_TIME_ZONE_CONVERSION_FAILED; } return ResultSuccess; } -ResultCode TimeZoneManager::ToPosixTime(const TimeZoneRule& rules, - const CalendarTime& calendar_time, s64& posix_time) const { +Result TimeZoneManager::ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time, + s64& posix_time) const { posix_time = 0; CalendarTimeInternal internal_time{ @@ -1022,8 +1020,8 @@ ResultCode TimeZoneManager::ToPosixTime(const TimeZoneRule& rules, return ResultSuccess; } -ResultCode TimeZoneManager::ToPosixTimeWithMyRule(const CalendarTime& calendar_time, - s64& posix_time) const { +Result TimeZoneManager::ToPosixTimeWithMyRule(const CalendarTime& calendar_time, + s64& posix_time) const { if (is_initialized) { return ToPosixTime(time_zone_rule, calendar_time, posix_time); } @@ -1031,7 +1029,7 @@ ResultCode TimeZoneManager::ToPosixTimeWithMyRule(const CalendarTime& calendar_t return ERROR_UNINITIALIZED_CLOCK; } -ResultCode TimeZoneManager::GetDeviceLocationName(LocationName& value) const { +Result TimeZoneManager::GetDeviceLocationName(LocationName& value) const { if (!is_initialized) { return ERROR_UNINITIALIZED_CLOCK; } diff --git a/src/core/hle/service/time/time_zone_manager.h b/src/core/hle/service/time/time_zone_manager.h index aaab0a1e0..5ebd4035e 100644 --- a/src/core/hle/service/time/time_zone_manager.h +++ b/src/core/hle/service/time/time_zone_manager.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -30,16 +29,16 @@ public: is_initialized = true; } - ResultCode SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name, - FileSys::VirtualFile& vfs_file); - ResultCode SetUpdatedTime(const Clock::SteadyClockTimePoint& value); - ResultCode GetDeviceLocationName(TimeZone::LocationName& value) const; - ResultCode ToCalendarTime(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) const; - ResultCode ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const; - ResultCode ParseTimeZoneRuleBinary(TimeZoneRule& rules, FileSys::VirtualFile& vfs_file) const; - ResultCode ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time, - s64& posix_time) const; - ResultCode ToPosixTimeWithMyRule(const CalendarTime& calendar_time, s64& posix_time) const; + Result SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name, + FileSys::VirtualFile& vfs_file); + Result SetUpdatedTime(const Clock::SteadyClockTimePoint& value); + Result GetDeviceLocationName(TimeZone::LocationName& value) const; + Result ToCalendarTime(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) const; + Result ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const; + Result ParseTimeZoneRuleBinary(TimeZoneRule& rules, FileSys::VirtualFile& vfs_file) const; + Result ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time, + s64& posix_time) const; + Result ToPosixTimeWithMyRule(const CalendarTime& calendar_time, s64& posix_time) const; private: bool is_initialized{}; diff --git a/src/core/hle/service/time/time_zone_service.cpp b/src/core/hle/service/time/time_zone_service.cpp index 3871e7316..961040bfc 100644 --- a/src/core/hle/service/time/time_zone_service.cpp +++ b/src/core/hle/service/time/time_zone_service.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" @@ -33,7 +32,7 @@ void ITimeZoneService::GetDeviceLocationName(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called"); TimeZone::LocationName location_name{}; - if (const ResultCode result{ + if (const Result result{ time_zone_content_manager.GetTimeZoneManager().GetDeviceLocationName(location_name)}; result != ResultSuccess) { IPC::ResponseBuilder rb{ctx, 2}; @@ -62,7 +61,7 @@ void ITimeZoneService::LoadTimeZoneRule(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called, location_name={}", location_name); TimeZone::TimeZoneRule time_zone_rule{}; - if (const ResultCode result{ + if (const Result result{ time_zone_content_manager.LoadTimeZoneRule(time_zone_rule, location_name)}; result != ResultSuccess) { IPC::ResponseBuilder rb{ctx, 2}; @@ -89,7 +88,7 @@ void ITimeZoneService::ToCalendarTime(Kernel::HLERequestContext& ctx) { std::memcpy(&time_zone_rule, buffer.data(), buffer.size()); TimeZone::CalendarInfo calendar_info{}; - if (const ResultCode result{time_zone_content_manager.GetTimeZoneManager().ToCalendarTime( + if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToCalendarTime( time_zone_rule, posix_time, calendar_info)}; result != ResultSuccess) { IPC::ResponseBuilder rb{ctx, 2}; @@ -109,7 +108,7 @@ void ITimeZoneService::ToCalendarTimeWithMyRule(Kernel::HLERequestContext& ctx) LOG_DEBUG(Service_Time, "called, posix_time=0x{:016X}", posix_time); TimeZone::CalendarInfo calendar_info{}; - if (const ResultCode result{ + if (const Result result{ time_zone_content_manager.GetTimeZoneManager().ToCalendarTimeWithMyRules( posix_time, calendar_info)}; result != ResultSuccess) { @@ -132,7 +131,7 @@ void ITimeZoneService::ToPosixTime(Kernel::HLERequestContext& ctx) { std::memcpy(&time_zone_rule, ctx.ReadBuffer().data(), sizeof(TimeZone::TimeZoneRule)); s64 posix_time{}; - if (const ResultCode result{time_zone_content_manager.GetTimeZoneManager().ToPosixTime( + if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToPosixTime( time_zone_rule, calendar_time, posix_time)}; result != ResultSuccess) { IPC::ResponseBuilder rb{ctx, 2}; @@ -155,9 +154,8 @@ void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) { const auto calendar_time{rp.PopRaw<TimeZone::CalendarTime>()}; s64 posix_time{}; - if (const ResultCode result{ - time_zone_content_manager.GetTimeZoneManager().ToPosixTimeWithMyRule(calendar_time, - posix_time)}; + if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToPosixTimeWithMyRule( + calendar_time, posix_time)}; result != ResultSuccess) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); diff --git a/src/core/hle/service/time/time_zone_service.h b/src/core/hle/service/time/time_zone_service.h index 2c9b97603..f151f4b56 100644 --- a/src/core/hle/service/time/time_zone_service.h +++ b/src/core/hle/service/time/time_zone_service.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/time/time_zone_types.h b/src/core/hle/service/time/time_zone_types.h index d39103253..eb4fb52d1 100644 --- a/src/core/hle/service/time/time_zone_types.h +++ b/src/core/hle/service/time/time_zone_types.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp index 0747c33cd..ac46a406c 100644 --- a/src/core/hle/service/usb/usb.cpp +++ b/src/core/hle/service/usb/usb.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/usb/usb.h b/src/core/hle/service/usb/usb.h index fc366df34..b41b9684c 100644 --- a/src/core/hle/service/usb/usb.h +++ b/src/core/hle/service/usb/usb.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp index b7705c02a..288aafaaf 100644 --- a/src/core/hle/service/vi/display/vi_display.cpp +++ b/src/core/hle/service/vi/display/vi_display.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <utility> @@ -13,14 +12,38 @@ #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_writable_event.h" #include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvflinger/buffer_item_consumer.h" +#include "core/hle/service/nvflinger/buffer_queue_consumer.h" +#include "core/hle/service/nvflinger/buffer_queue_core.h" +#include "core/hle/service/nvflinger/buffer_queue_producer.h" +#include "core/hle/service/nvflinger/hos_binder_driver_server.h" #include "core/hle/service/vi/display/vi_display.h" #include "core/hle/service/vi/layer/vi_layer.h" +#include "core/hle/service/vi/vi_results.h" namespace Service::VI { -Display::Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_, - Core::System& system_) - : display_id{id}, name{std::move(name_)}, service_context{service_context_} { +struct BufferQueue { + std::shared_ptr<android::BufferQueueCore> core; + std::unique_ptr<android::BufferQueueProducer> producer; + std::unique_ptr<android::BufferQueueConsumer> consumer; +}; + +static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context, + Service::Nvidia::NvCore::NvMap& nvmap) { + auto buffer_queue_core = std::make_shared<android::BufferQueueCore>(); + return { + buffer_queue_core, + std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap), + std::make_unique<android::BufferQueueConsumer>(buffer_queue_core, nvmap)}; +} + +Display::Display(u64 id, std::string name_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_, + KernelHelpers::ServiceContext& service_context_, Core::System& system_) + : display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_}, + service_context{service_context_} { vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id)); } @@ -36,29 +59,48 @@ const Layer& Display::GetLayer(std::size_t index) const { return *layers.at(index); } -Kernel::KReadableEvent& Display::GetVSyncEvent() { - return vsync_event->GetReadableEvent(); +ResultVal<Kernel::KReadableEvent*> Display::GetVSyncEvent() { + if (got_vsync_event) { + return ResultPermissionDenied; + } + + got_vsync_event = true; + + return GetVSyncEventUnchecked(); +} + +Kernel::KReadableEvent* Display::GetVSyncEventUnchecked() { + return &vsync_event->GetReadableEvent(); } void Display::SignalVSyncEvent() { vsync_event->GetWritableEvent().Signal(); } -void Display::CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue) { - // TODO(Subv): Support more than 1 layer. +void Display::CreateLayer(u64 layer_id, u32 binder_id, + Service::Nvidia::NvCore::Container& nv_core) { ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment"); - layers.emplace_back(std::make_shared<Layer>(layer_id, buffer_queue)); + auto [core, producer, consumer] = CreateBufferQueue(service_context, nv_core.GetNvMapFile()); + + auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer)); + buffer_item_consumer->Connect(false); + + layers.emplace_back(std::make_unique<Layer>(layer_id, binder_id, *core, *producer, + std::move(buffer_item_consumer))); + + hos_binder_driver_server.RegisterProducer(std::move(producer)); } void Display::CloseLayer(u64 layer_id) { - std::erase_if(layers, [layer_id](const auto& layer) { return layer->GetID() == layer_id; }); + std::erase_if(layers, + [layer_id](const auto& layer) { return layer->GetLayerId() == layer_id; }); } Layer* Display::FindLayer(u64 layer_id) { const auto itr = - std::find_if(layers.begin(), layers.end(), [layer_id](const std::shared_ptr<Layer>& layer) { - return layer->GetID() == layer_id; + std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) { + return layer->GetLayerId() == layer_id; }); if (itr == layers.end()) { @@ -70,8 +112,8 @@ Layer* Display::FindLayer(u64 layer_id) { const Layer* Display::FindLayer(u64 layer_id) const { const auto itr = - std::find_if(layers.begin(), layers.end(), [layer_id](const std::shared_ptr<Layer>& layer) { - return layer->GetID() == layer_id; + std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) { + return layer->GetLayerId() == layer_id; }); if (itr == layers.end()) { diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h index 329f4ba86..33d5f398c 100644 --- a/src/core/hle/service/vi/display/vi_display.h +++ b/src/core/hle/service/vi/display/vi_display.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -10,17 +9,28 @@ #include "common/common_funcs.h" #include "common/common_types.h" +#include "core/hle/result.h" namespace Kernel { class KEvent; } -namespace Service::NVFlinger { -class BufferQueue; +namespace Service::android { +class BufferQueueProducer; } + namespace Service::KernelHelpers { class ServiceContext; -} // namespace Service::KernelHelpers +} + +namespace Service::NVFlinger { +class HosBinderDriverServer; +} + +namespace Service::Nvidia::NvCore { +class Container; +class NvMap; +} // namespace Service::Nvidia::NvCore namespace Service::VI { @@ -35,12 +45,13 @@ public: /// Constructs a display with a given unique ID and name. /// /// @param id The unique ID for this display. + /// @param hos_binder_driver_server_ NVFlinger HOSBinderDriver server instance. /// @param service_context_ The ServiceContext for the owning service. /// @param name_ The name for this display. /// @param system_ The global system instance. /// - Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_, - Core::System& system_); + Display(u64 id, std::string name_, NVFlinger::HosBinderDriverServer& hos_binder_driver_server_, + KernelHelpers::ServiceContext& service_context_, Core::System& system_); ~Display(); /// Gets the unique ID assigned to this display. @@ -64,18 +75,30 @@ public: /// Gets a layer for this display based off an index. const Layer& GetLayer(std::size_t index) const; - /// Gets the readable vsync event. - Kernel::KReadableEvent& GetVSyncEvent(); + std::size_t GetNumLayers() const { + return layers.size(); + } + + /** + * Gets the internal vsync event. + * + * @returns The internal Vsync event if it has not yet been retrieved, + * VI::ResultPermissionDenied otherwise. + */ + [[nodiscard]] ResultVal<Kernel::KReadableEvent*> GetVSyncEvent(); + + /// Gets the internal vsync event. + Kernel::KReadableEvent* GetVSyncEventUnchecked(); /// Signals the internal vsync event. void SignalVSyncEvent(); /// Creates and adds a layer to this display with the given ID. /// - /// @param layer_id The ID to assign to the created layer. - /// @param buffer_queue The buffer queue for the layer instance to use. + /// @param layer_id The ID to assign to the created layer. + /// @param binder_id The ID assigned to the buffer queue. /// - void CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue); + void CreateLayer(u64 layer_id, u32 binder_id, Service::Nvidia::NvCore::Container& core); /// Closes and removes a layer from this display with the given ID. /// @@ -104,10 +127,12 @@ public: private: u64 display_id; std::string name; + NVFlinger::HosBinderDriverServer& hos_binder_driver_server; KernelHelpers::ServiceContext& service_context; - std::vector<std::shared_ptr<Layer>> layers; + std::vector<std::unique_ptr<Layer>> layers; Kernel::KEvent* vsync_event{}; + bool got_vsync_event{false}; }; } // namespace Service::VI diff --git a/src/core/hle/service/vi/layer/vi_layer.cpp b/src/core/hle/service/vi/layer/vi_layer.cpp index 9bc382587..9ae2e0e44 100644 --- a/src/core/hle/service/vi/layer/vi_layer.cpp +++ b/src/core/hle/service/vi/layer/vi_layer.cpp @@ -1,12 +1,15 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/vi/layer/vi_layer.h" namespace Service::VI { -Layer::Layer(u64 id, NVFlinger::BufferQueue& queue) : layer_id{id}, buffer_queue{queue} {} +Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_, + android::BufferQueueProducer& binder_, + std::shared_ptr<android::BufferItemConsumer>&& consumer_) + : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, consumer{std::move( + consumer_)} {} Layer::~Layer() = default; diff --git a/src/core/hle/service/vi/layer/vi_layer.h b/src/core/hle/service/vi/layer/vi_layer.h index ebdd85505..8cf1b5275 100644 --- a/src/core/hle/service/vi/layer/vi_layer.h +++ b/src/core/hle/service/vi/layer/vi_layer.h @@ -1,14 +1,17 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include <memory> + #include "common/common_types.h" -namespace Service::NVFlinger { -class BufferQueue; -} +namespace Service::android { +class BufferItemConsumer; +class BufferQueueCore; +class BufferQueueProducer; +} // namespace Service::android namespace Service::VI { @@ -17,10 +20,13 @@ class Layer { public: /// Constructs a layer with a given ID and buffer queue. /// - /// @param id The ID to assign to this layer. - /// @param queue The buffer queue for this layer to use. + /// @param layer_id_ The ID to assign to this layer. + /// @param binder_id_ The binder ID to assign to this layer. + /// @param binder_ The buffer producer queue for this layer to use. /// - Layer(u64 id, NVFlinger::BufferQueue& queue); + Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_, + android::BufferQueueProducer& binder_, + std::shared_ptr<android::BufferItemConsumer>&& consumer_); ~Layer(); Layer(const Layer&) = delete; @@ -30,23 +36,47 @@ public: Layer& operator=(Layer&&) = delete; /// Gets the ID for this layer. - u64 GetID() const { + u64 GetLayerId() const { return layer_id; } + /// Gets the binder ID for this layer. + u32 GetBinderId() const { + return binder_id; + } + /// Gets a reference to the buffer queue this layer is using. - NVFlinger::BufferQueue& GetBufferQueue() { - return buffer_queue; + android::BufferQueueProducer& GetBufferQueue() { + return binder; } /// Gets a const reference to the buffer queue this layer is using. - const NVFlinger::BufferQueue& GetBufferQueue() const { - return buffer_queue; + const android::BufferQueueProducer& GetBufferQueue() const { + return binder; + } + + android::BufferItemConsumer& GetConsumer() { + return *consumer; + } + + const android::BufferItemConsumer& GetConsumer() const { + return *consumer; + } + + android::BufferQueueCore& Core() { + return core; + } + + const android::BufferQueueCore& Core() const { + return core; } private: - u64 layer_id; - NVFlinger::BufferQueue& buffer_queue; + const u64 layer_id; + const u32 binder_id; + android::BufferQueueCore& core; + android::BufferQueueProducer& binder; + std::shared_ptr<android::BufferItemConsumer> consumer; }; } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 75ee3e5e4..9c917cacf 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <array> @@ -22,21 +21,20 @@ #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/service/nvdrv/nvdata.h" -#include "core/hle/service/nvflinger/buffer_queue.h" +#include "core/hle/service/nvflinger/binder.h" +#include "core/hle/service/nvflinger/buffer_queue_producer.h" +#include "core/hle/service/nvflinger/hos_binder_driver_server.h" #include "core/hle/service/nvflinger/nvflinger.h" +#include "core/hle/service/nvflinger/parcel.h" #include "core/hle/service/service.h" #include "core/hle/service/vi/vi.h" #include "core/hle/service/vi/vi_m.h" +#include "core/hle/service/vi/vi_results.h" #include "core/hle/service/vi/vi_s.h" #include "core/hle/service/vi/vi_u.h" namespace Service::VI { -constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::VI, 1}; -constexpr ResultCode ERR_PERMISSION_DENIED{ErrorModule::VI, 5}; -constexpr ResultCode ERR_UNSUPPORTED{ErrorModule::VI, 6}; -constexpr ResultCode ERR_NOT_FOUND{ErrorModule::VI, 7}; - struct DisplayInfo { /// The name of this particular display. char display_name[0x40]{"Default"}; @@ -57,447 +55,26 @@ struct DisplayInfo { }; static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size"); -class Parcel { -public: - // This default size was chosen arbitrarily. - static constexpr std::size_t DefaultBufferSize = 0x40; - Parcel() : buffer(DefaultBufferSize) {} - explicit Parcel(std::vector<u8> data) : buffer(std::move(data)) {} - virtual ~Parcel() = default; - - template <typename T> - T Read() { - static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); - ASSERT(read_index + sizeof(T) <= buffer.size()); - - T val; - std::memcpy(&val, buffer.data() + read_index, sizeof(T)); - read_index += sizeof(T); - read_index = Common::AlignUp(read_index, 4); - return val; - } - - template <typename T> - T ReadUnaligned() { - static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); - ASSERT(read_index + sizeof(T) <= buffer.size()); - - T val; - std::memcpy(&val, buffer.data() + read_index, sizeof(T)); - read_index += sizeof(T); - return val; - } - - std::vector<u8> ReadBlock(std::size_t length) { - ASSERT(read_index + length <= buffer.size()); - const u8* const begin = buffer.data() + read_index; - const u8* const end = begin + length; - std::vector<u8> data(begin, end); - read_index += length; - read_index = Common::AlignUp(read_index, 4); - return data; - } - - std::u16string ReadInterfaceToken() { - [[maybe_unused]] const u32 unknown = Read<u32_le>(); - const u32 length = Read<u32_le>(); - - std::u16string token{}; - - for (u32 ch = 0; ch < length + 1; ++ch) { - token.push_back(ReadUnaligned<u16_le>()); - } - - read_index = Common::AlignUp(read_index, 4); - - return token; - } - - template <typename T> - void Write(const T& val) { - static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); - - if (buffer.size() < write_index + sizeof(T)) { - buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize); - } - - std::memcpy(buffer.data() + write_index, &val, sizeof(T)); - write_index += sizeof(T); - write_index = Common::AlignUp(write_index, 4); - } - - template <typename T> - void WriteObject(const T& val) { - static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); - - const u32_le size = static_cast<u32>(sizeof(val)); - Write(size); - // TODO(Subv): Support file descriptors. - Write<u32_le>(0); // Fd count. - Write(val); - } - - void Deserialize() { - ASSERT(buffer.size() > sizeof(Header)); - - Header header{}; - std::memcpy(&header, buffer.data(), sizeof(Header)); - - read_index = header.data_offset; - DeserializeData(); - } - - std::vector<u8> Serialize() { - ASSERT(read_index == 0); - write_index = sizeof(Header); - - SerializeData(); - - Header header{}; - header.data_size = static_cast<u32_le>(write_index - sizeof(Header)); - header.data_offset = sizeof(Header); - header.objects_size = 4; - header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size); - std::memcpy(buffer.data(), &header, sizeof(Header)); - - return buffer; - } - -protected: - virtual void SerializeData() {} - - virtual void DeserializeData() {} - -private: - struct Header { - u32_le data_size; - u32_le data_offset; - u32_le objects_size; - u32_le objects_offset; - }; - static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size"); - - std::vector<u8> buffer; - std::size_t read_index = 0; - std::size_t write_index = 0; -}; - -class NativeWindow : public Parcel { -public: - explicit NativeWindow(u32 id) { - data.id = id; - } - ~NativeWindow() override = default; - -protected: - void SerializeData() override { - Write(data); - } - -private: - struct Data { - u32_le magic = 2; - u32_le process_id = 1; - u32_le id; - INSERT_PADDING_WORDS(3); - std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'}; - INSERT_PADDING_WORDS(2); - }; - static_assert(sizeof(Data) == 0x28, "ParcelData has wrong size"); - - Data data{}; -}; - -class IGBPConnectRequestParcel : public Parcel { -public: - explicit IGBPConnectRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) { - Deserialize(); - } - - void DeserializeData() override { - [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); - data = Read<Data>(); - } - - struct Data { - u32_le unk; - u32_le api; - u32_le producer_controlled_by_app; - }; - - Data data; -}; - -class IGBPConnectResponseParcel : public Parcel { +class NativeWindow final { public: - explicit IGBPConnectResponseParcel(u32 width, u32 height) { - data.width = width; - data.height = height; - } - ~IGBPConnectResponseParcel() override = default; - -protected: - void SerializeData() override { - Write(data); - } - -private: - struct Data { - u32_le width; - u32_le height; - u32_le transform_hint; - u32_le num_pending_buffers; - u32_le status; - }; - static_assert(sizeof(Data) == 20, "ParcelData has wrong size"); - - Data data{}; -}; - -/// Represents a parcel containing one int '0' as its data -/// Used by DetachBuffer and Disconnect -class IGBPEmptyResponseParcel : public Parcel { -protected: - void SerializeData() override { - Write(data); - } + constexpr explicit NativeWindow(u32 id_) : id{id_} {} + constexpr explicit NativeWindow(const NativeWindow& other) = default; private: - struct Data { - u32_le unk_0{}; - }; - - Data data{}; -}; - -class IGBPSetPreallocatedBufferRequestParcel : public Parcel { -public: - explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer_) - : Parcel(std::move(buffer_)) { - Deserialize(); - } - - void DeserializeData() override { - [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); - data = Read<Data>(); - if (data.contains_object != 0) { - buffer_container = Read<BufferContainer>(); - } - } - - struct Data { - u32_le slot; - u32_le contains_object; - }; - - struct BufferContainer { - u32_le graphic_buffer_length; - INSERT_PADDING_WORDS(1); - NVFlinger::IGBPBuffer buffer{}; - }; - - Data data{}; - BufferContainer buffer_container{}; -}; - -class IGBPSetPreallocatedBufferResponseParcel : public Parcel { -protected: - void SerializeData() override { - // TODO(Subv): Find out what this means - Write<u32>(0); - } -}; - -class IGBPCancelBufferRequestParcel : public Parcel { -public: - explicit IGBPCancelBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) { - Deserialize(); - } - - void DeserializeData() override { - [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); - data = Read<Data>(); - } - - struct Data { - u32_le slot; - Service::Nvidia::MultiFence multi_fence; - }; - - Data data; -}; - -class IGBPCancelBufferResponseParcel : public Parcel { -protected: - void SerializeData() override { - Write<u32>(0); // Success - } -}; - -class IGBPDequeueBufferRequestParcel : public Parcel { -public: - explicit IGBPDequeueBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) { - Deserialize(); - } - - void DeserializeData() override { - [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); - data = Read<Data>(); - } - - struct Data { - u32_le pixel_format; - u32_le width; - u32_le height; - u32_le get_frame_timestamps; - u32_le usage; - }; - - Data data; -}; - -class IGBPDequeueBufferResponseParcel : public Parcel { -public: - explicit IGBPDequeueBufferResponseParcel(u32 slot_, Nvidia::MultiFence& multi_fence_) - : slot(slot_), multi_fence(multi_fence_) {} - -protected: - void SerializeData() override { - Write(slot); - Write<u32_le>(1); - WriteObject(multi_fence); - Write<u32_le>(0); - } - - u32_le slot; - Service::Nvidia::MultiFence multi_fence; -}; - -class IGBPRequestBufferRequestParcel : public Parcel { -public: - explicit IGBPRequestBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) { - Deserialize(); - } - - void DeserializeData() override { - [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); - slot = Read<u32_le>(); - } - - u32_le slot; -}; - -class IGBPRequestBufferResponseParcel : public Parcel { -public: - explicit IGBPRequestBufferResponseParcel(NVFlinger::IGBPBuffer buffer_) : buffer(buffer_) {} - ~IGBPRequestBufferResponseParcel() override = default; - -protected: - void SerializeData() override { - // TODO(Subv): Figure out what this value means, writing non-zero here will make libnx - // try to read an IGBPBuffer object from the parcel. - Write<u32_le>(1); - WriteObject(buffer); - Write<u32_le>(0); - } - - NVFlinger::IGBPBuffer buffer; -}; - -class IGBPQueueBufferRequestParcel : public Parcel { -public: - explicit IGBPQueueBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) { - Deserialize(); - } - - void DeserializeData() override { - [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); - data = Read<Data>(); - } - - struct Data { - u32_le slot; - INSERT_PADDING_WORDS(3); - u32_le timestamp; - s32_le is_auto_timestamp; - s32_le crop_top; - s32_le crop_left; - s32_le crop_right; - s32_le crop_bottom; - s32_le scaling_mode; - NVFlinger::BufferQueue::BufferTransformFlags transform; - u32_le sticky_transform; - INSERT_PADDING_WORDS(1); - u32_le swap_interval; - Service::Nvidia::MultiFence multi_fence; - - Common::Rectangle<int> GetCropRect() const { - return {crop_left, crop_top, crop_right, crop_bottom}; - } - }; - static_assert(sizeof(Data) == 96, "ParcelData has wrong size"); - - Data data; -}; - -class IGBPQueueBufferResponseParcel : public Parcel { -public: - explicit IGBPQueueBufferResponseParcel(u32 width, u32 height) { - data.width = width; - data.height = height; - } - ~IGBPQueueBufferResponseParcel() override = default; - -protected: - void SerializeData() override { - Write(data); - } - -private: - struct Data { - u32_le width; - u32_le height; - u32_le transform_hint; - u32_le num_pending_buffers; - u32_le status; - }; - static_assert(sizeof(Data) == 20, "ParcelData has wrong size"); - - Data data{}; -}; - -class IGBPQueryRequestParcel : public Parcel { -public: - explicit IGBPQueryRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) { - Deserialize(); - } - - void DeserializeData() override { - [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); - type = Read<u32_le>(); - } - - u32 type; -}; - -class IGBPQueryResponseParcel : public Parcel { -public: - explicit IGBPQueryResponseParcel(u32 value_) : value{value_} {} - ~IGBPQueryResponseParcel() override = default; - -protected: - void SerializeData() override { - Write(value); - } - -private: - u32_le value; + const u32 magic = 2; + const u32 process_id = 1; + const u32 id; + INSERT_PADDING_WORDS(3); + std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'}; + INSERT_PADDING_WORDS(2); }; +static_assert(sizeof(NativeWindow) == 0x28, "NativeWindow has wrong size"); class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> { public: - explicit IHOSBinderDriver(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) - : ServiceFramework{system_, "IHOSBinderDriver"}, nv_flinger(nv_flinger_) { + explicit IHOSBinderDriver(Core::System& system_, NVFlinger::HosBinderDriverServer& server_) + : ServiceFramework{system_, "IHOSBinderDriver", ServiceThreadType::CreateNew}, + server(server_) { static const FunctionInfo functions[] = { {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"}, {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"}, @@ -508,147 +85,16 @@ public: } private: - enum class TransactionId { - RequestBuffer = 1, - SetBufferCount = 2, - DequeueBuffer = 3, - DetachBuffer = 4, - DetachNextBuffer = 5, - AttachBuffer = 6, - QueueBuffer = 7, - CancelBuffer = 8, - Query = 9, - Connect = 10, - Disconnect = 11, - - AllocateBuffers = 13, - SetPreallocatedBuffer = 14, - - GetBufferHistory = 17 - }; - void TransactParcel(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u32 id = rp.Pop<u32>(); - const auto transaction = static_cast<TransactionId>(rp.Pop<u32>()); + const auto transaction = static_cast<android::TransactionId>(rp.Pop<u32>()); const u32 flags = rp.Pop<u32>(); LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, transaction, flags); - auto& buffer_queue = *nv_flinger.FindBufferQueue(id); - - switch (transaction) { - case TransactionId::Connect: { - IGBPConnectRequestParcel request{ctx.ReadBuffer()}; - IGBPConnectResponseParcel response{static_cast<u32>(DisplayResolution::UndockedWidth), - static_cast<u32>(DisplayResolution::UndockedHeight)}; - - buffer_queue.Connect(); - - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::SetPreallocatedBuffer: { - IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()}; - - buffer_queue.SetPreallocatedBuffer(request.data.slot, request.buffer_container.buffer); - - IGBPSetPreallocatedBufferResponseParcel response{}; - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::DequeueBuffer: { - IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()}; - const u32 width{request.data.width}; - const u32 height{request.data.height}; - - do { - if (auto result = buffer_queue.DequeueBuffer(width, height); result) { - // Buffer is available - IGBPDequeueBufferResponseParcel response{result->first, *result->second}; - ctx.WriteBuffer(response.Serialize()); - break; - } - } while (buffer_queue.IsConnected()); - - break; - } - case TransactionId::RequestBuffer: { - IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()}; - - auto& buffer = buffer_queue.RequestBuffer(request.slot); - IGBPRequestBufferResponseParcel response{buffer}; - ctx.WriteBuffer(response.Serialize()); - - break; - } - case TransactionId::QueueBuffer: { - IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()}; - - buffer_queue.QueueBuffer(request.data.slot, request.data.transform, - request.data.GetCropRect(), request.data.swap_interval, - request.data.multi_fence); - - IGBPQueueBufferResponseParcel response{1280, 720}; - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::Query: { - IGBPQueryRequestParcel request{ctx.ReadBuffer()}; - - const u32 value = - buffer_queue.Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type)); - - IGBPQueryResponseParcel response{value}; - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::CancelBuffer: { - IGBPCancelBufferRequestParcel request{ctx.ReadBuffer()}; - - buffer_queue.CancelBuffer(request.data.slot, request.data.multi_fence); - - IGBPCancelBufferResponseParcel response{}; - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::Disconnect: { - LOG_WARNING(Service_VI, "(STUBBED) called, transaction=Disconnect"); - const auto buffer = ctx.ReadBuffer(); - - buffer_queue.Disconnect(); - - IGBPEmptyResponseParcel response{}; - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::DetachBuffer: { - const auto buffer = ctx.ReadBuffer(); - - IGBPEmptyResponseParcel response{}; - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::SetBufferCount: { - LOG_WARNING(Service_VI, "(STUBBED) called, transaction=SetBufferCount"); - [[maybe_unused]] const auto buffer = ctx.ReadBuffer(); - - IGBPEmptyResponseParcel response{}; - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::GetBufferHistory: { - LOG_WARNING(Service_VI, "(STUBBED) called, transaction=GetBufferHistory"); - [[maybe_unused]] const auto buffer = ctx.ReadBuffer(); - - IGBPEmptyResponseParcel response{}; - ctx.WriteBuffer(response.Serialize()); - break; - } - default: - ASSERT_MSG(false, "Unimplemented"); - } + server.TryGetProducer(id)->Transact(ctx, transaction, flags); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -674,13 +120,13 @@ private: LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); - // TODO(Subv): Find out what this actually is. IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(nv_flinger.FindBufferQueue(id)->GetBufferWaitEvent()); + rb.PushCopyObjects(server.TryGetProducer(id)->GetNativeHandle()); } - NVFlinger::NVFlinger& nv_flinger; +private: + NVFlinger::HosBinderDriverServer& server; }; class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> { @@ -899,7 +345,7 @@ private: if (!layer_id) { LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } @@ -937,7 +383,40 @@ private: class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { public: - explicit IApplicationDisplayService(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); + IApplicationDisplayService(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_) + : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_}, + hos_binder_driver_server{hos_binder_driver_server_} { + + static const FunctionInfo functions[] = { + {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"}, + {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"}, + {102, &IApplicationDisplayService::GetManagerDisplayService, + "GetManagerDisplayService"}, + {103, &IApplicationDisplayService::GetIndirectDisplayTransactionService, + "GetIndirectDisplayTransactionService"}, + {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"}, + {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"}, + {1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"}, + {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"}, + {1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"}, + {1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"}, + {2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"}, + {2021, &IApplicationDisplayService::CloseLayer, "CloseLayer"}, + {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"}, + {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"}, + {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"}, + {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"}, + {2450, &IApplicationDisplayService::GetIndirectLayerImageMap, + "GetIndirectLayerImageMap"}, + {2451, nullptr, "GetIndirectLayerImageCropMap"}, + {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo, + "GetIndirectLayerImageRequiredMemoryInfo"}, + {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"}, + {5203, nullptr, "GetDisplayVsyncEventForDebug"}, + }; + RegisterHandlers(functions); + } private: enum class ConvertedScaleMode : u64 { @@ -961,7 +440,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface<IHOSBinderDriver>(system, nv_flinger); + rb.PushIpcInterface<IHOSBinderDriver>(system, hos_binder_driver_server); } void GetSystemDisplayService(Kernel::HLERequestContext& ctx) { @@ -985,7 +464,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface<IHOSBinderDriver>(system, nv_flinger); + rb.PushIpcInterface<IHOSBinderDriver>(system, hos_binder_driver_server); } void OpenDisplay(Kernel::HLERequestContext& ctx) { @@ -1016,7 +495,7 @@ private: if (!display_id) { LOG_ERROR(Service_VI, "Display not found! display_name={}", name); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } @@ -1072,14 +551,14 @@ private: if (scaling_mode > NintendoScaleMode::PreserveAspectRatio) { LOG_ERROR(Service_VI, "Invalid scaling mode provided."); - rb.Push(ERR_OPERATION_FAILED); + rb.Push(ResultOperationFailed); return; } if (scaling_mode != NintendoScaleMode::ScaleToWindow && scaling_mode != NintendoScaleMode::PreserveAspectRatio) { LOG_ERROR(Service_VI, "Unsupported scaling mode supplied."); - rb.Push(ERR_UNSUPPORTED); + rb.Push(ResultNotSupported); return; } @@ -1089,7 +568,7 @@ private: void ListDisplays(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_VI, "(STUBBED) called"); - DisplayInfo display_info; + const DisplayInfo display_info; ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); @@ -1112,7 +591,7 @@ private: if (!display_id) { LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } @@ -1120,12 +599,12 @@ private: if (!buffer_queue_id) { LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } - NativeWindow native_window{*buffer_queue_id}; - const auto buffer_size = ctx.WriteBuffer(native_window.Serialize()); + const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}}; + const auto buffer_size = ctx.WriteBuffer(parcel.Serialize()); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); @@ -1158,7 +637,7 @@ private: if (!layer_id) { LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } @@ -1166,12 +645,12 @@ private: if (!buffer_queue_id) { LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(ResultNotFound); return; } - NativeWindow native_window{*buffer_queue_id}; - const auto buffer_size = ctx.WriteBuffer(native_window.Serialize()); + const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}}; + const auto buffer_size = ctx.WriteBuffer(parcel.Serialize()); IPC::ResponseBuilder rb{ctx, 6}; rb.Push(ResultSuccess); @@ -1193,19 +672,23 @@ private: IPC::RequestParser rp{ctx}; const u64 display_id = rp.Pop<u64>(); - LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id); + LOG_DEBUG(Service_VI, "called. display_id={}", display_id); const auto vsync_event = nv_flinger.FindVsyncEvent(display_id); - if (!vsync_event) { - LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); + if (vsync_event.Failed()) { + const auto result = vsync_event.Code(); + if (result == ResultNotFound) { + LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); + } + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_FOUND); + rb.Push(result); return; } IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(vsync_event); + rb.PushCopyObjects(*vsync_event); } void ConvertScalingMode(Kernel::HLERequestContext& ctx) { @@ -1282,44 +765,14 @@ private: return ConvertedScaleMode::PreserveAspectRatio; default: LOG_ERROR(Service_VI, "Invalid scaling mode specified, mode={}", mode); - return ERR_OPERATION_FAILED; + return ResultOperationFailed; } } NVFlinger::NVFlinger& nv_flinger; + NVFlinger::HosBinderDriverServer& hos_binder_driver_server; }; -IApplicationDisplayService::IApplicationDisplayService(Core::System& system_, - NVFlinger::NVFlinger& nv_flinger_) - : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_} { - static const FunctionInfo functions[] = { - {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"}, - {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"}, - {102, &IApplicationDisplayService::GetManagerDisplayService, "GetManagerDisplayService"}, - {103, &IApplicationDisplayService::GetIndirectDisplayTransactionService, - "GetIndirectDisplayTransactionService"}, - {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"}, - {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"}, - {1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"}, - {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"}, - {1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"}, - {1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"}, - {2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"}, - {2021, &IApplicationDisplayService::CloseLayer, "CloseLayer"}, - {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"}, - {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"}, - {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"}, - {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"}, - {2450, &IApplicationDisplayService::GetIndirectLayerImageMap, "GetIndirectLayerImageMap"}, - {2451, nullptr, "GetIndirectLayerImageCropMap"}, - {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo, - "GetIndirectLayerImageRequiredMemoryInfo"}, - {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"}, - {5203, nullptr, "GetDisplayVsyncEventForDebug"}, - }; - RegisterHandlers(functions); -} - static bool IsValidServiceAccess(Permission permission, Policy policy) { if (permission == Permission::User) { return policy == Policy::User; @@ -1333,27 +786,33 @@ static bool IsValidServiceAccess(Permission permission, Policy policy) { } void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system, - NVFlinger::NVFlinger& nv_flinger, Permission permission) { + NVFlinger::NVFlinger& nv_flinger, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server, + Permission permission) { IPC::RequestParser rp{ctx}; const auto policy = rp.PopEnum<Policy>(); if (!IsValidServiceAccess(permission, policy)) { LOG_ERROR(Service_VI, "Permission denied for policy {}", policy); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_PERMISSION_DENIED); + rb.Push(ResultPermissionDenied); return; } IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface<IApplicationDisplayService>(system, nv_flinger); + rb.PushIpcInterface<IApplicationDisplayService>(system, nv_flinger, hos_binder_driver_server); } void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system, - NVFlinger::NVFlinger& nv_flinger) { - std::make_shared<VI_M>(system, nv_flinger)->InstallAsService(service_manager); - std::make_shared<VI_S>(system, nv_flinger)->InstallAsService(service_manager); - std::make_shared<VI_U>(system, nv_flinger)->InstallAsService(service_manager); + NVFlinger::NVFlinger& nv_flinger, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server) { + std::make_shared<VI_M>(system, nv_flinger, hos_binder_driver_server) + ->InstallAsService(service_manager); + std::make_shared<VI_S>(system, nv_flinger, hos_binder_driver_server) + ->InstallAsService(service_manager); + std::make_shared<VI_U>(system, nv_flinger, hos_binder_driver_server) + ->InstallAsService(service_manager); } } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h index 2fd7f8e61..fc2d717e7 100644 --- a/src/core/hle/service/vi/vi.h +++ b/src/core/hle/service/vi/vi.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -15,8 +14,9 @@ class HLERequestContext; } namespace Service::NVFlinger { +class HosBinderDriverServer; class NVFlinger; -} +} // namespace Service::NVFlinger namespace Service::SM { class ServiceManager; @@ -47,11 +47,14 @@ enum class Policy { namespace detail { void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system, - NVFlinger::NVFlinger& nv_flinger, Permission permission); + NVFlinger::NVFlinger& nv_flinger, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server, + Permission permission); } // namespace detail /// Registers all VI services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system, - NVFlinger::NVFlinger& nv_flinger); + NVFlinger::NVFlinger& nv_flinger, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server); } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp index 87db1c416..1ab7fe4ab 100644 --- a/src/core/hle/service/vi/vi_m.cpp +++ b/src/core/hle/service/vi/vi_m.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/hle/service/vi/vi.h" @@ -8,8 +7,10 @@ namespace Service::VI { -VI_M::VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) - : ServiceFramework{system_, "vi:m"}, nv_flinger{nv_flinger_} { +VI_M::VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_) + : ServiceFramework{system_, "vi:m"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{ + hos_binder_driver_server_} { static const FunctionInfo functions[] = { {2, &VI_M::GetDisplayService, "GetDisplayService"}, {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, @@ -22,7 +23,8 @@ VI_M::~VI_M() = default; void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_VI, "called"); - detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::Manager); + detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server, + Permission::Manager); } } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h index d79c41beb..3bf76d439 100644 --- a/src/core/hle/service/vi/vi_m.h +++ b/src/core/hle/service/vi/vi_m.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -15,20 +14,23 @@ class HLERequestContext; } namespace Service::NVFlinger { +class HosBinderDriverServer; class NVFlinger; -} +} // namespace Service::NVFlinger namespace Service::VI { class VI_M final : public ServiceFramework<VI_M> { public: - explicit VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); + explicit VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_); ~VI_M() override; private: void GetDisplayService(Kernel::HLERequestContext& ctx); NVFlinger::NVFlinger& nv_flinger; + NVFlinger::HosBinderDriverServer& hos_binder_driver_server; }; } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_results.h b/src/core/hle/service/vi/vi_results.h new file mode 100644 index 000000000..a46c247d2 --- /dev/null +++ b/src/core/hle/service/vi/vi_results.h @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/result.h" + +namespace Service::VI { + +constexpr Result ResultOperationFailed{ErrorModule::VI, 1}; +constexpr Result ResultPermissionDenied{ErrorModule::VI, 5}; +constexpr Result ResultNotSupported{ErrorModule::VI, 6}; +constexpr Result ResultNotFound{ErrorModule::VI, 7}; + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp index 5cd22f7df..fd799dac1 100644 --- a/src/core/hle/service/vi/vi_s.cpp +++ b/src/core/hle/service/vi/vi_s.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/hle/service/vi/vi.h" @@ -8,8 +7,10 @@ namespace Service::VI { -VI_S::VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) - : ServiceFramework{system_, "vi:s"}, nv_flinger{nv_flinger_} { +VI_S::VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_) + : ServiceFramework{system_, "vi:s"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{ + hos_binder_driver_server_} { static const FunctionInfo functions[] = { {1, &VI_S::GetDisplayService, "GetDisplayService"}, {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, @@ -22,7 +23,8 @@ VI_S::~VI_S() = default; void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_VI, "called"); - detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::System); + detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server, + Permission::System); } } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h index 5f1f8f290..97503ac7f 100644 --- a/src/core/hle/service/vi/vi_s.h +++ b/src/core/hle/service/vi/vi_s.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -15,20 +14,23 @@ class HLERequestContext; } namespace Service::NVFlinger { +class HosBinderDriverServer; class NVFlinger; -} +} // namespace Service::NVFlinger namespace Service::VI { class VI_S final : public ServiceFramework<VI_S> { public: - explicit VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); + explicit VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_); ~VI_S() override; private: void GetDisplayService(Kernel::HLERequestContext& ctx); NVFlinger::NVFlinger& nv_flinger; + NVFlinger::HosBinderDriverServer& hos_binder_driver_server; }; } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp index 0079d51f0..6cc54bd13 100644 --- a/src/core/hle/service/vi/vi_u.cpp +++ b/src/core/hle/service/vi/vi_u.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/hle/service/vi/vi.h" @@ -8,8 +7,10 @@ namespace Service::VI { -VI_U::VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) - : ServiceFramework{system_, "vi:u"}, nv_flinger{nv_flinger_} { +VI_U::VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_) + : ServiceFramework{system_, "vi:u"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{ + hos_binder_driver_server_} { static const FunctionInfo functions[] = { {0, &VI_U::GetDisplayService, "GetDisplayService"}, {1, nullptr, "GetDisplayServiceWithProxyNameExchange"}, @@ -22,7 +23,8 @@ VI_U::~VI_U() = default; void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_VI, "called"); - detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::User); + detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server, + Permission::User); } } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h index 8e3885c73..797941bd7 100644 --- a/src/core/hle/service/vi/vi_u.h +++ b/src/core/hle/service/vi/vi_u.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -15,20 +14,23 @@ class HLERequestContext; } namespace Service::NVFlinger { +class HosBinderDriverServer; class NVFlinger; -} +} // namespace Service::NVFlinger namespace Service::VI { class VI_U final : public ServiceFramework<VI_U> { public: - explicit VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); + explicit VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_); ~VI_U() override; private: void GetDisplayService(Kernel::HLERequestContext& ctx); NVFlinger::NVFlinger& nv_flinger; + NVFlinger::HosBinderDriverServer& hos_binder_driver_server; }; } // namespace Service::VI diff --git a/src/core/hle/service/wlan/wlan.cpp b/src/core/hle/service/wlan/wlan.cpp index f10b8c853..226e3034c 100644 --- a/src/core/hle/service/wlan/wlan.cpp +++ b/src/core/hle/service/wlan/wlan.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> diff --git a/src/core/hle/service/wlan/wlan.h b/src/core/hle/service/wlan/wlan.h index 3899eedbb..535c3bf0d 100644 --- a/src/core/hle/service/wlan/wlan.h +++ b/src/core/hle/service/wlan/wlan.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp new file mode 100644 index 000000000..447fbffaa --- /dev/null +++ b/src/core/internal_network/network.cpp @@ -0,0 +1,639 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <algorithm> +#include <cstring> +#include <limits> +#include <utility> +#include <vector> + +#include "common/error.h" + +#ifdef _WIN32 +#include <winsock2.h> +#include <ws2tcpip.h> +#elif YUZU_UNIX +#include <arpa/inet.h> +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <netinet/in.h> +#include <poll.h> +#include <sys/socket.h> +#include <unistd.h> +#else +#error "Unimplemented platform" +#endif + +#include "common/assert.h" +#include "common/common_types.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "core/internal_network/network.h" +#include "core/internal_network/network_interface.h" +#include "core/internal_network/sockets.h" +#include "network/network.h" + +namespace Network { + +namespace { + +#ifdef _WIN32 + +using socklen_t = int; + +void Initialize() { + WSADATA wsa_data; + (void)WSAStartup(MAKEWORD(2, 2), &wsa_data); +} + +void Finalize() { + WSACleanup(); +} + +sockaddr TranslateFromSockAddrIn(SockAddrIn input) { + sockaddr_in result; + +#if YUZU_UNIX + result.sin_len = sizeof(result); +#endif + + switch (static_cast<Domain>(input.family)) { + case Domain::INET: + result.sin_family = AF_INET; + break; + default: + UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.family); + result.sin_family = AF_INET; + break; + } + + result.sin_port = htons(input.portno); + + auto& ip = result.sin_addr.S_un.S_un_b; + ip.s_b1 = input.ip[0]; + ip.s_b2 = input.ip[1]; + ip.s_b3 = input.ip[2]; + ip.s_b4 = input.ip[3]; + + sockaddr addr; + std::memcpy(&addr, &result, sizeof(addr)); + return addr; +} + +LINGER MakeLinger(bool enable, u32 linger_value) { + ASSERT(linger_value <= std::numeric_limits<u_short>::max()); + + LINGER value; + value.l_onoff = enable ? 1 : 0; + value.l_linger = static_cast<u_short>(linger_value); + return value; +} + +bool EnableNonBlock(SOCKET fd, bool enable) { + u_long value = enable ? 1 : 0; + return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR; +} + +Errno TranslateNativeError(int e) { + switch (e) { + case WSAEBADF: + return Errno::BADF; + case WSAEINVAL: + return Errno::INVAL; + case WSAEMFILE: + return Errno::MFILE; + case WSAENOTCONN: + return Errno::NOTCONN; + case WSAEWOULDBLOCK: + return Errno::AGAIN; + case WSAECONNREFUSED: + return Errno::CONNREFUSED; + case WSAEHOSTUNREACH: + return Errno::HOSTUNREACH; + case WSAENETDOWN: + return Errno::NETDOWN; + case WSAENETUNREACH: + return Errno::NETUNREACH; + case WSAEMSGSIZE: + return Errno::MSGSIZE; + default: + UNIMPLEMENTED_MSG("Unimplemented errno={}", e); + return Errno::OTHER; + } +} + +#elif YUZU_UNIX // ^ _WIN32 v YUZU_UNIX + +using SOCKET = int; +using WSAPOLLFD = pollfd; +using ULONG = u64; + +constexpr SOCKET SOCKET_ERROR = -1; + +constexpr int SD_RECEIVE = SHUT_RD; +constexpr int SD_SEND = SHUT_WR; +constexpr int SD_BOTH = SHUT_RDWR; + +void Initialize() {} + +void Finalize() {} + +sockaddr TranslateFromSockAddrIn(SockAddrIn input) { + sockaddr_in result; + + switch (static_cast<Domain>(input.family)) { + case Domain::INET: + result.sin_family = AF_INET; + break; + default: + UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.family); + result.sin_family = AF_INET; + break; + } + + result.sin_port = htons(input.portno); + + result.sin_addr.s_addr = input.ip[0] | input.ip[1] << 8 | input.ip[2] << 16 | input.ip[3] << 24; + + sockaddr addr; + std::memcpy(&addr, &result, sizeof(addr)); + return addr; +} + +int WSAPoll(WSAPOLLFD* fds, ULONG nfds, int timeout) { + return poll(fds, static_cast<nfds_t>(nfds), timeout); +} + +int closesocket(SOCKET fd) { + return close(fd); +} + +linger MakeLinger(bool enable, u32 linger_value) { + linger value; + value.l_onoff = enable ? 1 : 0; + value.l_linger = linger_value; + return value; +} + +bool EnableNonBlock(int fd, bool enable) { + int flags = fcntl(fd, F_GETFL); + if (flags == -1) { + return false; + } + if (enable) { + flags |= O_NONBLOCK; + } else { + flags &= ~O_NONBLOCK; + } + return fcntl(fd, F_SETFL, flags) == 0; +} + +Errno TranslateNativeError(int e) { + switch (e) { + case EBADF: + return Errno::BADF; + case EINVAL: + return Errno::INVAL; + case EMFILE: + return Errno::MFILE; + case ENOTCONN: + return Errno::NOTCONN; + case EAGAIN: + return Errno::AGAIN; + case ECONNREFUSED: + return Errno::CONNREFUSED; + case EHOSTUNREACH: + return Errno::HOSTUNREACH; + case ENETDOWN: + return Errno::NETDOWN; + case ENETUNREACH: + return Errno::NETUNREACH; + case EMSGSIZE: + return Errno::MSGSIZE; + default: + UNIMPLEMENTED_MSG("Unimplemented errno={}", e); + return Errno::OTHER; + } +} + +#endif + +Errno GetAndLogLastError() { +#ifdef _WIN32 + int e = WSAGetLastError(); +#else + int e = errno; +#endif + const Errno err = TranslateNativeError(e); + if (err == Errno::AGAIN) { + return err; + } + LOG_ERROR(Network, "Socket operation error: {}", Common::NativeErrorToString(e)); + return err; +} + +int TranslateDomain(Domain domain) { + switch (domain) { + case Domain::INET: + return AF_INET; + default: + UNIMPLEMENTED_MSG("Unimplemented domain={}", domain); + return 0; + } +} + +int TranslateType(Type type) { + switch (type) { + case Type::STREAM: + return SOCK_STREAM; + case Type::DGRAM: + return SOCK_DGRAM; + default: + UNIMPLEMENTED_MSG("Unimplemented type={}", type); + return 0; + } +} + +int TranslateProtocol(Protocol protocol) { + switch (protocol) { + case Protocol::TCP: + return IPPROTO_TCP; + case Protocol::UDP: + return IPPROTO_UDP; + default: + UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol); + return 0; + } +} + +SockAddrIn TranslateToSockAddrIn(sockaddr input_) { + sockaddr_in input; + std::memcpy(&input, &input_, sizeof(input)); + + SockAddrIn result; + + switch (input.sin_family) { + case AF_INET: + result.family = Domain::INET; + break; + default: + UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.sin_family); + result.family = Domain::INET; + break; + } + + result.portno = ntohs(input.sin_port); + + result.ip = TranslateIPv4(input.sin_addr); + + return result; +} + +short TranslatePollEvents(PollEvents events) { + short result = 0; + + if (True(events & PollEvents::In)) { + events &= ~PollEvents::In; + result |= POLLIN; + } + if (True(events & PollEvents::Pri)) { + events &= ~PollEvents::Pri; +#ifdef _WIN32 + LOG_WARNING(Service, "Winsock doesn't support POLLPRI"); +#else + result |= POLLPRI; +#endif + } + if (True(events & PollEvents::Out)) { + events &= ~PollEvents::Out; + result |= POLLOUT; + } + + UNIMPLEMENTED_IF_MSG((u16)events != 0, "Unhandled guest events=0x{:x}", (u16)events); + + return result; +} + +PollEvents TranslatePollRevents(short revents) { + PollEvents result{}; + const auto translate = [&result, &revents](short host, PollEvents guest) { + if ((revents & host) != 0) { + revents &= static_cast<short>(~host); + result |= guest; + } + }; + + translate(POLLIN, PollEvents::In); + translate(POLLPRI, PollEvents::Pri); + translate(POLLOUT, PollEvents::Out); + translate(POLLERR, PollEvents::Err); + translate(POLLHUP, PollEvents::Hup); + + UNIMPLEMENTED_IF_MSG(revents != 0, "Unhandled host revents=0x{:x}", revents); + + return result; +} + +} // Anonymous namespace + +NetworkInstance::NetworkInstance() { + Initialize(); +} + +NetworkInstance::~NetworkInstance() { + Finalize(); +} + +std::optional<IPv4Address> GetHostIPv4Address() { + const auto network_interface = Network::GetSelectedNetworkInterface(); + if (!network_interface.has_value()) { + LOG_ERROR(Network, "GetSelectedNetworkInterface returned no interface"); + return {}; + } + + std::array<char, 16> ip_addr = {}; + ASSERT(inet_ntop(AF_INET, &network_interface->ip_address, ip_addr.data(), sizeof(ip_addr)) != + nullptr); + return TranslateIPv4(network_interface->ip_address); +} + +std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { + const size_t num = pollfds.size(); + + std::vector<WSAPOLLFD> host_pollfds(pollfds.size()); + std::transform(pollfds.begin(), pollfds.end(), host_pollfds.begin(), [](PollFD fd) { + WSAPOLLFD result; + result.fd = fd.socket->GetFD(); + result.events = TranslatePollEvents(fd.events); + result.revents = 0; + return result; + }); + + const int result = WSAPoll(host_pollfds.data(), static_cast<ULONG>(num), timeout); + if (result == 0) { + ASSERT(std::all_of(host_pollfds.begin(), host_pollfds.end(), + [](WSAPOLLFD fd) { return fd.revents == 0; })); + return {0, Errno::SUCCESS}; + } + + for (size_t i = 0; i < num; ++i) { + pollfds[i].revents = TranslatePollRevents(host_pollfds[i].revents); + } + + if (result > 0) { + return {result, Errno::SUCCESS}; + } + + ASSERT(result == SOCKET_ERROR); + + return {-1, GetAndLogLastError()}; +} + +Socket::~Socket() { + if (fd == INVALID_SOCKET) { + return; + } + (void)closesocket(fd); + fd = INVALID_SOCKET; +} + +Socket::Socket(Socket&& rhs) noexcept { + fd = std::exchange(rhs.fd, INVALID_SOCKET); +} + +template <typename T> +Errno Socket::SetSockOpt(SOCKET fd_, int option, T value) { + const int result = + setsockopt(fd_, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value)); + if (result != SOCKET_ERROR) { + return Errno::SUCCESS; + } + return GetAndLogLastError(); +} + +Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { + fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol)); + if (fd != INVALID_SOCKET) { + return Errno::SUCCESS; + } + + return GetAndLogLastError(); +} + +std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() { + sockaddr addr; + socklen_t addrlen = sizeof(addr); + const SOCKET new_socket = accept(fd, &addr, &addrlen); + + if (new_socket == INVALID_SOCKET) { + return {AcceptResult{}, GetAndLogLastError()}; + } + + ASSERT(addrlen == sizeof(sockaddr_in)); + + AcceptResult result{ + .socket = std::make_unique<Socket>(new_socket), + .sockaddr_in = TranslateToSockAddrIn(addr), + }; + + return {std::move(result), Errno::SUCCESS}; +} + +Errno Socket::Connect(SockAddrIn addr_in) { + const sockaddr host_addr_in = TranslateFromSockAddrIn(addr_in); + if (connect(fd, &host_addr_in, sizeof(host_addr_in)) != SOCKET_ERROR) { + return Errno::SUCCESS; + } + + return GetAndLogLastError(); +} + +std::pair<SockAddrIn, Errno> Socket::GetPeerName() { + sockaddr addr; + socklen_t addrlen = sizeof(addr); + if (getpeername(fd, &addr, &addrlen) == SOCKET_ERROR) { + return {SockAddrIn{}, GetAndLogLastError()}; + } + + ASSERT(addrlen == sizeof(sockaddr_in)); + return {TranslateToSockAddrIn(addr), Errno::SUCCESS}; +} + +std::pair<SockAddrIn, Errno> Socket::GetSockName() { + sockaddr addr; + socklen_t addrlen = sizeof(addr); + if (getsockname(fd, &addr, &addrlen) == SOCKET_ERROR) { + return {SockAddrIn{}, GetAndLogLastError()}; + } + + ASSERT(addrlen == sizeof(sockaddr_in)); + return {TranslateToSockAddrIn(addr), Errno::SUCCESS}; +} + +Errno Socket::Bind(SockAddrIn addr) { + const sockaddr addr_in = TranslateFromSockAddrIn(addr); + if (bind(fd, &addr_in, sizeof(addr_in)) != SOCKET_ERROR) { + return Errno::SUCCESS; + } + + return GetAndLogLastError(); +} + +Errno Socket::Listen(s32 backlog) { + if (listen(fd, backlog) != SOCKET_ERROR) { + return Errno::SUCCESS; + } + + return GetAndLogLastError(); +} + +Errno Socket::Shutdown(ShutdownHow how) { + int host_how = 0; + switch (how) { + case ShutdownHow::RD: + host_how = SD_RECEIVE; + break; + case ShutdownHow::WR: + host_how = SD_SEND; + break; + case ShutdownHow::RDWR: + host_how = SD_BOTH; + break; + default: + UNIMPLEMENTED_MSG("Unimplemented flag how={}", how); + return Errno::SUCCESS; + } + if (shutdown(fd, host_how) != SOCKET_ERROR) { + return Errno::SUCCESS; + } + + return GetAndLogLastError(); +} + +std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) { + ASSERT(flags == 0); + ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); + + const auto result = + recv(fd, reinterpret_cast<char*>(message.data()), static_cast<int>(message.size()), 0); + if (result != SOCKET_ERROR) { + return {static_cast<s32>(result), Errno::SUCCESS}; + } + + return {-1, GetAndLogLastError()}; +} + +std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) { + ASSERT(flags == 0); + ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); + + sockaddr addr_in{}; + socklen_t addrlen = sizeof(addr_in); + socklen_t* const p_addrlen = addr ? &addrlen : nullptr; + sockaddr* const p_addr_in = addr ? &addr_in : nullptr; + + const auto result = recvfrom(fd, reinterpret_cast<char*>(message.data()), + static_cast<int>(message.size()), 0, p_addr_in, p_addrlen); + if (result != SOCKET_ERROR) { + if (addr) { + ASSERT(addrlen == sizeof(addr_in)); + *addr = TranslateToSockAddrIn(addr_in); + } + return {static_cast<s32>(result), Errno::SUCCESS}; + } + + return {-1, GetAndLogLastError()}; +} + +std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) { + ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); + ASSERT(flags == 0); + + const auto result = send(fd, reinterpret_cast<const char*>(message.data()), + static_cast<int>(message.size()), 0); + if (result != SOCKET_ERROR) { + return {static_cast<s32>(result), Errno::SUCCESS}; + } + + return {-1, GetAndLogLastError()}; +} + +std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message, + const SockAddrIn* addr) { + ASSERT(flags == 0); + + const sockaddr* to = nullptr; + const int tolen = addr ? sizeof(sockaddr) : 0; + sockaddr host_addr_in; + + if (addr) { + host_addr_in = TranslateFromSockAddrIn(*addr); + to = &host_addr_in; + } + + const auto result = sendto(fd, reinterpret_cast<const char*>(message.data()), + static_cast<int>(message.size()), 0, to, tolen); + if (result != SOCKET_ERROR) { + return {static_cast<s32>(result), Errno::SUCCESS}; + } + + return {-1, GetAndLogLastError()}; +} + +Errno Socket::Close() { + [[maybe_unused]] const int result = closesocket(fd); + ASSERT(result == 0); + fd = INVALID_SOCKET; + + return Errno::SUCCESS; +} + +Errno Socket::SetLinger(bool enable, u32 linger) { + return SetSockOpt(fd, SO_LINGER, MakeLinger(enable, linger)); +} + +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); +} + +Errno Socket::SetSndBuf(u32 value) { + return SetSockOpt(fd, SO_SNDBUF, value); +} + +Errno Socket::SetRcvBuf(u32 value) { + return SetSockOpt(fd, SO_RCVBUF, value); +} + +Errno Socket::SetSndTimeo(u32 value) { + return SetSockOpt(fd, SO_SNDTIMEO, value); +} + +Errno Socket::SetRcvTimeo(u32 value) { + return SetSockOpt(fd, SO_RCVTIMEO, value); +} + +Errno Socket::SetNonBlock(bool enable) { + if (EnableNonBlock(fd, enable)) { + return Errno::SUCCESS; + } + return GetAndLogLastError(); +} + +bool Socket::IsOpened() const { + return fd != INVALID_SOCKET; +} + +void Socket::HandleProxyPacket(const ProxyPacket& packet) { + LOG_WARNING(Network, "ProxyPacket received, but not in Proxy mode!"); +} + +} // namespace Network diff --git a/src/core/internal_network/network.h b/src/core/internal_network/network.h new file mode 100644 index 000000000..36994c22e --- /dev/null +++ b/src/core/internal_network/network.h @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <optional> + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/socket_types.h" + +#ifdef _WIN32 +#include <winsock2.h> +#elif YUZU_UNIX +#include <netinet/in.h> +#endif + +namespace Network { + +class SocketBase; +class Socket; + +/// Error code for network functions +enum class Errno { + SUCCESS, + BADF, + INVAL, + MFILE, + NOTCONN, + AGAIN, + CONNREFUSED, + HOSTUNREACH, + NETDOWN, + NETUNREACH, + TIMEDOUT, + MSGSIZE, + OTHER, +}; + +/// Cross-platform poll fd structure + +enum class PollEvents : u16 { + // Using Pascal case because IN is a macro on Windows. + In = 1 << 0, + Pri = 1 << 1, + Out = 1 << 2, + Err = 1 << 3, + Hup = 1 << 4, + Nval = 1 << 5, +}; + +DECLARE_ENUM_FLAG_OPERATORS(PollEvents); + +struct PollFD { + SocketBase* socket; + PollEvents events; + PollEvents revents; +}; + +class NetworkInstance { +public: + explicit NetworkInstance(); + ~NetworkInstance(); +}; + +#ifdef _WIN32 +constexpr IPv4Address TranslateIPv4(in_addr addr) { + auto& bytes = addr.S_un.S_un_b; + return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4}; +} +#elif YUZU_UNIX +constexpr IPv4Address TranslateIPv4(in_addr addr) { + const u32 bytes = addr.s_addr; + return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8), + static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)}; +} +#endif + +/// @brief Returns host's IPv4 address +/// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array +std::optional<IPv4Address> GetHostIPv4Address(); + +} // namespace Network diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp new file mode 100644 index 000000000..057fd3661 --- /dev/null +++ b/src/core/internal_network/network_interface.cpp @@ -0,0 +1,219 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <algorithm> +#include <fstream> +#include <sstream> +#include <vector> + +#include "common/bit_cast.h" +#include "common/common_types.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "common/string_util.h" +#include "core/internal_network/network_interface.h" + +#ifdef _WIN32 +#include <iphlpapi.h> +#else +#include <cerrno> +#include <ifaddrs.h> +#include <net/if.h> +#endif + +namespace Network { + +#ifdef _WIN32 + +std::vector<NetworkInterface> GetAvailableNetworkInterfaces() { + std::vector<IP_ADAPTER_ADDRESSES> adapter_addresses; + DWORD ret = ERROR_BUFFER_OVERFLOW; + DWORD buf_size = 0; + + // retry up to 5 times + for (int i = 0; i < 5 && ret == ERROR_BUFFER_OVERFLOW; i++) { + ret = GetAdaptersAddresses( + AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_GATEWAYS, + nullptr, adapter_addresses.data(), &buf_size); + + if (ret != ERROR_BUFFER_OVERFLOW) { + break; + } + + adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1); + } + + if (ret != NO_ERROR) { + LOG_ERROR(Network, "Failed to get network interfaces with GetAdaptersAddresses"); + return {}; + } + + std::vector<NetworkInterface> result; + + for (auto current_address = adapter_addresses.data(); current_address != nullptr; + current_address = current_address->Next) { + if (current_address->FirstUnicastAddress == nullptr || + current_address->FirstUnicastAddress->Address.lpSockaddr == nullptr) { + continue; + } + + if (current_address->OperStatus != IfOperStatusUp) { + continue; + } + + const auto ip_addr = Common::BitCast<struct sockaddr_in>( + *current_address->FirstUnicastAddress->Address.lpSockaddr) + .sin_addr; + + ULONG mask = 0; + if (ConvertLengthToIpv4Mask(current_address->FirstUnicastAddress->OnLinkPrefixLength, + &mask) != NO_ERROR) { + LOG_ERROR(Network, "Failed to convert IPv4 prefix length to subnet mask"); + continue; + } + + struct in_addr gateway = {.S_un{.S_addr{0}}}; + if (current_address->FirstGatewayAddress != nullptr && + current_address->FirstGatewayAddress->Address.lpSockaddr != nullptr) { + gateway = Common::BitCast<struct sockaddr_in>( + *current_address->FirstGatewayAddress->Address.lpSockaddr) + .sin_addr; + } + + result.emplace_back(NetworkInterface{ + .name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})}, + .ip_address{ip_addr}, + .subnet_mask = in_addr{.S_un{.S_addr{mask}}}, + .gateway = gateway}); + } + + return result; +} + +#else + +std::vector<NetworkInterface> GetAvailableNetworkInterfaces() { + struct ifaddrs* ifaddr = nullptr; + + if (getifaddrs(&ifaddr) != 0) { + LOG_ERROR(Network, "Failed to get network interfaces with getifaddrs: {}", + std::strerror(errno)); + return {}; + } + + std::vector<NetworkInterface> result; + + for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr) { + continue; + } + + if (ifa->ifa_addr->sa_family != AF_INET) { + continue; + } + + if ((ifa->ifa_flags & IFF_UP) == 0 || (ifa->ifa_flags & IFF_LOOPBACK) != 0) { + continue; + } + + u32 gateway{}; + + std::ifstream file{"/proc/net/route"}; + if (!file.is_open()) { + LOG_ERROR(Network, "Failed to open \"/proc/net/route\""); + + result.emplace_back(NetworkInterface{ + .name{ifa->ifa_name}, + .ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr}, + .subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr}, + .gateway{in_addr{.s_addr = gateway}}}); + continue; + } + + // ignore header + file.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); + + bool gateway_found = false; + + for (std::string line; std::getline(file, line);) { + std::istringstream iss{line}; + + std::string iface_name; + iss >> iface_name; + if (iface_name != ifa->ifa_name) { + continue; + } + + iss >> std::hex; + + u32 dest{}; + iss >> dest; + if (dest != 0) { + // not the default route + continue; + } + + iss >> gateway; + + u16 flags{}; + iss >> flags; + + // flag RTF_GATEWAY (defined in <linux/route.h>) + if ((flags & 0x2) == 0) { + continue; + } + + gateway_found = true; + break; + } + + if (!gateway_found) { + gateway = 0; + } + + result.emplace_back(NetworkInterface{ + .name{ifa->ifa_name}, + .ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr}, + .subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr}, + .gateway{in_addr{.s_addr = gateway}}}); + } + + freeifaddrs(ifaddr); + + return result; +} + +#endif + +std::optional<NetworkInterface> GetSelectedNetworkInterface() { + const auto& selected_network_interface = Settings::values.network_interface.GetValue(); + const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); + if (network_interfaces.empty()) { + LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); + return std::nullopt; + } + + const auto res = + std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) { + return iface.name == selected_network_interface; + }); + + if (res == network_interfaces.end()) { + LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); + return std::nullopt; + } + + return *res; +} + +void SelectFirstNetworkInterface() { + const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); + + if (network_interfaces.empty()) { + return; + } + + Settings::values.network_interface.SetValue(network_interfaces[0].name); +} + +} // namespace Network diff --git a/src/core/internal_network/network_interface.h b/src/core/internal_network/network_interface.h new file mode 100644 index 000000000..175e61b1f --- /dev/null +++ b/src/core/internal_network/network_interface.h @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <optional> +#include <string> +#include <vector> + +#ifdef _WIN32 +#include <winsock2.h> +#else +#include <netinet/in.h> +#endif + +namespace Network { + +struct NetworkInterface { + std::string name; + struct in_addr ip_address; + struct in_addr subnet_mask; + struct in_addr gateway; +}; + +std::vector<NetworkInterface> GetAvailableNetworkInterfaces(); +std::optional<NetworkInterface> GetSelectedNetworkInterface(); +void SelectFirstNetworkInterface(); + +} // namespace Network diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp new file mode 100644 index 000000000..7d5d37bbc --- /dev/null +++ b/src/core/internal_network/socket_proxy.cpp @@ -0,0 +1,296 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <chrono> +#include <thread> + +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/zstd_compression.h" +#include "core/internal_network/network.h" +#include "core/internal_network/network_interface.h" +#include "core/internal_network/socket_proxy.h" + +namespace Network { + +ProxySocket::ProxySocket(RoomNetwork& room_network_) noexcept : room_network{room_network_} {} + +ProxySocket::~ProxySocket() { + if (fd == INVALID_SOCKET) { + return; + } + fd = INVALID_SOCKET; +} + +void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) { + if (protocol != packet.protocol || local_endpoint.portno != packet.remote_endpoint.portno || + closed) { + return; + } + + if (!broadcast && packet.broadcast) { + LOG_INFO(Network, "Received broadcast packet, but not configured for broadcast mode"); + return; + } + + auto decompressed = packet; + decompressed.data = Common::Compression::DecompressDataZSTD(packet.data); + + std::lock_guard guard(packets_mutex); + received_packets.push(decompressed); +} + +template <typename T> +Errno ProxySocket::SetSockOpt(SOCKET fd_, int option, T value) { + LOG_DEBUG(Network, "(STUBBED) called"); + return Errno::SUCCESS; +} + +Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) { + protocol = socket_protocol; + SetSockOpt(fd, SO_TYPE, type); + + return Errno::SUCCESS; +} + +std::pair<ProxySocket::AcceptResult, Errno> ProxySocket::Accept() { + LOG_WARNING(Network, "(STUBBED) called"); + return {AcceptResult{}, Errno::SUCCESS}; +} + +Errno ProxySocket::Connect(SockAddrIn addr_in) { + LOG_WARNING(Network, "(STUBBED) called"); + return Errno::SUCCESS; +} + +std::pair<SockAddrIn, Errno> ProxySocket::GetPeerName() { + LOG_WARNING(Network, "(STUBBED) called"); + return {SockAddrIn{}, Errno::SUCCESS}; +} + +std::pair<SockAddrIn, Errno> ProxySocket::GetSockName() { + LOG_WARNING(Network, "(STUBBED) called"); + return {SockAddrIn{}, Errno::SUCCESS}; +} + +Errno ProxySocket::Bind(SockAddrIn addr) { + if (is_bound) { + LOG_WARNING(Network, "Rebinding Socket is unimplemented!"); + return Errno::SUCCESS; + } + local_endpoint = addr; + is_bound = true; + + return Errno::SUCCESS; +} + +Errno ProxySocket::Listen(s32 backlog) { + LOG_WARNING(Network, "(STUBBED) called"); + return Errno::SUCCESS; +} + +Errno ProxySocket::Shutdown(ShutdownHow how) { + LOG_WARNING(Network, "(STUBBED) called"); + return Errno::SUCCESS; +} + +std::pair<s32, Errno> ProxySocket::Recv(int flags, std::vector<u8>& message) { + LOG_WARNING(Network, "(STUBBED) called"); + ASSERT(flags == 0); + ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); + + return {static_cast<s32>(0), Errno::SUCCESS}; +} + +std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) { + ASSERT(flags == 0); + ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); + + // TODO (flTobi): Verify the timeout behavior and break when connection is lost + const auto timestamp = std::chrono::steady_clock::now(); + // When receive_timeout is set to zero, the socket is supposed to wait indefinitely until a + // packet arrives. In order to prevent lost packets from hanging the emulation thread, we set + // the timeout to 5s instead + const auto timeout = receive_timeout == 0 ? 5000 : receive_timeout; + while (true) { + { + std::lock_guard guard(packets_mutex); + if (received_packets.size() > 0) { + return ReceivePacket(flags, message, addr, message.size()); + } + } + + if (!blocking) { + return {-1, Errno::AGAIN}; + } + + std::this_thread::yield(); + + const auto time_diff = std::chrono::steady_clock::now() - timestamp; + const auto time_diff_ms = + std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count(); + + if (time_diff_ms > timeout) { + return {-1, Errno::TIMEDOUT}; + } + } +} + +std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::vector<u8>& message, + SockAddrIn* addr, std::size_t max_length) { + ProxyPacket& packet = received_packets.front(); + if (addr) { + addr->family = Domain::INET; + addr->ip = packet.local_endpoint.ip; // The senders ip address + addr->portno = packet.local_endpoint.portno; // The senders port number + } + + bool peek = (flags & FLAG_MSG_PEEK) != 0; + std::size_t read_bytes; + if (packet.data.size() > max_length) { + read_bytes = max_length; + message.clear(); + std::copy(packet.data.begin(), packet.data.begin() + read_bytes, + std::back_inserter(message)); + message.resize(max_length); + + if (protocol == Protocol::UDP) { + if (!peek) { + received_packets.pop(); + } + return {-1, Errno::MSGSIZE}; + } else if (protocol == Protocol::TCP) { + std::vector<u8> numArray(packet.data.size() - max_length); + std::copy(packet.data.begin() + max_length, packet.data.end(), + std::back_inserter(numArray)); + packet.data = numArray; + } + } else { + read_bytes = packet.data.size(); + message.clear(); + std::copy(packet.data.begin(), packet.data.end(), std::back_inserter(message)); + message.resize(max_length); + if (!peek) { + received_packets.pop(); + } + } + + return {static_cast<u32>(read_bytes), Errno::SUCCESS}; +} + +std::pair<s32, Errno> ProxySocket::Send(const std::vector<u8>& message, int flags) { + LOG_WARNING(Network, "(STUBBED) called"); + ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); + ASSERT(flags == 0); + + return {static_cast<s32>(0), Errno::SUCCESS}; +} + +void ProxySocket::SendPacket(ProxyPacket& packet) { + if (auto room_member = room_network.GetRoomMember().lock()) { + if (room_member->IsConnected()) { + packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(), + packet.data.size()); + room_member->SendProxyPacket(packet); + } + } +} + +std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, const std::vector<u8>& message, + const SockAddrIn* addr) { + ASSERT(flags == 0); + + if (!is_bound) { + LOG_ERROR(Network, "ProxySocket is not bound!"); + return {static_cast<s32>(message.size()), Errno::SUCCESS}; + } + + if (auto room_member = room_network.GetRoomMember().lock()) { + if (!room_member->IsConnected()) { + return {static_cast<s32>(message.size()), Errno::SUCCESS}; + } + } + + ProxyPacket packet; + packet.local_endpoint = local_endpoint; + packet.remote_endpoint = *addr; + packet.protocol = protocol; + packet.broadcast = broadcast && packet.remote_endpoint.ip[3] == 255; + + auto& ip = local_endpoint.ip; + auto ipv4 = Network::GetHostIPv4Address(); + // If the ip is all zeroes (INADDR_ANY) or if it matches the hosts ip address, + // replace it with a "fake" routing address + if (std::all_of(ip.begin(), ip.end(), [](u8 i) { return i == 0; }) || (ipv4 && ipv4 == ip)) { + if (auto room_member = room_network.GetRoomMember().lock()) { + packet.local_endpoint.ip = room_member->GetFakeIpAddress(); + } + } + + packet.data.clear(); + std::copy(message.begin(), message.end(), std::back_inserter(packet.data)); + + SendPacket(packet); + + return {static_cast<s32>(message.size()), Errno::SUCCESS}; +} + +Errno ProxySocket::Close() { + fd = INVALID_SOCKET; + closed = true; + + return Errno::SUCCESS; +} + +Errno ProxySocket::SetLinger(bool enable, u32 linger) { + struct Linger { + u16 linger_enable; + u16 linger_time; + } values; + values.linger_enable = enable ? 1 : 0; + values.linger_time = static_cast<u16>(linger); + + return SetSockOpt(fd, SO_LINGER, values); +} + +Errno ProxySocket::SetReuseAddr(bool enable) { + return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0); +} + +Errno ProxySocket::SetBroadcast(bool enable) { + broadcast = enable; + return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0); +} + +Errno ProxySocket::SetSndBuf(u32 value) { + return SetSockOpt(fd, SO_SNDBUF, value); +} + +Errno ProxySocket::SetKeepAlive(bool enable) { + return Errno::SUCCESS; +} + +Errno ProxySocket::SetRcvBuf(u32 value) { + return SetSockOpt(fd, SO_RCVBUF, value); +} + +Errno ProxySocket::SetSndTimeo(u32 value) { + send_timeout = value; + return SetSockOpt(fd, SO_SNDTIMEO, static_cast<int>(value)); +} + +Errno ProxySocket::SetRcvTimeo(u32 value) { + receive_timeout = value; + return SetSockOpt(fd, SO_RCVTIMEO, static_cast<int>(value)); +} + +Errno ProxySocket::SetNonBlock(bool enable) { + blocking = !enable; + return Errno::SUCCESS; +} + +bool ProxySocket::IsOpened() const { + return fd != INVALID_SOCKET; +} + +} // namespace Network diff --git a/src/core/internal_network/socket_proxy.h b/src/core/internal_network/socket_proxy.h new file mode 100644 index 000000000..f12b5f567 --- /dev/null +++ b/src/core/internal_network/socket_proxy.h @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <mutex> +#include <vector> +#include <queue> + +#include "common/common_funcs.h" +#include "core/internal_network/sockets.h" +#include "network/network.h" + +namespace Network { + +class ProxySocket : public SocketBase { +public: + YUZU_NON_COPYABLE(ProxySocket); + YUZU_NON_MOVEABLE(ProxySocket); + + explicit ProxySocket(RoomNetwork& room_network_) noexcept; + ~ProxySocket() override; + + void HandleProxyPacket(const ProxyPacket& packet) override; + + Errno Initialize(Domain domain, Type type, Protocol socket_protocol) override; + + Errno Close() override; + + std::pair<AcceptResult, Errno> Accept() override; + + Errno Connect(SockAddrIn addr_in) override; + + std::pair<SockAddrIn, Errno> GetPeerName() override; + + std::pair<SockAddrIn, Errno> GetSockName() override; + + Errno Bind(SockAddrIn addr) override; + + Errno Listen(s32 backlog) override; + + Errno Shutdown(ShutdownHow how) override; + + std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override; + + std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override; + + std::pair<s32, Errno> ReceivePacket(int flags, std::vector<u8>& message, SockAddrIn* addr, + std::size_t max_length); + + std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override; + + void SendPacket(ProxyPacket& packet); + + std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, + const SockAddrIn* addr) override; + + Errno SetLinger(bool enable, u32 linger) override; + + Errno SetReuseAddr(bool enable) override; + + Errno SetBroadcast(bool enable) override; + + Errno SetKeepAlive(bool enable) override; + + Errno SetSndBuf(u32 value) override; + + Errno SetRcvBuf(u32 value) override; + + Errno SetSndTimeo(u32 value) override; + + Errno SetRcvTimeo(u32 value) override; + + Errno SetNonBlock(bool enable) override; + + template <typename T> + Errno SetSockOpt(SOCKET fd, int option, T value); + + bool IsOpened() const override; + +private: + bool broadcast = false; + bool closed = false; + u32 send_timeout = 0; + u32 receive_timeout = 0; + bool is_bound = false; + SockAddrIn local_endpoint{}; + bool blocking = true; + std::queue<ProxyPacket> received_packets; + Protocol protocol; + + std::mutex packets_mutex; + + RoomNetwork& room_network; +}; + +} // namespace Network diff --git a/src/core/internal_network/sockets.h b/src/core/internal_network/sockets.h new file mode 100644 index 000000000..2e328c645 --- /dev/null +++ b/src/core/internal_network/sockets.h @@ -0,0 +1,174 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <map> +#include <memory> +#include <utility> + +#if defined(_WIN32) +#elif !YUZU_UNIX +#error "Platform not implemented" +#endif + +#include "common/common_types.h" +#include "core/internal_network/network.h" +#include "network/network.h" + +// TODO: C++20 Replace std::vector usages with std::span + +namespace Network { + +class SocketBase { +public: +#ifdef YUZU_UNIX + using SOCKET = int; + static constexpr SOCKET INVALID_SOCKET = -1; + static constexpr SOCKET SOCKET_ERROR = -1; +#endif + + struct AcceptResult { + std::unique_ptr<SocketBase> socket; + SockAddrIn sockaddr_in; + }; + + SocketBase() = default; + explicit SocketBase(SOCKET fd_) : fd{fd_} {} + + virtual ~SocketBase() = default; + + virtual SocketBase& operator=(const SocketBase&) = delete; + + // Avoid closing sockets implicitly + virtual SocketBase& operator=(SocketBase&&) noexcept = delete; + + virtual Errno Initialize(Domain domain, Type type, Protocol protocol) = 0; + + virtual Errno Close() = 0; + + virtual std::pair<AcceptResult, Errno> Accept() = 0; + + virtual Errno Connect(SockAddrIn addr_in) = 0; + + virtual std::pair<SockAddrIn, Errno> GetPeerName() = 0; + + virtual std::pair<SockAddrIn, Errno> GetSockName() = 0; + + virtual Errno Bind(SockAddrIn addr) = 0; + + virtual Errno Listen(s32 backlog) = 0; + + virtual Errno Shutdown(ShutdownHow how) = 0; + + virtual std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) = 0; + + virtual std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, + SockAddrIn* addr) = 0; + + virtual std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) = 0; + + virtual std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, + const SockAddrIn* addr) = 0; + + virtual Errno SetLinger(bool enable, u32 linger) = 0; + + virtual Errno SetReuseAddr(bool enable) = 0; + + virtual Errno SetKeepAlive(bool enable) = 0; + + virtual Errno SetBroadcast(bool enable) = 0; + + virtual Errno SetSndBuf(u32 value) = 0; + + virtual Errno SetRcvBuf(u32 value) = 0; + + virtual Errno SetSndTimeo(u32 value) = 0; + + virtual Errno SetRcvTimeo(u32 value) = 0; + + virtual Errno SetNonBlock(bool enable) = 0; + + virtual bool IsOpened() const = 0; + + virtual void HandleProxyPacket(const ProxyPacket& packet) = 0; + + [[nodiscard]] SOCKET GetFD() const { + return fd; + } + +protected: + SOCKET fd = INVALID_SOCKET; +}; + +class Socket : public SocketBase { +public: + Socket() = default; + explicit Socket(SOCKET fd_) : SocketBase{fd_} {} + + ~Socket() override; + + Socket(const Socket&) = delete; + Socket& operator=(const Socket&) = delete; + + Socket(Socket&& rhs) noexcept; + + // Avoid closing sockets implicitly + Socket& operator=(Socket&&) noexcept = delete; + + Errno Initialize(Domain domain, Type type, Protocol protocol) override; + + Errno Close() override; + + std::pair<AcceptResult, Errno> Accept() override; + + Errno Connect(SockAddrIn addr_in) override; + + std::pair<SockAddrIn, Errno> GetPeerName() override; + + std::pair<SockAddrIn, Errno> GetSockName() override; + + Errno Bind(SockAddrIn addr) override; + + Errno Listen(s32 backlog) override; + + Errno Shutdown(ShutdownHow how) override; + + std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override; + + std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override; + + std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override; + + std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, + const SockAddrIn* addr) override; + + Errno SetLinger(bool enable, u32 linger) override; + + Errno SetReuseAddr(bool enable) override; + + Errno SetKeepAlive(bool enable) override; + + Errno SetBroadcast(bool enable) override; + + Errno SetSndBuf(u32 value) override; + + Errno SetRcvBuf(u32 value) override; + + Errno SetSndTimeo(u32 value) override; + + Errno SetRcvTimeo(u32 value) override; + + Errno SetNonBlock(bool enable) override; + + template <typename T> + Errno SetSockOpt(SOCKET fd, int option, T value); + + bool IsOpened() const override; + + void HandleProxyPacket(const ProxyPacket& packet) override; +}; + +std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout); + +} // namespace Network diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index b47e3bf69..192571d35 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include "common/logging/log.h" diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index 79a4d4db5..f7702225e 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp deleted file mode 100644 index d0250bdb4..000000000 --- a/src/core/loader/elf.cpp +++ /dev/null @@ -1,414 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <cstring> -#include <memory> -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "common/logging/log.h" -#include "core/hle/kernel/code_set.h" -#include "core/hle/kernel/k_page_table.h" -#include "core/hle/kernel/k_process.h" -#include "core/loader/elf.h" -#include "core/memory.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// ELF Header Constants - -// File type -enum ElfType { - ET_NONE = 0, - ET_REL = 1, - ET_EXEC = 2, - ET_DYN = 3, - ET_CORE = 4, - ET_LOPROC = 0xFF00, - ET_HIPROC = 0xFFFF, -}; - -// Machine/Architecture -enum ElfMachine { - EM_NONE = 0, - EM_M32 = 1, - EM_SPARC = 2, - EM_386 = 3, - EM_68K = 4, - EM_88K = 5, - EM_860 = 7, - EM_MIPS = 8 -}; - -// File version -#define EV_NONE 0 -#define EV_CURRENT 1 - -// Identification index -#define EI_MAG0 0 -#define EI_MAG1 1 -#define EI_MAG2 2 -#define EI_MAG3 3 -#define EI_CLASS 4 -#define EI_DATA 5 -#define EI_VERSION 6 -#define EI_PAD 7 -#define EI_NIDENT 16 - -// Sections constants - -// Section types -#define SHT_NULL 0 -#define SHT_PROGBITS 1 -#define SHT_SYMTAB 2 -#define SHT_STRTAB 3 -#define SHT_RELA 4 -#define SHT_HASH 5 -#define SHT_DYNAMIC 6 -#define SHT_NOTE 7 -#define SHT_NOBITS 8 -#define SHT_REL 9 -#define SHT_SHLIB 10 -#define SHT_DYNSYM 11 -#define SHT_LOPROC 0x70000000 -#define SHT_HIPROC 0x7FFFFFFF -#define SHT_LOUSER 0x80000000 -#define SHT_HIUSER 0xFFFFFFFF - -// Section flags -enum ElfSectionFlags { - SHF_WRITE = 0x1, - SHF_ALLOC = 0x2, - SHF_EXECINSTR = 0x4, - SHF_MASKPROC = 0xF0000000, -}; - -// Segment types -#define PT_NULL 0 -#define PT_LOAD 1 -#define PT_DYNAMIC 2 -#define PT_INTERP 3 -#define PT_NOTE 4 -#define PT_SHLIB 5 -#define PT_PHDR 6 -#define PT_LOPROC 0x70000000 -#define PT_HIPROC 0x7FFFFFFF - -// Segment flags -#define PF_X 0x1 -#define PF_W 0x2 -#define PF_R 0x4 -#define PF_MASKPROC 0xF0000000 - -typedef unsigned int Elf32_Addr; -typedef unsigned short Elf32_Half; -typedef unsigned int Elf32_Off; -typedef signed int Elf32_Sword; -typedef unsigned int Elf32_Word; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// ELF file header - -struct Elf32_Ehdr { - unsigned char e_ident[EI_NIDENT]; - Elf32_Half e_type; - Elf32_Half e_machine; - Elf32_Word e_version; - Elf32_Addr e_entry; - Elf32_Off e_phoff; - Elf32_Off e_shoff; - Elf32_Word e_flags; - Elf32_Half e_ehsize; - Elf32_Half e_phentsize; - Elf32_Half e_phnum; - Elf32_Half e_shentsize; - Elf32_Half e_shnum; - Elf32_Half e_shstrndx; -}; - -// Section header -struct Elf32_Shdr { - Elf32_Word sh_name; - Elf32_Word sh_type; - Elf32_Word sh_flags; - Elf32_Addr sh_addr; - Elf32_Off sh_offset; - Elf32_Word sh_size; - Elf32_Word sh_link; - Elf32_Word sh_info; - Elf32_Word sh_addralign; - Elf32_Word sh_entsize; -}; - -// Segment header -struct Elf32_Phdr { - Elf32_Word p_type; - Elf32_Off p_offset; - Elf32_Addr p_vaddr; - Elf32_Addr p_paddr; - Elf32_Word p_filesz; - Elf32_Word p_memsz; - Elf32_Word p_flags; - Elf32_Word p_align; -}; - -// Symbol table entry -struct Elf32_Sym { - Elf32_Word st_name; - Elf32_Addr st_value; - Elf32_Word st_size; - unsigned char st_info; - unsigned char st_other; - Elf32_Half st_shndx; -}; - -// Relocation entries -struct Elf32_Rel { - Elf32_Addr r_offset; - Elf32_Word r_info; -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// ElfReader class - -typedef int SectionID; - -class ElfReader { -private: - char* base; - u32* base32; - - Elf32_Ehdr* header; - Elf32_Phdr* segments; - Elf32_Shdr* sections; - - u32* sectionAddrs; - bool relocate; - VAddr entryPoint; - -public: - explicit ElfReader(void* ptr); - - u32 Read32(int off) const { - return base32[off >> 2]; - } - - // Quick accessors - ElfType GetType() const { - return (ElfType)(header->e_type); - } - ElfMachine GetMachine() const { - return (ElfMachine)(header->e_machine); - } - VAddr GetEntryPoint() const { - return entryPoint; - } - u32 GetFlags() const { - return (u32)(header->e_flags); - } - Kernel::CodeSet LoadInto(VAddr vaddr); - - int GetNumSegments() const { - return (int)(header->e_phnum); - } - int GetNumSections() const { - return (int)(header->e_shnum); - } - const u8* GetPtr(int offset) const { - return (u8*)base + offset; - } - const char* GetSectionName(int section) const; - const u8* GetSectionDataPtr(int section) const { - if (section < 0 || section >= header->e_shnum) - return nullptr; - if (sections[section].sh_type != SHT_NOBITS) - return GetPtr(sections[section].sh_offset); - else - return nullptr; - } - bool IsCodeSection(int section) const { - return sections[section].sh_type == SHT_PROGBITS; - } - const u8* GetSegmentPtr(int segment) { - return GetPtr(segments[segment].p_offset); - } - u32 GetSectionAddr(SectionID section) const { - return sectionAddrs[section]; - } - unsigned int GetSectionSize(SectionID section) const { - return sections[section].sh_size; - } - SectionID GetSectionByName(const char* name, int firstSection = 0) const; //-1 for not found - - bool DidRelocate() const { - return relocate; - } -}; - -ElfReader::ElfReader(void* ptr) { - base = (char*)ptr; - base32 = (u32*)ptr; - header = (Elf32_Ehdr*)ptr; - - segments = (Elf32_Phdr*)(base + header->e_phoff); - sections = (Elf32_Shdr*)(base + header->e_shoff); - - entryPoint = header->e_entry; -} - -const char* ElfReader::GetSectionName(int section) const { - if (sections[section].sh_type == SHT_NULL) - return nullptr; - - int name_offset = sections[section].sh_name; - const char* ptr = reinterpret_cast<const char*>(GetSectionDataPtr(header->e_shstrndx)); - - if (ptr) - return ptr + name_offset; - - return nullptr; -} - -Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) { - LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx); - - // Should we relocate? - relocate = (header->e_type != ET_EXEC); - - if (relocate) { - LOG_DEBUG(Loader, "Relocatable module"); - entryPoint += vaddr; - } else { - LOG_DEBUG(Loader, "Prerelocated executable"); - } - LOG_DEBUG(Loader, "{} segments:", header->e_phnum); - - // First pass : Get the bits into RAM - const VAddr base_addr = relocate ? vaddr : 0; - - u64 total_image_size = 0; - for (unsigned int i = 0; i < header->e_phnum; ++i) { - const Elf32_Phdr* p = &segments[i]; - if (p->p_type == PT_LOAD) { - total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF; - } - } - - Kernel::PhysicalMemory program_image(total_image_size); - std::size_t current_image_position = 0; - - Kernel::CodeSet codeset; - - for (unsigned int i = 0; i < header->e_phnum; ++i) { - const Elf32_Phdr* p = &segments[i]; - LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type, - p->p_vaddr, p->p_filesz, p->p_memsz); - - if (p->p_type == PT_LOAD) { - Kernel::CodeSet::Segment* codeset_segment; - u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X); - if (permission_flags == (PF_R | PF_X)) { - codeset_segment = &codeset.CodeSegment(); - } else if (permission_flags == (PF_R)) { - codeset_segment = &codeset.RODataSegment(); - } else if (permission_flags == (PF_R | PF_W)) { - codeset_segment = &codeset.DataSegment(); - } else { - LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i, - p->p_flags); - continue; - } - - if (codeset_segment->size != 0) { - LOG_ERROR(Loader, - "ELF has more than one segment of the same type. Skipping extra " - "segment (id {})", - i); - continue; - } - - const VAddr segment_addr = base_addr + p->p_vaddr; - const u32 aligned_size = (p->p_memsz + 0xFFF) & ~0xFFF; - - codeset_segment->offset = current_image_position; - codeset_segment->addr = segment_addr; - codeset_segment->size = aligned_size; - - std::memcpy(program_image.data() + current_image_position, GetSegmentPtr(i), - p->p_filesz); - current_image_position += aligned_size; - } - } - - codeset.entrypoint = base_addr + header->e_entry; - codeset.memory = std::move(program_image); - - LOG_DEBUG(Loader, "Done loading."); - - return codeset; -} - -SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const { - for (int i = firstSection; i < header->e_shnum; i++) { - const char* secname = GetSectionName(i); - - if (secname != nullptr && strcmp(name, secname) == 0) - return i; - } - return -1; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Loader namespace - -namespace Loader { - -AppLoader_ELF::AppLoader_ELF(FileSys::VirtualFile file_) : AppLoader(std::move(file_)) {} - -FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& elf_file) { - static constexpr u16 ELF_MACHINE_ARM{0x28}; - - u32 magic = 0; - if (4 != elf_file->ReadObject(&magic)) { - return FileType::Error; - } - - u16 machine = 0; - if (2 != elf_file->ReadObject(&machine, 18)) { - return FileType::Error; - } - - if (Common::MakeMagic('\x7f', 'E', 'L', 'F') == magic && ELF_MACHINE_ARM == machine) { - return FileType::ELF; - } - - return FileType::Error; -} - -AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::KProcess& process, - [[maybe_unused]] Core::System& system) { - if (is_loaded) { - return {ResultStatus::ErrorAlreadyLoaded, {}}; - } - - std::vector<u8> buffer = file->ReadAllBytes(); - if (buffer.size() != file->GetSize()) { - return {ResultStatus::ErrorIncorrectELFFileSize, {}}; - } - - const VAddr base_address = process.PageTable().GetCodeRegionStart(); - ElfReader elf_reader(&buffer[0]); - Kernel::CodeSet codeset = elf_reader.LoadInto(base_address); - const VAddr entry_point = codeset.entrypoint; - - // Setup the process code layout - if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), buffer.size()).IsError()) { - return {ResultStatus::ErrorNotInitialized, {}}; - } - - process.LoadModule(std::move(codeset), entry_point); - - is_loaded = true; - return {ResultStatus::Success, LoadParameters{48, Core::Memory::DEFAULT_STACK_SIZE}}; -} - -} // namespace Loader diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h deleted file mode 100644 index bff51ec17..000000000 --- a/src/core/loader/elf.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "core/loader/loader.h" - -namespace Core { -class System; -} - -namespace Loader { - -/// Loads an ELF/AXF file -class AppLoader_ELF final : public AppLoader { -public: - explicit AppLoader_ELF(FileSys::VirtualFile file); - - /** - * Identifies whether or not the given file is an ELF file. - * - * @param elf_file The file to identify. - * - * @return FileType::ELF, or FileType::Error if the file is not an ELF file. - */ - static FileType IdentifyType(const FileSys::VirtualFile& elf_file); - - FileType GetFileType() const override { - return IdentifyType(file); - } - - LoadResult Load(Kernel::KProcess& process, Core::System& system) override; -}; - -} // namespace Loader diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp index 99ed34b00..d8a1bf82a 100644 --- a/src/core/loader/kip.cpp +++ b/src/core/loader/kip.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include "core/file_sys/kernel_executable.h" @@ -15,7 +14,7 @@ namespace Loader { namespace { constexpr u32 PageAlignSize(u32 size) { - return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK); + return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); } } // Anonymous namespace diff --git a/src/core/loader/kip.h b/src/core/loader/kip.h index 5f914b4a8..63f66e85c 100644 --- a/src/core/loader/kip.h +++ b/src/core/loader/kip.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 199e69e89..f24474ed8 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> #include <optional> @@ -13,7 +12,6 @@ #include "core/core.h" #include "core/hle/kernel/k_process.h" #include "core/loader/deconstructed_rom_directory.h" -#include "core/loader/elf.h" #include "core/loader/kip.h" #include "core/loader/nax.h" #include "core/loader/nca.h" @@ -40,8 +38,6 @@ std::optional<FileType> IdentifyFileLoader(FileSys::VirtualFile file) { FileType IdentifyFile(FileSys::VirtualFile file) { if (const auto romdir_type = IdentifyFileLoader<AppLoader_DeconstructedRomDirectory>(file)) { return *romdir_type; - } else if (const auto elf_type = IdentifyFileLoader<AppLoader_ELF>(file)) { - return *elf_type; } else if (const auto nso_type = IdentifyFileLoader<AppLoader_NSO>(file)) { return *nso_type; } else if (const auto nro_type = IdentifyFileLoader<AppLoader_NRO>(file)) { @@ -70,8 +66,6 @@ FileType GuessFromFilename(const std::string& name) { const std::string extension = Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name))); - if (extension == "elf") - return FileType::ELF; if (extension == "nro") return FileType::NRO; if (extension == "nso") @@ -90,8 +84,6 @@ FileType GuessFromFilename(const std::string& name) { std::string GetFileTypeString(FileType type) { switch (type) { - case FileType::ELF: - return "ELF"; case FileType::NRO: return "NRO"; case FileType::NSO: @@ -209,10 +201,6 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V FileType type, u64 program_id, std::size_t program_index) { switch (type) { - // Standard ELF file format. - case FileType::ELF: - return std::make_unique<AppLoader_ELF>(std::move(file)); - // NX NSO file format. case FileType::NSO: return std::make_unique<AppLoader_NSO>(std::move(file)); @@ -256,12 +244,17 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file, u64 program_id, std::size_t program_index) { + if (!file) { + return nullptr; + } + FileType type = IdentifyFile(file); const FileType filename_type = GuessFromFilename(file->GetName()); // Special case: 00 is either a NCA or NAX. if (type != filename_type && !(file->GetName() == "00" && type == FileType::NAX)) { - LOG_WARNING(Loader, "File {} has a different type than its extension.", file->GetName()); + LOG_WARNING(Loader, "File {} has a different type ({}) than its extension.", + file->GetName(), GetFileTypeString(type)); if (FileType::Unknown == type) { type = filename_type; } diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 8b6b3b68f..7b43f70ed 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -35,7 +34,6 @@ namespace Loader { enum class FileType { Error, Unknown, - ELF, NSO, NRO, NCA, diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp index 3375dab7c..cf35b1249 100644 --- a/src/core/loader/nax.cpp +++ b/src/core/loader/nax.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "core/file_sys/content_archive.h" #include "core/file_sys/romfs.h" diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h index b3a50894f..d7f70db43 100644 --- a/src/core/loader/nax.h +++ b/src/core/loader/nax.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index 219bbeaf5..513af194d 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <utility> diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h index c0db8c740..d22d9146e 100644 --- a/src/core/loader/nca.h +++ b/src/core/loader/nca.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 951ea966e..73d04d7ee 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <utility> #include <vector> @@ -126,7 +125,7 @@ FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& nro_file) { } static constexpr u32 PageAlignSize(u32 size) { - return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK); + return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); } static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data) { diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index fd453b402..ccb77b581 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 4a2224c02..4c3b3c655 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cinttypes> #include <cstring> @@ -46,7 +45,7 @@ std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data, } constexpr u32 PageAlignSize(u32 size) { - return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK); + return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); } } // Anonymous namespace @@ -129,11 +128,10 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: // Apply patches if necessary if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) { - std::vector<u8> pi_header; - pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header), - reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader)); - pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.data(), - program_image.data() + program_image.size()); + std::vector<u8> pi_header(sizeof(NSOHeader) + program_image.size()); + std::memcpy(pi_header.data(), &nso_header, sizeof(NSOHeader)); + std::memcpy(pi_header.data() + sizeof(NSOHeader), program_image.data(), + program_image.size()); pi_header = pm->PatchNSO(pi_header, nso_file.GetName()); diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index f7b61bc2d..0b53b4ecd 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index f7ccc678d..80663e0e0 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <vector> diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h index 378e4077a..003cc345c 100644 --- a/src/core/loader/nsp.h +++ b/src/core/loader/nsp.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index 8c6c1a3fd..c7b1b3815 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <vector> diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h index 6e3810e48..2affb6c6e 100644 --- a/src/core/loader/xci.h +++ b/src/core/loader/xci.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 88d6ec908..2ac792566 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -1,6 +1,5 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2015 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <cstring> @@ -37,11 +36,11 @@ struct Memory::Impl { } void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { - ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); - ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); - ASSERT_MSG(target >= DramMemoryMap::Base && target < DramMemoryMap::End, - "Out of bounds target: {:016X}", target); - MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory); + ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size); + ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", base); + ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", target); + MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, target, + Common::PageType::Memory); if (Settings::IsFastmemEnabled()) { system.DeviceMemory().buffer.Map(base, target - DramMemoryMap::Base, size); @@ -49,9 +48,10 @@ struct Memory::Impl { } void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) { - ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); - ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); - MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, 0, Common::PageType::Unmapped); + ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size); + ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", base); + MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, 0, + Common::PageType::Unmapped); if (Settings::IsFastmemEnabled()) { system.DeviceMemory().buffer.Unmap(base, size); @@ -59,7 +59,7 @@ struct Memory::Impl { } [[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(VAddr vaddr) const { - const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]}; + const PAddr paddr{current_page_table->backing_addr[vaddr >> YUZU_PAGEBITS]}; if (!paddr) { return {}; @@ -68,6 +68,16 @@ struct Memory::Impl { return system.DeviceMemory().GetPointer(paddr) + vaddr; } + [[nodiscard]] u8* GetPointerFromDebugMemory(VAddr vaddr) const { + const PAddr paddr{current_page_table->backing_addr[vaddr >> YUZU_PAGEBITS]}; + + if (paddr == 0) { + return {}; + } + + return system.DeviceMemory().GetPointer(paddr) + vaddr; + } + u8 Read8(const VAddr addr) { return Read<u8>(addr); } @@ -168,13 +178,14 @@ struct Memory::Impl { auto on_unmapped, auto on_memory, auto on_rasterizer, auto increment) { const auto& page_table = process.PageTable().PageTableImpl(); std::size_t remaining_size = size; - std::size_t page_index = addr >> PAGE_BITS; - std::size_t page_offset = addr & PAGE_MASK; + std::size_t page_index = addr >> YUZU_PAGEBITS; + std::size_t page_offset = addr & YUZU_PAGEMASK; while (remaining_size) { const std::size_t copy_amount = - std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); - const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); + std::min(static_cast<std::size_t>(YUZU_PAGESIZE) - page_offset, remaining_size); + const auto current_vaddr = + static_cast<VAddr>((page_index << YUZU_PAGEBITS) + page_offset); const auto [pointer, type] = page_table.pointers[page_index].PointerType(); switch (type) { @@ -184,7 +195,13 @@ struct Memory::Impl { } case Common::PageType::Memory: { DEBUG_ASSERT(pointer); - u8* mem_ptr = pointer + page_offset + (page_index << PAGE_BITS); + u8* mem_ptr = pointer + page_offset + (page_index << YUZU_PAGEBITS); + on_memory(copy_amount, mem_ptr); + break; + } + case Common::PageType::DebugMemory: { + DEBUG_ASSERT(pointer); + u8* const mem_ptr{GetPointerFromDebugMemory(current_vaddr)}; on_memory(copy_amount, mem_ptr); break; } @@ -317,6 +334,58 @@ struct Memory::Impl { }); } + void MarkRegionDebug(VAddr vaddr, u64 size, bool debug) { + if (vaddr == 0) { + return; + } + + // Iterate over a contiguous CPU address space, marking/unmarking the region. + // The region is at a granularity of CPU pages. + + const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1; + for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) { + const Common::PageType page_type{ + current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Type()}; + if (debug) { + // Switch page type to debug if now debug + switch (page_type) { + case Common::PageType::Unmapped: + ASSERT_MSG(false, "Attempted to mark unmapped pages as debug"); + break; + case Common::PageType::RasterizerCachedMemory: + case Common::PageType::DebugMemory: + // Page is already marked. + break; + case Common::PageType::Memory: + current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store( + nullptr, Common::PageType::DebugMemory); + break; + default: + UNREACHABLE(); + } + } else { + // Switch page type to non-debug if now non-debug + switch (page_type) { + case Common::PageType::Unmapped: + ASSERT_MSG(false, "Attempted to mark unmapped pages as non-debug"); + break; + case Common::PageType::RasterizerCachedMemory: + case Common::PageType::Memory: + // Don't mess with already non-debug or rasterizer memory. + break; + case Common::PageType::DebugMemory: { + u8* const pointer{GetPointerFromDebugMemory(vaddr & ~YUZU_PAGEMASK)}; + current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store( + pointer - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory); + break; + } + default: + UNREACHABLE(); + } + } + } + } + void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { if (vaddr == 0) { return; @@ -332,10 +401,10 @@ struct Memory::Impl { // granularity of CPU pages, hence why we iterate on a CPU page basis (note: GPU page size // is different). This assumes the specified GPU address region is contiguous as well. - const u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1; - for (u64 i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) { + const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1; + for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) { const Common::PageType page_type{ - current_page_table->pointers[vaddr >> PAGE_BITS].Type()}; + current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Type()}; if (cached) { // Switch page type to cached if now cached switch (page_type) { @@ -343,8 +412,9 @@ struct Memory::Impl { // It is not necessary for a process to have this region mapped into its address // space, for example, a system module need not have a VRAM mapping. break; + case Common::PageType::DebugMemory: case Common::PageType::Memory: - current_page_table->pointers[vaddr >> PAGE_BITS].Store( + current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store( nullptr, Common::PageType::RasterizerCachedMemory); break; case Common::PageType::RasterizerCachedMemory: @@ -361,21 +431,22 @@ struct Memory::Impl { // It is not necessary for a process to have this region mapped into its address // space, for example, a system module need not have a VRAM mapping. break; + case Common::PageType::DebugMemory: case Common::PageType::Memory: // There can be more than one GPU region mapped per CPU region, so it's common // that this area is already unmarked as cached. break; case Common::PageType::RasterizerCachedMemory: { - u8* const pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~PAGE_MASK)}; + u8* const pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~YUZU_PAGEMASK)}; if (pointer == nullptr) { // It's possible that this function has been called while updating the // pagetable after unmapping a VMA. In that case the underlying VMA will no // longer exist, and we should just leave the pagetable entry blank. - current_page_table->pointers[vaddr >> PAGE_BITS].Store( + current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store( nullptr, Common::PageType::Unmapped); } else { - current_page_table->pointers[vaddr >> PAGE_BITS].Store( - pointer - (vaddr & ~PAGE_MASK), Common::PageType::Memory); + current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store( + pointer - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory); } break; } @@ -397,8 +468,8 @@ struct Memory::Impl { */ void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target, Common::PageType type) { - LOG_DEBUG(HW_Memory, "Mapping {:016X} onto {:016X}-{:016X}", target, base * PAGE_SIZE, - (base + size) * PAGE_SIZE); + LOG_DEBUG(HW_Memory, "Mapping {:016X} onto {:016X}-{:016X}", target, base * YUZU_PAGESIZE, + (base + size) * YUZU_PAGESIZE); // During boot, current_page_table might not be set yet, in which case we need not flush if (system.IsPoweredOn()) { @@ -406,7 +477,7 @@ struct Memory::Impl { for (u64 i = 0; i < size; i++) { const auto page = base + i; if (page_table.pointers[page].Type() == Common::PageType::RasterizerCachedMemory) { - gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE); + gpu.FlushAndInvalidateRegion(page << YUZU_PAGEBITS, YUZU_PAGESIZE); } } } @@ -417,7 +488,7 @@ struct Memory::Impl { if (!target) { ASSERT_MSG(type != Common::PageType::Memory, - "Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE); + "Mapping memory page without a pointer @ {:016x}", base * YUZU_PAGESIZE); while (base != end) { page_table.pointers[base].Store(nullptr, type); @@ -428,21 +499,21 @@ struct Memory::Impl { } else { while (base != end) { page_table.pointers[base].Store( - system.DeviceMemory().GetPointer(target) - (base << PAGE_BITS), type); - page_table.backing_addr[base] = target - (base << PAGE_BITS); + system.DeviceMemory().GetPointer(target) - (base << YUZU_PAGEBITS), type); + page_table.backing_addr[base] = target - (base << YUZU_PAGEBITS); ASSERT_MSG(page_table.pointers[base].Pointer(), "memory mapping base yield a nullptr within the table"); base += 1; - target += PAGE_SIZE; + target += YUZU_PAGESIZE; } } } [[nodiscard]] u8* GetPointerImpl(VAddr vaddr, auto on_unmapped, auto on_rasterizer) const { // AARCH64 masks the upper 16 bit of all memory accesses - vaddr &= 0xffffffffffffLL; + vaddr &= 0xffffffffffffULL; if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) { on_unmapped(); @@ -450,7 +521,7 @@ struct Memory::Impl { } // Avoid adding any extra logic to this fast-path block - const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); + const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Raw(); if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { return &pointer[vaddr]; } @@ -461,6 +532,8 @@ struct Memory::Impl { case Common::PageType::Memory: ASSERT_MSG(false, "Mapped memory page without a pointer @ 0x{:016X}", vaddr); return nullptr; + case Common::PageType::DebugMemory: + return GetPointerFromDebugMemory(vaddr); case Common::PageType::RasterizerCachedMemory: { u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; on_rasterizer(); @@ -478,6 +551,11 @@ struct Memory::Impl { []() {}); } + [[nodiscard]] u8* GetPointerSilent(const VAddr vaddr) const { + return GetPointerImpl( + vaddr, []() {}, []() {}); + } + /** * Reads a particular data type out of memory at the given virtual address. * @@ -587,18 +665,36 @@ void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) { bool Memory::IsValidVirtualAddress(const VAddr vaddr) const { const Kernel::KProcess& process = *system.CurrentProcess(); const auto& page_table = process.PageTable().PageTableImpl(); - const size_t page = vaddr >> PAGE_BITS; + const size_t page = vaddr >> YUZU_PAGEBITS; if (page >= page_table.pointers.size()) { return false; } const auto [pointer, type] = page_table.pointers[page].PointerType(); - return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory; + return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory || + type == Common::PageType::DebugMemory; +} + +bool Memory::IsValidVirtualAddressRange(VAddr base, u64 size) const { + VAddr end = base + size; + VAddr page = Common::AlignDown(base, YUZU_PAGESIZE); + + for (; page < end; page += YUZU_PAGESIZE) { + if (!IsValidVirtualAddress(page)) { + return false; + } + } + + return true; } u8* Memory::GetPointer(VAddr vaddr) { return impl->GetPointer(vaddr); } +u8* Memory::GetPointerSilent(VAddr vaddr) { + return impl->GetPointerSilent(vaddr); +} + const u8* Memory::GetPointer(VAddr vaddr) const { return impl->GetPointer(vaddr); } @@ -691,8 +787,16 @@ void Memory::CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr s impl->CopyBlock(process, dest_addr, src_addr, size); } +void Memory::ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, const std::size_t size) { + impl->ZeroBlock(process, dest_addr, size); +} + void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { impl->RasterizerMarkRegionCached(vaddr, size, cached); } +void Memory::MarkRegionDebug(VAddr vaddr, u64 size, bool debug) { + impl->MarkRegionDebug(vaddr, size, debug); +} + } // namespace Core::Memory diff --git a/src/core/memory.h b/src/core/memory.h index b5721b740..81eac448b 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -1,6 +1,5 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2014 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -28,9 +27,9 @@ namespace Core::Memory { * Page size used by the ARM architecture. This is the smallest granularity with which memory can * be mapped. */ -constexpr std::size_t PAGE_BITS = 12; -constexpr u64 PAGE_SIZE = 1ULL << PAGE_BITS; -constexpr u64 PAGE_MASK = PAGE_SIZE - 1; +constexpr std::size_t YUZU_PAGEBITS = 12; +constexpr u64 YUZU_PAGESIZE = 1ULL << YUZU_PAGEBITS; +constexpr u64 YUZU_PAGEMASK = YUZU_PAGESIZE - 1; /// Virtual user-space memory regions enum : VAddr { @@ -96,6 +95,17 @@ public: [[nodiscard]] bool IsValidVirtualAddress(VAddr vaddr) const; /** + * Checks whether or not the supplied range of addresses are all valid + * virtual addresses for the current process. + * + * @param base The address to begin checking. + * @param size The amount of bytes to check. + * + * @returns True if all bytes in the given range are valid, false otherwise. + */ + [[nodiscard]] bool IsValidVirtualAddressRange(VAddr base, u64 size) const; + + /** * Gets a pointer to the given address. * * @param vaddr Virtual address to retrieve a pointer to. @@ -104,6 +114,7 @@ public: * If the address is not valid, nullptr will be returned. */ u8* GetPointer(VAddr vaddr); + u8* GetPointerSilent(VAddr vaddr); template <typename T> T* GetPointer(VAddr vaddr) { @@ -426,6 +437,19 @@ public: std::size_t size); /** + * Zeros a range of bytes within the current process' address space at the specified + * virtual address. + * + * @param process The process that will have data zeroed within its address space. + * @param dest_addr The destination virtual address to zero the data from. + * @param size The size of the range to zero out, in bytes. + * + * @post The range [dest_addr, size) within the process' address space contains the + * value 0. + */ + void ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size); + + /** * Marks each page within the specified address range as cached or uncached. * * @param vaddr The virtual address indicating the start of the address range. @@ -435,6 +459,17 @@ public: */ void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached); + /** + * Marks each page within the specified address range as debug or non-debug. + * Debug addresses are not accessible from fastmem pointers. + * + * @param vaddr The virtual address indicating the start of the address range. + * @param size The size of the address range in bytes. + * @param debug Whether or not any pages within the address range should be + * marked as debug or non-debug. + */ + void MarkRegionDebug(VAddr vaddr, u64 size, bool debug); + private: Core::System& system; diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index 12446c9ac..ffdbacc18 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <locale> #include "common/hex_util.h" @@ -185,10 +184,12 @@ CheatEngine::~CheatEngine() { void CheatEngine::Initialize() { event = Core::Timing::CreateEvent( "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), - [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { FrameCallback(user_data, ns_late); + return std::nullopt; }); - core_timing.ScheduleEvent(CHEAT_ENGINE_NS, event); + core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event); metadata.process_id = system.CurrentProcess()->GetProcessID(); metadata.title_id = system.GetCurrentProcessProgramID(); @@ -238,8 +239,6 @@ void CheatEngine::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late MICROPROFILE_SCOPE(Cheat_Engine); vm.Execute(metadata); - - core_timing.ScheduleEvent(CHEAT_ENGINE_NS - ns_late, event); } } // namespace Core::Memory diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h index a8e041d9d..284abdd28 100644 --- a/src/core/memory/cheat_engine.h +++ b/src/core/memory/cheat_engine.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/memory/dmnt_cheat_types.h b/src/core/memory/dmnt_cheat_types.h index 5e60733dc..c6b40e505 100644 --- a/src/core/memory/dmnt_cheat_types.h +++ b/src/core/memory/dmnt_cheat_types.h @@ -1,26 +1,5 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * Adapted by DarkLordZach for use/interaction with yuzu - * - * Modifications Copyright 2019 yuzu emulator team - * Licensed under GPLv2 or any later version - * Refer to the license.txt file included. - */ +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp index dc04e37d2..de96fcb8e 100644 --- a/src/core/memory/dmnt_cheat_vm.cpp +++ b/src/core/memory/dmnt_cheat_vm.cpp @@ -1,26 +1,5 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * Adapted by DarkLordZach for use/interaction with yuzu - * - * Modifications Copyright 2019 yuzu emulator team - * Licensed under GPLv2 or any later version - * Refer to the license.txt file included. - */ +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "common/scope_exit.h" diff --git a/src/core/memory/dmnt_cheat_vm.h b/src/core/memory/dmnt_cheat_vm.h index 707bee82b..641cb09c4 100644 --- a/src/core/memory/dmnt_cheat_vm.h +++ b/src/core/memory/dmnt_cheat_vm.h @@ -1,26 +1,5 @@ -/* - * Copyright (c) 2018-2019 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * Adapted by DarkLordZach for use/interaction with yuzu - * - * Modifications Copyright 2019 yuzu emulator team - * Licensed under GPLv2 or any later version - * Refer to the license.txt file included. - */ +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp deleted file mode 100644 index a3e0664b9..000000000 --- a/src/core/network/network.cpp +++ /dev/null @@ -1,634 +0,0 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> -#include <cstring> -#include <limits> -#include <utility> -#include <vector> - -#include "common/error.h" - -#ifdef _WIN32 -#include <winsock2.h> -#include <ws2tcpip.h> -#elif YUZU_UNIX -#include <arpa/inet.h> -#include <errno.h> -#include <fcntl.h> -#include <netdb.h> -#include <netinet/in.h> -#include <poll.h> -#include <sys/socket.h> -#include <unistd.h> -#else -#error "Unimplemented platform" -#endif - -#include "common/assert.h" -#include "common/common_types.h" -#include "common/logging/log.h" -#include "common/settings.h" -#include "core/network/network.h" -#include "core/network/network_interface.h" -#include "core/network/sockets.h" - -namespace Network { - -namespace { - -#ifdef _WIN32 - -using socklen_t = int; - -void Initialize() { - WSADATA wsa_data; - (void)WSAStartup(MAKEWORD(2, 2), &wsa_data); -} - -void Finalize() { - WSACleanup(); -} - -sockaddr TranslateFromSockAddrIn(SockAddrIn input) { - sockaddr_in result; - -#if YUZU_UNIX - result.sin_len = sizeof(result); -#endif - - switch (static_cast<Domain>(input.family)) { - case Domain::INET: - result.sin_family = AF_INET; - break; - default: - UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.family); - result.sin_family = AF_INET; - break; - } - - result.sin_port = htons(input.portno); - - auto& ip = result.sin_addr.S_un.S_un_b; - ip.s_b1 = input.ip[0]; - ip.s_b2 = input.ip[1]; - ip.s_b3 = input.ip[2]; - ip.s_b4 = input.ip[3]; - - sockaddr addr; - std::memcpy(&addr, &result, sizeof(addr)); - return addr; -} - -LINGER MakeLinger(bool enable, u32 linger_value) { - ASSERT(linger_value <= std::numeric_limits<u_short>::max()); - - LINGER value; - value.l_onoff = enable ? 1 : 0; - value.l_linger = static_cast<u_short>(linger_value); - return value; -} - -bool EnableNonBlock(SOCKET fd, bool enable) { - u_long value = enable ? 1 : 0; - return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR; -} - -Errno TranslateNativeError(int e) { - switch (e) { - case WSAEBADF: - return Errno::BADF; - case WSAEINVAL: - return Errno::INVAL; - case WSAEMFILE: - return Errno::MFILE; - case WSAENOTCONN: - return Errno::NOTCONN; - case WSAEWOULDBLOCK: - return Errno::AGAIN; - case WSAECONNREFUSED: - return Errno::CONNREFUSED; - case WSAEHOSTUNREACH: - return Errno::HOSTUNREACH; - case WSAENETDOWN: - return Errno::NETDOWN; - case WSAENETUNREACH: - return Errno::NETUNREACH; - default: - return Errno::OTHER; - } -} - -#elif YUZU_UNIX // ^ _WIN32 v YUZU_UNIX - -using SOCKET = int; -using WSAPOLLFD = pollfd; -using ULONG = u64; - -constexpr SOCKET INVALID_SOCKET = -1; -constexpr SOCKET SOCKET_ERROR = -1; - -constexpr int SD_RECEIVE = SHUT_RD; -constexpr int SD_SEND = SHUT_WR; -constexpr int SD_BOTH = SHUT_RDWR; - -void Initialize() {} - -void Finalize() {} - -sockaddr TranslateFromSockAddrIn(SockAddrIn input) { - sockaddr_in result; - - switch (static_cast<Domain>(input.family)) { - case Domain::INET: - result.sin_family = AF_INET; - break; - default: - UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.family); - result.sin_family = AF_INET; - break; - } - - result.sin_port = htons(input.portno); - - result.sin_addr.s_addr = input.ip[0] | input.ip[1] << 8 | input.ip[2] << 16 | input.ip[3] << 24; - - sockaddr addr; - std::memcpy(&addr, &result, sizeof(addr)); - return addr; -} - -int WSAPoll(WSAPOLLFD* fds, ULONG nfds, int timeout) { - return poll(fds, static_cast<nfds_t>(nfds), timeout); -} - -int closesocket(SOCKET fd) { - return close(fd); -} - -linger MakeLinger(bool enable, u32 linger_value) { - linger value; - value.l_onoff = enable ? 1 : 0; - value.l_linger = linger_value; - return value; -} - -bool EnableNonBlock(int fd, bool enable) { - int flags = fcntl(fd, F_GETFL); - if (flags == -1) { - return false; - } - if (enable) { - flags |= O_NONBLOCK; - } else { - flags &= ~O_NONBLOCK; - } - return fcntl(fd, F_SETFL, flags) == 0; -} - -Errno TranslateNativeError(int e) { - switch (e) { - case EBADF: - return Errno::BADF; - case EINVAL: - return Errno::INVAL; - case EMFILE: - return Errno::MFILE; - case ENOTCONN: - return Errno::NOTCONN; - case EAGAIN: - return Errno::AGAIN; - case ECONNREFUSED: - return Errno::CONNREFUSED; - case EHOSTUNREACH: - return Errno::HOSTUNREACH; - case ENETDOWN: - return Errno::NETDOWN; - case ENETUNREACH: - return Errno::NETUNREACH; - default: - return Errno::OTHER; - } -} - -#endif - -Errno GetAndLogLastError() { -#ifdef _WIN32 - int e = WSAGetLastError(); -#else - int e = errno; -#endif - const Errno err = TranslateNativeError(e); - if (err == Errno::AGAIN) { - return err; - } - LOG_ERROR(Network, "Socket operation error: {}", Common::NativeErrorToString(e)); - return err; -} - -int TranslateDomain(Domain domain) { - switch (domain) { - case Domain::INET: - return AF_INET; - default: - UNIMPLEMENTED_MSG("Unimplemented domain={}", domain); - return 0; - } -} - -int TranslateType(Type type) { - switch (type) { - case Type::STREAM: - return SOCK_STREAM; - case Type::DGRAM: - return SOCK_DGRAM; - default: - UNIMPLEMENTED_MSG("Unimplemented type={}", type); - return 0; - } -} - -int TranslateProtocol(Protocol protocol) { - switch (protocol) { - case Protocol::TCP: - return IPPROTO_TCP; - case Protocol::UDP: - return IPPROTO_UDP; - default: - UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol); - return 0; - } -} - -SockAddrIn TranslateToSockAddrIn(sockaddr input_) { - sockaddr_in input; - std::memcpy(&input, &input_, sizeof(input)); - - SockAddrIn result; - - switch (input.sin_family) { - case AF_INET: - result.family = Domain::INET; - break; - default: - UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.sin_family); - result.family = Domain::INET; - break; - } - - result.portno = ntohs(input.sin_port); - - result.ip = TranslateIPv4(input.sin_addr); - - return result; -} - -short TranslatePollEvents(PollEvents events) { - short result = 0; - - if (True(events & PollEvents::In)) { - events &= ~PollEvents::In; - result |= POLLIN; - } - if (True(events & PollEvents::Pri)) { - events &= ~PollEvents::Pri; -#ifdef _WIN32 - LOG_WARNING(Service, "Winsock doesn't support POLLPRI"); -#else - result |= POLLPRI; -#endif - } - if (True(events & PollEvents::Out)) { - events &= ~PollEvents::Out; - result |= POLLOUT; - } - - UNIMPLEMENTED_IF_MSG((u16)events != 0, "Unhandled guest events=0x{:x}", (u16)events); - - return result; -} - -PollEvents TranslatePollRevents(short revents) { - PollEvents result{}; - const auto translate = [&result, &revents](short host, PollEvents guest) { - if ((revents & host) != 0) { - revents &= static_cast<short>(~host); - result |= guest; - } - }; - - translate(POLLIN, PollEvents::In); - translate(POLLPRI, PollEvents::Pri); - translate(POLLOUT, PollEvents::Out); - translate(POLLERR, PollEvents::Err); - translate(POLLHUP, PollEvents::Hup); - - UNIMPLEMENTED_IF_MSG(revents != 0, "Unhandled host revents=0x{:x}", revents); - - return result; -} - -template <typename T> -Errno SetSockOpt(SOCKET fd, int option, T value) { - const int result = - setsockopt(fd, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value)); - if (result != SOCKET_ERROR) { - return Errno::SUCCESS; - } - return GetAndLogLastError(); -} - -} // Anonymous namespace - -NetworkInstance::NetworkInstance() { - Initialize(); -} - -NetworkInstance::~NetworkInstance() { - Finalize(); -} - -std::optional<IPv4Address> GetHostIPv4Address() { - const std::string& selected_network_interface = Settings::values.network_interface.GetValue(); - const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); - if (network_interfaces.size() == 0) { - LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); - return {}; - } - - const auto res = - std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) { - return iface.name == selected_network_interface; - }); - - if (res != network_interfaces.end()) { - char ip_addr[16] = {}; - ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr); - return TranslateIPv4(res->ip_address); - } else { - LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); - return {}; - } -} - -std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { - const size_t num = pollfds.size(); - - std::vector<WSAPOLLFD> host_pollfds(pollfds.size()); - std::transform(pollfds.begin(), pollfds.end(), host_pollfds.begin(), [](PollFD fd) { - WSAPOLLFD result; - result.fd = fd.socket->fd; - result.events = TranslatePollEvents(fd.events); - result.revents = 0; - return result; - }); - - const int result = WSAPoll(host_pollfds.data(), static_cast<ULONG>(num), timeout); - if (result == 0) { - ASSERT(std::all_of(host_pollfds.begin(), host_pollfds.end(), - [](WSAPOLLFD fd) { return fd.revents == 0; })); - return {0, Errno::SUCCESS}; - } - - for (size_t i = 0; i < num; ++i) { - pollfds[i].revents = TranslatePollRevents(host_pollfds[i].revents); - } - - if (result > 0) { - return {result, Errno::SUCCESS}; - } - - ASSERT(result == SOCKET_ERROR); - - return {-1, GetAndLogLastError()}; -} - -Socket::~Socket() { - if (fd == INVALID_SOCKET) { - return; - } - (void)closesocket(fd); - fd = INVALID_SOCKET; -} - -Socket::Socket(Socket&& rhs) noexcept : fd{std::exchange(rhs.fd, INVALID_SOCKET)} {} - -Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { - fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol)); - if (fd != INVALID_SOCKET) { - return Errno::SUCCESS; - } - - return GetAndLogLastError(); -} - -std::pair<Socket::AcceptResult, Errno> Socket::Accept() { - sockaddr addr; - socklen_t addrlen = sizeof(addr); - const SOCKET new_socket = accept(fd, &addr, &addrlen); - - if (new_socket == INVALID_SOCKET) { - return {AcceptResult{}, GetAndLogLastError()}; - } - - AcceptResult result; - result.socket = std::make_unique<Socket>(); - result.socket->fd = new_socket; - - ASSERT(addrlen == sizeof(sockaddr_in)); - result.sockaddr_in = TranslateToSockAddrIn(addr); - - return {std::move(result), Errno::SUCCESS}; -} - -Errno Socket::Connect(SockAddrIn addr_in) { - const sockaddr host_addr_in = TranslateFromSockAddrIn(addr_in); - if (connect(fd, &host_addr_in, sizeof(host_addr_in)) != SOCKET_ERROR) { - return Errno::SUCCESS; - } - - return GetAndLogLastError(); -} - -std::pair<SockAddrIn, Errno> Socket::GetPeerName() { - sockaddr addr; - socklen_t addrlen = sizeof(addr); - if (getpeername(fd, &addr, &addrlen) == SOCKET_ERROR) { - return {SockAddrIn{}, GetAndLogLastError()}; - } - - ASSERT(addrlen == sizeof(sockaddr_in)); - return {TranslateToSockAddrIn(addr), Errno::SUCCESS}; -} - -std::pair<SockAddrIn, Errno> Socket::GetSockName() { - sockaddr addr; - socklen_t addrlen = sizeof(addr); - if (getsockname(fd, &addr, &addrlen) == SOCKET_ERROR) { - return {SockAddrIn{}, GetAndLogLastError()}; - } - - ASSERT(addrlen == sizeof(sockaddr_in)); - return {TranslateToSockAddrIn(addr), Errno::SUCCESS}; -} - -Errno Socket::Bind(SockAddrIn addr) { - const sockaddr addr_in = TranslateFromSockAddrIn(addr); - if (bind(fd, &addr_in, sizeof(addr_in)) != SOCKET_ERROR) { - return Errno::SUCCESS; - } - - return GetAndLogLastError(); -} - -Errno Socket::Listen(s32 backlog) { - if (listen(fd, backlog) != SOCKET_ERROR) { - return Errno::SUCCESS; - } - - return GetAndLogLastError(); -} - -Errno Socket::Shutdown(ShutdownHow how) { - int host_how = 0; - switch (how) { - case ShutdownHow::RD: - host_how = SD_RECEIVE; - break; - case ShutdownHow::WR: - host_how = SD_SEND; - break; - case ShutdownHow::RDWR: - host_how = SD_BOTH; - break; - default: - UNIMPLEMENTED_MSG("Unimplemented flag how={}", how); - return Errno::SUCCESS; - } - if (shutdown(fd, host_how) != SOCKET_ERROR) { - return Errno::SUCCESS; - } - - return GetAndLogLastError(); -} - -std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) { - ASSERT(flags == 0); - ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); - - const auto result = - recv(fd, reinterpret_cast<char*>(message.data()), static_cast<int>(message.size()), 0); - if (result != SOCKET_ERROR) { - return {static_cast<s32>(result), Errno::SUCCESS}; - } - - return {-1, GetAndLogLastError()}; -} - -std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) { - ASSERT(flags == 0); - ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); - - sockaddr addr_in{}; - socklen_t addrlen = sizeof(addr_in); - socklen_t* const p_addrlen = addr ? &addrlen : nullptr; - sockaddr* const p_addr_in = addr ? &addr_in : nullptr; - - const auto result = recvfrom(fd, reinterpret_cast<char*>(message.data()), - static_cast<int>(message.size()), 0, p_addr_in, p_addrlen); - if (result != SOCKET_ERROR) { - if (addr) { - ASSERT(addrlen == sizeof(addr_in)); - *addr = TranslateToSockAddrIn(addr_in); - } - return {static_cast<s32>(result), Errno::SUCCESS}; - } - - return {-1, GetAndLogLastError()}; -} - -std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) { - ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); - ASSERT(flags == 0); - - const auto result = send(fd, reinterpret_cast<const char*>(message.data()), - static_cast<int>(message.size()), 0); - if (result != SOCKET_ERROR) { - return {static_cast<s32>(result), Errno::SUCCESS}; - } - - return {-1, GetAndLogLastError()}; -} - -std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message, - const SockAddrIn* addr) { - ASSERT(flags == 0); - - const sockaddr* to = nullptr; - const int tolen = addr ? sizeof(sockaddr) : 0; - sockaddr host_addr_in; - - if (addr) { - host_addr_in = TranslateFromSockAddrIn(*addr); - to = &host_addr_in; - } - - const auto result = sendto(fd, reinterpret_cast<const char*>(message.data()), - static_cast<int>(message.size()), 0, to, tolen); - if (result != SOCKET_ERROR) { - return {static_cast<s32>(result), Errno::SUCCESS}; - } - - return {-1, GetAndLogLastError()}; -} - -Errno Socket::Close() { - [[maybe_unused]] const int result = closesocket(fd); - ASSERT(result == 0); - fd = INVALID_SOCKET; - - return Errno::SUCCESS; -} - -Errno Socket::SetLinger(bool enable, u32 linger) { - return SetSockOpt(fd, SO_LINGER, MakeLinger(enable, linger)); -} - -Errno Socket::SetReuseAddr(bool enable) { - return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0); -} - -Errno Socket::SetBroadcast(bool enable) { - return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0); -} - -Errno Socket::SetSndBuf(u32 value) { - return SetSockOpt(fd, SO_SNDBUF, value); -} - -Errno Socket::SetRcvBuf(u32 value) { - return SetSockOpt(fd, SO_RCVBUF, value); -} - -Errno Socket::SetSndTimeo(u32 value) { - return SetSockOpt(fd, SO_SNDTIMEO, value); -} - -Errno Socket::SetRcvTimeo(u32 value) { - return SetSockOpt(fd, SO_RCVTIMEO, value); -} - -Errno Socket::SetNonBlock(bool enable) { - if (EnableNonBlock(fd, enable)) { - return Errno::SUCCESS; - } - return GetAndLogLastError(); -} - -bool Socket::IsOpened() const { - return fd != INVALID_SOCKET; -} - -} // namespace Network diff --git a/src/core/network/network.h b/src/core/network/network.h deleted file mode 100644 index e85df3ab7..000000000 --- a/src/core/network/network.h +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <array> -#include <optional> - -#include "common/common_funcs.h" -#include "common/common_types.h" - -#ifdef _WIN32 -#include <winsock2.h> -#elif YUZU_UNIX -#include <netinet/in.h> -#endif - -namespace Network { - -class Socket; - -/// Error code for network functions -enum class Errno { - SUCCESS, - BADF, - INVAL, - MFILE, - NOTCONN, - AGAIN, - CONNREFUSED, - HOSTUNREACH, - NETDOWN, - NETUNREACH, - OTHER, -}; - -/// Address families -enum class Domain { - INET, ///< Address family for IPv4 -}; - -/// Socket types -enum class Type { - STREAM, - DGRAM, - RAW, - SEQPACKET, -}; - -/// Protocol values for sockets -enum class Protocol { - ICMP, - TCP, - UDP, -}; - -/// Shutdown mode -enum class ShutdownHow { - RD, - WR, - RDWR, -}; - -/// Array of IPv4 address -using IPv4Address = std::array<u8, 4>; - -/// Cross-platform sockaddr structure -struct SockAddrIn { - Domain family; - IPv4Address ip; - u16 portno; -}; - -/// Cross-platform poll fd structure - -enum class PollEvents : u16 { - // Using Pascal case because IN is a macro on Windows. - In = 1 << 0, - Pri = 1 << 1, - Out = 1 << 2, - Err = 1 << 3, - Hup = 1 << 4, - Nval = 1 << 5, -}; - -DECLARE_ENUM_FLAG_OPERATORS(PollEvents); - -struct PollFD { - Socket* socket; - PollEvents events; - PollEvents revents; -}; - -class NetworkInstance { -public: - explicit NetworkInstance(); - ~NetworkInstance(); -}; - -#ifdef _WIN32 -constexpr IPv4Address TranslateIPv4(in_addr addr) { - auto& bytes = addr.S_un.S_un_b; - return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4}; -} -#elif YUZU_UNIX -constexpr IPv4Address TranslateIPv4(in_addr addr) { - const u32 bytes = addr.s_addr; - return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8), - static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)}; -} -#endif - -/// @brief Returns host's IPv4 address -/// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array -std::optional<IPv4Address> GetHostIPv4Address(); - -} // namespace Network diff --git a/src/core/network/network_interface.cpp b/src/core/network/network_interface.cpp deleted file mode 100644 index 6811f21b1..000000000 --- a/src/core/network/network_interface.cpp +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> -#include <fstream> -#include <sstream> -#include <vector> - -#include "common/bit_cast.h" -#include "common/common_types.h" -#include "common/logging/log.h" -#include "common/settings.h" -#include "common/string_util.h" -#include "core/network/network_interface.h" - -#ifdef _WIN32 -#include <iphlpapi.h> -#else -#include <cerrno> -#include <ifaddrs.h> -#include <net/if.h> -#endif - -namespace Network { - -#ifdef _WIN32 - -std::vector<NetworkInterface> GetAvailableNetworkInterfaces() { - std::vector<IP_ADAPTER_ADDRESSES> adapter_addresses; - DWORD ret = ERROR_BUFFER_OVERFLOW; - DWORD buf_size = 0; - - // retry up to 5 times - for (int i = 0; i < 5 && ret == ERROR_BUFFER_OVERFLOW; i++) { - ret = GetAdaptersAddresses( - AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_GATEWAYS, - nullptr, adapter_addresses.data(), &buf_size); - - if (ret != ERROR_BUFFER_OVERFLOW) { - break; - } - - adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1); - } - - if (ret != NO_ERROR) { - LOG_ERROR(Network, "Failed to get network interfaces with GetAdaptersAddresses"); - return {}; - } - - std::vector<NetworkInterface> result; - - for (auto current_address = adapter_addresses.data(); current_address != nullptr; - current_address = current_address->Next) { - if (current_address->FirstUnicastAddress == nullptr || - current_address->FirstUnicastAddress->Address.lpSockaddr == nullptr) { - continue; - } - - if (current_address->OperStatus != IfOperStatusUp) { - continue; - } - - const auto ip_addr = Common::BitCast<struct sockaddr_in>( - *current_address->FirstUnicastAddress->Address.lpSockaddr) - .sin_addr; - - ULONG mask = 0; - if (ConvertLengthToIpv4Mask(current_address->FirstUnicastAddress->OnLinkPrefixLength, - &mask) != NO_ERROR) { - LOG_ERROR(Network, "Failed to convert IPv4 prefix length to subnet mask"); - continue; - } - - struct in_addr gateway = {.S_un{.S_addr{0}}}; - if (current_address->FirstGatewayAddress != nullptr && - current_address->FirstGatewayAddress->Address.lpSockaddr != nullptr) { - gateway = Common::BitCast<struct sockaddr_in>( - *current_address->FirstGatewayAddress->Address.lpSockaddr) - .sin_addr; - } - - result.emplace_back(NetworkInterface{ - .name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})}, - .ip_address{ip_addr}, - .subnet_mask = in_addr{.S_un{.S_addr{mask}}}, - .gateway = gateway}); - } - - return result; -} - -#else - -std::vector<NetworkInterface> GetAvailableNetworkInterfaces() { - struct ifaddrs* ifaddr = nullptr; - - if (getifaddrs(&ifaddr) != 0) { - LOG_ERROR(Network, "Failed to get network interfaces with getifaddrs: {}", - std::strerror(errno)); - return {}; - } - - std::vector<NetworkInterface> result; - - for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { - if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr) { - continue; - } - - if (ifa->ifa_addr->sa_family != AF_INET) { - continue; - } - - if ((ifa->ifa_flags & IFF_UP) == 0 || (ifa->ifa_flags & IFF_LOOPBACK) != 0) { - continue; - } - - u32 gateway{}; - - std::ifstream file{"/proc/net/route"}; - if (!file.is_open()) { - LOG_ERROR(Network, "Failed to open \"/proc/net/route\""); - - result.emplace_back(NetworkInterface{ - .name{ifa->ifa_name}, - .ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr}, - .subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr}, - .gateway{in_addr{.s_addr = gateway}}}); - continue; - } - - // ignore header - file.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); - - bool gateway_found = false; - - for (std::string line; std::getline(file, line);) { - std::istringstream iss{line}; - - std::string iface_name; - iss >> iface_name; - if (iface_name != ifa->ifa_name) { - continue; - } - - iss >> std::hex; - - u32 dest{}; - iss >> dest; - if (dest != 0) { - // not the default route - continue; - } - - iss >> gateway; - - u16 flags{}; - iss >> flags; - - // flag RTF_GATEWAY (defined in <linux/route.h>) - if ((flags & 0x2) == 0) { - continue; - } - - gateway_found = true; - break; - } - - if (!gateway_found) { - gateway = 0; - } - - result.emplace_back(NetworkInterface{ - .name{ifa->ifa_name}, - .ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr}, - .subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr}, - .gateway{in_addr{.s_addr = gateway}}}); - } - - freeifaddrs(ifaddr); - - return result; -} - -#endif - -std::optional<NetworkInterface> GetSelectedNetworkInterface() { - const auto& selected_network_interface = Settings::values.network_interface.GetValue(); - const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); - if (network_interfaces.size() == 0) { - LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); - return std::nullopt; - } - - const auto res = - std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) { - return iface.name == selected_network_interface; - }); - - if (res == network_interfaces.end()) { - LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); - return std::nullopt; - } - - return *res; -} - -} // namespace Network diff --git a/src/core/network/network_interface.h b/src/core/network/network_interface.h deleted file mode 100644 index 980edb2f5..000000000 --- a/src/core/network/network_interface.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2021 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <optional> -#include <string> -#include <vector> - -#ifdef _WIN32 -#include <winsock2.h> -#else -#include <netinet/in.h> -#endif - -namespace Network { - -struct NetworkInterface { - std::string name; - struct in_addr ip_address; - struct in_addr subnet_mask; - struct in_addr gateway; -}; - -std::vector<NetworkInterface> GetAvailableNetworkInterfaces(); -std::optional<NetworkInterface> GetSelectedNetworkInterface(); - -} // namespace Network diff --git a/src/core/network/sockets.h b/src/core/network/sockets.h deleted file mode 100644 index a44393325..000000000 --- a/src/core/network/sockets.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <memory> -#include <utility> - -#if defined(_WIN32) -#include <winsock.h> -#elif !YUZU_UNIX -#error "Platform not implemented" -#endif - -#include "common/common_types.h" -#include "core/network/network.h" - -// TODO: C++20 Replace std::vector usages with std::span - -namespace Network { - -class Socket { -public: - struct AcceptResult { - std::unique_ptr<Socket> socket; - SockAddrIn sockaddr_in; - }; - - explicit Socket() = default; - ~Socket(); - - Socket(const Socket&) = delete; - Socket& operator=(const Socket&) = delete; - - Socket(Socket&& rhs) noexcept; - - // Avoid closing sockets implicitly - Socket& operator=(Socket&&) noexcept = delete; - - Errno Initialize(Domain domain, Type type, Protocol protocol); - - Errno Close(); - - std::pair<AcceptResult, Errno> Accept(); - - Errno Connect(SockAddrIn addr_in); - - std::pair<SockAddrIn, Errno> GetPeerName(); - - std::pair<SockAddrIn, Errno> GetSockName(); - - Errno Bind(SockAddrIn addr); - - Errno Listen(s32 backlog); - - Errno Shutdown(ShutdownHow how); - - std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message); - - std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr); - - std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags); - - std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, const SockAddrIn* addr); - - Errno SetLinger(bool enable, u32 linger); - - Errno SetReuseAddr(bool enable); - - Errno SetBroadcast(bool enable); - - Errno SetSndBuf(u32 value); - - Errno SetRcvBuf(u32 value); - - Errno SetSndTimeo(u32 value); - - Errno SetRcvTimeo(u32 value); - - Errno SetNonBlock(bool enable); - - bool IsOpened() const; - -#if defined(_WIN32) - SOCKET fd = INVALID_SOCKET; -#elif YUZU_UNIX - int fd = -1; -#endif -}; - -std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout); - -} // namespace Network diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index 52c43c857..f09c176f8 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -1,6 +1,5 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include <chrono> @@ -53,13 +52,13 @@ PerfStats::~PerfStats() { } void PerfStats::BeginSystemFrame() { - std::lock_guard lock{object_mutex}; + std::scoped_lock lock{object_mutex}; frame_begin = Clock::now(); } void PerfStats::EndSystemFrame() { - std::lock_guard lock{object_mutex}; + std::scoped_lock lock{object_mutex}; auto frame_end = Clock::now(); const auto frame_time = frame_end - frame_begin; @@ -79,7 +78,7 @@ void PerfStats::EndGameFrame() { } double PerfStats::GetMeanFrametime() const { - std::lock_guard lock{object_mutex}; + std::scoped_lock lock{object_mutex}; if (current_index <= IgnoreFrames) { return 0; @@ -91,7 +90,7 @@ double PerfStats::GetMeanFrametime() const { } PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) { - std::lock_guard lock{object_mutex}; + std::scoped_lock lock{object_mutex}; const auto now = Clock::now(); // Walltime elapsed since stats were reset @@ -120,7 +119,7 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us } double PerfStats::GetLastFrameTimeScale() const { - std::lock_guard lock{object_mutex}; + std::scoped_lock lock{object_mutex}; constexpr double FRAME_LENGTH = 1.0 / 60; return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH; diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h index 816202588..dd6becc02 100644 --- a/src/core/perf_stats.h +++ b/src/core/perf_stats.h @@ -1,6 +1,5 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index d4becdc0a..6e21296f6 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <ctime> #include <fstream> @@ -8,7 +7,6 @@ #include <fmt/chrono.h> #include <fmt/format.h> -#include <fmt/ostream.h> #include <nlohmann/json.hpp> #include "common/fs/file.h" @@ -65,7 +63,7 @@ json GetYuzuVersionData() { }; } -json GetReportCommonData(u64 title_id, ResultCode result, const std::string& timestamp, +json GetReportCommonData(u64 title_id, Result result, const std::string& timestamp, std::optional<u128> user_id = {}) { auto out = json{ {"title_id", fmt::format("{:016X}", title_id)}, @@ -200,8 +198,8 @@ Reporter::Reporter(System& system_) : system(system_) { Reporter::~Reporter() = default; -void Reporter::SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point, - u64 sp, u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far, +void Reporter::SaveCrashReport(u64 title_id, Result result, u64 set_flags, u64 entry_point, u64 sp, + u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far, const std::array<u64, 31>& registers, const std::array<u64, 32>& backtrace, u32 backtrace_size, const std::string& arch, u32 unk10) const { @@ -340,7 +338,7 @@ void Reporter::SavePlayReport(PlayReportType type, u64 title_id, std::vector<std SaveToFile(std::move(out), GetPath("play_report", title_id, timestamp)); } -void Reporter::SaveErrorReport(u64 title_id, ResultCode result, +void Reporter::SaveErrorReport(u64 title_id, Result result, std::optional<std::string> custom_text_main, std::optional<std::string> custom_text_detail) const { if (!IsReportingEnabled()) { diff --git a/src/core/reporter.h b/src/core/reporter.h index 6e9edeea3..68755cbde 100644 --- a/src/core/reporter.h +++ b/src/core/reporter.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -10,7 +9,7 @@ #include <vector> #include "common/common_types.h" -union ResultCode; +union Result; namespace Kernel { class HLERequestContext; @@ -30,7 +29,7 @@ public: ~Reporter(); // Used by fatal services - void SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point, u64 sp, + void SaveCrashReport(u64 title_id, Result result, u64 set_flags, u64 entry_point, u64 sp, u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far, const std::array<u64, 31>& registers, const std::array<u64, 32>& backtrace, u32 backtrace_size, const std::string& arch, u32 unk10) const; @@ -61,7 +60,7 @@ public: std::optional<u64> process_id = {}, std::optional<u128> user_id = {}) const; // Used by error applet - void SaveErrorReport(u64 title_id, ResultCode result, + void SaveErrorReport(u64 title_id, Result result, std::optional<std::string> custom_text_main = {}, std::optional<std::string> custom_text_detail = {}) const; diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 654db0b52..abcf6eb11 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -1,6 +1,5 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <array> diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h index 6f3d45bea..887dc98f3 100644 --- a/src/core/telemetry_session.h +++ b/src/core/telemetry_session.h @@ -1,6 +1,5 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp index 032c71aff..98ebbbf32 100644 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" #include "common/logging/log.h" @@ -26,7 +25,6 @@ u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) { return memory.Read64(addr); default: UNREACHABLE(); - return 0; } } @@ -55,8 +53,10 @@ Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& m : core_timing{core_timing_}, memory{memory_} { event = Core::Timing::CreateEvent( "MemoryFreezer::FrameCallback", - [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + [this](std::uintptr_t user_data, s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { FrameCallback(user_data, ns_late); + return std::nullopt; }); core_timing.ScheduleEvent(memory_freezer_ns, event); } @@ -80,7 +80,7 @@ bool Freezer::IsActive() const { } void Freezer::Clear() { - std::lock_guard lock{entries_mutex}; + std::scoped_lock lock{entries_mutex}; LOG_DEBUG(Common_Memory, "Clearing all frozen memory values."); @@ -88,7 +88,7 @@ void Freezer::Clear() { } u64 Freezer::Freeze(VAddr address, u32 width) { - std::lock_guard lock{entries_mutex}; + std::scoped_lock lock{entries_mutex}; const auto current_value = MemoryReadWidth(memory, width, address); entries.push_back({address, width, current_value}); @@ -101,7 +101,7 @@ u64 Freezer::Freeze(VAddr address, u32 width) { } void Freezer::Unfreeze(VAddr address) { - std::lock_guard lock{entries_mutex}; + std::scoped_lock lock{entries_mutex}; LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address); @@ -109,13 +109,13 @@ void Freezer::Unfreeze(VAddr address) { } bool Freezer::IsFrozen(VAddr address) const { - std::lock_guard lock{entries_mutex}; + std::scoped_lock lock{entries_mutex}; return FindEntry(address) != entries.cend(); } void Freezer::SetFrozenValue(VAddr address, u64 value) { - std::lock_guard lock{entries_mutex}; + std::scoped_lock lock{entries_mutex}; const auto iter = FindEntry(address); @@ -132,7 +132,7 @@ void Freezer::SetFrozenValue(VAddr address, u64 value) { } std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const { - std::lock_guard lock{entries_mutex}; + std::scoped_lock lock{entries_mutex}; const auto iter = FindEntry(address); @@ -144,7 +144,7 @@ std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const { } std::vector<Freezer::Entry> Freezer::GetEntries() const { - std::lock_guard lock{entries_mutex}; + std::scoped_lock lock{entries_mutex}; return entries; } @@ -165,7 +165,7 @@ void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) { return; } - std::lock_guard lock{entries_mutex}; + std::scoped_lock lock{entries_mutex}; for (const auto& entry : entries) { LOG_DEBUG(Common_Memory, @@ -178,7 +178,7 @@ void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) { } void Freezer::FillEntryReads() { - std::lock_guard lock{entries_mutex}; + std::scoped_lock lock{entries_mutex}; LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values."); diff --git a/src/core/tools/freezer.h b/src/core/tools/freezer.h index 067134e93..0d6df5217 100644 --- a/src/core/tools/freezer.h +++ b/src/core/tools/freezer.h @@ -1,6 +1,5 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once |