diff options
| author | 2022-10-04 20:15:40 -0400 | |
|---|---|---|
| committer | 2023-02-04 22:37:43 -0500 | |
| commit | 92eb091ddb9dfd96e59a75937e185079a63626e3 (patch) | |
| tree | be79f36453a4735088d9230e02f426792077eeb4 /src/core/hle/kernel/svc | |
| parent | Merge pull request #9720 from SoRadGaming/discordPresenceUpdate (diff) | |
| download | yuzu-92eb091ddb9dfd96e59a75937e185079a63626e3.tar.gz yuzu-92eb091ddb9dfd96e59a75937e185079a63626e3.tar.xz yuzu-92eb091ddb9dfd96e59a75937e185079a63626e3.zip | |
kernel/svc: Split implementations into separate files
Diffstat (limited to 'src/core/hle/kernel/svc')
36 files changed, 2997 insertions, 0 deletions
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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/hle/kernel/k_process.h" | ||
| 6 | #include "core/hle/kernel/k_thread.h" | ||
| 7 | #include "core/hle/kernel/svc.h" | ||
| 8 | #include "core/hle/kernel/svc_results.h" | ||
| 9 | |||
| 10 | namespace Kernel::Svc { | ||
| 11 | |||
| 12 | /// Sets the thread activity | ||
| 13 | Result SetThreadActivity(Core::System& system, Handle thread_handle, | ||
| 14 | ThreadActivity thread_activity) { | ||
| 15 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle, | ||
| 16 | thread_activity); | ||
| 17 | |||
| 18 | // Validate the activity. | ||
| 19 | constexpr auto IsValidThreadActivity = [](ThreadActivity activity) { | ||
| 20 | return activity == ThreadActivity::Runnable || activity == ThreadActivity::Paused; | ||
| 21 | }; | ||
| 22 | R_UNLESS(IsValidThreadActivity(thread_activity), ResultInvalidEnumValue); | ||
| 23 | |||
| 24 | // Get the thread from its handle. | ||
| 25 | KScopedAutoObject thread = | ||
| 26 | system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); | ||
| 27 | R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); | ||
| 28 | |||
| 29 | // Check that the activity is being set on a non-current thread for the current process. | ||
| 30 | R_UNLESS(thread->GetOwnerProcess() == system.Kernel().CurrentProcess(), ResultInvalidHandle); | ||
| 31 | R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(system.Kernel()), ResultBusy); | ||
| 32 | |||
| 33 | // Set the activity. | ||
| 34 | R_TRY(thread->SetActivity(thread_activity)); | ||
| 35 | |||
| 36 | return ResultSuccess; | ||
| 37 | } | ||
| 38 | |||
| 39 | Result SetThreadActivity32(Core::System& system, Handle thread_handle, | ||
| 40 | ThreadActivity thread_activity) { | ||
| 41 | return SetThreadActivity(system, thread_handle, thread_activity); | ||
| 42 | } | ||
| 43 | |||
| 44 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/hle/kernel/k_memory_layout.h" | ||
| 6 | #include "core/hle/kernel/k_process.h" | ||
| 7 | #include "core/hle/kernel/kernel.h" | ||
| 8 | #include "core/hle/kernel/svc.h" | ||
| 9 | #include "core/hle/kernel/svc_results.h" | ||
| 10 | #include "core/hle/kernel/svc_types.h" | ||
| 11 | |||
| 12 | namespace Kernel::Svc { | ||
| 13 | namespace { | ||
| 14 | |||
| 15 | constexpr bool IsValidSignalType(Svc::SignalType type) { | ||
| 16 | switch (type) { | ||
| 17 | case Svc::SignalType::Signal: | ||
| 18 | case Svc::SignalType::SignalAndIncrementIfEqual: | ||
| 19 | case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual: | ||
| 20 | return true; | ||
| 21 | default: | ||
| 22 | return false; | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) { | ||
| 27 | switch (type) { | ||
| 28 | case Svc::ArbitrationType::WaitIfLessThan: | ||
| 29 | case Svc::ArbitrationType::DecrementAndWaitIfLessThan: | ||
| 30 | case Svc::ArbitrationType::WaitIfEqual: | ||
| 31 | return true; | ||
| 32 | default: | ||
| 33 | return false; | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | } // namespace | ||
| 38 | |||
| 39 | // Wait for an address (via Address Arbiter) | ||
| 40 | Result WaitForAddress(Core::System& system, VAddr address, ArbitrationType arb_type, s32 value, | ||
| 41 | s64 timeout_ns) { | ||
| 42 | LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}", | ||
| 43 | address, arb_type, value, timeout_ns); | ||
| 44 | |||
| 45 | // Validate input. | ||
| 46 | if (IsKernelAddress(address)) { | ||
| 47 | LOG_ERROR(Kernel_SVC, "Attempting to wait on kernel address (address={:08X})", address); | ||
| 48 | return ResultInvalidCurrentMemory; | ||
| 49 | } | ||
| 50 | if (!Common::IsAligned(address, sizeof(s32))) { | ||
| 51 | LOG_ERROR(Kernel_SVC, "Wait address must be 4 byte aligned (address={:08X})", address); | ||
| 52 | return ResultInvalidAddress; | ||
| 53 | } | ||
| 54 | if (!IsValidArbitrationType(arb_type)) { | ||
| 55 | LOG_ERROR(Kernel_SVC, "Invalid arbitration type specified (type={})", arb_type); | ||
| 56 | return ResultInvalidEnumValue; | ||
| 57 | } | ||
| 58 | |||
| 59 | // Convert timeout from nanoseconds to ticks. | ||
| 60 | s64 timeout{}; | ||
| 61 | if (timeout_ns > 0) { | ||
| 62 | const s64 offset_tick(timeout_ns); | ||
| 63 | if (offset_tick > 0) { | ||
| 64 | timeout = offset_tick + 2; | ||
| 65 | if (timeout <= 0) { | ||
| 66 | timeout = std::numeric_limits<s64>::max(); | ||
| 67 | } | ||
| 68 | } else { | ||
| 69 | timeout = std::numeric_limits<s64>::max(); | ||
| 70 | } | ||
| 71 | } else { | ||
| 72 | timeout = timeout_ns; | ||
| 73 | } | ||
| 74 | |||
| 75 | return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout); | ||
| 76 | } | ||
| 77 | |||
| 78 | Result WaitForAddress32(Core::System& system, u32 address, ArbitrationType arb_type, s32 value, | ||
| 79 | u32 timeout_ns_low, u32 timeout_ns_high) { | ||
| 80 | const auto timeout = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32)); | ||
| 81 | return WaitForAddress(system, address, arb_type, value, timeout); | ||
| 82 | } | ||
| 83 | |||
| 84 | // Signals to an address (via Address Arbiter) | ||
| 85 | Result SignalToAddress(Core::System& system, VAddr address, SignalType signal_type, s32 value, | ||
| 86 | s32 count) { | ||
| 87 | LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}", | ||
| 88 | address, signal_type, value, count); | ||
| 89 | |||
| 90 | // Validate input. | ||
| 91 | if (IsKernelAddress(address)) { | ||
| 92 | LOG_ERROR(Kernel_SVC, "Attempting to signal to a kernel address (address={:08X})", address); | ||
| 93 | return ResultInvalidCurrentMemory; | ||
| 94 | } | ||
| 95 | if (!Common::IsAligned(address, sizeof(s32))) { | ||
| 96 | LOG_ERROR(Kernel_SVC, "Signaled address must be 4 byte aligned (address={:08X})", address); | ||
| 97 | return ResultInvalidAddress; | ||
| 98 | } | ||
| 99 | if (!IsValidSignalType(signal_type)) { | ||
| 100 | LOG_ERROR(Kernel_SVC, "Invalid signal type specified (type={})", signal_type); | ||
| 101 | return ResultInvalidEnumValue; | ||
| 102 | } | ||
| 103 | |||
| 104 | return system.Kernel().CurrentProcess()->SignalAddressArbiter(address, signal_type, value, | ||
| 105 | count); | ||
| 106 | } | ||
| 107 | |||
| 108 | Result SignalToAddress32(Core::System& system, u32 address, SignalType signal_type, s32 value, | ||
| 109 | s32 count) { | ||
| 110 | return SignalToAddress(system, address, signal_type, value, count); | ||
| 111 | } | ||
| 112 | |||
| 113 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/kernel/svc.h" | ||
| 5 | |||
| 6 | 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/hle/kernel/k_process.h" | ||
| 6 | #include "core/hle/kernel/svc.h" | ||
| 7 | #include "core/hle/kernel/svc_results.h" | ||
| 8 | #include "core/hle/kernel/svc_types.h" | ||
| 9 | |||
| 10 | namespace Kernel::Svc { | ||
| 11 | |||
| 12 | Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address, u64 size) { | ||
| 13 | // Validate address/size. | ||
| 14 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 15 | R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory); | ||
| 16 | R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory); | ||
| 17 | |||
| 18 | // Get the process from its handle. | ||
| 19 | KScopedAutoObject process = | ||
| 20 | system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle); | ||
| 21 | R_UNLESS(process.IsNotNull(), ResultInvalidHandle); | ||
| 22 | |||
| 23 | // Verify the region is within range. | ||
| 24 | auto& page_table = process->PageTable(); | ||
| 25 | R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); | ||
| 26 | |||
| 27 | // Perform the operation. | ||
| 28 | R_RETURN(system.Memory().FlushDataCache(*process, address, size)); | ||
| 29 | } | ||
| 30 | |||
| 31 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/hle/kernel/k_code_memory.h" | ||
| 6 | #include "core/hle/kernel/k_process.h" | ||
| 7 | #include "core/hle/kernel/kernel.h" | ||
| 8 | #include "core/hle/kernel/svc.h" | ||
| 9 | |||
| 10 | namespace Kernel::Svc { | ||
| 11 | namespace { | ||
| 12 | |||
| 13 | constexpr bool IsValidMapCodeMemoryPermission(MemoryPermission perm) { | ||
| 14 | return perm == MemoryPermission::ReadWrite; | ||
| 15 | } | ||
| 16 | |||
| 17 | constexpr bool IsValidMapToOwnerCodeMemoryPermission(MemoryPermission perm) { | ||
| 18 | return perm == MemoryPermission::Read || perm == MemoryPermission::ReadExecute; | ||
| 19 | } | ||
| 20 | |||
| 21 | constexpr bool IsValidUnmapCodeMemoryPermission(MemoryPermission perm) { | ||
| 22 | return perm == MemoryPermission::None; | ||
| 23 | } | ||
| 24 | |||
| 25 | constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(MemoryPermission perm) { | ||
| 26 | return perm == MemoryPermission::None; | ||
| 27 | } | ||
| 28 | |||
| 29 | } // namespace | ||
| 30 | |||
| 31 | Result CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) { | ||
| 32 | LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size); | ||
| 33 | |||
| 34 | // Get kernel instance. | ||
| 35 | auto& kernel = system.Kernel(); | ||
| 36 | |||
| 37 | // Validate address / size. | ||
| 38 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 39 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 40 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 41 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 42 | |||
| 43 | // Create the code memory. | ||
| 44 | |||
| 45 | KCodeMemory* code_mem = KCodeMemory::Create(kernel); | ||
| 46 | R_UNLESS(code_mem != nullptr, ResultOutOfResource); | ||
| 47 | |||
| 48 | // Verify that the region is in range. | ||
| 49 | R_UNLESS(system.CurrentProcess()->PageTable().Contains(address, size), | ||
| 50 | ResultInvalidCurrentMemory); | ||
| 51 | |||
| 52 | // Initialize the code memory. | ||
| 53 | R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size)); | ||
| 54 | |||
| 55 | // Register the code memory. | ||
| 56 | KCodeMemory::Register(kernel, code_mem); | ||
| 57 | |||
| 58 | // Add the code memory to the handle table. | ||
| 59 | R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, code_mem)); | ||
| 60 | |||
| 61 | code_mem->Close(); | ||
| 62 | |||
| 63 | return ResultSuccess; | ||
| 64 | } | ||
| 65 | |||
| 66 | Result CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size) { | ||
| 67 | return CreateCodeMemory(system, out, address, size); | ||
| 68 | } | ||
| 69 | |||
| 70 | Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation, | ||
| 71 | VAddr address, size_t size, MemoryPermission perm) { | ||
| 72 | |||
| 73 | LOG_TRACE(Kernel_SVC, | ||
| 74 | "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, " | ||
| 75 | "permission=0x{:X}", | ||
| 76 | code_memory_handle, operation, address, size, perm); | ||
| 77 | |||
| 78 | // Validate the address / size. | ||
| 79 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 80 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 81 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 82 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 83 | |||
| 84 | // Get the code memory from its handle. | ||
| 85 | KScopedAutoObject code_mem = | ||
| 86 | system.CurrentProcess()->GetHandleTable().GetObject<KCodeMemory>(code_memory_handle); | ||
| 87 | R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle); | ||
| 88 | |||
| 89 | // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process. | ||
| 90 | // This enables homebrew usage of these SVCs for JIT. | ||
| 91 | |||
| 92 | // Perform the operation. | ||
| 93 | switch (static_cast<CodeMemoryOperation>(operation)) { | ||
| 94 | case CodeMemoryOperation::Map: { | ||
| 95 | // Check that the region is in range. | ||
| 96 | R_UNLESS( | ||
| 97 | system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut), | ||
| 98 | ResultInvalidMemoryRegion); | ||
| 99 | |||
| 100 | // Check the memory permission. | ||
| 101 | R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 102 | |||
| 103 | // Map the memory. | ||
| 104 | R_TRY(code_mem->Map(address, size)); | ||
| 105 | } break; | ||
| 106 | case CodeMemoryOperation::Unmap: { | ||
| 107 | // Check that the region is in range. | ||
| 108 | R_UNLESS( | ||
| 109 | system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut), | ||
| 110 | ResultInvalidMemoryRegion); | ||
| 111 | |||
| 112 | // Check the memory permission. | ||
| 113 | R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 114 | |||
| 115 | // Unmap the memory. | ||
| 116 | R_TRY(code_mem->Unmap(address, size)); | ||
| 117 | } break; | ||
| 118 | case CodeMemoryOperation::MapToOwner: { | ||
| 119 | // Check that the region is in range. | ||
| 120 | R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size, | ||
| 121 | KMemoryState::GeneratedCode), | ||
| 122 | ResultInvalidMemoryRegion); | ||
| 123 | |||
| 124 | // Check the memory permission. | ||
| 125 | R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 126 | |||
| 127 | // Map the memory to its owner. | ||
| 128 | R_TRY(code_mem->MapToOwner(address, size, perm)); | ||
| 129 | } break; | ||
| 130 | case CodeMemoryOperation::UnmapFromOwner: { | ||
| 131 | // Check that the region is in range. | ||
| 132 | R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size, | ||
| 133 | KMemoryState::GeneratedCode), | ||
| 134 | ResultInvalidMemoryRegion); | ||
| 135 | |||
| 136 | // Check the memory permission. | ||
| 137 | R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 138 | |||
| 139 | // Unmap the memory from its owner. | ||
| 140 | R_TRY(code_mem->UnmapFromOwner(address, size)); | ||
| 141 | } break; | ||
| 142 | default: | ||
| 143 | return ResultInvalidEnumValue; | ||
| 144 | } | ||
| 145 | |||
| 146 | return ResultSuccess; | ||
| 147 | } | ||
| 148 | |||
| 149 | Result ControlCodeMemory32(Core::System& system, Handle code_memory_handle, u32 operation, | ||
| 150 | u64 address, u64 size, MemoryPermission perm) { | ||
| 151 | return ControlCodeMemory(system, code_memory_handle, operation, address, size, perm); | ||
| 152 | } | ||
| 153 | |||
| 154 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/hle/kernel/k_memory_layout.h" | ||
| 6 | #include "core/hle/kernel/k_process.h" | ||
| 7 | #include "core/hle/kernel/kernel.h" | ||
| 8 | #include "core/hle/kernel/svc.h" | ||
| 9 | #include "core/hle/kernel/svc_results.h" | ||
| 10 | |||
| 11 | namespace Kernel::Svc { | ||
| 12 | |||
| 13 | /// Wait process wide key atomic | ||
| 14 | Result WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, u32 tag, | ||
| 15 | s64 timeout_ns) { | ||
| 16 | LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address, | ||
| 17 | cv_key, tag, timeout_ns); | ||
| 18 | |||
| 19 | // Validate input. | ||
| 20 | if (IsKernelAddress(address)) { | ||
| 21 | LOG_ERROR(Kernel_SVC, "Attempted to wait on kernel address (address={:08X})", address); | ||
| 22 | return ResultInvalidCurrentMemory; | ||
| 23 | } | ||
| 24 | if (!Common::IsAligned(address, sizeof(s32))) { | ||
| 25 | LOG_ERROR(Kernel_SVC, "Address must be 4 byte aligned (address={:08X})", address); | ||
| 26 | return ResultInvalidAddress; | ||
| 27 | } | ||
| 28 | |||
| 29 | // Convert timeout from nanoseconds to ticks. | ||
| 30 | s64 timeout{}; | ||
| 31 | if (timeout_ns > 0) { | ||
| 32 | const s64 offset_tick(timeout_ns); | ||
| 33 | if (offset_tick > 0) { | ||
| 34 | timeout = offset_tick + 2; | ||
| 35 | if (timeout <= 0) { | ||
| 36 | timeout = std::numeric_limits<s64>::max(); | ||
| 37 | } | ||
| 38 | } else { | ||
| 39 | timeout = std::numeric_limits<s64>::max(); | ||
| 40 | } | ||
| 41 | } else { | ||
| 42 | timeout = timeout_ns; | ||
| 43 | } | ||
| 44 | |||
| 45 | // Wait on the condition variable. | ||
| 46 | return system.Kernel().CurrentProcess()->WaitConditionVariable( | ||
| 47 | address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout); | ||
| 48 | } | ||
| 49 | |||
| 50 | Result WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag, | ||
| 51 | u32 timeout_ns_low, u32 timeout_ns_high) { | ||
| 52 | const auto timeout_ns = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32)); | ||
| 53 | return WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns); | ||
| 54 | } | ||
| 55 | |||
| 56 | /// Signal process wide key | ||
| 57 | void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count) { | ||
| 58 | LOG_TRACE(Kernel_SVC, "called, cv_key=0x{:X}, count=0x{:08X}", cv_key, count); | ||
| 59 | |||
| 60 | // Signal the condition variable. | ||
| 61 | return system.Kernel().CurrentProcess()->SignalConditionVariable( | ||
| 62 | Common::AlignDown(cv_key, sizeof(u32)), count); | ||
| 63 | } | ||
| 64 | |||
| 65 | void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count) { | ||
| 66 | SignalProcessWideKey(system, cv_key, count); | ||
| 67 | } | ||
| 68 | |||
| 69 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/kernel/svc.h" | ||
| 5 | |||
| 6 | 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/hle/kernel/svc.h" | ||
| 6 | #include "core/memory.h" | ||
| 7 | |||
| 8 | namespace Kernel::Svc { | ||
| 9 | |||
| 10 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit | ||
| 11 | void OutputDebugString(Core::System& system, VAddr address, u64 len) { | ||
| 12 | if (len == 0) { | ||
| 13 | return; | ||
| 14 | } | ||
| 15 | |||
| 16 | std::string str(len, '\0'); | ||
| 17 | system.Memory().ReadBlock(address, str.data(), str.size()); | ||
| 18 | LOG_DEBUG(Debug_Emulated, "{}", str); | ||
| 19 | } | ||
| 20 | |||
| 21 | void OutputDebugString32(Core::System& system, u32 address, u32 len) { | ||
| 22 | OutputDebugString(system, address, len); | ||
| 23 | } | ||
| 24 | |||
| 25 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/kernel/svc.h" | ||
| 5 | |||
| 6 | 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/scope_exit.h" | ||
| 5 | #include "core/core.h" | ||
| 6 | #include "core/hle/kernel/k_event.h" | ||
| 7 | #include "core/hle/kernel/k_process.h" | ||
| 8 | #include "core/hle/kernel/k_scoped_resource_reservation.h" | ||
| 9 | #include "core/hle/kernel/kernel.h" | ||
| 10 | #include "core/hle/kernel/svc.h" | ||
| 11 | |||
| 12 | namespace Kernel::Svc { | ||
| 13 | |||
| 14 | Result SignalEvent(Core::System& system, Handle event_handle) { | ||
| 15 | LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle); | ||
| 16 | |||
| 17 | // Get the current handle table. | ||
| 18 | const KHandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 19 | |||
| 20 | // Get the event. | ||
| 21 | KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle); | ||
| 22 | R_UNLESS(event.IsNotNull(), ResultInvalidHandle); | ||
| 23 | |||
| 24 | return event->Signal(); | ||
| 25 | } | ||
| 26 | |||
| 27 | Result SignalEvent32(Core::System& system, Handle event_handle) { | ||
| 28 | return SignalEvent(system, event_handle); | ||
| 29 | } | ||
| 30 | |||
| 31 | Result ClearEvent(Core::System& system, Handle event_handle) { | ||
| 32 | LOG_TRACE(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle); | ||
| 33 | |||
| 34 | // Get the current handle table. | ||
| 35 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 36 | |||
| 37 | // Try to clear the writable event. | ||
| 38 | { | ||
| 39 | KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle); | ||
| 40 | if (event.IsNotNull()) { | ||
| 41 | return event->Clear(); | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | // Try to clear the readable event. | ||
| 46 | { | ||
| 47 | KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(event_handle); | ||
| 48 | if (readable_event.IsNotNull()) { | ||
| 49 | return readable_event->Clear(); | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | LOG_ERROR(Kernel_SVC, "Event handle does not exist, event_handle=0x{:08X}", event_handle); | ||
| 54 | |||
| 55 | return ResultInvalidHandle; | ||
| 56 | } | ||
| 57 | |||
| 58 | Result ClearEvent32(Core::System& system, Handle event_handle) { | ||
| 59 | return ClearEvent(system, event_handle); | ||
| 60 | } | ||
| 61 | |||
| 62 | Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) { | ||
| 63 | LOG_DEBUG(Kernel_SVC, "called"); | ||
| 64 | |||
| 65 | // Get the kernel reference and handle table. | ||
| 66 | auto& kernel = system.Kernel(); | ||
| 67 | auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); | ||
| 68 | |||
| 69 | // Reserve a new event from the process resource limit | ||
| 70 | KScopedResourceReservation event_reservation(kernel.CurrentProcess(), | ||
| 71 | LimitableResource::EventCountMax); | ||
| 72 | R_UNLESS(event_reservation.Succeeded(), ResultLimitReached); | ||
| 73 | |||
| 74 | // Create a new event. | ||
| 75 | KEvent* event = KEvent::Create(kernel); | ||
| 76 | R_UNLESS(event != nullptr, ResultOutOfResource); | ||
| 77 | |||
| 78 | // Initialize the event. | ||
| 79 | event->Initialize(kernel.CurrentProcess()); | ||
| 80 | |||
| 81 | // Commit the thread reservation. | ||
| 82 | event_reservation.Commit(); | ||
| 83 | |||
| 84 | // Ensure that we clean up the event (and its only references are handle table) on function end. | ||
| 85 | SCOPE_EXIT({ | ||
| 86 | event->GetReadableEvent().Close(); | ||
| 87 | event->Close(); | ||
| 88 | }); | ||
| 89 | |||
| 90 | // Register the event. | ||
| 91 | KEvent::Register(kernel, event); | ||
| 92 | |||
| 93 | // Add the event to the handle table. | ||
| 94 | R_TRY(handle_table.Add(out_write, event)); | ||
| 95 | |||
| 96 | // Ensure that we maintaing a clean handle state on exit. | ||
| 97 | auto handle_guard = SCOPE_GUARD({ handle_table.Remove(*out_write); }); | ||
| 98 | |||
| 99 | // Add the readable event to the handle table. | ||
| 100 | R_TRY(handle_table.Add(out_read, std::addressof(event->GetReadableEvent()))); | ||
| 101 | |||
| 102 | // We succeeded. | ||
| 103 | handle_guard.Cancel(); | ||
| 104 | return ResultSuccess; | ||
| 105 | } | ||
| 106 | |||
| 107 | Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read) { | ||
| 108 | return CreateEvent(system, out_write, out_read); | ||
| 109 | } | ||
| 110 | |||
| 111 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/debugger/debugger.h" | ||
| 6 | #include "core/hle/kernel/k_thread.h" | ||
| 7 | #include "core/hle/kernel/svc.h" | ||
| 8 | #include "core/hle/kernel/svc_types.h" | ||
| 9 | #include "core/memory.h" | ||
| 10 | #include "core/reporter.h" | ||
| 11 | |||
| 12 | namespace Kernel::Svc { | ||
| 13 | |||
| 14 | /// Break program execution | ||
| 15 | void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { | ||
| 16 | BreakReason break_reason = | ||
| 17 | static_cast<BreakReason>(reason & ~static_cast<u32>(BreakReason::NotificationOnlyFlag)); | ||
| 18 | bool notification_only = (reason & static_cast<u32>(BreakReason::NotificationOnlyFlag)) != 0; | ||
| 19 | |||
| 20 | bool has_dumped_buffer{}; | ||
| 21 | std::vector<u8> debug_buffer; | ||
| 22 | |||
| 23 | const auto handle_debug_buffer = [&](VAddr addr, u64 sz) { | ||
| 24 | if (sz == 0 || addr == 0 || has_dumped_buffer) { | ||
| 25 | return; | ||
| 26 | } | ||
| 27 | |||
| 28 | auto& memory = system.Memory(); | ||
| 29 | |||
| 30 | // This typically is an error code so we're going to assume this is the case | ||
| 31 | if (sz == sizeof(u32)) { | ||
| 32 | LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", memory.Read32(addr)); | ||
| 33 | } else { | ||
| 34 | // We don't know what's in here so we'll hexdump it | ||
| 35 | debug_buffer.resize(sz); | ||
| 36 | memory.ReadBlock(addr, debug_buffer.data(), sz); | ||
| 37 | std::string hexdump; | ||
| 38 | for (std::size_t i = 0; i < debug_buffer.size(); i++) { | ||
| 39 | hexdump += fmt::format("{:02X} ", debug_buffer[i]); | ||
| 40 | if (i != 0 && i % 16 == 0) { | ||
| 41 | hexdump += '\n'; | ||
| 42 | } | ||
| 43 | } | ||
| 44 | LOG_CRITICAL(Debug_Emulated, "debug_buffer=\n{}", hexdump); | ||
| 45 | } | ||
| 46 | has_dumped_buffer = true; | ||
| 47 | }; | ||
| 48 | switch (break_reason) { | ||
| 49 | case BreakReason::Panic: | ||
| 50 | LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1, | ||
| 51 | info2); | ||
| 52 | handle_debug_buffer(info1, info2); | ||
| 53 | break; | ||
| 54 | case BreakReason::Assert: | ||
| 55 | LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}", | ||
| 56 | info1, info2); | ||
| 57 | handle_debug_buffer(info1, info2); | ||
| 58 | break; | ||
| 59 | case BreakReason::User: | ||
| 60 | LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2); | ||
| 61 | handle_debug_buffer(info1, info2); | ||
| 62 | break; | ||
| 63 | case BreakReason::PreLoadDll: | ||
| 64 | LOG_INFO(Debug_Emulated, | ||
| 65 | "Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1, | ||
| 66 | info2); | ||
| 67 | break; | ||
| 68 | case BreakReason::PostLoadDll: | ||
| 69 | LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1, | ||
| 70 | info2); | ||
| 71 | break; | ||
| 72 | case BreakReason::PreUnloadDll: | ||
| 73 | LOG_INFO(Debug_Emulated, | ||
| 74 | "Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1, | ||
| 75 | info2); | ||
| 76 | break; | ||
| 77 | case BreakReason::PostUnloadDll: | ||
| 78 | LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}", | ||
| 79 | info1, info2); | ||
| 80 | break; | ||
| 81 | case BreakReason::CppException: | ||
| 82 | LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered."); | ||
| 83 | break; | ||
| 84 | default: | ||
| 85 | LOG_WARNING( | ||
| 86 | Debug_Emulated, | ||
| 87 | "Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}", | ||
| 88 | reason, info1, info2); | ||
| 89 | handle_debug_buffer(info1, info2); | ||
| 90 | break; | ||
| 91 | } | ||
| 92 | |||
| 93 | system.GetReporter().SaveSvcBreakReport(reason, notification_only, info1, info2, | ||
| 94 | has_dumped_buffer ? std::make_optional(debug_buffer) | ||
| 95 | : std::nullopt); | ||
| 96 | |||
| 97 | if (!notification_only) { | ||
| 98 | LOG_CRITICAL( | ||
| 99 | Debug_Emulated, | ||
| 100 | "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", | ||
| 101 | reason, info1, info2); | ||
| 102 | |||
| 103 | handle_debug_buffer(info1, info2); | ||
| 104 | |||
| 105 | auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); | ||
| 106 | const auto thread_processor_id = current_thread->GetActiveCore(); | ||
| 107 | system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace(); | ||
| 108 | } | ||
| 109 | |||
| 110 | if (system.DebuggerEnabled()) { | ||
| 111 | auto* thread = system.Kernel().GetCurrentEmuThread(); | ||
| 112 | system.GetDebugger().NotifyThreadStopped(thread); | ||
| 113 | thread->RequestSuspend(Kernel::SuspendType::Debug); | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) { | ||
| 118 | Break(system, reason, info1, info2); | ||
| 119 | } | ||
| 120 | |||
| 121 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/core_timing.h" | ||
| 6 | #include "core/hle/kernel/k_process.h" | ||
| 7 | #include "core/hle/kernel/k_resource_limit.h" | ||
| 8 | #include "core/hle/kernel/svc.h" | ||
| 9 | |||
| 10 | namespace Kernel::Svc { | ||
| 11 | |||
| 12 | /// Gets system/memory information for the current process | ||
| 13 | Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle, u64 info_sub_id) { | ||
| 14 | LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, | ||
| 15 | info_sub_id, handle); | ||
| 16 | |||
| 17 | const auto info_id_type = static_cast<InfoType>(info_id); | ||
| 18 | |||
| 19 | switch (info_id_type) { | ||
| 20 | case InfoType::CoreMask: | ||
| 21 | case InfoType::PriorityMask: | ||
| 22 | case InfoType::AliasRegionAddress: | ||
| 23 | case InfoType::AliasRegionSize: | ||
| 24 | case InfoType::HeapRegionAddress: | ||
| 25 | case InfoType::HeapRegionSize: | ||
| 26 | case InfoType::AslrRegionAddress: | ||
| 27 | case InfoType::AslrRegionSize: | ||
| 28 | case InfoType::StackRegionAddress: | ||
| 29 | case InfoType::StackRegionSize: | ||
| 30 | case InfoType::TotalMemorySize: | ||
| 31 | case InfoType::UsedMemorySize: | ||
| 32 | case InfoType::SystemResourceSizeTotal: | ||
| 33 | case InfoType::SystemResourceSizeUsed: | ||
| 34 | case InfoType::ProgramId: | ||
| 35 | case InfoType::UserExceptionContextAddress: | ||
| 36 | case InfoType::TotalNonSystemMemorySize: | ||
| 37 | case InfoType::UsedNonSystemMemorySize: | ||
| 38 | case InfoType::IsApplication: | ||
| 39 | case InfoType::FreeThreadCount: { | ||
| 40 | if (info_sub_id != 0) { | ||
| 41 | LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id, | ||
| 42 | info_sub_id); | ||
| 43 | return ResultInvalidEnumValue; | ||
| 44 | } | ||
| 45 | |||
| 46 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 47 | KScopedAutoObject process = handle_table.GetObject<KProcess>(handle); | ||
| 48 | if (process.IsNull()) { | ||
| 49 | LOG_ERROR(Kernel_SVC, "Process is not valid! info_id={}, info_sub_id={}, handle={:08X}", | ||
| 50 | info_id, info_sub_id, handle); | ||
| 51 | return ResultInvalidHandle; | ||
| 52 | } | ||
| 53 | |||
| 54 | switch (info_id_type) { | ||
| 55 | case InfoType::CoreMask: | ||
| 56 | *result = process->GetCoreMask(); | ||
| 57 | return ResultSuccess; | ||
| 58 | |||
| 59 | case InfoType::PriorityMask: | ||
| 60 | *result = process->GetPriorityMask(); | ||
| 61 | return ResultSuccess; | ||
| 62 | |||
| 63 | case InfoType::AliasRegionAddress: | ||
| 64 | *result = process->PageTable().GetAliasRegionStart(); | ||
| 65 | return ResultSuccess; | ||
| 66 | |||
| 67 | case InfoType::AliasRegionSize: | ||
| 68 | *result = process->PageTable().GetAliasRegionSize(); | ||
| 69 | return ResultSuccess; | ||
| 70 | |||
| 71 | case InfoType::HeapRegionAddress: | ||
| 72 | *result = process->PageTable().GetHeapRegionStart(); | ||
| 73 | return ResultSuccess; | ||
| 74 | |||
| 75 | case InfoType::HeapRegionSize: | ||
| 76 | *result = process->PageTable().GetHeapRegionSize(); | ||
| 77 | return ResultSuccess; | ||
| 78 | |||
| 79 | case InfoType::AslrRegionAddress: | ||
| 80 | *result = process->PageTable().GetAliasCodeRegionStart(); | ||
| 81 | return ResultSuccess; | ||
| 82 | |||
| 83 | case InfoType::AslrRegionSize: | ||
| 84 | *result = process->PageTable().GetAliasCodeRegionSize(); | ||
| 85 | return ResultSuccess; | ||
| 86 | |||
| 87 | case InfoType::StackRegionAddress: | ||
| 88 | *result = process->PageTable().GetStackRegionStart(); | ||
| 89 | return ResultSuccess; | ||
| 90 | |||
| 91 | case InfoType::StackRegionSize: | ||
| 92 | *result = process->PageTable().GetStackRegionSize(); | ||
| 93 | return ResultSuccess; | ||
| 94 | |||
| 95 | case InfoType::TotalMemorySize: | ||
| 96 | *result = process->GetTotalPhysicalMemoryAvailable(); | ||
| 97 | return ResultSuccess; | ||
| 98 | |||
| 99 | case InfoType::UsedMemorySize: | ||
| 100 | *result = process->GetTotalPhysicalMemoryUsed(); | ||
| 101 | return ResultSuccess; | ||
| 102 | |||
| 103 | case InfoType::SystemResourceSizeTotal: | ||
| 104 | *result = process->GetSystemResourceSize(); | ||
| 105 | return ResultSuccess; | ||
| 106 | |||
| 107 | case InfoType::SystemResourceSizeUsed: | ||
| 108 | LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage"); | ||
| 109 | *result = process->GetSystemResourceUsage(); | ||
| 110 | return ResultSuccess; | ||
| 111 | |||
| 112 | case InfoType::ProgramId: | ||
| 113 | *result = process->GetProgramID(); | ||
| 114 | return ResultSuccess; | ||
| 115 | |||
| 116 | case InfoType::UserExceptionContextAddress: | ||
| 117 | *result = process->GetProcessLocalRegionAddress(); | ||
| 118 | return ResultSuccess; | ||
| 119 | |||
| 120 | case InfoType::TotalNonSystemMemorySize: | ||
| 121 | *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource(); | ||
| 122 | return ResultSuccess; | ||
| 123 | |||
| 124 | case InfoType::UsedNonSystemMemorySize: | ||
| 125 | *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource(); | ||
| 126 | return ResultSuccess; | ||
| 127 | |||
| 128 | case InfoType::FreeThreadCount: | ||
| 129 | *result = process->GetFreeThreadCount(); | ||
| 130 | return ResultSuccess; | ||
| 131 | |||
| 132 | default: | ||
| 133 | break; | ||
| 134 | } | ||
| 135 | |||
| 136 | LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id); | ||
| 137 | return ResultInvalidEnumValue; | ||
| 138 | } | ||
| 139 | |||
| 140 | case InfoType::DebuggerAttached: | ||
| 141 | *result = 0; | ||
| 142 | return ResultSuccess; | ||
| 143 | |||
| 144 | case InfoType::ResourceLimit: { | ||
| 145 | if (handle != 0) { | ||
| 146 | LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle); | ||
| 147 | return ResultInvalidHandle; | ||
| 148 | } | ||
| 149 | |||
| 150 | if (info_sub_id != 0) { | ||
| 151 | LOG_ERROR(Kernel, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id, | ||
| 152 | info_sub_id); | ||
| 153 | return ResultInvalidCombination; | ||
| 154 | } | ||
| 155 | |||
| 156 | KProcess* const current_process = system.Kernel().CurrentProcess(); | ||
| 157 | KHandleTable& handle_table = current_process->GetHandleTable(); | ||
| 158 | const auto resource_limit = current_process->GetResourceLimit(); | ||
| 159 | if (!resource_limit) { | ||
| 160 | *result = Svc::InvalidHandle; | ||
| 161 | // Yes, the kernel considers this a successful operation. | ||
| 162 | return ResultSuccess; | ||
| 163 | } | ||
| 164 | |||
| 165 | Handle resource_handle{}; | ||
| 166 | R_TRY(handle_table.Add(&resource_handle, resource_limit)); | ||
| 167 | |||
| 168 | *result = resource_handle; | ||
| 169 | return ResultSuccess; | ||
| 170 | } | ||
| 171 | |||
| 172 | case InfoType::RandomEntropy: | ||
| 173 | if (handle != 0) { | ||
| 174 | LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}", | ||
| 175 | handle); | ||
| 176 | return ResultInvalidHandle; | ||
| 177 | } | ||
| 178 | |||
| 179 | if (info_sub_id >= KProcess::RANDOM_ENTROPY_SIZE) { | ||
| 180 | LOG_ERROR(Kernel_SVC, "Entropy size is out of range, expected {} but got {}", | ||
| 181 | KProcess::RANDOM_ENTROPY_SIZE, info_sub_id); | ||
| 182 | return ResultInvalidCombination; | ||
| 183 | } | ||
| 184 | |||
| 185 | *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id); | ||
| 186 | return ResultSuccess; | ||
| 187 | |||
| 188 | case InfoType::InitialProcessIdRange: | ||
| 189 | LOG_WARNING(Kernel_SVC, | ||
| 190 | "(STUBBED) Attempted to query privileged process id bounds, returned 0"); | ||
| 191 | *result = 0; | ||
| 192 | return ResultSuccess; | ||
| 193 | |||
| 194 | case InfoType::ThreadTickCount: { | ||
| 195 | constexpr u64 num_cpus = 4; | ||
| 196 | if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) { | ||
| 197 | LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus, | ||
| 198 | info_sub_id); | ||
| 199 | return ResultInvalidCombination; | ||
| 200 | } | ||
| 201 | |||
| 202 | KScopedAutoObject thread = | ||
| 203 | system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>( | ||
| 204 | static_cast<Handle>(handle)); | ||
| 205 | if (thread.IsNull()) { | ||
| 206 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", | ||
| 207 | static_cast<Handle>(handle)); | ||
| 208 | return ResultInvalidHandle; | ||
| 209 | } | ||
| 210 | |||
| 211 | const auto& core_timing = system.CoreTiming(); | ||
| 212 | const auto& scheduler = *system.Kernel().CurrentScheduler(); | ||
| 213 | const auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); | ||
| 214 | const bool same_thread = current_thread == thread.GetPointerUnsafe(); | ||
| 215 | |||
| 216 | const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTime(); | ||
| 217 | u64 out_ticks = 0; | ||
| 218 | if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { | ||
| 219 | const u64 thread_ticks = current_thread->GetCpuTime(); | ||
| 220 | |||
| 221 | out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); | ||
| 222 | } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) { | ||
| 223 | out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks; | ||
| 224 | } | ||
| 225 | |||
| 226 | *result = out_ticks; | ||
| 227 | return ResultSuccess; | ||
| 228 | } | ||
| 229 | case InfoType::IdleTickCount: { | ||
| 230 | // Verify the input handle is invalid. | ||
| 231 | R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); | ||
| 232 | |||
| 233 | // Verify the requested core is valid. | ||
| 234 | const bool core_valid = | ||
| 235 | (info_sub_id == 0xFFFFFFFFFFFFFFFF) || | ||
| 236 | (info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex())); | ||
| 237 | R_UNLESS(core_valid, ResultInvalidCombination); | ||
| 238 | |||
| 239 | // Get the idle tick count. | ||
| 240 | *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime(); | ||
| 241 | return ResultSuccess; | ||
| 242 | } | ||
| 243 | case InfoType::MesosphereCurrentProcess: { | ||
| 244 | // Verify the input handle is invalid. | ||
| 245 | R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); | ||
| 246 | |||
| 247 | // Verify the sub-type is valid. | ||
| 248 | R_UNLESS(info_sub_id == 0, ResultInvalidCombination); | ||
| 249 | |||
| 250 | // Get the handle table. | ||
| 251 | KProcess* current_process = system.Kernel().CurrentProcess(); | ||
| 252 | KHandleTable& handle_table = current_process->GetHandleTable(); | ||
| 253 | |||
| 254 | // Get a new handle for the current process. | ||
| 255 | Handle tmp; | ||
| 256 | R_TRY(handle_table.Add(&tmp, current_process)); | ||
| 257 | |||
| 258 | // Set the output. | ||
| 259 | *result = tmp; | ||
| 260 | |||
| 261 | // We succeeded. | ||
| 262 | return ResultSuccess; | ||
| 263 | } | ||
| 264 | default: | ||
| 265 | LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id); | ||
| 266 | return ResultInvalidEnumValue; | ||
| 267 | } | ||
| 268 | } | ||
| 269 | |||
| 270 | Result GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low, | ||
| 271 | u32 info_id, u32 handle, u32 sub_id_high) { | ||
| 272 | const u64 sub_id{u64{sub_id_low} | (u64{sub_id_high} << 32)}; | ||
| 273 | u64 res_value{}; | ||
| 274 | |||
| 275 | const Result result{GetInfo(system, &res_value, info_id, handle, sub_id)}; | ||
| 276 | *result_high = static_cast<u32>(res_value >> 32); | ||
| 277 | *result_low = static_cast<u32>(res_value & std::numeric_limits<u32>::max()); | ||
| 278 | |||
| 279 | return result; | ||
| 280 | } | ||
| 281 | |||
| 282 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/kernel/svc.h" | ||
| 5 | |||
| 6 | 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/kernel/svc.h" | ||
| 5 | |||
| 6 | 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/scope_exit.h" | ||
| 5 | #include "core/core.h" | ||
| 6 | #include "core/hle/kernel/k_client_session.h" | ||
| 7 | #include "core/hle/kernel/k_process.h" | ||
| 8 | #include "core/hle/kernel/k_server_session.h" | ||
| 9 | #include "core/hle/kernel/svc.h" | ||
| 10 | |||
| 11 | namespace Kernel::Svc { | ||
| 12 | |||
| 13 | /// Makes a blocking IPC call to a service. | ||
| 14 | Result SendSyncRequest(Core::System& system, Handle handle) { | ||
| 15 | auto& kernel = system.Kernel(); | ||
| 16 | |||
| 17 | // Get the client session from its handle. | ||
| 18 | KScopedAutoObject session = | ||
| 19 | kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle); | ||
| 20 | R_UNLESS(session.IsNotNull(), ResultInvalidHandle); | ||
| 21 | |||
| 22 | LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); | ||
| 23 | |||
| 24 | return session->SendSyncRequest(); | ||
| 25 | } | ||
| 26 | |||
| 27 | Result SendSyncRequest32(Core::System& system, Handle handle) { | ||
| 28 | return SendSyncRequest(system, handle); | ||
| 29 | } | ||
| 30 | |||
| 31 | Result ReplyAndReceive(Core::System& system, s32* out_index, Handle* handles, s32 num_handles, | ||
| 32 | Handle reply_target, s64 timeout_ns) { | ||
| 33 | auto& kernel = system.Kernel(); | ||
| 34 | auto& handle_table = GetCurrentThread(kernel).GetOwnerProcess()->GetHandleTable(); | ||
| 35 | |||
| 36 | // Convert handle list to object table. | ||
| 37 | std::vector<KSynchronizationObject*> objs(num_handles); | ||
| 38 | R_UNLESS( | ||
| 39 | handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles, num_handles), | ||
| 40 | ResultInvalidHandle); | ||
| 41 | |||
| 42 | // Ensure handles are closed when we're done. | ||
| 43 | SCOPE_EXIT({ | ||
| 44 | for (auto i = 0; i < num_handles; ++i) { | ||
| 45 | objs[i]->Close(); | ||
| 46 | } | ||
| 47 | }); | ||
| 48 | |||
| 49 | // Reply to the target, if one is specified. | ||
| 50 | if (reply_target != InvalidHandle) { | ||
| 51 | KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target); | ||
| 52 | R_UNLESS(session.IsNotNull(), ResultInvalidHandle); | ||
| 53 | |||
| 54 | // If we fail to reply, we want to set the output index to -1. | ||
| 55 | ON_RESULT_FAILURE { | ||
| 56 | *out_index = -1; | ||
| 57 | }; | ||
| 58 | |||
| 59 | // Send the reply. | ||
| 60 | R_TRY(session->SendReply()); | ||
| 61 | } | ||
| 62 | |||
| 63 | // Wait for a message. | ||
| 64 | while (true) { | ||
| 65 | // Wait for an object. | ||
| 66 | s32 index; | ||
| 67 | Result result = KSynchronizationObject::Wait(kernel, &index, objs.data(), | ||
| 68 | static_cast<s32>(objs.size()), timeout_ns); | ||
| 69 | if (result == ResultTimedOut) { | ||
| 70 | return result; | ||
| 71 | } | ||
| 72 | |||
| 73 | // Receive the request. | ||
| 74 | if (R_SUCCEEDED(result)) { | ||
| 75 | KServerSession* session = objs[index]->DynamicCast<KServerSession*>(); | ||
| 76 | if (session != nullptr) { | ||
| 77 | result = session->ReceiveRequest(); | ||
| 78 | if (result == ResultNotFound) { | ||
| 79 | continue; | ||
| 80 | } | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | *out_index = index; | ||
| 85 | return result; | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/kernel/svc.h" | ||
| 5 | |||
| 6 | namespace Kernel::Svc { | ||
| 7 | |||
| 8 | void KernelDebug([[maybe_unused]] Core::System& system, [[maybe_unused]] u32 kernel_debug_type, | ||
| 9 | [[maybe_unused]] u64 param1, [[maybe_unused]] u64 param2, | ||
| 10 | [[maybe_unused]] u64 param3) { | ||
| 11 | // Intentionally do nothing, as this does nothing in released kernel binaries. | ||
| 12 | } | ||
| 13 | |||
| 14 | void ChangeKernelTraceState([[maybe_unused]] Core::System& system, | ||
| 15 | [[maybe_unused]] u32 trace_state) { | ||
| 16 | // Intentionally do nothing, as this does nothing in released kernel binaries. | ||
| 17 | } | ||
| 18 | |||
| 19 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/kernel/svc.h" | ||
| 5 | |||
| 6 | 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/hle/kernel/k_memory_layout.h" | ||
| 6 | #include "core/hle/kernel/k_process.h" | ||
| 7 | #include "core/hle/kernel/svc.h" | ||
| 8 | |||
| 9 | namespace Kernel::Svc { | ||
| 10 | |||
| 11 | /// Attempts to locks a mutex | ||
| 12 | Result ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, u32 tag) { | ||
| 13 | LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}", | ||
| 14 | thread_handle, address, tag); | ||
| 15 | |||
| 16 | // Validate the input address. | ||
| 17 | if (IsKernelAddress(address)) { | ||
| 18 | LOG_ERROR(Kernel_SVC, "Attempting to arbitrate a lock on a kernel address (address={:08X})", | ||
| 19 | address); | ||
| 20 | return ResultInvalidCurrentMemory; | ||
| 21 | } | ||
| 22 | if (!Common::IsAligned(address, sizeof(u32))) { | ||
| 23 | LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address); | ||
| 24 | return ResultInvalidAddress; | ||
| 25 | } | ||
| 26 | |||
| 27 | return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag); | ||
| 28 | } | ||
| 29 | |||
| 30 | Result ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, u32 tag) { | ||
| 31 | return ArbitrateLock(system, thread_handle, address, tag); | ||
| 32 | } | ||
| 33 | |||
| 34 | /// Unlock a mutex | ||
| 35 | Result ArbitrateUnlock(Core::System& system, VAddr address) { | ||
| 36 | LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address); | ||
| 37 | |||
| 38 | // Validate the input address. | ||
| 39 | if (IsKernelAddress(address)) { | ||
| 40 | LOG_ERROR(Kernel_SVC, | ||
| 41 | "Attempting to arbitrate an unlock on a kernel address (address={:08X})", | ||
| 42 | address); | ||
| 43 | return ResultInvalidCurrentMemory; | ||
| 44 | } | ||
| 45 | if (!Common::IsAligned(address, sizeof(u32))) { | ||
| 46 | LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address); | ||
| 47 | return ResultInvalidAddress; | ||
| 48 | } | ||
| 49 | |||
| 50 | return system.Kernel().CurrentProcess()->SignalToAddress(address); | ||
| 51 | } | ||
| 52 | |||
| 53 | Result ArbitrateUnlock32(Core::System& system, u32 address) { | ||
| 54 | return ArbitrateUnlock(system, address); | ||
| 55 | } | ||
| 56 | |||
| 57 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/hle/kernel/k_process.h" | ||
| 6 | #include "core/hle/kernel/svc.h" | ||
| 7 | |||
| 8 | namespace Kernel::Svc { | ||
| 9 | namespace { | ||
| 10 | |||
| 11 | constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) { | ||
| 12 | switch (perm) { | ||
| 13 | case MemoryPermission::None: | ||
| 14 | case MemoryPermission::Read: | ||
| 15 | case MemoryPermission::ReadWrite: | ||
| 16 | return true; | ||
| 17 | default: | ||
| 18 | return false; | ||
| 19 | } | ||
| 20 | } | ||
| 21 | |||
| 22 | // Checks if address + size is greater than the given address | ||
| 23 | // This can return false if the size causes an overflow of a 64-bit type | ||
| 24 | // or if the given size is zero. | ||
| 25 | constexpr bool IsValidAddressRange(VAddr address, u64 size) { | ||
| 26 | return address + size > address; | ||
| 27 | } | ||
| 28 | |||
| 29 | // Helper function that performs the common sanity checks for svcMapMemory | ||
| 30 | // and svcUnmapMemory. This is doable, as both functions perform their sanitizing | ||
| 31 | // in the same order. | ||
| 32 | Result MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr, VAddr src_addr, | ||
| 33 | u64 size) { | ||
| 34 | if (!Common::Is4KBAligned(dst_addr)) { | ||
| 35 | LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr); | ||
| 36 | return ResultInvalidAddress; | ||
| 37 | } | ||
| 38 | |||
| 39 | if (!Common::Is4KBAligned(src_addr)) { | ||
| 40 | LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, 0x{:016X}", src_addr); | ||
| 41 | return ResultInvalidSize; | ||
| 42 | } | ||
| 43 | |||
| 44 | if (size == 0) { | ||
| 45 | LOG_ERROR(Kernel_SVC, "Size is 0"); | ||
| 46 | return ResultInvalidSize; | ||
| 47 | } | ||
| 48 | |||
| 49 | if (!Common::Is4KBAligned(size)) { | ||
| 50 | LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size); | ||
| 51 | return ResultInvalidSize; | ||
| 52 | } | ||
| 53 | |||
| 54 | if (!IsValidAddressRange(dst_addr, size)) { | ||
| 55 | LOG_ERROR(Kernel_SVC, | ||
| 56 | "Destination is not a valid address range, addr=0x{:016X}, size=0x{:016X}", | ||
| 57 | dst_addr, size); | ||
| 58 | return ResultInvalidCurrentMemory; | ||
| 59 | } | ||
| 60 | |||
| 61 | if (!IsValidAddressRange(src_addr, size)) { | ||
| 62 | LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr=0x{:016X}, size=0x{:016X}", | ||
| 63 | src_addr, size); | ||
| 64 | return ResultInvalidCurrentMemory; | ||
| 65 | } | ||
| 66 | |||
| 67 | if (!manager.IsInsideAddressSpace(src_addr, size)) { | ||
| 68 | LOG_ERROR(Kernel_SVC, | ||
| 69 | "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", | ||
| 70 | src_addr, size); | ||
| 71 | return ResultInvalidCurrentMemory; | ||
| 72 | } | ||
| 73 | |||
| 74 | if (manager.IsOutsideStackRegion(dst_addr, size)) { | ||
| 75 | LOG_ERROR(Kernel_SVC, | ||
| 76 | "Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}", | ||
| 77 | dst_addr, size); | ||
| 78 | return ResultInvalidMemoryRegion; | ||
| 79 | } | ||
| 80 | |||
| 81 | if (manager.IsInsideHeapRegion(dst_addr, size)) { | ||
| 82 | LOG_ERROR(Kernel_SVC, | ||
| 83 | "Destination does not fit within the heap region, addr=0x{:016X}, " | ||
| 84 | "size=0x{:016X}", | ||
| 85 | dst_addr, size); | ||
| 86 | return ResultInvalidMemoryRegion; | ||
| 87 | } | ||
| 88 | |||
| 89 | if (manager.IsInsideAliasRegion(dst_addr, size)) { | ||
| 90 | LOG_ERROR(Kernel_SVC, | ||
| 91 | "Destination does not fit within the map region, addr=0x{:016X}, " | ||
| 92 | "size=0x{:016X}", | ||
| 93 | dst_addr, size); | ||
| 94 | return ResultInvalidMemoryRegion; | ||
| 95 | } | ||
| 96 | |||
| 97 | return ResultSuccess; | ||
| 98 | } | ||
| 99 | |||
| 100 | } // namespace | ||
| 101 | |||
| 102 | Result SetMemoryPermission(Core::System& system, VAddr address, u64 size, MemoryPermission perm) { | ||
| 103 | LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X", address, size, | ||
| 104 | perm); | ||
| 105 | |||
| 106 | // Validate address / size. | ||
| 107 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 108 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 109 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 110 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 111 | |||
| 112 | // Validate the permission. | ||
| 113 | R_UNLESS(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 114 | |||
| 115 | // Validate that the region is in range for the current process. | ||
| 116 | auto& page_table = system.Kernel().CurrentProcess()->PageTable(); | ||
| 117 | R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); | ||
| 118 | |||
| 119 | // Set the memory attribute. | ||
| 120 | return page_table.SetMemoryPermission(address, size, perm); | ||
| 121 | } | ||
| 122 | |||
| 123 | Result SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, u32 attr) { | ||
| 124 | LOG_DEBUG(Kernel_SVC, | ||
| 125 | "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, | ||
| 126 | size, mask, attr); | ||
| 127 | |||
| 128 | // Validate address / size. | ||
| 129 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 130 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 131 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 132 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 133 | |||
| 134 | // Validate the attribute and mask. | ||
| 135 | constexpr u32 SupportedMask = static_cast<u32>(MemoryAttribute::Uncached); | ||
| 136 | R_UNLESS((mask | attr) == mask, ResultInvalidCombination); | ||
| 137 | R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination); | ||
| 138 | |||
| 139 | // Validate that the region is in range for the current process. | ||
| 140 | auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; | ||
| 141 | R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); | ||
| 142 | |||
| 143 | // Set the memory attribute. | ||
| 144 | return page_table.SetMemoryAttribute(address, size, mask, attr); | ||
| 145 | } | ||
| 146 | |||
| 147 | Result SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask, u32 attr) { | ||
| 148 | return SetMemoryAttribute(system, address, size, mask, attr); | ||
| 149 | } | ||
| 150 | |||
| 151 | /// Maps a memory range into a different range. | ||
| 152 | Result MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { | ||
| 153 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, | ||
| 154 | src_addr, size); | ||
| 155 | |||
| 156 | auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; | ||
| 157 | |||
| 158 | if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; | ||
| 159 | result.IsError()) { | ||
| 160 | return result; | ||
| 161 | } | ||
| 162 | |||
| 163 | return page_table.MapMemory(dst_addr, src_addr, size); | ||
| 164 | } | ||
| 165 | |||
| 166 | Result MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { | ||
| 167 | return MapMemory(system, dst_addr, src_addr, size); | ||
| 168 | } | ||
| 169 | |||
| 170 | /// Unmaps a region that was previously mapped with svcMapMemory | ||
| 171 | Result UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { | ||
| 172 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, | ||
| 173 | src_addr, size); | ||
| 174 | |||
| 175 | auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; | ||
| 176 | |||
| 177 | if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; | ||
| 178 | result.IsError()) { | ||
| 179 | return result; | ||
| 180 | } | ||
| 181 | |||
| 182 | return page_table.UnmapMemory(dst_addr, src_addr, size); | ||
| 183 | } | ||
| 184 | |||
| 185 | Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { | ||
| 186 | return UnmapMemory(system, dst_addr, src_addr, size); | ||
| 187 | } | ||
| 188 | |||
| 189 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/hle/kernel/k_process.h" | ||
| 6 | #include "core/hle/kernel/svc.h" | ||
| 7 | |||
| 8 | namespace Kernel::Svc { | ||
| 9 | |||
| 10 | /// Set the process heap to a given Size. It can both extend and shrink the heap. | ||
| 11 | Result SetHeapSize(Core::System& system, VAddr* out_address, u64 size) { | ||
| 12 | LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size); | ||
| 13 | |||
| 14 | // Validate size. | ||
| 15 | R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize); | ||
| 16 | R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize); | ||
| 17 | |||
| 18 | // Set the heap size. | ||
| 19 | R_TRY(system.Kernel().CurrentProcess()->PageTable().SetHeapSize(out_address, size)); | ||
| 20 | |||
| 21 | return ResultSuccess; | ||
| 22 | } | ||
| 23 | |||
| 24 | Result SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) { | ||
| 25 | VAddr temp_heap_addr{}; | ||
| 26 | const Result result{SetHeapSize(system, &temp_heap_addr, heap_size)}; | ||
| 27 | *heap_addr = static_cast<u32>(temp_heap_addr); | ||
| 28 | return result; | ||
| 29 | } | ||
| 30 | |||
| 31 | /// Maps memory at a desired address | ||
| 32 | Result MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { | ||
| 33 | LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); | ||
| 34 | |||
| 35 | if (!Common::Is4KBAligned(addr)) { | ||
| 36 | LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr); | ||
| 37 | return ResultInvalidAddress; | ||
| 38 | } | ||
| 39 | |||
| 40 | if (!Common::Is4KBAligned(size)) { | ||
| 41 | LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size); | ||
| 42 | return ResultInvalidSize; | ||
| 43 | } | ||
| 44 | |||
| 45 | if (size == 0) { | ||
| 46 | LOG_ERROR(Kernel_SVC, "Size is zero"); | ||
| 47 | return ResultInvalidSize; | ||
| 48 | } | ||
| 49 | |||
| 50 | if (!(addr < addr + size)) { | ||
| 51 | LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address"); | ||
| 52 | return ResultInvalidMemoryRegion; | ||
| 53 | } | ||
| 54 | |||
| 55 | KProcess* const current_process{system.Kernel().CurrentProcess()}; | ||
| 56 | auto& page_table{current_process->PageTable()}; | ||
| 57 | |||
| 58 | if (current_process->GetSystemResourceSize() == 0) { | ||
| 59 | LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); | ||
| 60 | return ResultInvalidState; | ||
| 61 | } | ||
| 62 | |||
| 63 | if (!page_table.IsInsideAddressSpace(addr, size)) { | ||
| 64 | LOG_ERROR(Kernel_SVC, | ||
| 65 | "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, | ||
| 66 | size); | ||
| 67 | return ResultInvalidMemoryRegion; | ||
| 68 | } | ||
| 69 | |||
| 70 | if (page_table.IsOutsideAliasRegion(addr, size)) { | ||
| 71 | LOG_ERROR(Kernel_SVC, | ||
| 72 | "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr, | ||
| 73 | size); | ||
| 74 | return ResultInvalidMemoryRegion; | ||
| 75 | } | ||
| 76 | |||
| 77 | return page_table.MapPhysicalMemory(addr, size); | ||
| 78 | } | ||
| 79 | |||
| 80 | Result MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { | ||
| 81 | return MapPhysicalMemory(system, addr, size); | ||
| 82 | } | ||
| 83 | |||
| 84 | /// Unmaps memory previously mapped via MapPhysicalMemory | ||
| 85 | Result UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { | ||
| 86 | LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); | ||
| 87 | |||
| 88 | if (!Common::Is4KBAligned(addr)) { | ||
| 89 | LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr); | ||
| 90 | return ResultInvalidAddress; | ||
| 91 | } | ||
| 92 | |||
| 93 | if (!Common::Is4KBAligned(size)) { | ||
| 94 | LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size); | ||
| 95 | return ResultInvalidSize; | ||
| 96 | } | ||
| 97 | |||
| 98 | if (size == 0) { | ||
| 99 | LOG_ERROR(Kernel_SVC, "Size is zero"); | ||
| 100 | return ResultInvalidSize; | ||
| 101 | } | ||
| 102 | |||
| 103 | if (!(addr < addr + size)) { | ||
| 104 | LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address"); | ||
| 105 | return ResultInvalidMemoryRegion; | ||
| 106 | } | ||
| 107 | |||
| 108 | KProcess* const current_process{system.Kernel().CurrentProcess()}; | ||
| 109 | auto& page_table{current_process->PageTable()}; | ||
| 110 | |||
| 111 | if (current_process->GetSystemResourceSize() == 0) { | ||
| 112 | LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); | ||
| 113 | return ResultInvalidState; | ||
| 114 | } | ||
| 115 | |||
| 116 | if (!page_table.IsInsideAddressSpace(addr, size)) { | ||
| 117 | LOG_ERROR(Kernel_SVC, | ||
| 118 | "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, | ||
| 119 | size); | ||
| 120 | return ResultInvalidMemoryRegion; | ||
| 121 | } | ||
| 122 | |||
| 123 | if (page_table.IsOutsideAliasRegion(addr, size)) { | ||
| 124 | LOG_ERROR(Kernel_SVC, | ||
| 125 | "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr, | ||
| 126 | size); | ||
| 127 | return ResultInvalidMemoryRegion; | ||
| 128 | } | ||
| 129 | |||
| 130 | return page_table.UnmapPhysicalMemory(addr, size); | ||
| 131 | } | ||
| 132 | |||
| 133 | Result UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { | ||
| 134 | return UnmapPhysicalMemory(system, addr, size); | ||
| 135 | } | ||
| 136 | |||
| 137 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/scope_exit.h" | ||
| 5 | #include "core/core.h" | ||
| 6 | #include "core/hle/kernel/k_client_port.h" | ||
| 7 | #include "core/hle/kernel/k_client_session.h" | ||
| 8 | #include "core/hle/kernel/k_port.h" | ||
| 9 | #include "core/hle/kernel/k_process.h" | ||
| 10 | #include "core/hle/kernel/svc.h" | ||
| 11 | |||
| 12 | namespace Kernel::Svc { | ||
| 13 | |||
| 14 | /// Connect to an OS service given the port name, returns the handle to the port to out | ||
| 15 | Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) { | ||
| 16 | auto& memory = system.Memory(); | ||
| 17 | if (!memory.IsValidVirtualAddress(port_name_address)) { | ||
| 18 | LOG_ERROR(Kernel_SVC, | ||
| 19 | "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}", | ||
| 20 | port_name_address); | ||
| 21 | return ResultNotFound; | ||
| 22 | } | ||
| 23 | |||
| 24 | static constexpr std::size_t PortNameMaxLength = 11; | ||
| 25 | // Read 1 char beyond the max allowed port name to detect names that are too long. | ||
| 26 | const std::string port_name = memory.ReadCString(port_name_address, PortNameMaxLength + 1); | ||
| 27 | if (port_name.size() > PortNameMaxLength) { | ||
| 28 | LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength, | ||
| 29 | port_name.size()); | ||
| 30 | return ResultOutOfRange; | ||
| 31 | } | ||
| 32 | |||
| 33 | LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); | ||
| 34 | |||
| 35 | // Get the current handle table. | ||
| 36 | auto& kernel = system.Kernel(); | ||
| 37 | auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); | ||
| 38 | |||
| 39 | // Find the client port. | ||
| 40 | auto port = kernel.CreateNamedServicePort(port_name); | ||
| 41 | if (!port) { | ||
| 42 | LOG_ERROR(Kernel_SVC, "tried to connect to unknown port: {}", port_name); | ||
| 43 | return ResultNotFound; | ||
| 44 | } | ||
| 45 | |||
| 46 | // Reserve a handle for the port. | ||
| 47 | // NOTE: Nintendo really does write directly to the output handle here. | ||
| 48 | R_TRY(handle_table.Reserve(out)); | ||
| 49 | auto handle_guard = SCOPE_GUARD({ handle_table.Unreserve(*out); }); | ||
| 50 | |||
| 51 | // Create a session. | ||
| 52 | KClientSession* session{}; | ||
| 53 | R_TRY(port->CreateSession(std::addressof(session))); | ||
| 54 | |||
| 55 | kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort()); | ||
| 56 | |||
| 57 | // Register the session in the table, close the extra reference. | ||
| 58 | handle_table.Register(*out, session); | ||
| 59 | session->Close(); | ||
| 60 | |||
| 61 | // We succeeded. | ||
| 62 | handle_guard.Cancel(); | ||
| 63 | return ResultSuccess; | ||
| 64 | } | ||
| 65 | |||
| 66 | Result ConnectToNamedPort32(Core::System& system, Handle* out_handle, u32 port_name_address) { | ||
| 67 | |||
| 68 | return ConnectToNamedPort(system, out_handle, port_name_address); | ||
| 69 | } | ||
| 70 | |||
| 71 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/kernel/svc.h" | ||
| 5 | |||
| 6 | 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/hle/kernel/k_process.h" | ||
| 6 | #include "core/hle/kernel/svc.h" | ||
| 7 | |||
| 8 | namespace Kernel::Svc { | ||
| 9 | |||
| 10 | /// Exits the current process | ||
| 11 | void ExitProcess(Core::System& system) { | ||
| 12 | auto* current_process = system.Kernel().CurrentProcess(); | ||
| 13 | |||
| 14 | LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); | ||
| 15 | ASSERT_MSG(current_process->GetState() == KProcess::State::Running, | ||
| 16 | "Process has already exited"); | ||
| 17 | |||
| 18 | system.Exit(); | ||
| 19 | } | ||
| 20 | |||
| 21 | void ExitProcess32(Core::System& system) { | ||
| 22 | ExitProcess(system); | ||
| 23 | } | ||
| 24 | |||
| 25 | /// Gets the ID of the specified process or a specified thread's owning process. | ||
| 26 | Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle) { | ||
| 27 | LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle); | ||
| 28 | |||
| 29 | // Get the object from the handle table. | ||
| 30 | KScopedAutoObject obj = | ||
| 31 | system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KAutoObject>( | ||
| 32 | static_cast<Handle>(handle)); | ||
| 33 | R_UNLESS(obj.IsNotNull(), ResultInvalidHandle); | ||
| 34 | |||
| 35 | // Get the process from the object. | ||
| 36 | KProcess* process = nullptr; | ||
| 37 | if (KProcess* p = obj->DynamicCast<KProcess*>(); p != nullptr) { | ||
| 38 | // The object is a process, so we can use it directly. | ||
| 39 | process = p; | ||
| 40 | } else if (KThread* t = obj->DynamicCast<KThread*>(); t != nullptr) { | ||
| 41 | // The object is a thread, so we want to use its parent. | ||
| 42 | process = reinterpret_cast<KThread*>(obj.GetPointerUnsafe())->GetOwnerProcess(); | ||
| 43 | } else { | ||
| 44 | // TODO(bunnei): This should also handle debug objects before returning. | ||
| 45 | UNIMPLEMENTED_MSG("Debug objects not implemented"); | ||
| 46 | } | ||
| 47 | |||
| 48 | // Make sure the target process exists. | ||
| 49 | R_UNLESS(process != nullptr, ResultInvalidHandle); | ||
| 50 | |||
| 51 | // Get the process id. | ||
| 52 | *out_process_id = process->GetId(); | ||
| 53 | |||
| 54 | return ResultSuccess; | ||
| 55 | } | ||
| 56 | |||
| 57 | Result GetProcessId32(Core::System& system, u32* out_process_id_low, u32* out_process_id_high, | ||
| 58 | Handle handle) { | ||
| 59 | u64 out_process_id{}; | ||
| 60 | const auto result = GetProcessId(system, &out_process_id, handle); | ||
| 61 | *out_process_id_low = static_cast<u32>(out_process_id); | ||
| 62 | *out_process_id_high = static_cast<u32>(out_process_id >> 32); | ||
| 63 | return result; | ||
| 64 | } | ||
| 65 | |||
| 66 | Result GetProcessList(Core::System& system, u32* out_num_processes, VAddr out_process_ids, | ||
| 67 | u32 out_process_ids_size) { | ||
| 68 | LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}", | ||
| 69 | out_process_ids, out_process_ids_size); | ||
| 70 | |||
| 71 | // If the supplied size is negative or greater than INT32_MAX / sizeof(u64), bail. | ||
| 72 | if ((out_process_ids_size & 0xF0000000) != 0) { | ||
| 73 | LOG_ERROR(Kernel_SVC, | ||
| 74 | "Supplied size outside [0, 0x0FFFFFFF] range. out_process_ids_size={}", | ||
| 75 | out_process_ids_size); | ||
| 76 | return ResultOutOfRange; | ||
| 77 | } | ||
| 78 | |||
| 79 | const auto& kernel = system.Kernel(); | ||
| 80 | const auto total_copy_size = out_process_ids_size * sizeof(u64); | ||
| 81 | |||
| 82 | if (out_process_ids_size > 0 && !kernel.CurrentProcess()->PageTable().IsInsideAddressSpace( | ||
| 83 | out_process_ids, total_copy_size)) { | ||
| 84 | LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}", | ||
| 85 | out_process_ids, out_process_ids + total_copy_size); | ||
| 86 | return ResultInvalidCurrentMemory; | ||
| 87 | } | ||
| 88 | |||
| 89 | auto& memory = system.Memory(); | ||
| 90 | const auto& process_list = kernel.GetProcessList(); | ||
| 91 | const auto num_processes = process_list.size(); | ||
| 92 | const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes); | ||
| 93 | |||
| 94 | for (std::size_t i = 0; i < copy_amount; ++i) { | ||
| 95 | memory.Write64(out_process_ids, process_list[i]->GetProcessID()); | ||
| 96 | out_process_ids += sizeof(u64); | ||
| 97 | } | ||
| 98 | |||
| 99 | *out_num_processes = static_cast<u32>(num_processes); | ||
| 100 | return ResultSuccess; | ||
| 101 | } | ||
| 102 | |||
| 103 | Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { | ||
| 104 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); | ||
| 105 | |||
| 106 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 107 | KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); | ||
| 108 | if (process.IsNull()) { | ||
| 109 | LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", | ||
| 110 | process_handle); | ||
| 111 | return ResultInvalidHandle; | ||
| 112 | } | ||
| 113 | |||
| 114 | const auto info_type = static_cast<ProcessInfoType>(type); | ||
| 115 | if (info_type != ProcessInfoType::ProcessState) { | ||
| 116 | LOG_ERROR(Kernel_SVC, "Expected info_type to be ProcessState but got {} instead", type); | ||
| 117 | return ResultInvalidEnumValue; | ||
| 118 | } | ||
| 119 | |||
| 120 | *out = static_cast<u64>(process->GetState()); | ||
| 121 | return ResultSuccess; | ||
| 122 | } | ||
| 123 | |||
| 124 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/hle/kernel/k_process.h" | ||
| 6 | #include "core/hle/kernel/svc.h" | ||
| 7 | |||
| 8 | namespace Kernel::Svc { | ||
| 9 | namespace { | ||
| 10 | |||
| 11 | constexpr bool IsValidAddressRange(VAddr address, u64 size) { | ||
| 12 | return address + size > address; | ||
| 13 | } | ||
| 14 | |||
| 15 | constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) { | ||
| 16 | switch (perm) { | ||
| 17 | case Svc::MemoryPermission::None: | ||
| 18 | case Svc::MemoryPermission::Read: | ||
| 19 | case Svc::MemoryPermission::ReadWrite: | ||
| 20 | case Svc::MemoryPermission::ReadExecute: | ||
| 21 | return true; | ||
| 22 | default: | ||
| 23 | return false; | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | } // namespace | ||
| 28 | |||
| 29 | Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, VAddr address, | ||
| 30 | u64 size, Svc::MemoryPermission perm) { | ||
| 31 | LOG_TRACE(Kernel_SVC, | ||
| 32 | "called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", | ||
| 33 | process_handle, address, size, perm); | ||
| 34 | |||
| 35 | // Validate the address/size. | ||
| 36 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 37 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 38 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 39 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 40 | R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory); | ||
| 41 | R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory); | ||
| 42 | |||
| 43 | // Validate the memory permission. | ||
| 44 | R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 45 | |||
| 46 | // Get the process from its handle. | ||
| 47 | KScopedAutoObject process = | ||
| 48 | system.CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle); | ||
| 49 | R_UNLESS(process.IsNotNull(), ResultInvalidHandle); | ||
| 50 | |||
| 51 | // Validate that the address is in range. | ||
| 52 | auto& page_table = process->PageTable(); | ||
| 53 | R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); | ||
| 54 | |||
| 55 | // Set the memory permission. | ||
| 56 | return page_table.SetProcessMemoryPermission(address, size, perm); | ||
| 57 | } | ||
| 58 | |||
| 59 | Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, | ||
| 60 | VAddr src_address, u64 size) { | ||
| 61 | LOG_TRACE(Kernel_SVC, | ||
| 62 | "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}", | ||
| 63 | dst_address, process_handle, src_address, size); | ||
| 64 | |||
| 65 | // Validate the address/size. | ||
| 66 | R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress); | ||
| 67 | R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress); | ||
| 68 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 69 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 70 | R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory); | ||
| 71 | R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory); | ||
| 72 | |||
| 73 | // Get the processes. | ||
| 74 | KProcess* dst_process = system.CurrentProcess(); | ||
| 75 | KScopedAutoObject src_process = | ||
| 76 | dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle); | ||
| 77 | R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); | ||
| 78 | |||
| 79 | // Get the page tables. | ||
| 80 | auto& dst_pt = dst_process->PageTable(); | ||
| 81 | auto& src_pt = src_process->PageTable(); | ||
| 82 | |||
| 83 | // Validate that the mapping is in range. | ||
| 84 | R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory); | ||
| 85 | R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode), | ||
| 86 | ResultInvalidMemoryRegion); | ||
| 87 | |||
| 88 | // Create a new page group. | ||
| 89 | KPageGroup pg{system.Kernel(), dst_pt.GetBlockInfoManager()}; | ||
| 90 | R_TRY(src_pt.MakeAndOpenPageGroup( | ||
| 91 | std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess, | ||
| 92 | KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None, | ||
| 93 | KMemoryAttribute::All, KMemoryAttribute::None)); | ||
| 94 | |||
| 95 | // Map the group. | ||
| 96 | R_TRY(dst_pt.MapPageGroup(dst_address, pg, KMemoryState::SharedCode, | ||
| 97 | KMemoryPermission::UserReadWrite)); | ||
| 98 | |||
| 99 | return ResultSuccess; | ||
| 100 | } | ||
| 101 | |||
| 102 | Result UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, | ||
| 103 | VAddr src_address, u64 size) { | ||
| 104 | LOG_TRACE(Kernel_SVC, | ||
| 105 | "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}", | ||
| 106 | dst_address, process_handle, src_address, size); | ||
| 107 | |||
| 108 | // Validate the address/size. | ||
| 109 | R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress); | ||
| 110 | R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress); | ||
| 111 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 112 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 113 | R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory); | ||
| 114 | R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory); | ||
| 115 | |||
| 116 | // Get the processes. | ||
| 117 | KProcess* dst_process = system.CurrentProcess(); | ||
| 118 | KScopedAutoObject src_process = | ||
| 119 | dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle); | ||
| 120 | R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); | ||
| 121 | |||
| 122 | // Get the page tables. | ||
| 123 | auto& dst_pt = dst_process->PageTable(); | ||
| 124 | auto& src_pt = src_process->PageTable(); | ||
| 125 | |||
| 126 | // Validate that the mapping is in range. | ||
| 127 | R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory); | ||
| 128 | R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode), | ||
| 129 | ResultInvalidMemoryRegion); | ||
| 130 | |||
| 131 | // Unmap the memory. | ||
| 132 | R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address)); | ||
| 133 | |||
| 134 | return ResultSuccess; | ||
| 135 | } | ||
| 136 | |||
| 137 | Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, | ||
| 138 | u64 src_address, u64 size) { | ||
| 139 | LOG_DEBUG(Kernel_SVC, | ||
| 140 | "called. process_handle=0x{:08X}, dst_address=0x{:016X}, " | ||
| 141 | "src_address=0x{:016X}, size=0x{:016X}", | ||
| 142 | process_handle, dst_address, src_address, size); | ||
| 143 | |||
| 144 | if (!Common::Is4KBAligned(src_address)) { | ||
| 145 | LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", | ||
| 146 | src_address); | ||
| 147 | return ResultInvalidAddress; | ||
| 148 | } | ||
| 149 | |||
| 150 | if (!Common::Is4KBAligned(dst_address)) { | ||
| 151 | LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", | ||
| 152 | dst_address); | ||
| 153 | return ResultInvalidAddress; | ||
| 154 | } | ||
| 155 | |||
| 156 | if (size == 0 || !Common::Is4KBAligned(size)) { | ||
| 157 | LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size); | ||
| 158 | return ResultInvalidSize; | ||
| 159 | } | ||
| 160 | |||
| 161 | if (!IsValidAddressRange(dst_address, size)) { | ||
| 162 | LOG_ERROR(Kernel_SVC, | ||
| 163 | "Destination address range overflows the address space (dst_address=0x{:016X}, " | ||
| 164 | "size=0x{:016X}).", | ||
| 165 | dst_address, size); | ||
| 166 | return ResultInvalidCurrentMemory; | ||
| 167 | } | ||
| 168 | |||
| 169 | if (!IsValidAddressRange(src_address, size)) { | ||
| 170 | LOG_ERROR(Kernel_SVC, | ||
| 171 | "Source address range overflows the address space (src_address=0x{:016X}, " | ||
| 172 | "size=0x{:016X}).", | ||
| 173 | src_address, size); | ||
| 174 | return ResultInvalidCurrentMemory; | ||
| 175 | } | ||
| 176 | |||
| 177 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 178 | KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); | ||
| 179 | if (process.IsNull()) { | ||
| 180 | LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", | ||
| 181 | process_handle); | ||
| 182 | return ResultInvalidHandle; | ||
| 183 | } | ||
| 184 | |||
| 185 | auto& page_table = process->PageTable(); | ||
| 186 | if (!page_table.IsInsideAddressSpace(src_address, size)) { | ||
| 187 | LOG_ERROR(Kernel_SVC, | ||
| 188 | "Source address range is not within the address space (src_address=0x{:016X}, " | ||
| 189 | "size=0x{:016X}).", | ||
| 190 | src_address, size); | ||
| 191 | return ResultInvalidCurrentMemory; | ||
| 192 | } | ||
| 193 | |||
| 194 | if (!page_table.IsInsideASLRRegion(dst_address, size)) { | ||
| 195 | LOG_ERROR(Kernel_SVC, | ||
| 196 | "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " | ||
| 197 | "size=0x{:016X}).", | ||
| 198 | dst_address, size); | ||
| 199 | return ResultInvalidMemoryRegion; | ||
| 200 | } | ||
| 201 | |||
| 202 | return page_table.MapCodeMemory(dst_address, src_address, size); | ||
| 203 | } | ||
| 204 | |||
| 205 | Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, | ||
| 206 | u64 src_address, u64 size) { | ||
| 207 | LOG_DEBUG(Kernel_SVC, | ||
| 208 | "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, " | ||
| 209 | "size=0x{:016X}", | ||
| 210 | process_handle, dst_address, src_address, size); | ||
| 211 | |||
| 212 | if (!Common::Is4KBAligned(dst_address)) { | ||
| 213 | LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", | ||
| 214 | dst_address); | ||
| 215 | return ResultInvalidAddress; | ||
| 216 | } | ||
| 217 | |||
| 218 | if (!Common::Is4KBAligned(src_address)) { | ||
| 219 | LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", | ||
| 220 | src_address); | ||
| 221 | return ResultInvalidAddress; | ||
| 222 | } | ||
| 223 | |||
| 224 | if (size == 0 || !Common::Is4KBAligned(size)) { | ||
| 225 | LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size); | ||
| 226 | return ResultInvalidSize; | ||
| 227 | } | ||
| 228 | |||
| 229 | if (!IsValidAddressRange(dst_address, size)) { | ||
| 230 | LOG_ERROR(Kernel_SVC, | ||
| 231 | "Destination address range overflows the address space (dst_address=0x{:016X}, " | ||
| 232 | "size=0x{:016X}).", | ||
| 233 | dst_address, size); | ||
| 234 | return ResultInvalidCurrentMemory; | ||
| 235 | } | ||
| 236 | |||
| 237 | if (!IsValidAddressRange(src_address, size)) { | ||
| 238 | LOG_ERROR(Kernel_SVC, | ||
| 239 | "Source address range overflows the address space (src_address=0x{:016X}, " | ||
| 240 | "size=0x{:016X}).", | ||
| 241 | src_address, size); | ||
| 242 | return ResultInvalidCurrentMemory; | ||
| 243 | } | ||
| 244 | |||
| 245 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 246 | KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); | ||
| 247 | if (process.IsNull()) { | ||
| 248 | LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", | ||
| 249 | process_handle); | ||
| 250 | return ResultInvalidHandle; | ||
| 251 | } | ||
| 252 | |||
| 253 | auto& page_table = process->PageTable(); | ||
| 254 | if (!page_table.IsInsideAddressSpace(src_address, size)) { | ||
| 255 | LOG_ERROR(Kernel_SVC, | ||
| 256 | "Source address range is not within the address space (src_address=0x{:016X}, " | ||
| 257 | "size=0x{:016X}).", | ||
| 258 | src_address, size); | ||
| 259 | return ResultInvalidCurrentMemory; | ||
| 260 | } | ||
| 261 | |||
| 262 | if (!page_table.IsInsideASLRRegion(dst_address, size)) { | ||
| 263 | LOG_ERROR(Kernel_SVC, | ||
| 264 | "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " | ||
| 265 | "size=0x{:016X}).", | ||
| 266 | dst_address, size); | ||
| 267 | return ResultInvalidMemoryRegion; | ||
| 268 | } | ||
| 269 | |||
| 270 | return page_table.UnmapCodeMemory(dst_address, src_address, size, | ||
| 271 | KPageTable::ICacheInvalidationStrategy::InvalidateAll); | ||
| 272 | } | ||
| 273 | |||
| 274 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/logging/log.h" | ||
| 5 | #include "core/core.h" | ||
| 6 | #include "core/hle/kernel/physical_core.h" | ||
| 7 | #include "core/hle/kernel/svc.h" | ||
| 8 | |||
| 9 | namespace Kernel::Svc { | ||
| 10 | |||
| 11 | /// Get which CPU core is executing the current thread | ||
| 12 | u32 GetCurrentProcessorNumber(Core::System& system) { | ||
| 13 | LOG_TRACE(Kernel_SVC, "called"); | ||
| 14 | return static_cast<u32>(system.CurrentPhysicalCore().CoreIndex()); | ||
| 15 | } | ||
| 16 | |||
| 17 | u32 GetCurrentProcessorNumber32(Core::System& system) { | ||
| 18 | return GetCurrentProcessorNumber(system); | ||
| 19 | } | ||
| 20 | |||
| 21 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/hle/kernel/k_process.h" | ||
| 6 | #include "core/hle/kernel/svc.h" | ||
| 7 | |||
| 8 | namespace Kernel::Svc { | ||
| 9 | |||
| 10 | Result QueryMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address, | ||
| 11 | VAddr query_address) { | ||
| 12 | LOG_TRACE(Kernel_SVC, | ||
| 13 | "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, " | ||
| 14 | "query_address=0x{:016X}", | ||
| 15 | memory_info_address, page_info_address, query_address); | ||
| 16 | |||
| 17 | return QueryProcessMemory(system, memory_info_address, page_info_address, CurrentProcess, | ||
| 18 | query_address); | ||
| 19 | } | ||
| 20 | |||
| 21 | Result QueryMemory32(Core::System& system, u32 memory_info_address, u32 page_info_address, | ||
| 22 | u32 query_address) { | ||
| 23 | return QueryMemory(system, memory_info_address, page_info_address, query_address); | ||
| 24 | } | ||
| 25 | |||
| 26 | Result QueryProcessMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address, | ||
| 27 | Handle process_handle, VAddr address) { | ||
| 28 | LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address); | ||
| 29 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 30 | KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); | ||
| 31 | if (process.IsNull()) { | ||
| 32 | LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", | ||
| 33 | process_handle); | ||
| 34 | return ResultInvalidHandle; | ||
| 35 | } | ||
| 36 | |||
| 37 | auto& memory{system.Memory()}; | ||
| 38 | const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()}; | ||
| 39 | |||
| 40 | memory.Write64(memory_info_address + 0x00, memory_info.base_address); | ||
| 41 | memory.Write64(memory_info_address + 0x08, memory_info.size); | ||
| 42 | memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff); | ||
| 43 | memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attribute)); | ||
| 44 | memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.permission)); | ||
| 45 | memory.Write32(memory_info_address + 0x1c, memory_info.ipc_count); | ||
| 46 | memory.Write32(memory_info_address + 0x20, memory_info.device_count); | ||
| 47 | memory.Write32(memory_info_address + 0x24, 0); | ||
| 48 | |||
| 49 | // Page info appears to be currently unused by the kernel and is always set to zero. | ||
| 50 | memory.Write32(page_info_address, 0); | ||
| 51 | |||
| 52 | return ResultSuccess; | ||
| 53 | } | ||
| 54 | |||
| 55 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/kernel/svc.h" | ||
| 5 | |||
| 6 | 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/scope_exit.h" | ||
| 5 | #include "core/core.h" | ||
| 6 | #include "core/hle/kernel/k_process.h" | ||
| 7 | #include "core/hle/kernel/k_resource_limit.h" | ||
| 8 | #include "core/hle/kernel/svc.h" | ||
| 9 | |||
| 10 | namespace Kernel::Svc { | ||
| 11 | |||
| 12 | Result CreateResourceLimit(Core::System& system, Handle* out_handle) { | ||
| 13 | LOG_DEBUG(Kernel_SVC, "called"); | ||
| 14 | |||
| 15 | // Create a new resource limit. | ||
| 16 | auto& kernel = system.Kernel(); | ||
| 17 | KResourceLimit* resource_limit = KResourceLimit::Create(kernel); | ||
| 18 | R_UNLESS(resource_limit != nullptr, ResultOutOfResource); | ||
| 19 | |||
| 20 | // Ensure we don't leak a reference to the limit. | ||
| 21 | SCOPE_EXIT({ resource_limit->Close(); }); | ||
| 22 | |||
| 23 | // Initialize the resource limit. | ||
| 24 | resource_limit->Initialize(&system.CoreTiming()); | ||
| 25 | |||
| 26 | // Register the limit. | ||
| 27 | KResourceLimit::Register(kernel, resource_limit); | ||
| 28 | |||
| 29 | // Add the limit to the handle table. | ||
| 30 | R_TRY(kernel.CurrentProcess()->GetHandleTable().Add(out_handle, resource_limit)); | ||
| 31 | |||
| 32 | return ResultSuccess; | ||
| 33 | } | ||
| 34 | |||
| 35 | Result GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value, | ||
| 36 | Handle resource_limit_handle, LimitableResource which) { | ||
| 37 | LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle, | ||
| 38 | which); | ||
| 39 | |||
| 40 | // Validate the resource. | ||
| 41 | R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue); | ||
| 42 | |||
| 43 | // Get the resource limit. | ||
| 44 | auto& kernel = system.Kernel(); | ||
| 45 | KScopedAutoObject resource_limit = | ||
| 46 | kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle); | ||
| 47 | R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle); | ||
| 48 | |||
| 49 | // Get the limit value. | ||
| 50 | *out_limit_value = resource_limit->GetLimitValue(which); | ||
| 51 | |||
| 52 | return ResultSuccess; | ||
| 53 | } | ||
| 54 | |||
| 55 | Result GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value, | ||
| 56 | Handle resource_limit_handle, LimitableResource which) { | ||
| 57 | LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle, | ||
| 58 | which); | ||
| 59 | |||
| 60 | // Validate the resource. | ||
| 61 | R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue); | ||
| 62 | |||
| 63 | // Get the resource limit. | ||
| 64 | auto& kernel = system.Kernel(); | ||
| 65 | KScopedAutoObject resource_limit = | ||
| 66 | kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle); | ||
| 67 | R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle); | ||
| 68 | |||
| 69 | // Get the current value. | ||
| 70 | *out_current_value = resource_limit->GetCurrentValue(which); | ||
| 71 | |||
| 72 | return ResultSuccess; | ||
| 73 | } | ||
| 74 | |||
| 75 | Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle, | ||
| 76 | LimitableResource which, u64 limit_value) { | ||
| 77 | LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}, limit_value={}", | ||
| 78 | resource_limit_handle, which, limit_value); | ||
| 79 | |||
| 80 | // Validate the resource. | ||
| 81 | R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue); | ||
| 82 | |||
| 83 | // Get the resource limit. | ||
| 84 | auto& kernel = system.Kernel(); | ||
| 85 | KScopedAutoObject resource_limit = | ||
| 86 | kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle); | ||
| 87 | R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle); | ||
| 88 | |||
| 89 | // Set the limit value. | ||
| 90 | R_TRY(resource_limit->SetLimitValue(which, limit_value)); | ||
| 91 | |||
| 92 | return ResultSuccess; | ||
| 93 | } | ||
| 94 | |||
| 95 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/kernel/svc.h" | ||
| 5 | |||
| 6 | 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/scope_exit.h" | ||
| 5 | #include "core/core.h" | ||
| 6 | #include "core/hle/kernel/k_process.h" | ||
| 7 | #include "core/hle/kernel/k_scoped_resource_reservation.h" | ||
| 8 | #include "core/hle/kernel/k_session.h" | ||
| 9 | #include "core/hle/kernel/svc.h" | ||
| 10 | |||
| 11 | namespace Kernel::Svc { | ||
| 12 | namespace { | ||
| 13 | |||
| 14 | template <typename T> | ||
| 15 | Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u64 name) { | ||
| 16 | auto& process = *system.CurrentProcess(); | ||
| 17 | auto& handle_table = process.GetHandleTable(); | ||
| 18 | |||
| 19 | // Declare the session we're going to allocate. | ||
| 20 | T* session; | ||
| 21 | |||
| 22 | // Reserve a new session from the process resource limit. | ||
| 23 | // FIXME: LimitableResource_SessionCountMax | ||
| 24 | KScopedResourceReservation session_reservation(&process, LimitableResource::SessionCountMax); | ||
| 25 | if (session_reservation.Succeeded()) { | ||
| 26 | session = T::Create(system.Kernel()); | ||
| 27 | } else { | ||
| 28 | return ResultLimitReached; | ||
| 29 | |||
| 30 | // // We couldn't reserve a session. Check that we support dynamically expanding the | ||
| 31 | // // resource limit. | ||
| 32 | // R_UNLESS(process.GetResourceLimit() == | ||
| 33 | // &system.Kernel().GetSystemResourceLimit(), ResultLimitReached); | ||
| 34 | // R_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled(), ResultLimitReached()); | ||
| 35 | |||
| 36 | // // Try to allocate a session from unused slab memory. | ||
| 37 | // session = T::CreateFromUnusedSlabMemory(); | ||
| 38 | // R_UNLESS(session != nullptr, ResultLimitReached); | ||
| 39 | // ON_RESULT_FAILURE { session->Close(); }; | ||
| 40 | |||
| 41 | // // If we're creating a KSession, we want to add two KSessionRequests to the heap, to | ||
| 42 | // // prevent request exhaustion. | ||
| 43 | // // NOTE: Nintendo checks if session->DynamicCast<KSession *>() != nullptr, but there's | ||
| 44 | // // no reason to not do this statically. | ||
| 45 | // if constexpr (std::same_as<T, KSession>) { | ||
| 46 | // for (size_t i = 0; i < 2; i++) { | ||
| 47 | // KSessionRequest* request = KSessionRequest::CreateFromUnusedSlabMemory(); | ||
| 48 | // R_UNLESS(request != nullptr, ResultLimitReached); | ||
| 49 | // request->Close(); | ||
| 50 | // } | ||
| 51 | // } | ||
| 52 | |||
| 53 | // We successfully allocated a session, so add the object we allocated to the resource | ||
| 54 | // limit. | ||
| 55 | // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::SessionCountMax, 1); | ||
| 56 | } | ||
| 57 | |||
| 58 | // Check that we successfully created a session. | ||
| 59 | R_UNLESS(session != nullptr, ResultOutOfResource); | ||
| 60 | |||
| 61 | // Initialize the session. | ||
| 62 | session->Initialize(nullptr, fmt::format("{}", name)); | ||
| 63 | |||
| 64 | // Commit the session reservation. | ||
| 65 | session_reservation.Commit(); | ||
| 66 | |||
| 67 | // Ensure that we clean up the session (and its only references are handle table) on function | ||
| 68 | // end. | ||
| 69 | SCOPE_EXIT({ | ||
| 70 | session->GetClientSession().Close(); | ||
| 71 | session->GetServerSession().Close(); | ||
| 72 | }); | ||
| 73 | |||
| 74 | // Register the session. | ||
| 75 | T::Register(system.Kernel(), session); | ||
| 76 | |||
| 77 | // Add the server session to the handle table. | ||
| 78 | R_TRY(handle_table.Add(out_server, &session->GetServerSession())); | ||
| 79 | |||
| 80 | // Add the client session to the handle table. | ||
| 81 | const auto result = handle_table.Add(out_client, &session->GetClientSession()); | ||
| 82 | |||
| 83 | if (!R_SUCCEEDED(result)) { | ||
| 84 | // Ensure that we maintaing a clean handle state on exit. | ||
| 85 | handle_table.Remove(*out_server); | ||
| 86 | } | ||
| 87 | |||
| 88 | return result; | ||
| 89 | } | ||
| 90 | |||
| 91 | } // namespace | ||
| 92 | |||
| 93 | Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u32 is_light, | ||
| 94 | u64 name) { | ||
| 95 | if (is_light) { | ||
| 96 | // return CreateSession<KLightSession>(system, out_server, out_client, name); | ||
| 97 | return ResultUnknown; | ||
| 98 | } else { | ||
| 99 | return CreateSession<KSession>(system, out_server, out_client, name); | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/scope_exit.h" | ||
| 5 | #include "core/core.h" | ||
| 6 | #include "core/hle/kernel/k_process.h" | ||
| 7 | #include "core/hle/kernel/k_shared_memory.h" | ||
| 8 | #include "core/hle/kernel/svc.h" | ||
| 9 | |||
| 10 | namespace Kernel::Svc { | ||
| 11 | namespace { | ||
| 12 | |||
| 13 | constexpr bool IsValidSharedMemoryPermission(MemoryPermission perm) { | ||
| 14 | switch (perm) { | ||
| 15 | case MemoryPermission::Read: | ||
| 16 | case MemoryPermission::ReadWrite: | ||
| 17 | return true; | ||
| 18 | default: | ||
| 19 | return false; | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | [[maybe_unused]] constexpr bool IsValidRemoteSharedMemoryPermission(MemoryPermission perm) { | ||
| 24 | return IsValidSharedMemoryPermission(perm) || perm == MemoryPermission::DontCare; | ||
| 25 | } | ||
| 26 | |||
| 27 | } // namespace | ||
| 28 | |||
| 29 | Result MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size, | ||
| 30 | Svc::MemoryPermission map_perm) { | ||
| 31 | LOG_TRACE(Kernel_SVC, | ||
| 32 | "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", | ||
| 33 | shmem_handle, address, size, map_perm); | ||
| 34 | |||
| 35 | // Validate the address/size. | ||
| 36 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 37 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 38 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 39 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 40 | |||
| 41 | // Validate the permission. | ||
| 42 | R_UNLESS(IsValidSharedMemoryPermission(map_perm), ResultInvalidNewMemoryPermission); | ||
| 43 | |||
| 44 | // Get the current process. | ||
| 45 | auto& process = *system.Kernel().CurrentProcess(); | ||
| 46 | auto& page_table = process.PageTable(); | ||
| 47 | |||
| 48 | // Get the shared memory. | ||
| 49 | KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle); | ||
| 50 | R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle); | ||
| 51 | |||
| 52 | // Verify that the mapping is in range. | ||
| 53 | R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion); | ||
| 54 | |||
| 55 | // Add the shared memory to the process. | ||
| 56 | R_TRY(process.AddSharedMemory(shmem.GetPointerUnsafe(), address, size)); | ||
| 57 | |||
| 58 | // Ensure that we clean up the shared memory if we fail to map it. | ||
| 59 | auto guard = | ||
| 60 | SCOPE_GUARD({ process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size); }); | ||
| 61 | |||
| 62 | // Map the shared memory. | ||
| 63 | R_TRY(shmem->Map(process, address, size, map_perm)); | ||
| 64 | |||
| 65 | // We succeeded. | ||
| 66 | guard.Cancel(); | ||
| 67 | return ResultSuccess; | ||
| 68 | } | ||
| 69 | |||
| 70 | Result MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size, | ||
| 71 | Svc::MemoryPermission map_perm) { | ||
| 72 | return MapSharedMemory(system, shmem_handle, address, size, map_perm); | ||
| 73 | } | ||
| 74 | |||
| 75 | Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size) { | ||
| 76 | // Validate the address/size. | ||
| 77 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 78 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 79 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 80 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 81 | |||
| 82 | // Get the current process. | ||
| 83 | auto& process = *system.Kernel().CurrentProcess(); | ||
| 84 | auto& page_table = process.PageTable(); | ||
| 85 | |||
| 86 | // Get the shared memory. | ||
| 87 | KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle); | ||
| 88 | R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle); | ||
| 89 | |||
| 90 | // Verify that the mapping is in range. | ||
| 91 | R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion); | ||
| 92 | |||
| 93 | // Unmap the shared memory. | ||
| 94 | R_TRY(shmem->Unmap(process, address, size)); | ||
| 95 | |||
| 96 | // Remove the shared memory from the process. | ||
| 97 | process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size); | ||
| 98 | |||
| 99 | return ResultSuccess; | ||
| 100 | } | ||
| 101 | |||
| 102 | Result UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size) { | ||
| 103 | return UnmapSharedMemory(system, shmem_handle, address, size); | ||
| 104 | } | ||
| 105 | |||
| 106 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/scope_exit.h" | ||
| 5 | #include "core/core.h" | ||
| 6 | #include "core/hle/kernel/k_process.h" | ||
| 7 | #include "core/hle/kernel/k_readable_event.h" | ||
| 8 | #include "core/hle/kernel/svc.h" | ||
| 9 | |||
| 10 | namespace Kernel::Svc { | ||
| 11 | |||
| 12 | /// Close a handle | ||
| 13 | Result CloseHandle(Core::System& system, Handle handle) { | ||
| 14 | LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); | ||
| 15 | |||
| 16 | // Remove the handle. | ||
| 17 | R_UNLESS(system.Kernel().CurrentProcess()->GetHandleTable().Remove(handle), | ||
| 18 | ResultInvalidHandle); | ||
| 19 | |||
| 20 | return ResultSuccess; | ||
| 21 | } | ||
| 22 | |||
| 23 | Result CloseHandle32(Core::System& system, Handle handle) { | ||
| 24 | return CloseHandle(system, handle); | ||
| 25 | } | ||
| 26 | |||
| 27 | /// Clears the signaled state of an event or process. | ||
| 28 | Result ResetSignal(Core::System& system, Handle handle) { | ||
| 29 | LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle); | ||
| 30 | |||
| 31 | // Get the current handle table. | ||
| 32 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 33 | |||
| 34 | // Try to reset as readable event. | ||
| 35 | { | ||
| 36 | KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(handle); | ||
| 37 | if (readable_event.IsNotNull()) { | ||
| 38 | return readable_event->Reset(); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | // Try to reset as process. | ||
| 43 | { | ||
| 44 | KScopedAutoObject process = handle_table.GetObject<KProcess>(handle); | ||
| 45 | if (process.IsNotNull()) { | ||
| 46 | return process->Reset(); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | LOG_ERROR(Kernel_SVC, "invalid handle (0x{:08X})", handle); | ||
| 51 | |||
| 52 | return ResultInvalidHandle; | ||
| 53 | } | ||
| 54 | |||
| 55 | Result ResetSignal32(Core::System& system, Handle handle) { | ||
| 56 | return ResetSignal(system, handle); | ||
| 57 | } | ||
| 58 | |||
| 59 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds | ||
| 60 | Result WaitSynchronization(Core::System& system, s32* index, VAddr handles_address, s32 num_handles, | ||
| 61 | s64 nano_seconds) { | ||
| 62 | LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, num_handles={}, nano_seconds={}", | ||
| 63 | handles_address, num_handles, nano_seconds); | ||
| 64 | |||
| 65 | // Ensure number of handles is valid. | ||
| 66 | R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange); | ||
| 67 | |||
| 68 | auto& kernel = system.Kernel(); | ||
| 69 | std::vector<KSynchronizationObject*> objs(num_handles); | ||
| 70 | const auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); | ||
| 71 | Handle* handles = system.Memory().GetPointer<Handle>(handles_address); | ||
| 72 | |||
| 73 | // Copy user handles. | ||
| 74 | if (num_handles > 0) { | ||
| 75 | // Convert the handles to objects. | ||
| 76 | R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles, | ||
| 77 | num_handles), | ||
| 78 | ResultInvalidHandle); | ||
| 79 | for (const auto& obj : objs) { | ||
| 80 | kernel.RegisterInUseObject(obj); | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | // Ensure handles are closed when we're done. | ||
| 85 | SCOPE_EXIT({ | ||
| 86 | for (s32 i = 0; i < num_handles; ++i) { | ||
| 87 | kernel.UnregisterInUseObject(objs[i]); | ||
| 88 | objs[i]->Close(); | ||
| 89 | } | ||
| 90 | }); | ||
| 91 | |||
| 92 | return KSynchronizationObject::Wait(kernel, index, objs.data(), static_cast<s32>(objs.size()), | ||
| 93 | nano_seconds); | ||
| 94 | } | ||
| 95 | |||
| 96 | Result WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address, | ||
| 97 | s32 num_handles, u32 timeout_high, s32* index) { | ||
| 98 | const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)}; | ||
| 99 | return WaitSynchronization(system, index, handles_address, num_handles, nano_seconds); | ||
| 100 | } | ||
| 101 | |||
| 102 | /// Resumes a thread waiting on WaitSynchronization | ||
| 103 | Result CancelSynchronization(Core::System& system, Handle handle) { | ||
| 104 | LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle); | ||
| 105 | |||
| 106 | // Get the thread from its handle. | ||
| 107 | KScopedAutoObject thread = | ||
| 108 | system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle); | ||
| 109 | R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); | ||
| 110 | |||
| 111 | // Cancel the thread's wait. | ||
| 112 | thread->WaitCancel(); | ||
| 113 | return ResultSuccess; | ||
| 114 | } | ||
| 115 | |||
| 116 | Result CancelSynchronization32(Core::System& system, Handle handle) { | ||
| 117 | return CancelSynchronization(system, handle); | ||
| 118 | } | ||
| 119 | |||
| 120 | void SynchronizePreemptionState(Core::System& system) { | ||
| 121 | auto& kernel = system.Kernel(); | ||
| 122 | |||
| 123 | // Lock the scheduler. | ||
| 124 | KScopedSchedulerLock sl{kernel}; | ||
| 125 | |||
| 126 | // If the current thread is pinned, unpin it. | ||
| 127 | KProcess* cur_process = system.Kernel().CurrentProcess(); | ||
| 128 | const auto core_id = GetCurrentCoreId(kernel); | ||
| 129 | |||
| 130 | if (cur_process->GetPinnedThread(core_id) == GetCurrentThreadPointer(kernel)) { | ||
| 131 | // Clear the current thread's interrupt flag. | ||
| 132 | GetCurrentThread(kernel).ClearInterruptFlag(); | ||
| 133 | |||
| 134 | // Unpin the current thread. | ||
| 135 | cur_process->UnpinCurrentThread(core_id); | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 139 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/scope_exit.h" | ||
| 5 | #include "core/core.h" | ||
| 6 | #include "core/core_timing.h" | ||
| 7 | #include "core/hle/kernel/k_process.h" | ||
| 8 | #include "core/hle/kernel/k_scoped_resource_reservation.h" | ||
| 9 | #include "core/hle/kernel/k_thread.h" | ||
| 10 | #include "core/hle/kernel/svc.h" | ||
| 11 | |||
| 12 | namespace Kernel::Svc { | ||
| 13 | namespace { | ||
| 14 | |||
| 15 | constexpr bool IsValidVirtualCoreId(int32_t core_id) { | ||
| 16 | return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES)); | ||
| 17 | } | ||
| 18 | |||
| 19 | } // Anonymous namespace | ||
| 20 | |||
| 21 | /// Creates a new thread | ||
| 22 | Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, | ||
| 23 | VAddr stack_bottom, u32 priority, s32 core_id) { | ||
| 24 | LOG_DEBUG(Kernel_SVC, | ||
| 25 | "called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, " | ||
| 26 | "priority=0x{:08X}, core_id=0x{:08X}", | ||
| 27 | entry_point, arg, stack_bottom, priority, core_id); | ||
| 28 | |||
| 29 | // Adjust core id, if it's the default magic. | ||
| 30 | auto& kernel = system.Kernel(); | ||
| 31 | auto& process = *kernel.CurrentProcess(); | ||
| 32 | if (core_id == IdealCoreUseProcessValue) { | ||
| 33 | core_id = process.GetIdealCoreId(); | ||
| 34 | } | ||
| 35 | |||
| 36 | // Validate arguments. | ||
| 37 | if (!IsValidVirtualCoreId(core_id)) { | ||
| 38 | LOG_ERROR(Kernel_SVC, "Invalid Core ID specified (id={})", core_id); | ||
| 39 | return ResultInvalidCoreId; | ||
| 40 | } | ||
| 41 | if (((1ULL << core_id) & process.GetCoreMask()) == 0) { | ||
| 42 | LOG_ERROR(Kernel_SVC, "Core ID doesn't fall within allowable cores (id={})", core_id); | ||
| 43 | return ResultInvalidCoreId; | ||
| 44 | } | ||
| 45 | |||
| 46 | if (HighestThreadPriority > priority || priority > LowestThreadPriority) { | ||
| 47 | LOG_ERROR(Kernel_SVC, "Invalid priority specified (priority={})", priority); | ||
| 48 | return ResultInvalidPriority; | ||
| 49 | } | ||
| 50 | if (!process.CheckThreadPriority(priority)) { | ||
| 51 | LOG_ERROR(Kernel_SVC, "Invalid allowable thread priority (priority={})", priority); | ||
| 52 | return ResultInvalidPriority; | ||
| 53 | } | ||
| 54 | |||
| 55 | // Reserve a new thread from the process resource limit (waiting up to 100ms). | ||
| 56 | KScopedResourceReservation thread_reservation( | ||
| 57 | kernel.CurrentProcess(), LimitableResource::ThreadCountMax, 1, | ||
| 58 | system.CoreTiming().GetGlobalTimeNs().count() + 100000000); | ||
| 59 | if (!thread_reservation.Succeeded()) { | ||
| 60 | LOG_ERROR(Kernel_SVC, "Could not reserve a new thread"); | ||
| 61 | return ResultLimitReached; | ||
| 62 | } | ||
| 63 | |||
| 64 | // Create the thread. | ||
| 65 | KThread* thread = KThread::Create(kernel); | ||
| 66 | if (!thread) { | ||
| 67 | LOG_ERROR(Kernel_SVC, "Unable to create new threads. Thread creation limit reached."); | ||
| 68 | return ResultOutOfResource; | ||
| 69 | } | ||
| 70 | SCOPE_EXIT({ thread->Close(); }); | ||
| 71 | |||
| 72 | // Initialize the thread. | ||
| 73 | { | ||
| 74 | KScopedLightLock lk{process.GetStateLock()}; | ||
| 75 | R_TRY(KThread::InitializeUserThread(system, thread, entry_point, arg, stack_bottom, | ||
| 76 | priority, core_id, &process)); | ||
| 77 | } | ||
| 78 | |||
| 79 | // Set the thread name for debugging purposes. | ||
| 80 | thread->SetName(fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *out_handle)); | ||
| 81 | |||
| 82 | // Commit the thread reservation. | ||
| 83 | thread_reservation.Commit(); | ||
| 84 | |||
| 85 | // Register the new thread. | ||
| 86 | KThread::Register(kernel, thread); | ||
| 87 | |||
| 88 | // Add the thread to the handle table. | ||
| 89 | R_TRY(process.GetHandleTable().Add(out_handle, thread)); | ||
| 90 | |||
| 91 | return ResultSuccess; | ||
| 92 | } | ||
| 93 | |||
| 94 | Result CreateThread32(Core::System& system, Handle* out_handle, u32 priority, u32 entry_point, | ||
| 95 | u32 arg, u32 stack_top, s32 processor_id) { | ||
| 96 | return CreateThread(system, out_handle, entry_point, arg, stack_top, priority, processor_id); | ||
| 97 | } | ||
| 98 | |||
| 99 | /// Starts the thread for the provided handle | ||
| 100 | Result StartThread(Core::System& system, Handle thread_handle) { | ||
| 101 | LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle); | ||
| 102 | |||
| 103 | // Get the thread from its handle. | ||
| 104 | KScopedAutoObject thread = | ||
| 105 | system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); | ||
| 106 | R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); | ||
| 107 | |||
| 108 | // Try to start the thread. | ||
| 109 | R_TRY(thread->Run()); | ||
| 110 | |||
| 111 | // If we succeeded, persist a reference to the thread. | ||
| 112 | thread->Open(); | ||
| 113 | system.Kernel().RegisterInUseObject(thread.GetPointerUnsafe()); | ||
| 114 | |||
| 115 | return ResultSuccess; | ||
| 116 | } | ||
| 117 | |||
| 118 | Result StartThread32(Core::System& system, Handle thread_handle) { | ||
| 119 | return StartThread(system, thread_handle); | ||
| 120 | } | ||
| 121 | |||
| 122 | /// Called when a thread exits | ||
| 123 | void ExitThread(Core::System& system) { | ||
| 124 | LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); | ||
| 125 | |||
| 126 | auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); | ||
| 127 | system.GlobalSchedulerContext().RemoveThread(current_thread); | ||
| 128 | current_thread->Exit(); | ||
| 129 | system.Kernel().UnregisterInUseObject(current_thread); | ||
| 130 | } | ||
| 131 | |||
| 132 | void ExitThread32(Core::System& system) { | ||
| 133 | ExitThread(system); | ||
| 134 | } | ||
| 135 | |||
| 136 | /// Sleep the current thread | ||
| 137 | void SleepThread(Core::System& system, s64 nanoseconds) { | ||
| 138 | auto& kernel = system.Kernel(); | ||
| 139 | const auto yield_type = static_cast<Svc::YieldType>(nanoseconds); | ||
| 140 | |||
| 141 | LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); | ||
| 142 | |||
| 143 | // When the input tick is positive, sleep. | ||
| 144 | if (nanoseconds > 0) { | ||
| 145 | // Convert the timeout from nanoseconds to ticks. | ||
| 146 | // NOTE: Nintendo does not use this conversion logic in WaitSynchronization... | ||
| 147 | |||
| 148 | // Sleep. | ||
| 149 | // NOTE: Nintendo does not check the result of this sleep. | ||
| 150 | static_cast<void>(GetCurrentThread(kernel).Sleep(nanoseconds)); | ||
| 151 | } else if (yield_type == Svc::YieldType::WithoutCoreMigration) { | ||
| 152 | KScheduler::YieldWithoutCoreMigration(kernel); | ||
| 153 | } else if (yield_type == Svc::YieldType::WithCoreMigration) { | ||
| 154 | KScheduler::YieldWithCoreMigration(kernel); | ||
| 155 | } else if (yield_type == Svc::YieldType::ToAnyThread) { | ||
| 156 | KScheduler::YieldToAnyThread(kernel); | ||
| 157 | } else { | ||
| 158 | // Nintendo does nothing at all if an otherwise invalid value is passed. | ||
| 159 | ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds); | ||
| 160 | } | ||
| 161 | } | ||
| 162 | |||
| 163 | void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanoseconds_high) { | ||
| 164 | const auto nanoseconds = static_cast<s64>(u64{nanoseconds_low} | (u64{nanoseconds_high} << 32)); | ||
| 165 | SleepThread(system, nanoseconds); | ||
| 166 | } | ||
| 167 | |||
| 168 | /// Gets the thread context | ||
| 169 | Result GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) { | ||
| 170 | LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context, | ||
| 171 | thread_handle); | ||
| 172 | |||
| 173 | auto& kernel = system.Kernel(); | ||
| 174 | |||
| 175 | // Get the thread from its handle. | ||
| 176 | KScopedAutoObject thread = | ||
| 177 | kernel.CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); | ||
| 178 | R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); | ||
| 179 | |||
| 180 | // Require the handle be to a non-current thread in the current process. | ||
| 181 | const auto* current_process = kernel.CurrentProcess(); | ||
| 182 | R_UNLESS(current_process == thread->GetOwnerProcess(), ResultInvalidId); | ||
| 183 | |||
| 184 | // Verify that the thread isn't terminated. | ||
| 185 | R_UNLESS(thread->GetState() != ThreadState::Terminated, ResultTerminationRequested); | ||
| 186 | |||
| 187 | /// Check that the thread is not the current one. | ||
| 188 | /// NOTE: Nintendo does not check this, and thus the following loop will deadlock. | ||
| 189 | R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(kernel), ResultInvalidId); | ||
| 190 | |||
| 191 | // Try to get the thread context until the thread isn't current on any core. | ||
| 192 | while (true) { | ||
| 193 | KScopedSchedulerLock sl{kernel}; | ||
| 194 | |||
| 195 | // TODO(bunnei): Enforce that thread is suspended for debug here. | ||
| 196 | |||
| 197 | // If the thread's raw state isn't runnable, check if it's current on some core. | ||
| 198 | if (thread->GetRawState() != ThreadState::Runnable) { | ||
| 199 | bool current = false; | ||
| 200 | for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { | ||
| 201 | if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetSchedulerCurrentThread()) { | ||
| 202 | current = true; | ||
| 203 | break; | ||
| 204 | } | ||
| 205 | } | ||
| 206 | |||
| 207 | // If the thread is current, retry until it isn't. | ||
| 208 | if (current) { | ||
| 209 | continue; | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | // Get the thread context. | ||
| 214 | std::vector<u8> context; | ||
| 215 | R_TRY(thread->GetThreadContext3(context)); | ||
| 216 | |||
| 217 | // Copy the thread context to user space. | ||
| 218 | system.Memory().WriteBlock(out_context, context.data(), context.size()); | ||
| 219 | |||
| 220 | return ResultSuccess; | ||
| 221 | } | ||
| 222 | |||
| 223 | return ResultSuccess; | ||
| 224 | } | ||
| 225 | |||
| 226 | Result GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) { | ||
| 227 | return GetThreadContext(system, out_context, thread_handle); | ||
| 228 | } | ||
| 229 | |||
| 230 | /// Gets the priority for the specified thread | ||
| 231 | Result GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) { | ||
| 232 | LOG_TRACE(Kernel_SVC, "called"); | ||
| 233 | |||
| 234 | // Get the thread from its handle. | ||
| 235 | KScopedAutoObject thread = | ||
| 236 | system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle); | ||
| 237 | R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); | ||
| 238 | |||
| 239 | // Get the thread's priority. | ||
| 240 | *out_priority = thread->GetPriority(); | ||
| 241 | return ResultSuccess; | ||
| 242 | } | ||
| 243 | |||
| 244 | Result GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) { | ||
| 245 | return GetThreadPriority(system, out_priority, handle); | ||
| 246 | } | ||
| 247 | |||
| 248 | /// Sets the priority for the specified thread | ||
| 249 | Result SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority) { | ||
| 250 | // Get the current process. | ||
| 251 | KProcess& process = *system.Kernel().CurrentProcess(); | ||
| 252 | |||
| 253 | // Validate the priority. | ||
| 254 | R_UNLESS(HighestThreadPriority <= priority && priority <= LowestThreadPriority, | ||
| 255 | ResultInvalidPriority); | ||
| 256 | R_UNLESS(process.CheckThreadPriority(priority), ResultInvalidPriority); | ||
| 257 | |||
| 258 | // Get the thread from its handle. | ||
| 259 | KScopedAutoObject thread = process.GetHandleTable().GetObject<KThread>(thread_handle); | ||
| 260 | R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); | ||
| 261 | |||
| 262 | // Set the thread priority. | ||
| 263 | thread->SetBasePriority(priority); | ||
| 264 | return ResultSuccess; | ||
| 265 | } | ||
| 266 | |||
| 267 | Result SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority) { | ||
| 268 | return SetThreadPriority(system, thread_handle, priority); | ||
| 269 | } | ||
| 270 | |||
| 271 | Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids, | ||
| 272 | u32 out_thread_ids_size, Handle debug_handle) { | ||
| 273 | // TODO: Handle this case when debug events are supported. | ||
| 274 | UNIMPLEMENTED_IF(debug_handle != InvalidHandle); | ||
| 275 | |||
| 276 | LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{:016X}, out_thread_ids_size={}", | ||
| 277 | out_thread_ids, out_thread_ids_size); | ||
| 278 | |||
| 279 | // If the size is negative or larger than INT32_MAX / sizeof(u64) | ||
| 280 | if ((out_thread_ids_size & 0xF0000000) != 0) { | ||
| 281 | LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}", | ||
| 282 | out_thread_ids_size); | ||
| 283 | return ResultOutOfRange; | ||
| 284 | } | ||
| 285 | |||
| 286 | auto* const current_process = system.Kernel().CurrentProcess(); | ||
| 287 | const auto total_copy_size = out_thread_ids_size * sizeof(u64); | ||
| 288 | |||
| 289 | if (out_thread_ids_size > 0 && | ||
| 290 | !current_process->PageTable().IsInsideAddressSpace(out_thread_ids, total_copy_size)) { | ||
| 291 | LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}", | ||
| 292 | out_thread_ids, out_thread_ids + total_copy_size); | ||
| 293 | return ResultInvalidCurrentMemory; | ||
| 294 | } | ||
| 295 | |||
| 296 | auto& memory = system.Memory(); | ||
| 297 | const auto& thread_list = current_process->GetThreadList(); | ||
| 298 | const auto num_threads = thread_list.size(); | ||
| 299 | const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads); | ||
| 300 | |||
| 301 | auto list_iter = thread_list.cbegin(); | ||
| 302 | for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) { | ||
| 303 | memory.Write64(out_thread_ids, (*list_iter)->GetThreadID()); | ||
| 304 | out_thread_ids += sizeof(u64); | ||
| 305 | } | ||
| 306 | |||
| 307 | *out_num_threads = static_cast<u32>(num_threads); | ||
| 308 | return ResultSuccess; | ||
| 309 | } | ||
| 310 | |||
| 311 | Result GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id, | ||
| 312 | u64* out_affinity_mask) { | ||
| 313 | LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); | ||
| 314 | |||
| 315 | // Get the thread from its handle. | ||
| 316 | KScopedAutoObject thread = | ||
| 317 | system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); | ||
| 318 | R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); | ||
| 319 | |||
| 320 | // Get the core mask. | ||
| 321 | R_TRY(thread->GetCoreMask(out_core_id, out_affinity_mask)); | ||
| 322 | |||
| 323 | return ResultSuccess; | ||
| 324 | } | ||
| 325 | |||
| 326 | Result GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id, | ||
| 327 | u32* out_affinity_mask_low, u32* out_affinity_mask_high) { | ||
| 328 | u64 out_affinity_mask{}; | ||
| 329 | const auto result = GetThreadCoreMask(system, thread_handle, out_core_id, &out_affinity_mask); | ||
| 330 | *out_affinity_mask_high = static_cast<u32>(out_affinity_mask >> 32); | ||
| 331 | *out_affinity_mask_low = static_cast<u32>(out_affinity_mask); | ||
| 332 | return result; | ||
| 333 | } | ||
| 334 | |||
| 335 | Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id, | ||
| 336 | u64 affinity_mask) { | ||
| 337 | // Determine the core id/affinity mask. | ||
| 338 | if (core_id == IdealCoreUseProcessValue) { | ||
| 339 | core_id = system.Kernel().CurrentProcess()->GetIdealCoreId(); | ||
| 340 | affinity_mask = (1ULL << core_id); | ||
| 341 | } else { | ||
| 342 | // Validate the affinity mask. | ||
| 343 | const u64 process_core_mask = system.Kernel().CurrentProcess()->GetCoreMask(); | ||
| 344 | R_UNLESS((affinity_mask | process_core_mask) == process_core_mask, ResultInvalidCoreId); | ||
| 345 | R_UNLESS(affinity_mask != 0, ResultInvalidCombination); | ||
| 346 | |||
| 347 | // Validate the core id. | ||
| 348 | if (IsValidVirtualCoreId(core_id)) { | ||
| 349 | R_UNLESS(((1ULL << core_id) & affinity_mask) != 0, ResultInvalidCombination); | ||
| 350 | } else { | ||
| 351 | R_UNLESS(core_id == IdealCoreNoUpdate || core_id == IdealCoreDontCare, | ||
| 352 | ResultInvalidCoreId); | ||
| 353 | } | ||
| 354 | } | ||
| 355 | |||
| 356 | // Get the thread from its handle. | ||
| 357 | KScopedAutoObject thread = | ||
| 358 | system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); | ||
| 359 | R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); | ||
| 360 | |||
| 361 | // Set the core mask. | ||
| 362 | R_TRY(thread->SetCoreMask(core_id, affinity_mask)); | ||
| 363 | |||
| 364 | return ResultSuccess; | ||
| 365 | } | ||
| 366 | |||
| 367 | Result SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id, | ||
| 368 | u32 affinity_mask_low, u32 affinity_mask_high) { | ||
| 369 | const auto affinity_mask = u64{affinity_mask_low} | (u64{affinity_mask_high} << 32); | ||
| 370 | return SetThreadCoreMask(system, thread_handle, core_id, affinity_mask); | ||
| 371 | } | ||
| 372 | |||
| 373 | /// Get the ID for the specified thread. | ||
| 374 | Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) { | ||
| 375 | // Get the thread from its handle. | ||
| 376 | KScopedAutoObject thread = | ||
| 377 | system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); | ||
| 378 | R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); | ||
| 379 | |||
| 380 | // Get the thread's id. | ||
| 381 | *out_thread_id = thread->GetId(); | ||
| 382 | return ResultSuccess; | ||
| 383 | } | ||
| 384 | |||
| 385 | Result GetThreadId32(Core::System& system, u32* out_thread_id_low, u32* out_thread_id_high, | ||
| 386 | Handle thread_handle) { | ||
| 387 | u64 out_thread_id{}; | ||
| 388 | const Result result{GetThreadId(system, &out_thread_id, thread_handle)}; | ||
| 389 | |||
| 390 | *out_thread_id_low = static_cast<u32>(out_thread_id >> 32); | ||
| 391 | *out_thread_id_high = static_cast<u32>(out_thread_id & std::numeric_limits<u32>::max()); | ||
| 392 | |||
| 393 | return result; | ||
| 394 | } | ||
| 395 | |||
| 396 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/kernel/svc.h" | ||
| 5 | |||
| 6 | 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/core_timing.h" | ||
| 6 | #include "core/hle/kernel/kernel.h" | ||
| 7 | #include "core/hle/kernel/svc.h" | ||
| 8 | |||
| 9 | namespace Kernel::Svc { | ||
| 10 | |||
| 11 | /// This returns the total CPU ticks elapsed since the CPU was powered-on | ||
| 12 | u64 GetSystemTick(Core::System& system) { | ||
| 13 | LOG_TRACE(Kernel_SVC, "called"); | ||
| 14 | |||
| 15 | auto& core_timing = system.CoreTiming(); | ||
| 16 | |||
| 17 | // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick) | ||
| 18 | const u64 result{core_timing.GetClockTicks()}; | ||
| 19 | |||
| 20 | if (!system.Kernel().IsMulticore()) { | ||
| 21 | core_timing.AddTicks(400U); | ||
| 22 | } | ||
| 23 | |||
| 24 | return result; | ||
| 25 | } | ||
| 26 | |||
| 27 | void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high) { | ||
| 28 | const auto time = GetSystemTick(system); | ||
| 29 | *time_low = static_cast<u32>(time); | ||
| 30 | *time_high = static_cast<u32>(time >> 32); | ||
| 31 | } | ||
| 32 | |||
| 33 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/scope_exit.h" | ||
| 5 | #include "core/core.h" | ||
| 6 | #include "core/hle/kernel/k_process.h" | ||
| 7 | #include "core/hle/kernel/k_scoped_resource_reservation.h" | ||
| 8 | #include "core/hle/kernel/k_transfer_memory.h" | ||
| 9 | #include "core/hle/kernel/svc.h" | ||
| 10 | |||
| 11 | namespace Kernel::Svc { | ||
| 12 | namespace { | ||
| 13 | |||
| 14 | constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) { | ||
| 15 | switch (perm) { | ||
| 16 | case MemoryPermission::None: | ||
| 17 | case MemoryPermission::Read: | ||
| 18 | case MemoryPermission::ReadWrite: | ||
| 19 | return true; | ||
| 20 | default: | ||
| 21 | return false; | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | } // Anonymous namespace | ||
| 26 | |||
| 27 | /// Creates a TransferMemory object | ||
| 28 | Result CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size, | ||
| 29 | MemoryPermission map_perm) { | ||
| 30 | auto& kernel = system.Kernel(); | ||
| 31 | |||
| 32 | // Validate the size. | ||
| 33 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 34 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 35 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 36 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 37 | |||
| 38 | // Validate the permissions. | ||
| 39 | R_UNLESS(IsValidTransferMemoryPermission(map_perm), ResultInvalidNewMemoryPermission); | ||
| 40 | |||
| 41 | // Get the current process and handle table. | ||
| 42 | auto& process = *kernel.CurrentProcess(); | ||
| 43 | auto& handle_table = process.GetHandleTable(); | ||
| 44 | |||
| 45 | // Reserve a new transfer memory from the process resource limit. | ||
| 46 | KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(), | ||
| 47 | LimitableResource::TransferMemoryCountMax); | ||
| 48 | R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached); | ||
| 49 | |||
| 50 | // Create the transfer memory. | ||
| 51 | KTransferMemory* trmem = KTransferMemory::Create(kernel); | ||
| 52 | R_UNLESS(trmem != nullptr, ResultOutOfResource); | ||
| 53 | |||
| 54 | // Ensure the only reference is in the handle table when we're done. | ||
| 55 | SCOPE_EXIT({ trmem->Close(); }); | ||
| 56 | |||
| 57 | // Ensure that the region is in range. | ||
| 58 | R_UNLESS(process.PageTable().Contains(address, size), ResultInvalidCurrentMemory); | ||
| 59 | |||
| 60 | // Initialize the transfer memory. | ||
| 61 | R_TRY(trmem->Initialize(address, size, map_perm)); | ||
| 62 | |||
| 63 | // Commit the reservation. | ||
| 64 | trmem_reservation.Commit(); | ||
| 65 | |||
| 66 | // Register the transfer memory. | ||
| 67 | KTransferMemory::Register(kernel, trmem); | ||
| 68 | |||
| 69 | // Add the transfer memory to the handle table. | ||
| 70 | R_TRY(handle_table.Add(out, trmem)); | ||
| 71 | |||
| 72 | return ResultSuccess; | ||
| 73 | } | ||
| 74 | |||
| 75 | Result CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size, | ||
| 76 | MemoryPermission map_perm) { | ||
| 77 | return CreateTransferMemory(system, out, address, size, map_perm); | ||
| 78 | } | ||
| 79 | } // namespace Kernel::Svc | ||