From 92eb091ddb9dfd96e59a75937e185079a63626e3 Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 4 Oct 2022 20:15:40 -0400 Subject: kernel/svc: Split implementations into separate files --- src/core/hle/kernel/svc/svc_activity.cpp | 44 +++ src/core/hle/kernel/svc/svc_address_arbiter.cpp | 113 ++++++ .../hle/kernel/svc/svc_address_translation.cpp | 6 + src/core/hle/kernel/svc/svc_cache.cpp | 31 ++ src/core/hle/kernel/svc/svc_code_memory.cpp | 154 ++++++++ src/core/hle/kernel/svc/svc_condition_variable.cpp | 69 ++++ src/core/hle/kernel/svc/svc_debug.cpp | 6 + src/core/hle/kernel/svc/svc_debug_string.cpp | 25 ++ .../hle/kernel/svc/svc_device_address_space.cpp | 6 + src/core/hle/kernel/svc/svc_event.cpp | 111 ++++++ src/core/hle/kernel/svc/svc_exception.cpp | 121 +++++++ src/core/hle/kernel/svc/svc_info.cpp | 282 +++++++++++++++ src/core/hle/kernel/svc/svc_interrupt_event.cpp | 6 + src/core/hle/kernel/svc/svc_io_pool.cpp | 6 + src/core/hle/kernel/svc/svc_ipc.cpp | 89 +++++ src/core/hle/kernel/svc/svc_kernel_debug.cpp | 19 + src/core/hle/kernel/svc/svc_light_ipc.cpp | 6 + src/core/hle/kernel/svc/svc_lock.cpp | 57 +++ src/core/hle/kernel/svc/svc_memory.cpp | 189 ++++++++++ src/core/hle/kernel/svc/svc_physical_memory.cpp | 137 +++++++ src/core/hle/kernel/svc/svc_port.cpp | 71 ++++ src/core/hle/kernel/svc/svc_power_management.cpp | 6 + src/core/hle/kernel/svc/svc_process.cpp | 124 +++++++ src/core/hle/kernel/svc/svc_process_memory.cpp | 274 ++++++++++++++ src/core/hle/kernel/svc/svc_processor.cpp | 21 ++ src/core/hle/kernel/svc/svc_query_memory.cpp | 55 +++ src/core/hle/kernel/svc/svc_register.cpp | 6 + src/core/hle/kernel/svc/svc_resource_limit.cpp | 95 +++++ .../hle/kernel/svc/svc_secure_monitor_call.cpp | 6 + src/core/hle/kernel/svc/svc_session.cpp | 103 ++++++ src/core/hle/kernel/svc/svc_shared_memory.cpp | 106 ++++++ src/core/hle/kernel/svc/svc_synchronization.cpp | 139 ++++++++ src/core/hle/kernel/svc/svc_thread.cpp | 396 +++++++++++++++++++++ src/core/hle/kernel/svc/svc_thread_profiler.cpp | 6 + src/core/hle/kernel/svc/svc_tick.cpp | 33 ++ src/core/hle/kernel/svc/svc_transfer_memory.cpp | 79 ++++ 36 files changed, 2997 insertions(+) create mode 100644 src/core/hle/kernel/svc/svc_activity.cpp create mode 100644 src/core/hle/kernel/svc/svc_address_arbiter.cpp create mode 100644 src/core/hle/kernel/svc/svc_address_translation.cpp create mode 100644 src/core/hle/kernel/svc/svc_cache.cpp create mode 100644 src/core/hle/kernel/svc/svc_code_memory.cpp create mode 100644 src/core/hle/kernel/svc/svc_condition_variable.cpp create mode 100644 src/core/hle/kernel/svc/svc_debug.cpp create mode 100644 src/core/hle/kernel/svc/svc_debug_string.cpp create mode 100644 src/core/hle/kernel/svc/svc_device_address_space.cpp create mode 100644 src/core/hle/kernel/svc/svc_event.cpp create mode 100644 src/core/hle/kernel/svc/svc_exception.cpp create mode 100644 src/core/hle/kernel/svc/svc_info.cpp create mode 100644 src/core/hle/kernel/svc/svc_interrupt_event.cpp create mode 100644 src/core/hle/kernel/svc/svc_io_pool.cpp create mode 100644 src/core/hle/kernel/svc/svc_ipc.cpp create mode 100644 src/core/hle/kernel/svc/svc_kernel_debug.cpp create mode 100644 src/core/hle/kernel/svc/svc_light_ipc.cpp create mode 100644 src/core/hle/kernel/svc/svc_lock.cpp create mode 100644 src/core/hle/kernel/svc/svc_memory.cpp create mode 100644 src/core/hle/kernel/svc/svc_physical_memory.cpp create mode 100644 src/core/hle/kernel/svc/svc_port.cpp create mode 100644 src/core/hle/kernel/svc/svc_power_management.cpp create mode 100644 src/core/hle/kernel/svc/svc_process.cpp create mode 100644 src/core/hle/kernel/svc/svc_process_memory.cpp create mode 100644 src/core/hle/kernel/svc/svc_processor.cpp create mode 100644 src/core/hle/kernel/svc/svc_query_memory.cpp create mode 100644 src/core/hle/kernel/svc/svc_register.cpp create mode 100644 src/core/hle/kernel/svc/svc_resource_limit.cpp create mode 100644 src/core/hle/kernel/svc/svc_secure_monitor_call.cpp create mode 100644 src/core/hle/kernel/svc/svc_session.cpp create mode 100644 src/core/hle/kernel/svc/svc_shared_memory.cpp create mode 100644 src/core/hle/kernel/svc/svc_synchronization.cpp create mode 100644 src/core/hle/kernel/svc/svc_thread.cpp create mode 100644 src/core/hle/kernel/svc/svc_thread_profiler.cpp create mode 100644 src/core/hle/kernel/svc/svc_tick.cpp create mode 100644 src/core/hle/kernel/svc/svc_transfer_memory.cpp (limited to 'src/core/hle/kernel/svc') diff --git a/src/core/hle/kernel/svc/svc_activity.cpp b/src/core/hle/kernel/svc/svc_activity.cpp new file mode 100644 index 000000000..8774a5c98 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_activity.cpp @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_thread.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/kernel/svc_results.h" + +namespace Kernel::Svc { + +/// Sets the thread activity +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); + + // Validate the activity. + constexpr auto IsValidThreadActivity = [](ThreadActivity activity) { + return activity == ThreadActivity::Runnable || activity == ThreadActivity::Paused; + }; + R_UNLESS(IsValidThreadActivity(thread_activity), ResultInvalidEnumValue); + + // Get the thread from its handle. + KScopedAutoObject thread = + system.Kernel().CurrentProcess()->GetHandleTable().GetObject(thread_handle); + R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); + + // Check that the activity is being set on a non-current thread for the current process. + R_UNLESS(thread->GetOwnerProcess() == system.Kernel().CurrentProcess(), ResultInvalidHandle); + R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(system.Kernel()), ResultBusy); + + // Set the activity. + R_TRY(thread->SetActivity(thread_activity)); + + return ResultSuccess; +} + +Result SetThreadActivity32(Core::System& system, Handle thread_handle, + ThreadActivity thread_activity) { + return SetThreadActivity(system, thread_handle, thread_activity); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_address_arbiter.cpp b/src/core/hle/kernel/svc/svc_address_arbiter.cpp new file mode 100644 index 000000000..842107726 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_address_arbiter.cpp @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_memory_layout.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/kernel/svc_results.h" +#include "core/hle/kernel/svc_types.h" + +namespace Kernel::Svc { +namespace { + +constexpr bool IsValidSignalType(Svc::SignalType type) { + switch (type) { + case Svc::SignalType::Signal: + case Svc::SignalType::SignalAndIncrementIfEqual: + case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual: + return true; + default: + return false; + } +} + +constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) { + switch (type) { + case Svc::ArbitrationType::WaitIfLessThan: + case Svc::ArbitrationType::DecrementAndWaitIfLessThan: + case Svc::ArbitrationType::WaitIfEqual: + return true; + default: + return false; + } +} + +} // namespace + +// Wait for an address (via Address Arbiter) +Result WaitForAddress(Core::System& system, VAddr address, 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); + + // Validate input. + if (IsKernelAddress(address)) { + LOG_ERROR(Kernel_SVC, "Attempting to wait on kernel address (address={:08X})", address); + return ResultInvalidCurrentMemory; + } + if (!Common::IsAligned(address, sizeof(s32))) { + LOG_ERROR(Kernel_SVC, "Wait address must be 4 byte aligned (address={:08X})", address); + return ResultInvalidAddress; + } + if (!IsValidArbitrationType(arb_type)) { + LOG_ERROR(Kernel_SVC, "Invalid arbitration type specified (type={})", arb_type); + return ResultInvalidEnumValue; + } + + // Convert timeout from nanoseconds to ticks. + s64 timeout{}; + if (timeout_ns > 0) { + const s64 offset_tick(timeout_ns); + if (offset_tick > 0) { + timeout = offset_tick + 2; + if (timeout <= 0) { + timeout = std::numeric_limits::max(); + } + } else { + timeout = std::numeric_limits::max(); + } + } else { + timeout = timeout_ns; + } + + return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout); +} + +Result WaitForAddress32(Core::System& system, u32 address, ArbitrationType arb_type, s32 value, + u32 timeout_ns_low, u32 timeout_ns_high) { + const auto timeout = static_cast(timeout_ns_low | (u64{timeout_ns_high} << 32)); + return WaitForAddress(system, address, arb_type, value, timeout); +} + +// Signals to an address (via Address Arbiter) +Result SignalToAddress(Core::System& system, VAddr address, 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); + + // Validate input. + if (IsKernelAddress(address)) { + LOG_ERROR(Kernel_SVC, "Attempting to signal to a kernel address (address={:08X})", address); + return ResultInvalidCurrentMemory; + } + if (!Common::IsAligned(address, sizeof(s32))) { + LOG_ERROR(Kernel_SVC, "Signaled address must be 4 byte aligned (address={:08X})", address); + return ResultInvalidAddress; + } + if (!IsValidSignalType(signal_type)) { + LOG_ERROR(Kernel_SVC, "Invalid signal type specified (type={})", signal_type); + return ResultInvalidEnumValue; + } + + return system.Kernel().CurrentProcess()->SignalAddressArbiter(address, signal_type, value, + count); +} + +Result SignalToAddress32(Core::System& system, u32 address, SignalType signal_type, s32 value, + s32 count) { + return SignalToAddress(system, address, signal_type, value, count); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_address_translation.cpp b/src/core/hle/kernel/svc/svc_address_translation.cpp new file mode 100644 index 000000000..299e22ae6 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_address_translation.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc {} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_cache.cpp b/src/core/hle/kernel/svc/svc_cache.cpp new file mode 100644 index 000000000..42167d35b --- /dev/null +++ b/src/core/hle/kernel/svc/svc_cache.cpp @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/kernel/svc_results.h" +#include "core/hle/kernel/svc_types.h" + +namespace Kernel::Svc { + +Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address, u64 size) { + // Validate address/size. + R_UNLESS(size > 0, ResultInvalidSize); + R_UNLESS(address == static_cast(address), ResultInvalidCurrentMemory); + R_UNLESS(size == static_cast(size), ResultInvalidCurrentMemory); + + // Get the process from its handle. + KScopedAutoObject process = + system.Kernel().CurrentProcess()->GetHandleTable().GetObject(process_handle); + R_UNLESS(process.IsNotNull(), ResultInvalidHandle); + + // Verify the region is within range. + auto& page_table = process->PageTable(); + R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); + + // Perform the operation. + R_RETURN(system.Memory().FlushDataCache(*process, address, size)); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_code_memory.cpp b/src/core/hle/kernel/svc/svc_code_memory.cpp new file mode 100644 index 000000000..4cb21e101 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_code_memory.cpp @@ -0,0 +1,154 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_code_memory.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { +namespace { + +constexpr bool IsValidMapCodeMemoryPermission(MemoryPermission perm) { + return perm == MemoryPermission::ReadWrite; +} + +constexpr bool IsValidMapToOwnerCodeMemoryPermission(MemoryPermission perm) { + return perm == MemoryPermission::Read || perm == MemoryPermission::ReadExecute; +} + +constexpr bool IsValidUnmapCodeMemoryPermission(MemoryPermission perm) { + return perm == MemoryPermission::None; +} + +constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(MemoryPermission perm) { + return perm == MemoryPermission::None; +} + +} // namespace + +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(); + + // Validate address / size. + R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); + R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); + R_UNLESS(size > 0, ResultInvalidSize); + R_UNLESS((address < address + size), ResultInvalidCurrentMemory); + + // Create the code memory. + + KCodeMemory* code_mem = KCodeMemory::Create(kernel); + R_UNLESS(code_mem != nullptr, ResultOutOfResource); + + // Verify that the region is in range. + R_UNLESS(system.CurrentProcess()->PageTable().Contains(address, size), + ResultInvalidCurrentMemory); + + // Initialize the code memory. + R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size)); + + // Register the code memory. + KCodeMemory::Register(kernel, code_mem); + + // Add the code memory to the handle table. + R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, code_mem)); + + code_mem->Close(); + + return ResultSuccess; +} + +Result CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size) { + return CreateCodeMemory(system, out, address, size); +} + +Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation, + VAddr address, size_t size, MemoryPermission perm) { + + LOG_TRACE(Kernel_SVC, + "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, " + "permission=0x{:X}", + code_memory_handle, operation, address, size, perm); + + // Validate the address / size. + R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); + R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); + R_UNLESS(size > 0, ResultInvalidSize); + R_UNLESS((address < address + size), ResultInvalidCurrentMemory); + + // Get the code memory from its handle. + KScopedAutoObject code_mem = + system.CurrentProcess()->GetHandleTable().GetObject(code_memory_handle); + R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle); + + // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process. + // This enables homebrew usage of these SVCs for JIT. + + // Perform the operation. + switch (static_cast(operation)) { + case CodeMemoryOperation::Map: { + // Check that the region is in range. + R_UNLESS( + system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut), + ResultInvalidMemoryRegion); + + // Check the memory permission. + R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); + + // Map the memory. + R_TRY(code_mem->Map(address, size)); + } break; + case CodeMemoryOperation::Unmap: { + // Check that the region is in range. + R_UNLESS( + system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut), + ResultInvalidMemoryRegion); + + // Check the memory permission. + R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); + + // Unmap the memory. + R_TRY(code_mem->Unmap(address, size)); + } break; + case CodeMemoryOperation::MapToOwner: { + // Check that the region is in range. + R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size, + KMemoryState::GeneratedCode), + ResultInvalidMemoryRegion); + + // Check the memory permission. + R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); + + // Map the memory to its owner. + R_TRY(code_mem->MapToOwner(address, size, perm)); + } break; + case CodeMemoryOperation::UnmapFromOwner: { + // Check that the region is in range. + R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size, + KMemoryState::GeneratedCode), + ResultInvalidMemoryRegion); + + // Check the memory permission. + R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); + + // Unmap the memory from its owner. + R_TRY(code_mem->UnmapFromOwner(address, size)); + } break; + default: + return ResultInvalidEnumValue; + } + + return ResultSuccess; +} + +Result ControlCodeMemory32(Core::System& system, Handle code_memory_handle, u32 operation, + u64 address, u64 size, MemoryPermission perm) { + return ControlCodeMemory(system, code_memory_handle, operation, address, size, perm); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_condition_variable.cpp b/src/core/hle/kernel/svc/svc_condition_variable.cpp new file mode 100644 index 000000000..d6cfc87c5 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_condition_variable.cpp @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_memory_layout.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/kernel/svc_results.h" + +namespace Kernel::Svc { + +/// Wait process wide key atomic +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); + + // Validate input. + if (IsKernelAddress(address)) { + LOG_ERROR(Kernel_SVC, "Attempted to wait on kernel address (address={:08X})", address); + return ResultInvalidCurrentMemory; + } + if (!Common::IsAligned(address, sizeof(s32))) { + LOG_ERROR(Kernel_SVC, "Address must be 4 byte aligned (address={:08X})", address); + return ResultInvalidAddress; + } + + // Convert timeout from nanoseconds to ticks. + s64 timeout{}; + if (timeout_ns > 0) { + const s64 offset_tick(timeout_ns); + if (offset_tick > 0) { + timeout = offset_tick + 2; + if (timeout <= 0) { + timeout = std::numeric_limits::max(); + } + } else { + timeout = std::numeric_limits::max(); + } + } else { + timeout = timeout_ns; + } + + // Wait on the condition variable. + return system.Kernel().CurrentProcess()->WaitConditionVariable( + address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout); +} + +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(timeout_ns_low | (u64{timeout_ns_high} << 32)); + return WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns); +} + +/// Signal process wide key +void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count) { + LOG_TRACE(Kernel_SVC, "called, cv_key=0x{:X}, count=0x{:08X}", cv_key, count); + + // Signal the condition variable. + return system.Kernel().CurrentProcess()->SignalConditionVariable( + Common::AlignDown(cv_key, sizeof(u32)), count); +} + +void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count) { + SignalProcessWideKey(system, cv_key, count); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_debug.cpp b/src/core/hle/kernel/svc/svc_debug.cpp new file mode 100644 index 000000000..299e22ae6 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_debug.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc {} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_debug_string.cpp b/src/core/hle/kernel/svc/svc_debug_string.cpp new file mode 100644 index 000000000..486e62cc4 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_debug_string.cpp @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/svc.h" +#include "core/memory.h" + +namespace Kernel::Svc { + +/// Used to output a message on a debug hardware unit - does nothing on a retail unit +void OutputDebugString(Core::System& system, VAddr address, u64 len) { + if (len == 0) { + return; + } + + std::string str(len, '\0'); + system.Memory().ReadBlock(address, str.data(), str.size()); + LOG_DEBUG(Debug_Emulated, "{}", str); +} + +void OutputDebugString32(Core::System& system, u32 address, u32 len) { + OutputDebugString(system, address, len); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_device_address_space.cpp b/src/core/hle/kernel/svc/svc_device_address_space.cpp new file mode 100644 index 000000000..299e22ae6 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_device_address_space.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc {} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_event.cpp b/src/core/hle/kernel/svc/svc_event.cpp new file mode 100644 index 000000000..885f02f50 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_event.cpp @@ -0,0 +1,111 @@ +// SPDX-FileCopyrightText: Copyright 2023 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_event.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_scoped_resource_reservation.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +Result SignalEvent(Core::System& system, Handle event_handle) { + LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle); + + // Get the current handle table. + const KHandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + + // Get the event. + KScopedAutoObject event = handle_table.GetObject(event_handle); + R_UNLESS(event.IsNotNull(), ResultInvalidHandle); + + return event->Signal(); +} + +Result SignalEvent32(Core::System& system, Handle event_handle) { + return SignalEvent(system, event_handle); +} + +Result ClearEvent(Core::System& system, Handle event_handle) { + LOG_TRACE(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle); + + // Get the current handle table. + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + + // Try to clear the writable event. + { + KScopedAutoObject event = handle_table.GetObject(event_handle); + if (event.IsNotNull()) { + return event->Clear(); + } + } + + // Try to clear the readable event. + { + KScopedAutoObject readable_event = handle_table.GetObject(event_handle); + if (readable_event.IsNotNull()) { + return readable_event->Clear(); + } + } + + LOG_ERROR(Kernel_SVC, "Event handle does not exist, event_handle=0x{:08X}", event_handle); + + return ResultInvalidHandle; +} + +Result ClearEvent32(Core::System& system, Handle event_handle) { + return ClearEvent(system, event_handle); +} + +Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) { + LOG_DEBUG(Kernel_SVC, "called"); + + // Get the kernel reference and handle table. + auto& kernel = system.Kernel(); + auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); + + // Reserve a new event from the process resource limit + KScopedResourceReservation event_reservation(kernel.CurrentProcess(), + LimitableResource::EventCountMax); + R_UNLESS(event_reservation.Succeeded(), ResultLimitReached); + + // Create a new event. + KEvent* event = KEvent::Create(kernel); + R_UNLESS(event != nullptr, ResultOutOfResource); + + // Initialize the event. + event->Initialize(kernel.CurrentProcess()); + + // Commit the thread reservation. + event_reservation.Commit(); + + // Ensure that we clean up the event (and its only references are handle table) on function end. + SCOPE_EXIT({ + event->GetReadableEvent().Close(); + event->Close(); + }); + + // Register the event. + KEvent::Register(kernel, event); + + // Add the event to the handle table. + R_TRY(handle_table.Add(out_write, event)); + + // Ensure that we maintaing a clean handle state on exit. + auto handle_guard = SCOPE_GUARD({ handle_table.Remove(*out_write); }); + + // Add the readable event to the handle table. + R_TRY(handle_table.Add(out_read, std::addressof(event->GetReadableEvent()))); + + // We succeeded. + handle_guard.Cancel(); + return ResultSuccess; +} + +Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read) { + return CreateEvent(system, out_write, out_read); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_exception.cpp b/src/core/hle/kernel/svc/svc_exception.cpp new file mode 100644 index 000000000..fb9f133c1 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_exception.cpp @@ -0,0 +1,121 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/debugger/debugger.h" +#include "core/hle/kernel/k_thread.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/kernel/svc_types.h" +#include "core/memory.h" +#include "core/reporter.h" + +namespace Kernel::Svc { + +/// Break program execution +void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { + BreakReason break_reason = + static_cast(reason & ~static_cast(BreakReason::NotificationOnlyFlag)); + bool notification_only = (reason & static_cast(BreakReason::NotificationOnlyFlag)) != 0; + + bool has_dumped_buffer{}; + std::vector debug_buffer; + + const auto handle_debug_buffer = [&](VAddr addr, u64 sz) { + if (sz == 0 || addr == 0 || has_dumped_buffer) { + return; + } + + auto& memory = system.Memory(); + + // This typically is an error code so we're going to assume this is the case + if (sz == sizeof(u32)) { + LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", memory.Read32(addr)); + } else { + // We don't know what's in here so we'll hexdump it + debug_buffer.resize(sz); + memory.ReadBlock(addr, debug_buffer.data(), sz); + std::string hexdump; + for (std::size_t i = 0; i < debug_buffer.size(); i++) { + hexdump += fmt::format("{:02X} ", debug_buffer[i]); + if (i != 0 && i % 16 == 0) { + hexdump += '\n'; + } + } + LOG_CRITICAL(Debug_Emulated, "debug_buffer=\n{}", hexdump); + } + has_dumped_buffer = true; + }; + switch (break_reason) { + case BreakReason::Panic: + LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1, + info2); + handle_debug_buffer(info1, info2); + break; + case BreakReason::Assert: + LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}", + info1, info2); + handle_debug_buffer(info1, info2); + break; + case BreakReason::User: + LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2); + handle_debug_buffer(info1, info2); + break; + case BreakReason::PreLoadDll: + LOG_INFO(Debug_Emulated, + "Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1, + info2); + break; + case BreakReason::PostLoadDll: + LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1, + info2); + break; + case BreakReason::PreUnloadDll: + LOG_INFO(Debug_Emulated, + "Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1, + info2); + break; + case BreakReason::PostUnloadDll: + LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}", + info1, info2); + break; + case BreakReason::CppException: + LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered."); + break; + default: + LOG_WARNING( + Debug_Emulated, + "Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}", + reason, info1, info2); + handle_debug_buffer(info1, info2); + break; + } + + system.GetReporter().SaveSvcBreakReport(reason, notification_only, info1, info2, + has_dumped_buffer ? std::make_optional(debug_buffer) + : std::nullopt); + + if (!notification_only) { + LOG_CRITICAL( + Debug_Emulated, + "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", + reason, info1, info2); + + handle_debug_buffer(info1, info2); + + auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); + const auto thread_processor_id = current_thread->GetActiveCore(); + system.ArmInterface(static_cast(thread_processor_id)).LogBacktrace(); + } + + if (system.DebuggerEnabled()) { + auto* thread = system.Kernel().GetCurrentEmuThread(); + system.GetDebugger().NotifyThreadStopped(thread); + thread->RequestSuspend(Kernel::SuspendType::Debug); + } +} + +void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) { + Break(system, reason, info1, info2); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_info.cpp b/src/core/hle/kernel/svc/svc_info.cpp new file mode 100644 index 000000000..df5dd85a4 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_info.cpp @@ -0,0 +1,282 @@ +// SPDX-FileCopyrightText: Copyright 2023 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_process.h" +#include "core/hle/kernel/k_resource_limit.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +/// Gets system/memory information for the current process +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); + + const auto info_id_type = static_cast(info_id); + + switch (info_id_type) { + case InfoType::CoreMask: + case InfoType::PriorityMask: + case InfoType::AliasRegionAddress: + case InfoType::AliasRegionSize: + case InfoType::HeapRegionAddress: + case InfoType::HeapRegionSize: + case InfoType::AslrRegionAddress: + case InfoType::AslrRegionSize: + case InfoType::StackRegionAddress: + case InfoType::StackRegionSize: + case InfoType::TotalMemorySize: + case InfoType::UsedMemorySize: + case InfoType::SystemResourceSizeTotal: + case InfoType::SystemResourceSizeUsed: + case InfoType::ProgramId: + case InfoType::UserExceptionContextAddress: + case InfoType::TotalNonSystemMemorySize: + case InfoType::UsedNonSystemMemorySize: + case InfoType::IsApplication: + case InfoType::FreeThreadCount: { + if (info_sub_id != 0) { + LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id, + info_sub_id); + return ResultInvalidEnumValue; + } + + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + KScopedAutoObject process = handle_table.GetObject(handle); + if (process.IsNull()) { + LOG_ERROR(Kernel_SVC, "Process is not valid! info_id={}, info_sub_id={}, handle={:08X}", + info_id, info_sub_id, handle); + return ResultInvalidHandle; + } + + switch (info_id_type) { + case InfoType::CoreMask: + *result = process->GetCoreMask(); + return ResultSuccess; + + case InfoType::PriorityMask: + *result = process->GetPriorityMask(); + return ResultSuccess; + + case InfoType::AliasRegionAddress: + *result = process->PageTable().GetAliasRegionStart(); + return ResultSuccess; + + case InfoType::AliasRegionSize: + *result = process->PageTable().GetAliasRegionSize(); + return ResultSuccess; + + case InfoType::HeapRegionAddress: + *result = process->PageTable().GetHeapRegionStart(); + return ResultSuccess; + + case InfoType::HeapRegionSize: + *result = process->PageTable().GetHeapRegionSize(); + return ResultSuccess; + + case InfoType::AslrRegionAddress: + *result = process->PageTable().GetAliasCodeRegionStart(); + return ResultSuccess; + + case InfoType::AslrRegionSize: + *result = process->PageTable().GetAliasCodeRegionSize(); + return ResultSuccess; + + case InfoType::StackRegionAddress: + *result = process->PageTable().GetStackRegionStart(); + return ResultSuccess; + + case InfoType::StackRegionSize: + *result = process->PageTable().GetStackRegionSize(); + return ResultSuccess; + + case InfoType::TotalMemorySize: + *result = process->GetTotalPhysicalMemoryAvailable(); + return ResultSuccess; + + case InfoType::UsedMemorySize: + *result = process->GetTotalPhysicalMemoryUsed(); + return ResultSuccess; + + case InfoType::SystemResourceSizeTotal: + *result = process->GetSystemResourceSize(); + return ResultSuccess; + + case InfoType::SystemResourceSizeUsed: + LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage"); + *result = process->GetSystemResourceUsage(); + return ResultSuccess; + + case InfoType::ProgramId: + *result = process->GetProgramID(); + return ResultSuccess; + + case InfoType::UserExceptionContextAddress: + *result = process->GetProcessLocalRegionAddress(); + return ResultSuccess; + + case InfoType::TotalNonSystemMemorySize: + *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource(); + return ResultSuccess; + + case InfoType::UsedNonSystemMemorySize: + *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource(); + return ResultSuccess; + + case InfoType::FreeThreadCount: + *result = process->GetFreeThreadCount(); + return ResultSuccess; + + default: + break; + } + + LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id); + return ResultInvalidEnumValue; + } + + case InfoType::DebuggerAttached: + *result = 0; + return ResultSuccess; + + case InfoType::ResourceLimit: { + if (handle != 0) { + LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle); + return ResultInvalidHandle; + } + + if (info_sub_id != 0) { + LOG_ERROR(Kernel, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id, + info_sub_id); + return ResultInvalidCombination; + } + + KProcess* const current_process = system.Kernel().CurrentProcess(); + KHandleTable& handle_table = current_process->GetHandleTable(); + const auto resource_limit = current_process->GetResourceLimit(); + if (!resource_limit) { + *result = Svc::InvalidHandle; + // Yes, the kernel considers this a successful operation. + return ResultSuccess; + } + + Handle resource_handle{}; + R_TRY(handle_table.Add(&resource_handle, resource_limit)); + + *result = resource_handle; + return ResultSuccess; + } + + case InfoType::RandomEntropy: + if (handle != 0) { + LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}", + handle); + return ResultInvalidHandle; + } + + if (info_sub_id >= KProcess::RANDOM_ENTROPY_SIZE) { + LOG_ERROR(Kernel_SVC, "Entropy size is out of range, expected {} but got {}", + KProcess::RANDOM_ENTROPY_SIZE, info_sub_id); + return ResultInvalidCombination; + } + + *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id); + return ResultSuccess; + + case InfoType::InitialProcessIdRange: + LOG_WARNING(Kernel_SVC, + "(STUBBED) Attempted to query privileged process id bounds, returned 0"); + *result = 0; + return ResultSuccess; + + case InfoType::ThreadTickCount: { + constexpr u64 num_cpus = 4; + if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) { + LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus, + info_sub_id); + return ResultInvalidCombination; + } + + KScopedAutoObject thread = + system.Kernel().CurrentProcess()->GetHandleTable().GetObject( + static_cast(handle)); + if (thread.IsNull()) { + LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", + static_cast(handle)); + return ResultInvalidHandle; + } + + const auto& core_timing = system.CoreTiming(); + const auto& scheduler = *system.Kernel().CurrentScheduler(); + const auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); + const bool same_thread = current_thread == thread.GetPointerUnsafe(); + + 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(); + + out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); + } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) { + out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks; + } + + *result = out_ticks; + return ResultSuccess; + } + case InfoType::IdleTickCount: { + // Verify the input handle is invalid. + R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); + + // Verify the requested core is valid. + const bool core_valid = + (info_sub_id == 0xFFFFFFFFFFFFFFFF) || + (info_sub_id == static_cast(system.Kernel().CurrentPhysicalCoreIndex())); + R_UNLESS(core_valid, ResultInvalidCombination); + + // Get the idle tick count. + *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime(); + return ResultSuccess; + } + case InfoType::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; + } +} + +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 Result result{GetInfo(system, &res_value, info_id, handle, sub_id)}; + *result_high = static_cast(res_value >> 32); + *result_low = static_cast(res_value & std::numeric_limits::max()); + + return result; +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_interrupt_event.cpp b/src/core/hle/kernel/svc/svc_interrupt_event.cpp new file mode 100644 index 000000000..299e22ae6 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_interrupt_event.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc {} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_io_pool.cpp b/src/core/hle/kernel/svc/svc_io_pool.cpp new file mode 100644 index 000000000..299e22ae6 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_io_pool.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc {} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp new file mode 100644 index 000000000..dbb68e89a --- /dev/null +++ b/src/core/hle/kernel/svc/svc_ipc.cpp @@ -0,0 +1,89 @@ +// SPDX-FileCopyrightText: Copyright 2023 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_client_session.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_server_session.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +/// Makes a blocking IPC call to a service. +Result SendSyncRequest(Core::System& system, Handle handle) { + auto& kernel = system.Kernel(); + + // Get the client session from its handle. + KScopedAutoObject session = + kernel.CurrentProcess()->GetHandleTable().GetObject(handle); + R_UNLESS(session.IsNotNull(), ResultInvalidHandle); + + LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); + + return session->SendSyncRequest(); +} + +Result SendSyncRequest32(Core::System& system, Handle handle) { + return SendSyncRequest(system, handle); +} + +Result ReplyAndReceive(Core::System& system, s32* out_index, Handle* handles, s32 num_handles, + Handle reply_target, s64 timeout_ns) { + auto& kernel = system.Kernel(); + auto& handle_table = GetCurrentThread(kernel).GetOwnerProcess()->GetHandleTable(); + + // Convert handle list to object table. + std::vector objs(num_handles); + R_UNLESS( + handle_table.GetMultipleObjects(objs.data(), handles, num_handles), + ResultInvalidHandle); + + // Ensure handles are closed when we're done. + SCOPE_EXIT({ + for (auto i = 0; i < num_handles; ++i) { + objs[i]->Close(); + } + }); + + // Reply to the target, if one is specified. + if (reply_target != InvalidHandle) { + KScopedAutoObject session = handle_table.GetObject(reply_target); + R_UNLESS(session.IsNotNull(), ResultInvalidHandle); + + // If we fail to reply, we want to set the output index to -1. + ON_RESULT_FAILURE { + *out_index = -1; + }; + + // Send the reply. + R_TRY(session->SendReply()); + } + + // Wait for a message. + while (true) { + // Wait for an object. + s32 index; + Result result = KSynchronizationObject::Wait(kernel, &index, objs.data(), + static_cast(objs.size()), timeout_ns); + if (result == ResultTimedOut) { + return result; + } + + // Receive the request. + if (R_SUCCEEDED(result)) { + KServerSession* session = objs[index]->DynamicCast(); + if (session != nullptr) { + result = session->ReceiveRequest(); + if (result == ResultNotFound) { + continue; + } + } + } + + *out_index = index; + return result; + } +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_kernel_debug.cpp b/src/core/hle/kernel/svc/svc_kernel_debug.cpp new file mode 100644 index 000000000..454255e7a --- /dev/null +++ b/src/core/hle/kernel/svc/svc_kernel_debug.cpp @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +void KernelDebug([[maybe_unused]] Core::System& system, [[maybe_unused]] u32 kernel_debug_type, + [[maybe_unused]] u64 param1, [[maybe_unused]] u64 param2, + [[maybe_unused]] u64 param3) { + // Intentionally do nothing, as this does nothing in released kernel binaries. +} + +void ChangeKernelTraceState([[maybe_unused]] Core::System& system, + [[maybe_unused]] u32 trace_state) { + // Intentionally do nothing, as this does nothing in released kernel binaries. +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_light_ipc.cpp b/src/core/hle/kernel/svc/svc_light_ipc.cpp new file mode 100644 index 000000000..299e22ae6 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_light_ipc.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc {} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_lock.cpp b/src/core/hle/kernel/svc/svc_lock.cpp new file mode 100644 index 000000000..45f2a6553 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_lock.cpp @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_memory_layout.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +/// Attempts to locks a mutex +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); + + // Validate the input address. + if (IsKernelAddress(address)) { + LOG_ERROR(Kernel_SVC, "Attempting to arbitrate a lock on a kernel address (address={:08X})", + address); + return ResultInvalidCurrentMemory; + } + if (!Common::IsAligned(address, sizeof(u32))) { + LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address); + return ResultInvalidAddress; + } + + return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag); +} + +Result ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, u32 tag) { + return ArbitrateLock(system, thread_handle, address, tag); +} + +/// Unlock a mutex +Result ArbitrateUnlock(Core::System& system, VAddr address) { + LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address); + + // Validate the input address. + if (IsKernelAddress(address)) { + LOG_ERROR(Kernel_SVC, + "Attempting to arbitrate an unlock on a kernel address (address={:08X})", + address); + return ResultInvalidCurrentMemory; + } + if (!Common::IsAligned(address, sizeof(u32))) { + LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address); + return ResultInvalidAddress; + } + + return system.Kernel().CurrentProcess()->SignalToAddress(address); +} + +Result ArbitrateUnlock32(Core::System& system, u32 address) { + return ArbitrateUnlock(system, address); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_memory.cpp b/src/core/hle/kernel/svc/svc_memory.cpp new file mode 100644 index 000000000..f78b1239b --- /dev/null +++ b/src/core/hle/kernel/svc/svc_memory.cpp @@ -0,0 +1,189 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { +namespace { + +constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) { + switch (perm) { + case MemoryPermission::None: + case MemoryPermission::Read: + case MemoryPermission::ReadWrite: + return true; + default: + return false; + } +} + +// Checks if address + size is greater than the given address +// This can return false if the size causes an overflow of a 64-bit type +// or if the given size is zero. +constexpr bool IsValidAddressRange(VAddr address, u64 size) { + return address + size > address; +} + +// 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. +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; + } + + if (!Common::Is4KBAligned(src_addr)) { + LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, 0x{:016X}", src_addr); + return ResultInvalidSize; + } + + if (size == 0) { + LOG_ERROR(Kernel_SVC, "Size is 0"); + return ResultInvalidSize; + } + + if (!Common::Is4KBAligned(size)) { + LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size); + return ResultInvalidSize; + } + + if (!IsValidAddressRange(dst_addr, size)) { + LOG_ERROR(Kernel_SVC, + "Destination is not a valid address range, addr=0x{:016X}, size=0x{:016X}", + dst_addr, size); + return ResultInvalidCurrentMemory; + } + + if (!IsValidAddressRange(src_addr, size)) { + LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr=0x{:016X}, size=0x{:016X}", + src_addr, size); + return ResultInvalidCurrentMemory; + } + + if (!manager.IsInsideAddressSpace(src_addr, size)) { + LOG_ERROR(Kernel_SVC, + "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", + src_addr, size); + return ResultInvalidCurrentMemory; + } + + if (manager.IsOutsideStackRegion(dst_addr, size)) { + LOG_ERROR(Kernel_SVC, + "Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}", + dst_addr, size); + return ResultInvalidMemoryRegion; + } + + if (manager.IsInsideHeapRegion(dst_addr, size)) { + LOG_ERROR(Kernel_SVC, + "Destination does not fit within the heap region, addr=0x{:016X}, " + "size=0x{:016X}", + dst_addr, size); + return ResultInvalidMemoryRegion; + } + + if (manager.IsInsideAliasRegion(dst_addr, size)) { + LOG_ERROR(Kernel_SVC, + "Destination does not fit within the map region, addr=0x{:016X}, " + "size=0x{:016X}", + dst_addr, size); + return ResultInvalidMemoryRegion; + } + + return ResultSuccess; +} + +} // namespace + +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); + + // Validate address / size. + R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); + R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); + R_UNLESS(size > 0, ResultInvalidSize); + R_UNLESS((address < address + size), ResultInvalidCurrentMemory); + + // Validate the permission. + R_UNLESS(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission); + + // Validate that the region is in range for the current process. + auto& page_table = system.Kernel().CurrentProcess()->PageTable(); + R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); + + // Set the memory attribute. + return page_table.SetMemoryPermission(address, size, perm); +} + +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); + + // Validate address / size. + R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); + R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); + R_UNLESS(size > 0, ResultInvalidSize); + R_UNLESS((address < address + size), ResultInvalidCurrentMemory); + + // Validate the attribute and mask. + constexpr u32 SupportedMask = static_cast(MemoryAttribute::Uncached); + R_UNLESS((mask | attr) == mask, ResultInvalidCombination); + R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination); + + // Validate that the region is in range for the current process. + auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; + R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); + + // Set the memory attribute. + return page_table.SetMemoryAttribute(address, size, mask, attr); +} + +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. +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 Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; + result.IsError()) { + return result; + } + + return page_table.MapMemory(dst_addr, src_addr, size); +} + +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 +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 Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; + result.IsError()) { + return result; + } + + return page_table.UnmapMemory(dst_addr, src_addr, size); +} + +Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { + return UnmapMemory(system, dst_addr, src_addr, size); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_physical_memory.cpp b/src/core/hle/kernel/svc/svc_physical_memory.cpp new file mode 100644 index 000000000..0fc262203 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_physical_memory.cpp @@ -0,0 +1,137 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +/// Set the process heap to a given Size. It can both extend and shrink the heap. +Result SetHeapSize(Core::System& system, VAddr* out_address, u64 size) { + LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size); + + // Validate size. + R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize); + R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize); + + // Set the heap size. + R_TRY(system.Kernel().CurrentProcess()->PageTable().SetHeapSize(out_address, size)); + + return ResultSuccess; +} + +Result SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) { + VAddr temp_heap_addr{}; + const Result result{SetHeapSize(system, &temp_heap_addr, heap_size)}; + *heap_addr = static_cast(temp_heap_addr); + return result; +} + +/// Maps memory at a desired address +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)) { + LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr); + return ResultInvalidAddress; + } + + if (!Common::Is4KBAligned(size)) { + LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size); + return ResultInvalidSize; + } + + if (size == 0) { + LOG_ERROR(Kernel_SVC, "Size is zero"); + return ResultInvalidSize; + } + + if (!(addr < addr + size)) { + LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address"); + return ResultInvalidMemoryRegion; + } + + KProcess* const current_process{system.Kernel().CurrentProcess()}; + auto& page_table{current_process->PageTable()}; + + if (current_process->GetSystemResourceSize() == 0) { + LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); + return ResultInvalidState; + } + + if (!page_table.IsInsideAddressSpace(addr, size)) { + LOG_ERROR(Kernel_SVC, + "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, + size); + return ResultInvalidMemoryRegion; + } + + if (page_table.IsOutsideAliasRegion(addr, size)) { + LOG_ERROR(Kernel_SVC, + "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr, + size); + return ResultInvalidMemoryRegion; + } + + return page_table.MapPhysicalMemory(addr, size); +} + +Result MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { + return MapPhysicalMemory(system, addr, size); +} + +/// Unmaps memory previously mapped via MapPhysicalMemory +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)) { + LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr); + return ResultInvalidAddress; + } + + if (!Common::Is4KBAligned(size)) { + LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size); + return ResultInvalidSize; + } + + if (size == 0) { + LOG_ERROR(Kernel_SVC, "Size is zero"); + return ResultInvalidSize; + } + + if (!(addr < addr + size)) { + LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address"); + return ResultInvalidMemoryRegion; + } + + KProcess* const current_process{system.Kernel().CurrentProcess()}; + auto& page_table{current_process->PageTable()}; + + if (current_process->GetSystemResourceSize() == 0) { + LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); + return ResultInvalidState; + } + + if (!page_table.IsInsideAddressSpace(addr, size)) { + LOG_ERROR(Kernel_SVC, + "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, + size); + return ResultInvalidMemoryRegion; + } + + if (page_table.IsOutsideAliasRegion(addr, size)) { + LOG_ERROR(Kernel_SVC, + "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr, + size); + return ResultInvalidMemoryRegion; + } + + return page_table.UnmapPhysicalMemory(addr, size); +} + +Result UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { + return UnmapPhysicalMemory(system, addr, size); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_port.cpp b/src/core/hle/kernel/svc/svc_port.cpp new file mode 100644 index 000000000..cdfe0dd16 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_port.cpp @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: Copyright 2023 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_client_port.h" +#include "core/hle/kernel/k_client_session.h" +#include "core/hle/kernel/k_port.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +/// Connect to an OS service given the port name, returns the handle to the port to out +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, + "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}", + port_name_address); + return ResultNotFound; + } + + static constexpr std::size_t PortNameMaxLength = 11; + // Read 1 char beyond the max allowed port name to detect names that are too long. + const std::string port_name = memory.ReadCString(port_name_address, PortNameMaxLength + 1); + if (port_name.size() > PortNameMaxLength) { + LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength, + port_name.size()); + return ResultOutOfRange; + } + + LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); + + // Get the current handle table. + auto& kernel = system.Kernel(); + auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); + + // Find the client port. + auto port = kernel.CreateNamedServicePort(port_name); + if (!port) { + LOG_ERROR(Kernel_SVC, "tried to connect to unknown port: {}", port_name); + return ResultNotFound; + } + + // Reserve a handle for the port. + // NOTE: Nintendo really does write directly to the output handle here. + R_TRY(handle_table.Reserve(out)); + auto handle_guard = SCOPE_GUARD({ handle_table.Unreserve(*out); }); + + // Create a session. + KClientSession* session{}; + R_TRY(port->CreateSession(std::addressof(session))); + + kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort()); + + // Register the session in the table, close the extra reference. + handle_table.Register(*out, session); + session->Close(); + + // We succeeded. + handle_guard.Cancel(); + return ResultSuccess; +} + +Result ConnectToNamedPort32(Core::System& system, Handle* out_handle, u32 port_name_address) { + + return ConnectToNamedPort(system, out_handle, port_name_address); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_power_management.cpp b/src/core/hle/kernel/svc/svc_power_management.cpp new file mode 100644 index 000000000..299e22ae6 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_power_management.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc {} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_process.cpp b/src/core/hle/kernel/svc/svc_process.cpp new file mode 100644 index 000000000..d6c8b4561 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_process.cpp @@ -0,0 +1,124 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +/// Exits the current process +void ExitProcess(Core::System& system) { + auto* current_process = system.Kernel().CurrentProcess(); + + LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); + ASSERT_MSG(current_process->GetState() == KProcess::State::Running, + "Process has already exited"); + + system.Exit(); +} + +void ExitProcess32(Core::System& system) { + ExitProcess(system); +} + +/// Gets the ID of the specified process or a specified thread's owning process. +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. + KScopedAutoObject obj = + system.Kernel().CurrentProcess()->GetHandleTable().GetObject( + static_cast(handle)); + R_UNLESS(obj.IsNotNull(), ResultInvalidHandle); + + // Get the process from the object. + KProcess* process = nullptr; + if (KProcess* p = obj->DynamicCast(); p != nullptr) { + // The object is a process, so we can use it directly. + process = p; + } else if (KThread* t = obj->DynamicCast(); t != nullptr) { + // The object is a thread, so we want to use its parent. + process = reinterpret_cast(obj.GetPointerUnsafe())->GetOwnerProcess(); + } else { + // TODO(bunnei): This should also handle debug objects before returning. + UNIMPLEMENTED_MSG("Debug objects not implemented"); + } + + // Make sure the target process exists. + R_UNLESS(process != nullptr, ResultInvalidHandle); + + // Get the process id. + *out_process_id = process->GetId(); + + return ResultSuccess; +} + +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(out_process_id); + *out_process_id_high = static_cast(out_process_id >> 32); + return result; +} + +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); + + // If the supplied size is negative or greater than INT32_MAX / sizeof(u64), bail. + if ((out_process_ids_size & 0xF0000000) != 0) { + LOG_ERROR(Kernel_SVC, + "Supplied size outside [0, 0x0FFFFFFF] range. out_process_ids_size={}", + out_process_ids_size); + return ResultOutOfRange; + } + + const auto& kernel = system.Kernel(); + const auto total_copy_size = out_process_ids_size * sizeof(u64); + + if (out_process_ids_size > 0 && !kernel.CurrentProcess()->PageTable().IsInsideAddressSpace( + out_process_ids, total_copy_size)) { + LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}", + out_process_ids, out_process_ids + total_copy_size); + return ResultInvalidCurrentMemory; + } + + auto& memory = system.Memory(); + const auto& process_list = kernel.GetProcessList(); + const auto num_processes = process_list.size(); + const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes); + + for (std::size_t i = 0; i < copy_amount; ++i) { + memory.Write64(out_process_ids, process_list[i]->GetProcessID()); + out_process_ids += sizeof(u64); + } + + *out_num_processes = static_cast(num_processes); + return ResultSuccess; +} + +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); + + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + KScopedAutoObject process = handle_table.GetObject(process_handle); + if (process.IsNull()) { + LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", + process_handle); + return ResultInvalidHandle; + } + + const auto info_type = static_cast(type); + if (info_type != ProcessInfoType::ProcessState) { + LOG_ERROR(Kernel_SVC, "Expected info_type to be ProcessState but got {} instead", type); + return ResultInvalidEnumValue; + } + + *out = static_cast(process->GetState()); + return ResultSuccess; +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_process_memory.cpp b/src/core/hle/kernel/svc/svc_process_memory.cpp new file mode 100644 index 000000000..b6ac43af2 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_process_memory.cpp @@ -0,0 +1,274 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { +namespace { + +constexpr bool IsValidAddressRange(VAddr address, u64 size) { + return address + size > address; +} + +constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) { + switch (perm) { + case Svc::MemoryPermission::None: + case Svc::MemoryPermission::Read: + case Svc::MemoryPermission::ReadWrite: + case Svc::MemoryPermission::ReadExecute: + return true; + default: + return false; + } +} + +} // namespace + +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); + + // Validate the address/size. + R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); + R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); + R_UNLESS(size > 0, ResultInvalidSize); + R_UNLESS((address < address + size), ResultInvalidCurrentMemory); + R_UNLESS(address == static_cast(address), ResultInvalidCurrentMemory); + R_UNLESS(size == static_cast(size), ResultInvalidCurrentMemory); + + // Validate the memory permission. + R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission); + + // Get the process from its handle. + KScopedAutoObject process = + system.CurrentProcess()->GetHandleTable().GetObject(process_handle); + R_UNLESS(process.IsNotNull(), ResultInvalidHandle); + + // Validate that the address is in range. + auto& page_table = process->PageTable(); + R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); + + // Set the memory permission. + return page_table.SetProcessMemoryPermission(address, size, perm); +} + +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); + + // Validate the address/size. + R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress); + R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress); + R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); + R_UNLESS(size > 0, ResultInvalidSize); + R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory); + R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory); + + // Get the processes. + KProcess* dst_process = system.CurrentProcess(); + KScopedAutoObject src_process = + dst_process->GetHandleTable().GetObjectWithoutPseudoHandle(process_handle); + R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); + + // Get the page tables. + auto& dst_pt = dst_process->PageTable(); + auto& src_pt = src_process->PageTable(); + + // Validate that the mapping is in range. + R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory); + R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode), + ResultInvalidMemoryRegion); + + // Create a new page group. + KPageGroup pg{system.Kernel(), dst_pt.GetBlockInfoManager()}; + 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.MapPageGroup(dst_address, pg, KMemoryState::SharedCode, + KMemoryPermission::UserReadWrite)); + + return ResultSuccess; +} + +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); + + // Validate the address/size. + R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress); + R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress); + R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); + R_UNLESS(size > 0, ResultInvalidSize); + R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory); + R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory); + + // Get the processes. + KProcess* dst_process = system.CurrentProcess(); + KScopedAutoObject src_process = + dst_process->GetHandleTable().GetObjectWithoutPseudoHandle(process_handle); + R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); + + // Get the page tables. + auto& dst_pt = dst_process->PageTable(); + auto& src_pt = src_process->PageTable(); + + // Validate that the mapping is in range. + R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory); + R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode), + ResultInvalidMemoryRegion); + + // Unmap the memory. + R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address)); + + return ResultSuccess; +} + +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}", + process_handle, dst_address, src_address, size); + + if (!Common::Is4KBAligned(src_address)) { + LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", + src_address); + return ResultInvalidAddress; + } + + if (!Common::Is4KBAligned(dst_address)) { + LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", + dst_address); + return ResultInvalidAddress; + } + + if (size == 0 || !Common::Is4KBAligned(size)) { + LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size); + return ResultInvalidSize; + } + + if (!IsValidAddressRange(dst_address, size)) { + LOG_ERROR(Kernel_SVC, + "Destination address range overflows the address space (dst_address=0x{:016X}, " + "size=0x{:016X}).", + dst_address, size); + return ResultInvalidCurrentMemory; + } + + if (!IsValidAddressRange(src_address, size)) { + LOG_ERROR(Kernel_SVC, + "Source address range overflows the address space (src_address=0x{:016X}, " + "size=0x{:016X}).", + src_address, size); + return ResultInvalidCurrentMemory; + } + + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + KScopedAutoObject process = handle_table.GetObject(process_handle); + if (process.IsNull()) { + LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", + process_handle); + return ResultInvalidHandle; + } + + auto& page_table = process->PageTable(); + if (!page_table.IsInsideAddressSpace(src_address, size)) { + LOG_ERROR(Kernel_SVC, + "Source address range is not within the address space (src_address=0x{:016X}, " + "size=0x{:016X}).", + src_address, size); + return ResultInvalidCurrentMemory; + } + + if (!page_table.IsInsideASLRRegion(dst_address, size)) { + LOG_ERROR(Kernel_SVC, + "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " + "size=0x{:016X}).", + dst_address, size); + return ResultInvalidMemoryRegion; + } + + return page_table.MapCodeMemory(dst_address, src_address, size); +} + +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}", + process_handle, dst_address, src_address, size); + + if (!Common::Is4KBAligned(dst_address)) { + LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", + dst_address); + return ResultInvalidAddress; + } + + if (!Common::Is4KBAligned(src_address)) { + LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", + src_address); + return ResultInvalidAddress; + } + + if (size == 0 || !Common::Is4KBAligned(size)) { + LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size); + return ResultInvalidSize; + } + + if (!IsValidAddressRange(dst_address, size)) { + LOG_ERROR(Kernel_SVC, + "Destination address range overflows the address space (dst_address=0x{:016X}, " + "size=0x{:016X}).", + dst_address, size); + return ResultInvalidCurrentMemory; + } + + if (!IsValidAddressRange(src_address, size)) { + LOG_ERROR(Kernel_SVC, + "Source address range overflows the address space (src_address=0x{:016X}, " + "size=0x{:016X}).", + src_address, size); + return ResultInvalidCurrentMemory; + } + + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + KScopedAutoObject process = handle_table.GetObject(process_handle); + if (process.IsNull()) { + LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", + process_handle); + return ResultInvalidHandle; + } + + auto& page_table = process->PageTable(); + if (!page_table.IsInsideAddressSpace(src_address, size)) { + LOG_ERROR(Kernel_SVC, + "Source address range is not within the address space (src_address=0x{:016X}, " + "size=0x{:016X}).", + src_address, size); + return ResultInvalidCurrentMemory; + } + + if (!page_table.IsInsideASLRRegion(dst_address, size)) { + LOG_ERROR(Kernel_SVC, + "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " + "size=0x{:016X}).", + dst_address, size); + return ResultInvalidMemoryRegion; + } + + return page_table.UnmapCodeMemory(dst_address, src_address, size, + KPageTable::ICacheInvalidationStrategy::InvalidateAll); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_processor.cpp b/src/core/hle/kernel/svc/svc_processor.cpp new file mode 100644 index 000000000..8561cf74f --- /dev/null +++ b/src/core/hle/kernel/svc/svc_processor.cpp @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/core.h" +#include "core/hle/kernel/physical_core.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +/// Get which CPU core is executing the current thread +u32 GetCurrentProcessorNumber(Core::System& system) { + LOG_TRACE(Kernel_SVC, "called"); + return static_cast(system.CurrentPhysicalCore().CoreIndex()); +} + +u32 GetCurrentProcessorNumber32(Core::System& system) { + return GetCurrentProcessorNumber(system); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_query_memory.cpp b/src/core/hle/kernel/svc/svc_query_memory.cpp new file mode 100644 index 000000000..aac3b2eca --- /dev/null +++ b/src/core/hle/kernel/svc/svc_query_memory.cpp @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +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}", + memory_info_address, page_info_address, query_address); + + return QueryProcessMemory(system, memory_info_address, page_info_address, CurrentProcess, + query_address); +} + +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); +} + +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(process_handle); + if (process.IsNull()) { + LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", + process_handle); + return ResultInvalidHandle; + } + + auto& memory{system.Memory()}; + const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()}; + + memory.Write64(memory_info_address + 0x00, memory_info.base_address); + memory.Write64(memory_info_address + 0x08, memory_info.size); + memory.Write32(memory_info_address + 0x10, static_cast(memory_info.state) & 0xff); + memory.Write32(memory_info_address + 0x14, static_cast(memory_info.attribute)); + memory.Write32(memory_info_address + 0x18, static_cast(memory_info.permission)); + memory.Write32(memory_info_address + 0x1c, memory_info.ipc_count); + memory.Write32(memory_info_address + 0x20, memory_info.device_count); + memory.Write32(memory_info_address + 0x24, 0); + + // Page info appears to be currently unused by the kernel and is always set to zero. + memory.Write32(page_info_address, 0); + + return ResultSuccess; +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_register.cpp b/src/core/hle/kernel/svc/svc_register.cpp new file mode 100644 index 000000000..299e22ae6 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_register.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc {} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_resource_limit.cpp b/src/core/hle/kernel/svc/svc_resource_limit.cpp new file mode 100644 index 000000000..679ba10fa --- /dev/null +++ b/src/core/hle/kernel/svc/svc_resource_limit.cpp @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: Copyright 2023 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_process.h" +#include "core/hle/kernel/k_resource_limit.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +Result CreateResourceLimit(Core::System& system, Handle* out_handle) { + LOG_DEBUG(Kernel_SVC, "called"); + + // Create a new resource limit. + auto& kernel = system.Kernel(); + KResourceLimit* resource_limit = KResourceLimit::Create(kernel); + R_UNLESS(resource_limit != nullptr, ResultOutOfResource); + + // Ensure we don't leak a reference to the limit. + SCOPE_EXIT({ resource_limit->Close(); }); + + // Initialize the resource limit. + resource_limit->Initialize(&system.CoreTiming()); + + // Register the limit. + KResourceLimit::Register(kernel, resource_limit); + + // Add the limit to the handle table. + R_TRY(kernel.CurrentProcess()->GetHandleTable().Add(out_handle, resource_limit)); + + return ResultSuccess; +} + +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); + + // Validate the resource. + R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue); + + // Get the resource limit. + auto& kernel = system.Kernel(); + KScopedAutoObject resource_limit = + kernel.CurrentProcess()->GetHandleTable().GetObject(resource_limit_handle); + R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle); + + // Get the limit value. + *out_limit_value = resource_limit->GetLimitValue(which); + + return ResultSuccess; +} + +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); + + // Validate the resource. + R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue); + + // Get the resource limit. + auto& kernel = system.Kernel(); + KScopedAutoObject resource_limit = + kernel.CurrentProcess()->GetHandleTable().GetObject(resource_limit_handle); + R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle); + + // Get the current value. + *out_current_value = resource_limit->GetCurrentValue(which); + + return ResultSuccess; +} + +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); + + // Validate the resource. + R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue); + + // Get the resource limit. + auto& kernel = system.Kernel(); + KScopedAutoObject resource_limit = + kernel.CurrentProcess()->GetHandleTable().GetObject(resource_limit_handle); + R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle); + + // Set the limit value. + R_TRY(resource_limit->SetLimitValue(which, limit_value)); + + return ResultSuccess; +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_secure_monitor_call.cpp b/src/core/hle/kernel/svc/svc_secure_monitor_call.cpp new file mode 100644 index 000000000..299e22ae6 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_secure_monitor_call.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc {} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_session.cpp b/src/core/hle/kernel/svc/svc_session.cpp new file mode 100644 index 000000000..dac8ce33c --- /dev/null +++ b/src/core/hle/kernel/svc/svc_session.cpp @@ -0,0 +1,103 @@ +// SPDX-FileCopyrightText: Copyright 2023 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_process.h" +#include "core/hle/kernel/k_scoped_resource_reservation.h" +#include "core/hle/kernel/k_session.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { +namespace { + +template +Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u64 name) { + auto& process = *system.CurrentProcess(); + auto& handle_table = process.GetHandleTable(); + + // Declare the session we're going to allocate. + T* session; + + // Reserve a new session from the process resource limit. + // FIXME: LimitableResource_SessionCountMax + KScopedResourceReservation session_reservation(&process, LimitableResource::SessionCountMax); + if (session_reservation.Succeeded()) { + session = T::Create(system.Kernel()); + } else { + return ResultLimitReached; + + // // We couldn't reserve a session. Check that we support dynamically expanding the + // // resource limit. + // R_UNLESS(process.GetResourceLimit() == + // &system.Kernel().GetSystemResourceLimit(), ResultLimitReached); + // R_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled(), ResultLimitReached()); + + // // Try to allocate a session from unused slab memory. + // session = T::CreateFromUnusedSlabMemory(); + // R_UNLESS(session != nullptr, ResultLimitReached); + // ON_RESULT_FAILURE { session->Close(); }; + + // // If we're creating a KSession, we want to add two KSessionRequests to the heap, to + // // prevent request exhaustion. + // // NOTE: Nintendo checks if session->DynamicCast() != nullptr, but there's + // // no reason to not do this statically. + // if constexpr (std::same_as) { + // for (size_t i = 0; i < 2; i++) { + // KSessionRequest* request = KSessionRequest::CreateFromUnusedSlabMemory(); + // R_UNLESS(request != nullptr, ResultLimitReached); + // request->Close(); + // } + // } + + // We successfully allocated a session, so add the object we allocated to the resource + // limit. + // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::SessionCountMax, 1); + } + + // Check that we successfully created a session. + R_UNLESS(session != nullptr, ResultOutOfResource); + + // Initialize the session. + session->Initialize(nullptr, fmt::format("{}", name)); + + // Commit the session reservation. + session_reservation.Commit(); + + // Ensure that we clean up the session (and its only references are handle table) on function + // end. + SCOPE_EXIT({ + session->GetClientSession().Close(); + session->GetServerSession().Close(); + }); + + // Register the session. + T::Register(system.Kernel(), session); + + // Add the server session to the handle table. + R_TRY(handle_table.Add(out_server, &session->GetServerSession())); + + // Add the client session to the handle table. + const auto result = handle_table.Add(out_client, &session->GetClientSession()); + + if (!R_SUCCEEDED(result)) { + // Ensure that we maintaing a clean handle state on exit. + handle_table.Remove(*out_server); + } + + return result; +} + +} // namespace + +Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u32 is_light, + u64 name) { + if (is_light) { + // return CreateSession(system, out_server, out_client, name); + return ResultUnknown; + } else { + return CreateSession(system, out_server, out_client, name); + } +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_shared_memory.cpp b/src/core/hle/kernel/svc/svc_shared_memory.cpp new file mode 100644 index 000000000..d465bcbe7 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_shared_memory.cpp @@ -0,0 +1,106 @@ +// SPDX-FileCopyrightText: Copyright 2023 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_process.h" +#include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { +namespace { + +constexpr bool IsValidSharedMemoryPermission(MemoryPermission perm) { + switch (perm) { + case MemoryPermission::Read: + case MemoryPermission::ReadWrite: + return true; + default: + return false; + } +} + +[[maybe_unused]] constexpr bool IsValidRemoteSharedMemoryPermission(MemoryPermission perm) { + return IsValidSharedMemoryPermission(perm) || perm == MemoryPermission::DontCare; +} + +} // namespace + +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); + + // Validate the address/size. + R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); + R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); + R_UNLESS(size > 0, ResultInvalidSize); + R_UNLESS((address < address + size), ResultInvalidCurrentMemory); + + // Validate the permission. + R_UNLESS(IsValidSharedMemoryPermission(map_perm), ResultInvalidNewMemoryPermission); + + // Get the current process. + auto& process = *system.Kernel().CurrentProcess(); + auto& page_table = process.PageTable(); + + // Get the shared memory. + KScopedAutoObject shmem = process.GetHandleTable().GetObject(shmem_handle); + R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle); + + // Verify that the mapping is in range. + R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion); + + // Add the shared memory to the process. + R_TRY(process.AddSharedMemory(shmem.GetPointerUnsafe(), address, size)); + + // Ensure that we clean up the shared memory if we fail to map it. + auto guard = + SCOPE_GUARD({ process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size); }); + + // Map the shared memory. + R_TRY(shmem->Map(process, address, size, map_perm)); + + // We succeeded. + guard.Cancel(); + return ResultSuccess; +} + +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); +} + +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); + R_UNLESS(size > 0, ResultInvalidSize); + R_UNLESS((address < address + size), ResultInvalidCurrentMemory); + + // Get the current process. + auto& process = *system.Kernel().CurrentProcess(); + auto& page_table = process.PageTable(); + + // Get the shared memory. + KScopedAutoObject shmem = process.GetHandleTable().GetObject(shmem_handle); + R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle); + + // Verify that the mapping is in range. + R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion); + + // Unmap the shared memory. + R_TRY(shmem->Unmap(process, address, size)); + + // Remove the shared memory from the process. + process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size); + + return ResultSuccess; +} + +Result UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size) { + return UnmapSharedMemory(system, shmem_handle, address, size); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_synchronization.cpp b/src/core/hle/kernel/svc/svc_synchronization.cpp new file mode 100644 index 000000000..1bf6a612a --- /dev/null +++ b/src/core/hle/kernel/svc/svc_synchronization.cpp @@ -0,0 +1,139 @@ +// SPDX-FileCopyrightText: Copyright 2023 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_process.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +/// Close a handle +Result CloseHandle(Core::System& system, Handle handle) { + LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); + + // Remove the handle. + R_UNLESS(system.Kernel().CurrentProcess()->GetHandleTable().Remove(handle), + ResultInvalidHandle); + + return ResultSuccess; +} + +Result CloseHandle32(Core::System& system, Handle handle) { + return CloseHandle(system, handle); +} + +/// Clears the signaled state of an event or process. +Result ResetSignal(Core::System& system, Handle handle) { + LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle); + + // Get the current handle table. + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + + // Try to reset as readable event. + { + KScopedAutoObject readable_event = handle_table.GetObject(handle); + if (readable_event.IsNotNull()) { + return readable_event->Reset(); + } + } + + // Try to reset as process. + { + KScopedAutoObject process = handle_table.GetObject(handle); + if (process.IsNotNull()) { + return process->Reset(); + } + } + + LOG_ERROR(Kernel_SVC, "invalid handle (0x{:08X})", handle); + + return ResultInvalidHandle; +} + +Result ResetSignal32(Core::System& system, Handle handle) { + return ResetSignal(system, handle); +} + +/// Wait for the given handles to synchronize, timeout after the specified nanoseconds +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); + + // Ensure number of handles is valid. + R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange); + + auto& kernel = system.Kernel(); + std::vector objs(num_handles); + const auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); + Handle* handles = system.Memory().GetPointer(handles_address); + + // Copy user handles. + if (num_handles > 0) { + // Convert the handles to objects. + R_UNLESS(handle_table.GetMultipleObjects(objs.data(), handles, + num_handles), + ResultInvalidHandle); + for (const auto& obj : objs) { + kernel.RegisterInUseObject(obj); + } + } + + // Ensure handles are closed when we're done. + SCOPE_EXIT({ + for (s32 i = 0; i < num_handles; ++i) { + kernel.UnregisterInUseObject(objs[i]); + objs[i]->Close(); + } + }); + + return KSynchronizationObject::Wait(kernel, index, objs.data(), static_cast(objs.size()), + nano_seconds); +} + +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(timeout_high) << 32) | static_cast(timeout_low)}; + return WaitSynchronization(system, index, handles_address, num_handles, nano_seconds); +} + +/// Resumes a thread waiting on WaitSynchronization +Result CancelSynchronization(Core::System& system, Handle handle) { + LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle); + + // Get the thread from its handle. + KScopedAutoObject thread = + system.Kernel().CurrentProcess()->GetHandleTable().GetObject(handle); + R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); + + // Cancel the thread's wait. + thread->WaitCancel(); + return ResultSuccess; +} + +Result CancelSynchronization32(Core::System& system, Handle handle) { + return CancelSynchronization(system, handle); +} + +void SynchronizePreemptionState(Core::System& system) { + auto& kernel = system.Kernel(); + + // Lock the scheduler. + KScopedSchedulerLock sl{kernel}; + + // If the current thread is pinned, unpin it. + KProcess* cur_process = system.Kernel().CurrentProcess(); + const auto core_id = GetCurrentCoreId(kernel); + + if (cur_process->GetPinnedThread(core_id) == GetCurrentThreadPointer(kernel)) { + // Clear the current thread's interrupt flag. + GetCurrentThread(kernel).ClearInterruptFlag(); + + // Unpin the current thread. + cur_process->UnpinCurrentThread(core_id); + } +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_thread.cpp b/src/core/hle/kernel/svc/svc_thread.cpp new file mode 100644 index 000000000..dd9f8e8b1 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_thread.cpp @@ -0,0 +1,396 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/scope_exit.h" +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_scoped_resource_reservation.h" +#include "core/hle/kernel/k_thread.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { +namespace { + +constexpr bool IsValidVirtualCoreId(int32_t core_id) { + return (0 <= core_id && core_id < static_cast(Core::Hardware::NUM_CPU_CORES)); +} + +} // Anonymous namespace + +/// Creates a new thread +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}", + entry_point, arg, stack_bottom, priority, core_id); + + // Adjust core id, if it's the default magic. + auto& kernel = system.Kernel(); + auto& process = *kernel.CurrentProcess(); + if (core_id == IdealCoreUseProcessValue) { + core_id = process.GetIdealCoreId(); + } + + // Validate arguments. + if (!IsValidVirtualCoreId(core_id)) { + LOG_ERROR(Kernel_SVC, "Invalid Core ID specified (id={})", core_id); + return ResultInvalidCoreId; + } + if (((1ULL << core_id) & process.GetCoreMask()) == 0) { + LOG_ERROR(Kernel_SVC, "Core ID doesn't fall within allowable cores (id={})", core_id); + return ResultInvalidCoreId; + } + + if (HighestThreadPriority > priority || priority > LowestThreadPriority) { + LOG_ERROR(Kernel_SVC, "Invalid priority specified (priority={})", priority); + return ResultInvalidPriority; + } + if (!process.CheckThreadPriority(priority)) { + LOG_ERROR(Kernel_SVC, "Invalid allowable thread priority (priority={})", priority); + return ResultInvalidPriority; + } + + // Reserve a new thread from the process resource limit (waiting up to 100ms). + KScopedResourceReservation thread_reservation( + kernel.CurrentProcess(), LimitableResource::ThreadCountMax, 1, + system.CoreTiming().GetGlobalTimeNs().count() + 100000000); + if (!thread_reservation.Succeeded()) { + LOG_ERROR(Kernel_SVC, "Could not reserve a new thread"); + return ResultLimitReached; + } + + // Create the thread. + KThread* thread = KThread::Create(kernel); + if (!thread) { + LOG_ERROR(Kernel_SVC, "Unable to create new threads. Thread creation limit reached."); + return ResultOutOfResource; + } + SCOPE_EXIT({ thread->Close(); }); + + // Initialize the thread. + { + KScopedLightLock lk{process.GetStateLock()}; + R_TRY(KThread::InitializeUserThread(system, thread, entry_point, arg, stack_bottom, + priority, core_id, &process)); + } + + // Set the thread name for debugging purposes. + thread->SetName(fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *out_handle)); + + // Commit the thread reservation. + thread_reservation.Commit(); + + // Register the new thread. + KThread::Register(kernel, thread); + + // Add the thread to the handle table. + R_TRY(process.GetHandleTable().Add(out_handle, thread)); + + return ResultSuccess; +} + +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 +Result StartThread(Core::System& system, Handle thread_handle) { + LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle); + + // Get the thread from its handle. + KScopedAutoObject thread = + system.Kernel().CurrentProcess()->GetHandleTable().GetObject(thread_handle); + R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); + + // Try to start the thread. + R_TRY(thread->Run()); + + // If we succeeded, persist a reference to the thread. + thread->Open(); + system.Kernel().RegisterInUseObject(thread.GetPointerUnsafe()); + + return ResultSuccess; +} + +Result StartThread32(Core::System& system, Handle thread_handle) { + return StartThread(system, thread_handle); +} + +/// Called when a thread exits +void ExitThread(Core::System& system) { + LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); + + auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); + system.GlobalSchedulerContext().RemoveThread(current_thread); + current_thread->Exit(); + system.Kernel().UnregisterInUseObject(current_thread); +} + +void ExitThread32(Core::System& system) { + ExitThread(system); +} + +/// Sleep the current thread +void SleepThread(Core::System& system, s64 nanoseconds) { + auto& kernel = system.Kernel(); + const auto yield_type = static_cast(nanoseconds); + + LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); + + // When the input tick is positive, sleep. + if (nanoseconds > 0) { + // Convert the timeout from nanoseconds to ticks. + // NOTE: Nintendo does not use this conversion logic in WaitSynchronization... + + // Sleep. + // NOTE: Nintendo does not check the result of this sleep. + static_cast(GetCurrentThread(kernel).Sleep(nanoseconds)); + } else if (yield_type == Svc::YieldType::WithoutCoreMigration) { + KScheduler::YieldWithoutCoreMigration(kernel); + } else if (yield_type == Svc::YieldType::WithCoreMigration) { + KScheduler::YieldWithCoreMigration(kernel); + } else if (yield_type == Svc::YieldType::ToAnyThread) { + KScheduler::YieldToAnyThread(kernel); + } else { + // Nintendo does nothing at all if an otherwise invalid value is passed. + ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds); + } +} + +void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanoseconds_high) { + const auto nanoseconds = static_cast(u64{nanoseconds_low} | (u64{nanoseconds_high} << 32)); + SleepThread(system, nanoseconds); +} + +/// Gets the thread context +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); + + auto& kernel = system.Kernel(); + + // Get the thread from its handle. + KScopedAutoObject thread = + kernel.CurrentProcess()->GetHandleTable().GetObject(thread_handle); + R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); + + // Require the handle be to a non-current thread in the current process. + const auto* current_process = kernel.CurrentProcess(); + R_UNLESS(current_process == thread->GetOwnerProcess(), ResultInvalidId); + + // Verify that the thread isn't terminated. + R_UNLESS(thread->GetState() != ThreadState::Terminated, ResultTerminationRequested); + + /// Check that the thread is not the current one. + /// NOTE: Nintendo does not check this, and thus the following loop will deadlock. + R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(kernel), ResultInvalidId); + + // Try to get the thread context until the thread isn't current on any core. + while (true) { + KScopedSchedulerLock sl{kernel}; + + // TODO(bunnei): Enforce that thread is suspended for debug here. + + // If the thread's raw state isn't runnable, check if it's current on some core. + if (thread->GetRawState() != ThreadState::Runnable) { + bool current = false; + for (auto i = 0; i < static_cast(Core::Hardware::NUM_CPU_CORES); ++i) { + if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetSchedulerCurrentThread()) { + current = true; + break; + } + } + + // If the thread is current, retry until it isn't. + if (current) { + continue; + } + } + + // Get the thread context. + std::vector context; + R_TRY(thread->GetThreadContext3(context)); + + // Copy the thread context to user space. + system.Memory().WriteBlock(out_context, context.data(), context.size()); + + return ResultSuccess; + } + + return ResultSuccess; +} + +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 +Result GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) { + LOG_TRACE(Kernel_SVC, "called"); + + // Get the thread from its handle. + KScopedAutoObject thread = + system.Kernel().CurrentProcess()->GetHandleTable().GetObject(handle); + R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); + + // Get the thread's priority. + *out_priority = thread->GetPriority(); + return ResultSuccess; +} + +Result GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) { + return GetThreadPriority(system, out_priority, handle); +} + +/// Sets the priority for the specified thread +Result SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority) { + // Get the current process. + KProcess& process = *system.Kernel().CurrentProcess(); + + // Validate the priority. + R_UNLESS(HighestThreadPriority <= priority && priority <= LowestThreadPriority, + ResultInvalidPriority); + R_UNLESS(process.CheckThreadPriority(priority), ResultInvalidPriority); + + // Get the thread from its handle. + KScopedAutoObject thread = process.GetHandleTable().GetObject(thread_handle); + R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); + + // Set the thread priority. + thread->SetBasePriority(priority); + return ResultSuccess; +} + +Result SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority) { + return SetThreadPriority(system, thread_handle, priority); +} + +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); + + LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{:016X}, out_thread_ids_size={}", + out_thread_ids, out_thread_ids_size); + + // If the size is negative or larger than INT32_MAX / sizeof(u64) + if ((out_thread_ids_size & 0xF0000000) != 0) { + LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}", + out_thread_ids_size); + return ResultOutOfRange; + } + + auto* const current_process = system.Kernel().CurrentProcess(); + const auto total_copy_size = out_thread_ids_size * sizeof(u64); + + if (out_thread_ids_size > 0 && + !current_process->PageTable().IsInsideAddressSpace(out_thread_ids, total_copy_size)) { + LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}", + out_thread_ids, out_thread_ids + total_copy_size); + return ResultInvalidCurrentMemory; + } + + auto& memory = system.Memory(); + const auto& thread_list = current_process->GetThreadList(); + const auto num_threads = thread_list.size(); + const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads); + + auto list_iter = thread_list.cbegin(); + for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) { + memory.Write64(out_thread_ids, (*list_iter)->GetThreadID()); + out_thread_ids += sizeof(u64); + } + + *out_num_threads = static_cast(num_threads); + return ResultSuccess; +} + +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. + KScopedAutoObject thread = + system.Kernel().CurrentProcess()->GetHandleTable().GetObject(thread_handle); + R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); + + // Get the core mask. + R_TRY(thread->GetCoreMask(out_core_id, out_affinity_mask)); + + return ResultSuccess; +} + +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(out_affinity_mask >> 32); + *out_affinity_mask_low = static_cast(out_affinity_mask); + return result; +} + +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(); + affinity_mask = (1ULL << core_id); + } else { + // Validate the affinity mask. + const u64 process_core_mask = system.Kernel().CurrentProcess()->GetCoreMask(); + R_UNLESS((affinity_mask | process_core_mask) == process_core_mask, ResultInvalidCoreId); + R_UNLESS(affinity_mask != 0, ResultInvalidCombination); + + // Validate the core id. + if (IsValidVirtualCoreId(core_id)) { + R_UNLESS(((1ULL << core_id) & affinity_mask) != 0, ResultInvalidCombination); + } else { + R_UNLESS(core_id == IdealCoreNoUpdate || core_id == IdealCoreDontCare, + ResultInvalidCoreId); + } + } + + // Get the thread from its handle. + KScopedAutoObject thread = + system.Kernel().CurrentProcess()->GetHandleTable().GetObject(thread_handle); + R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); + + // Set the core mask. + R_TRY(thread->SetCoreMask(core_id, affinity_mask)); + + return ResultSuccess; +} + +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); +} + +/// Get the ID for the specified thread. +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(thread_handle); + R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); + + // Get the thread's id. + *out_thread_id = thread->GetId(); + return ResultSuccess; +} + +Result GetThreadId32(Core::System& system, u32* out_thread_id_low, u32* out_thread_id_high, + Handle thread_handle) { + u64 out_thread_id{}; + const Result result{GetThreadId(system, &out_thread_id, thread_handle)}; + + *out_thread_id_low = static_cast(out_thread_id >> 32); + *out_thread_id_high = static_cast(out_thread_id & std::numeric_limits::max()); + + return result; +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_thread_profiler.cpp b/src/core/hle/kernel/svc/svc_thread_profiler.cpp new file mode 100644 index 000000000..299e22ae6 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_thread_profiler.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc {} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_tick.cpp b/src/core/hle/kernel/svc/svc_tick.cpp new file mode 100644 index 000000000..e9b4fd5a6 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_tick.cpp @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +/// This returns the total CPU ticks elapsed since the CPU was powered-on +u64 GetSystemTick(Core::System& system) { + LOG_TRACE(Kernel_SVC, "called"); + + auto& core_timing = system.CoreTiming(); + + // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick) + const u64 result{core_timing.GetClockTicks()}; + + if (!system.Kernel().IsMulticore()) { + core_timing.AddTicks(400U); + } + + return result; +} + +void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high) { + const auto time = GetSystemTick(system); + *time_low = static_cast(time); + *time_high = static_cast(time >> 32); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_transfer_memory.cpp b/src/core/hle/kernel/svc/svc_transfer_memory.cpp new file mode 100644 index 000000000..b14ae24a1 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_transfer_memory.cpp @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: Copyright 2023 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_process.h" +#include "core/hle/kernel/k_scoped_resource_reservation.h" +#include "core/hle/kernel/k_transfer_memory.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { +namespace { + +constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) { + switch (perm) { + case MemoryPermission::None: + case MemoryPermission::Read: + case MemoryPermission::ReadWrite: + return true; + default: + return false; + } +} + +} // Anonymous namespace + +/// Creates a TransferMemory object +Result CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size, + MemoryPermission map_perm) { + auto& kernel = system.Kernel(); + + // Validate the size. + R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); + R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); + R_UNLESS(size > 0, ResultInvalidSize); + R_UNLESS((address < address + size), ResultInvalidCurrentMemory); + + // Validate the permissions. + R_UNLESS(IsValidTransferMemoryPermission(map_perm), ResultInvalidNewMemoryPermission); + + // Get the current process and handle table. + auto& process = *kernel.CurrentProcess(); + auto& handle_table = process.GetHandleTable(); + + // Reserve a new transfer memory from the process resource limit. + KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(), + LimitableResource::TransferMemoryCountMax); + R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached); + + // Create the transfer memory. + KTransferMemory* trmem = KTransferMemory::Create(kernel); + R_UNLESS(trmem != nullptr, ResultOutOfResource); + + // Ensure the only reference is in the handle table when we're done. + SCOPE_EXIT({ trmem->Close(); }); + + // Ensure that the region is in range. + R_UNLESS(process.PageTable().Contains(address, size), ResultInvalidCurrentMemory); + + // Initialize the transfer memory. + R_TRY(trmem->Initialize(address, size, map_perm)); + + // Commit the reservation. + trmem_reservation.Commit(); + + // Register the transfer memory. + KTransferMemory::Register(kernel, trmem); + + // Add the transfer memory to the handle table. + R_TRY(handle_table.Add(out, trmem)); + + return ResultSuccess; +} + +Result CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size, + MemoryPermission map_perm) { + return CreateTransferMemory(system, out, address, size, map_perm); +} +} // namespace Kernel::Svc -- cgit v1.2.3