diff options
Diffstat (limited to 'src')
40 files changed, 3196 insertions, 2688 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 112c61b80..f16072e6c 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -298,7 +298,42 @@ add_library(core STATIC | |||
| 298 | hle/kernel/svc.h | 298 | hle/kernel/svc.h |
| 299 | hle/kernel/svc_common.h | 299 | hle/kernel/svc_common.h |
| 300 | hle/kernel/svc_types.h | 300 | hle/kernel/svc_types.h |
| 301 | hle/kernel/svc_wrap.h | 301 | hle/kernel/svc/svc_activity.cpp |
| 302 | hle/kernel/svc/svc_address_arbiter.cpp | ||
| 303 | hle/kernel/svc/svc_address_translation.cpp | ||
| 304 | hle/kernel/svc/svc_cache.cpp | ||
| 305 | hle/kernel/svc/svc_code_memory.cpp | ||
| 306 | hle/kernel/svc/svc_condition_variable.cpp | ||
| 307 | hle/kernel/svc/svc_debug.cpp | ||
| 308 | hle/kernel/svc/svc_debug_string.cpp | ||
| 309 | hle/kernel/svc/svc_device_address_space.cpp | ||
| 310 | hle/kernel/svc/svc_event.cpp | ||
| 311 | hle/kernel/svc/svc_exception.cpp | ||
| 312 | hle/kernel/svc/svc_info.cpp | ||
| 313 | hle/kernel/svc/svc_interrupt_event.cpp | ||
| 314 | hle/kernel/svc/svc_io_pool.cpp | ||
| 315 | hle/kernel/svc/svc_ipc.cpp | ||
| 316 | hle/kernel/svc/svc_kernel_debug.cpp | ||
| 317 | hle/kernel/svc/svc_light_ipc.cpp | ||
| 318 | hle/kernel/svc/svc_lock.cpp | ||
| 319 | hle/kernel/svc/svc_memory.cpp | ||
| 320 | hle/kernel/svc/svc_physical_memory.cpp | ||
| 321 | hle/kernel/svc/svc_port.cpp | ||
| 322 | hle/kernel/svc/svc_power_management.cpp | ||
| 323 | hle/kernel/svc/svc_process.cpp | ||
| 324 | hle/kernel/svc/svc_process_memory.cpp | ||
| 325 | hle/kernel/svc/svc_processor.cpp | ||
| 326 | hle/kernel/svc/svc_query_memory.cpp | ||
| 327 | hle/kernel/svc/svc_register.cpp | ||
| 328 | hle/kernel/svc/svc_resource_limit.cpp | ||
| 329 | hle/kernel/svc/svc_secure_monitor_call.cpp | ||
| 330 | hle/kernel/svc/svc_session.cpp | ||
| 331 | hle/kernel/svc/svc_shared_memory.cpp | ||
| 332 | hle/kernel/svc/svc_synchronization.cpp | ||
| 333 | hle/kernel/svc/svc_thread.cpp | ||
| 334 | hle/kernel/svc/svc_thread_profiler.cpp | ||
| 335 | hle/kernel/svc/svc_tick.cpp | ||
| 336 | hle/kernel/svc/svc_transfer_memory.cpp | ||
| 302 | hle/result.h | 337 | hle/result.h |
| 303 | hle/service/acc/acc.cpp | 338 | hle/service/acc/acc.cpp |
| 304 | hle/service/acc/acc.h | 339 | hle/service/acc/acc.h |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 67fa5d71c..4cb6f40a0 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -1,2697 +1,16 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <algorithm> | 4 | #include "common/common_types.h" |
| 5 | #include <cinttypes> | ||
| 6 | #include <iterator> | ||
| 7 | #include <mutex> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 10 | #include "common/alignment.h" | ||
| 11 | #include "common/assert.h" | ||
| 12 | #include "common/common_funcs.h" | ||
| 13 | #include "common/fiber.h" | ||
| 14 | #include "common/logging/log.h" | ||
| 15 | #include "common/scope_exit.h" | ||
| 16 | #include "core/core.h" | ||
| 17 | #include "core/core_timing.h" | ||
| 18 | #include "core/debugger/debugger.h" | ||
| 19 | #include "core/hle/kernel/k_client_port.h" | ||
| 20 | #include "core/hle/kernel/k_client_session.h" | ||
| 21 | #include "core/hle/kernel/k_code_memory.h" | ||
| 22 | #include "core/hle/kernel/k_event.h" | ||
| 23 | #include "core/hle/kernel/k_handle_table.h" | ||
| 24 | #include "core/hle/kernel/k_memory_block.h" | ||
| 25 | #include "core/hle/kernel/k_memory_layout.h" | ||
| 26 | #include "core/hle/kernel/k_page_table.h" | ||
| 27 | #include "core/hle/kernel/k_port.h" | ||
| 28 | #include "core/hle/kernel/k_process.h" | 5 | #include "core/hle/kernel/k_process.h" |
| 29 | #include "core/hle/kernel/k_readable_event.h" | ||
| 30 | #include "core/hle/kernel/k_resource_limit.h" | ||
| 31 | #include "core/hle/kernel/k_scheduler.h" | ||
| 32 | #include "core/hle/kernel/k_scoped_resource_reservation.h" | ||
| 33 | #include "core/hle/kernel/k_session.h" | ||
| 34 | #include "core/hle/kernel/k_shared_memory.h" | ||
| 35 | #include "core/hle/kernel/k_synchronization_object.h" | ||
| 36 | #include "core/hle/kernel/k_thread.h" | 6 | #include "core/hle/kernel/k_thread.h" |
| 37 | #include "core/hle/kernel/k_thread_queue.h" | ||
| 38 | #include "core/hle/kernel/k_transfer_memory.h" | ||
| 39 | #include "core/hle/kernel/kernel.h" | ||
| 40 | #include "core/hle/kernel/physical_core.h" | ||
| 41 | #include "core/hle/kernel/svc.h" | 7 | #include "core/hle/kernel/svc.h" |
| 42 | #include "core/hle/kernel/svc_results.h" | ||
| 43 | #include "core/hle/kernel/svc_types.h" | ||
| 44 | #include "core/hle/kernel/svc_wrap.h" | 8 | #include "core/hle/kernel/svc_wrap.h" |
| 45 | #include "core/hle/result.h" | 9 | #include "core/hle/result.h" |
| 46 | #include "core/memory.h" | ||
| 47 | #include "core/reporter.h" | ||
| 48 | 10 | ||
| 49 | namespace Kernel::Svc { | 11 | namespace Kernel::Svc { |
| 50 | namespace { | 12 | namespace { |
| 51 | 13 | ||
| 52 | // Checks if address + size is greater than the given address | ||
| 53 | // This can return false if the size causes an overflow of a 64-bit type | ||
| 54 | // or if the given size is zero. | ||
| 55 | constexpr bool IsValidAddressRange(VAddr address, u64 size) { | ||
| 56 | return address + size > address; | ||
| 57 | } | ||
| 58 | |||
| 59 | // Helper function that performs the common sanity checks for svcMapMemory | ||
| 60 | // and svcUnmapMemory. This is doable, as both functions perform their sanitizing | ||
| 61 | // in the same order. | ||
| 62 | Result MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr, VAddr src_addr, | ||
| 63 | u64 size) { | ||
| 64 | if (!Common::Is4KBAligned(dst_addr)) { | ||
| 65 | LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr); | ||
| 66 | return ResultInvalidAddress; | ||
| 67 | } | ||
| 68 | |||
| 69 | if (!Common::Is4KBAligned(src_addr)) { | ||
| 70 | LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, 0x{:016X}", src_addr); | ||
| 71 | return ResultInvalidSize; | ||
| 72 | } | ||
| 73 | |||
| 74 | if (size == 0) { | ||
| 75 | LOG_ERROR(Kernel_SVC, "Size is 0"); | ||
| 76 | return ResultInvalidSize; | ||
| 77 | } | ||
| 78 | |||
| 79 | if (!Common::Is4KBAligned(size)) { | ||
| 80 | LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size); | ||
| 81 | return ResultInvalidSize; | ||
| 82 | } | ||
| 83 | |||
| 84 | if (!IsValidAddressRange(dst_addr, size)) { | ||
| 85 | LOG_ERROR(Kernel_SVC, | ||
| 86 | "Destination is not a valid address range, addr=0x{:016X}, size=0x{:016X}", | ||
| 87 | dst_addr, size); | ||
| 88 | return ResultInvalidCurrentMemory; | ||
| 89 | } | ||
| 90 | |||
| 91 | if (!IsValidAddressRange(src_addr, size)) { | ||
| 92 | LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr=0x{:016X}, size=0x{:016X}", | ||
| 93 | src_addr, size); | ||
| 94 | return ResultInvalidCurrentMemory; | ||
| 95 | } | ||
| 96 | |||
| 97 | if (!manager.IsInsideAddressSpace(src_addr, size)) { | ||
| 98 | LOG_ERROR(Kernel_SVC, | ||
| 99 | "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", | ||
| 100 | src_addr, size); | ||
| 101 | return ResultInvalidCurrentMemory; | ||
| 102 | } | ||
| 103 | |||
| 104 | if (manager.IsOutsideStackRegion(dst_addr, size)) { | ||
| 105 | LOG_ERROR(Kernel_SVC, | ||
| 106 | "Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}", | ||
| 107 | dst_addr, size); | ||
| 108 | return ResultInvalidMemoryRegion; | ||
| 109 | } | ||
| 110 | |||
| 111 | if (manager.IsInsideHeapRegion(dst_addr, size)) { | ||
| 112 | LOG_ERROR(Kernel_SVC, | ||
| 113 | "Destination does not fit within the heap region, addr=0x{:016X}, " | ||
| 114 | "size=0x{:016X}", | ||
| 115 | dst_addr, size); | ||
| 116 | return ResultInvalidMemoryRegion; | ||
| 117 | } | ||
| 118 | |||
| 119 | if (manager.IsInsideAliasRegion(dst_addr, size)) { | ||
| 120 | LOG_ERROR(Kernel_SVC, | ||
| 121 | "Destination does not fit within the map region, addr=0x{:016X}, " | ||
| 122 | "size=0x{:016X}", | ||
| 123 | dst_addr, size); | ||
| 124 | return ResultInvalidMemoryRegion; | ||
| 125 | } | ||
| 126 | |||
| 127 | return ResultSuccess; | ||
| 128 | } | ||
| 129 | |||
| 130 | enum class ResourceLimitValueType { | ||
| 131 | CurrentValue, | ||
| 132 | LimitValue, | ||
| 133 | PeakValue, | ||
| 134 | }; | ||
| 135 | |||
| 136 | } // Anonymous namespace | ||
| 137 | |||
| 138 | /// Set the process heap to a given Size. It can both extend and shrink the heap. | ||
| 139 | static Result SetHeapSize(Core::System& system, VAddr* out_address, u64 size) { | ||
| 140 | LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size); | ||
| 141 | |||
| 142 | // Validate size. | ||
| 143 | R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize); | ||
| 144 | R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize); | ||
| 145 | |||
| 146 | // Set the heap size. | ||
| 147 | R_TRY(system.Kernel().CurrentProcess()->PageTable().SetHeapSize(out_address, size)); | ||
| 148 | |||
| 149 | return ResultSuccess; | ||
| 150 | } | ||
| 151 | |||
| 152 | static Result SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) { | ||
| 153 | VAddr temp_heap_addr{}; | ||
| 154 | const Result result{SetHeapSize(system, &temp_heap_addr, heap_size)}; | ||
| 155 | *heap_addr = static_cast<u32>(temp_heap_addr); | ||
| 156 | return result; | ||
| 157 | } | ||
| 158 | |||
| 159 | constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) { | ||
| 160 | switch (perm) { | ||
| 161 | case MemoryPermission::None: | ||
| 162 | case MemoryPermission::Read: | ||
| 163 | case MemoryPermission::ReadWrite: | ||
| 164 | return true; | ||
| 165 | default: | ||
| 166 | return false; | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | static Result SetMemoryPermission(Core::System& system, VAddr address, u64 size, | ||
| 171 | MemoryPermission perm) { | ||
| 172 | LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X", address, size, | ||
| 173 | perm); | ||
| 174 | |||
| 175 | // Validate address / size. | ||
| 176 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 177 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 178 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 179 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 180 | |||
| 181 | // Validate the permission. | ||
| 182 | R_UNLESS(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 183 | |||
| 184 | // Validate that the region is in range for the current process. | ||
| 185 | auto& page_table = system.Kernel().CurrentProcess()->PageTable(); | ||
| 186 | R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); | ||
| 187 | |||
| 188 | // Set the memory attribute. | ||
| 189 | return page_table.SetMemoryPermission(address, size, perm); | ||
| 190 | } | ||
| 191 | |||
| 192 | static Result SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, | ||
| 193 | u32 attr) { | ||
| 194 | LOG_DEBUG(Kernel_SVC, | ||
| 195 | "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, | ||
| 196 | size, mask, attr); | ||
| 197 | |||
| 198 | // Validate address / size. | ||
| 199 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 200 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 201 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 202 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 203 | |||
| 204 | // Validate the attribute and mask. | ||
| 205 | constexpr u32 SupportedMask = static_cast<u32>(MemoryAttribute::Uncached); | ||
| 206 | R_UNLESS((mask | attr) == mask, ResultInvalidCombination); | ||
| 207 | R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination); | ||
| 208 | |||
| 209 | // Validate that the region is in range for the current process. | ||
| 210 | auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; | ||
| 211 | R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); | ||
| 212 | |||
| 213 | // Set the memory attribute. | ||
| 214 | return page_table.SetMemoryAttribute(address, size, mask, attr); | ||
| 215 | } | ||
| 216 | |||
| 217 | static Result SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask, | ||
| 218 | u32 attr) { | ||
| 219 | return SetMemoryAttribute(system, address, size, mask, attr); | ||
| 220 | } | ||
| 221 | |||
| 222 | /// Maps a memory range into a different range. | ||
| 223 | static Result MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { | ||
| 224 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, | ||
| 225 | src_addr, size); | ||
| 226 | |||
| 227 | auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; | ||
| 228 | |||
| 229 | if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; | ||
| 230 | result.IsError()) { | ||
| 231 | return result; | ||
| 232 | } | ||
| 233 | |||
| 234 | return page_table.MapMemory(dst_addr, src_addr, size); | ||
| 235 | } | ||
| 236 | |||
| 237 | static Result MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { | ||
| 238 | return MapMemory(system, dst_addr, src_addr, size); | ||
| 239 | } | ||
| 240 | |||
| 241 | /// Unmaps a region that was previously mapped with svcMapMemory | ||
| 242 | static Result UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { | ||
| 243 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, | ||
| 244 | src_addr, size); | ||
| 245 | |||
| 246 | auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; | ||
| 247 | |||
| 248 | if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; | ||
| 249 | result.IsError()) { | ||
| 250 | return result; | ||
| 251 | } | ||
| 252 | |||
| 253 | return page_table.UnmapMemory(dst_addr, src_addr, size); | ||
| 254 | } | ||
| 255 | |||
| 256 | static Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { | ||
| 257 | return UnmapMemory(system, dst_addr, src_addr, size); | ||
| 258 | } | ||
| 259 | |||
| 260 | template <typename T> | ||
| 261 | Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u64 name) { | ||
| 262 | auto& process = *system.CurrentProcess(); | ||
| 263 | auto& handle_table = process.GetHandleTable(); | ||
| 264 | |||
| 265 | // Declare the session we're going to allocate. | ||
| 266 | T* session; | ||
| 267 | |||
| 268 | // Reserve a new session from the process resource limit. | ||
| 269 | // FIXME: LimitableResource_SessionCountMax | ||
| 270 | KScopedResourceReservation session_reservation(&process, LimitableResource::SessionCountMax); | ||
| 271 | if (session_reservation.Succeeded()) { | ||
| 272 | session = T::Create(system.Kernel()); | ||
| 273 | } else { | ||
| 274 | return ResultLimitReached; | ||
| 275 | |||
| 276 | // // We couldn't reserve a session. Check that we support dynamically expanding the | ||
| 277 | // // resource limit. | ||
| 278 | // R_UNLESS(process.GetResourceLimit() == | ||
| 279 | // &system.Kernel().GetSystemResourceLimit(), ResultLimitReached); | ||
| 280 | // R_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled(), ResultLimitReached()); | ||
| 281 | |||
| 282 | // // Try to allocate a session from unused slab memory. | ||
| 283 | // session = T::CreateFromUnusedSlabMemory(); | ||
| 284 | // R_UNLESS(session != nullptr, ResultLimitReached); | ||
| 285 | // ON_RESULT_FAILURE { session->Close(); }; | ||
| 286 | |||
| 287 | // // If we're creating a KSession, we want to add two KSessionRequests to the heap, to | ||
| 288 | // // prevent request exhaustion. | ||
| 289 | // // NOTE: Nintendo checks if session->DynamicCast<KSession *>() != nullptr, but there's | ||
| 290 | // // no reason to not do this statically. | ||
| 291 | // if constexpr (std::same_as<T, KSession>) { | ||
| 292 | // for (size_t i = 0; i < 2; i++) { | ||
| 293 | // KSessionRequest* request = KSessionRequest::CreateFromUnusedSlabMemory(); | ||
| 294 | // R_UNLESS(request != nullptr, ResultLimitReached); | ||
| 295 | // request->Close(); | ||
| 296 | // } | ||
| 297 | // } | ||
| 298 | |||
| 299 | // We successfully allocated a session, so add the object we allocated to the resource | ||
| 300 | // limit. | ||
| 301 | // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::SessionCountMax, 1); | ||
| 302 | } | ||
| 303 | |||
| 304 | // Check that we successfully created a session. | ||
| 305 | R_UNLESS(session != nullptr, ResultOutOfResource); | ||
| 306 | |||
| 307 | // Initialize the session. | ||
| 308 | session->Initialize(nullptr, fmt::format("{}", name)); | ||
| 309 | |||
| 310 | // Commit the session reservation. | ||
| 311 | session_reservation.Commit(); | ||
| 312 | |||
| 313 | // Ensure that we clean up the session (and its only references are handle table) on function | ||
| 314 | // end. | ||
| 315 | SCOPE_EXIT({ | ||
| 316 | session->GetClientSession().Close(); | ||
| 317 | session->GetServerSession().Close(); | ||
| 318 | }); | ||
| 319 | |||
| 320 | // Register the session. | ||
| 321 | T::Register(system.Kernel(), session); | ||
| 322 | |||
| 323 | // Add the server session to the handle table. | ||
| 324 | R_TRY(handle_table.Add(out_server, &session->GetServerSession())); | ||
| 325 | |||
| 326 | // Add the client session to the handle table. | ||
| 327 | const auto result = handle_table.Add(out_client, &session->GetClientSession()); | ||
| 328 | |||
| 329 | if (!R_SUCCEEDED(result)) { | ||
| 330 | // Ensure that we maintaing a clean handle state on exit. | ||
| 331 | handle_table.Remove(*out_server); | ||
| 332 | } | ||
| 333 | |||
| 334 | return result; | ||
| 335 | } | ||
| 336 | |||
| 337 | static Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, | ||
| 338 | u32 is_light, u64 name) { | ||
| 339 | if (is_light) { | ||
| 340 | // return CreateSession<KLightSession>(system, out_server, out_client, name); | ||
| 341 | return ResultUnknown; | ||
| 342 | } else { | ||
| 343 | return CreateSession<KSession>(system, out_server, out_client, name); | ||
| 344 | } | ||
| 345 | } | ||
| 346 | |||
| 347 | /// Connect to an OS service given the port name, returns the handle to the port to out | ||
| 348 | static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) { | ||
| 349 | auto& memory = system.Memory(); | ||
| 350 | if (!memory.IsValidVirtualAddress(port_name_address)) { | ||
| 351 | LOG_ERROR(Kernel_SVC, | ||
| 352 | "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}", | ||
| 353 | port_name_address); | ||
| 354 | return ResultNotFound; | ||
| 355 | } | ||
| 356 | |||
| 357 | static constexpr std::size_t PortNameMaxLength = 11; | ||
| 358 | // Read 1 char beyond the max allowed port name to detect names that are too long. | ||
| 359 | const std::string port_name = memory.ReadCString(port_name_address, PortNameMaxLength + 1); | ||
| 360 | if (port_name.size() > PortNameMaxLength) { | ||
| 361 | LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength, | ||
| 362 | port_name.size()); | ||
| 363 | return ResultOutOfRange; | ||
| 364 | } | ||
| 365 | |||
| 366 | LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); | ||
| 367 | |||
| 368 | // Get the current handle table. | ||
| 369 | auto& kernel = system.Kernel(); | ||
| 370 | auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); | ||
| 371 | |||
| 372 | // Find the client port. | ||
| 373 | auto port = kernel.CreateNamedServicePort(port_name); | ||
| 374 | if (!port) { | ||
| 375 | LOG_ERROR(Kernel_SVC, "tried to connect to unknown port: {}", port_name); | ||
| 376 | return ResultNotFound; | ||
| 377 | } | ||
| 378 | |||
| 379 | // Reserve a handle for the port. | ||
| 380 | // NOTE: Nintendo really does write directly to the output handle here. | ||
| 381 | R_TRY(handle_table.Reserve(out)); | ||
| 382 | auto handle_guard = SCOPE_GUARD({ handle_table.Unreserve(*out); }); | ||
| 383 | |||
| 384 | // Create a session. | ||
| 385 | KClientSession* session{}; | ||
| 386 | R_TRY(port->CreateSession(std::addressof(session))); | ||
| 387 | |||
| 388 | kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort()); | ||
| 389 | |||
| 390 | // Register the session in the table, close the extra reference. | ||
| 391 | handle_table.Register(*out, session); | ||
| 392 | session->Close(); | ||
| 393 | |||
| 394 | // We succeeded. | ||
| 395 | handle_guard.Cancel(); | ||
| 396 | return ResultSuccess; | ||
| 397 | } | ||
| 398 | |||
| 399 | static Result ConnectToNamedPort32(Core::System& system, Handle* out_handle, | ||
| 400 | u32 port_name_address) { | ||
| 401 | |||
| 402 | return ConnectToNamedPort(system, out_handle, port_name_address); | ||
| 403 | } | ||
| 404 | |||
| 405 | /// Makes a blocking IPC call to a service. | ||
| 406 | static Result SendSyncRequest(Core::System& system, Handle handle) { | ||
| 407 | auto& kernel = system.Kernel(); | ||
| 408 | |||
| 409 | // Create the wait queue. | ||
| 410 | KThreadQueue wait_queue(kernel); | ||
| 411 | |||
| 412 | // Get the client session from its handle. | ||
| 413 | KScopedAutoObject session = | ||
| 414 | kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle); | ||
| 415 | R_UNLESS(session.IsNotNull(), ResultInvalidHandle); | ||
| 416 | |||
| 417 | LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); | ||
| 418 | |||
| 419 | return session->SendSyncRequest(); | ||
| 420 | } | ||
| 421 | |||
| 422 | static Result SendSyncRequest32(Core::System& system, Handle handle) { | ||
| 423 | return SendSyncRequest(system, handle); | ||
| 424 | } | ||
| 425 | |||
| 426 | static Result ReplyAndReceive(Core::System& system, s32* out_index, Handle* handles, | ||
| 427 | s32 num_handles, Handle reply_target, s64 timeout_ns) { | ||
| 428 | auto& kernel = system.Kernel(); | ||
| 429 | auto& handle_table = GetCurrentThread(kernel).GetOwnerProcess()->GetHandleTable(); | ||
| 430 | |||
| 431 | // Convert handle list to object table. | ||
| 432 | std::vector<KSynchronizationObject*> objs(num_handles); | ||
| 433 | R_UNLESS( | ||
| 434 | handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles, num_handles), | ||
| 435 | ResultInvalidHandle); | ||
| 436 | |||
| 437 | // Ensure handles are closed when we're done. | ||
| 438 | SCOPE_EXIT({ | ||
| 439 | for (auto i = 0; i < num_handles; ++i) { | ||
| 440 | objs[i]->Close(); | ||
| 441 | } | ||
| 442 | }); | ||
| 443 | |||
| 444 | // Reply to the target, if one is specified. | ||
| 445 | if (reply_target != InvalidHandle) { | ||
| 446 | KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target); | ||
| 447 | R_UNLESS(session.IsNotNull(), ResultInvalidHandle); | ||
| 448 | |||
| 449 | // If we fail to reply, we want to set the output index to -1. | ||
| 450 | // ON_RESULT_FAILURE { *out_index = -1; }; | ||
| 451 | |||
| 452 | // Send the reply. | ||
| 453 | // R_TRY(session->SendReply()); | ||
| 454 | |||
| 455 | Result rc = session->SendReply(); | ||
| 456 | if (!R_SUCCEEDED(rc)) { | ||
| 457 | *out_index = -1; | ||
| 458 | return rc; | ||
| 459 | } | ||
| 460 | } | ||
| 461 | |||
| 462 | // Wait for a message. | ||
| 463 | while (true) { | ||
| 464 | // Wait for an object. | ||
| 465 | s32 index; | ||
| 466 | Result result = KSynchronizationObject::Wait(kernel, &index, objs.data(), | ||
| 467 | static_cast<s32>(objs.size()), timeout_ns); | ||
| 468 | if (result == ResultTimedOut) { | ||
| 469 | return result; | ||
| 470 | } | ||
| 471 | |||
| 472 | // Receive the request. | ||
| 473 | if (R_SUCCEEDED(result)) { | ||
| 474 | KServerSession* session = objs[index]->DynamicCast<KServerSession*>(); | ||
| 475 | if (session != nullptr) { | ||
| 476 | result = session->ReceiveRequest(); | ||
| 477 | if (result == ResultNotFound) { | ||
| 478 | continue; | ||
| 479 | } | ||
| 480 | } | ||
| 481 | } | ||
| 482 | |||
| 483 | *out_index = index; | ||
| 484 | return result; | ||
| 485 | } | ||
| 486 | } | ||
| 487 | |||
| 488 | /// Get the ID for the specified thread. | ||
| 489 | static Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) { | ||
| 490 | // Get the thread from its handle. | ||
| 491 | KScopedAutoObject thread = | ||
| 492 | system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); | ||
| 493 | R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); | ||
| 494 | |||
| 495 | // Get the thread's id. | ||
| 496 | *out_thread_id = thread->GetId(); | ||
| 497 | return ResultSuccess; | ||
| 498 | } | ||
| 499 | |||
| 500 | static Result GetThreadId32(Core::System& system, u32* out_thread_id_low, u32* out_thread_id_high, | ||
| 501 | Handle thread_handle) { | ||
| 502 | u64 out_thread_id{}; | ||
| 503 | const Result result{GetThreadId(system, &out_thread_id, thread_handle)}; | ||
| 504 | |||
| 505 | *out_thread_id_low = static_cast<u32>(out_thread_id >> 32); | ||
| 506 | *out_thread_id_high = static_cast<u32>(out_thread_id & std::numeric_limits<u32>::max()); | ||
| 507 | |||
| 508 | return result; | ||
| 509 | } | ||
| 510 | |||
| 511 | /// Gets the ID of the specified process or a specified thread's owning process. | ||
| 512 | static Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle) { | ||
| 513 | LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle); | ||
| 514 | |||
| 515 | // Get the object from the handle table. | ||
| 516 | KScopedAutoObject obj = | ||
| 517 | system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KAutoObject>( | ||
| 518 | static_cast<Handle>(handle)); | ||
| 519 | R_UNLESS(obj.IsNotNull(), ResultInvalidHandle); | ||
| 520 | |||
| 521 | // Get the process from the object. | ||
| 522 | KProcess* process = nullptr; | ||
| 523 | if (KProcess* p = obj->DynamicCast<KProcess*>(); p != nullptr) { | ||
| 524 | // The object is a process, so we can use it directly. | ||
| 525 | process = p; | ||
| 526 | } else if (KThread* t = obj->DynamicCast<KThread*>(); t != nullptr) { | ||
| 527 | // The object is a thread, so we want to use its parent. | ||
| 528 | process = reinterpret_cast<KThread*>(obj.GetPointerUnsafe())->GetOwnerProcess(); | ||
| 529 | } else { | ||
| 530 | // TODO(bunnei): This should also handle debug objects before returning. | ||
| 531 | UNIMPLEMENTED_MSG("Debug objects not implemented"); | ||
| 532 | } | ||
| 533 | |||
| 534 | // Make sure the target process exists. | ||
| 535 | R_UNLESS(process != nullptr, ResultInvalidHandle); | ||
| 536 | |||
| 537 | // Get the process id. | ||
| 538 | *out_process_id = process->GetId(); | ||
| 539 | |||
| 540 | return ResultSuccess; | ||
| 541 | } | ||
| 542 | |||
| 543 | static Result GetProcessId32(Core::System& system, u32* out_process_id_low, | ||
| 544 | u32* out_process_id_high, Handle handle) { | ||
| 545 | u64 out_process_id{}; | ||
| 546 | const auto result = GetProcessId(system, &out_process_id, handle); | ||
| 547 | *out_process_id_low = static_cast<u32>(out_process_id); | ||
| 548 | *out_process_id_high = static_cast<u32>(out_process_id >> 32); | ||
| 549 | return result; | ||
| 550 | } | ||
| 551 | |||
| 552 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds | ||
| 553 | static Result WaitSynchronization(Core::System& system, s32* index, VAddr handles_address, | ||
| 554 | s32 num_handles, s64 nano_seconds) { | ||
| 555 | LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, num_handles={}, nano_seconds={}", | ||
| 556 | handles_address, num_handles, nano_seconds); | ||
| 557 | |||
| 558 | // Ensure number of handles is valid. | ||
| 559 | R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange); | ||
| 560 | |||
| 561 | auto& kernel = system.Kernel(); | ||
| 562 | std::vector<KSynchronizationObject*> objs(num_handles); | ||
| 563 | const auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); | ||
| 564 | Handle* handles = system.Memory().GetPointer<Handle>(handles_address); | ||
| 565 | |||
| 566 | // Copy user handles. | ||
| 567 | if (num_handles > 0) { | ||
| 568 | // Convert the handles to objects. | ||
| 569 | R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles, | ||
| 570 | num_handles), | ||
| 571 | ResultInvalidHandle); | ||
| 572 | for (const auto& obj : objs) { | ||
| 573 | kernel.RegisterInUseObject(obj); | ||
| 574 | } | ||
| 575 | } | ||
| 576 | |||
| 577 | // Ensure handles are closed when we're done. | ||
| 578 | SCOPE_EXIT({ | ||
| 579 | for (s32 i = 0; i < num_handles; ++i) { | ||
| 580 | kernel.UnregisterInUseObject(objs[i]); | ||
| 581 | objs[i]->Close(); | ||
| 582 | } | ||
| 583 | }); | ||
| 584 | |||
| 585 | return KSynchronizationObject::Wait(kernel, index, objs.data(), static_cast<s32>(objs.size()), | ||
| 586 | nano_seconds); | ||
| 587 | } | ||
| 588 | |||
| 589 | static Result WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address, | ||
| 590 | s32 num_handles, u32 timeout_high, s32* index) { | ||
| 591 | const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)}; | ||
| 592 | return WaitSynchronization(system, index, handles_address, num_handles, nano_seconds); | ||
| 593 | } | ||
| 594 | |||
| 595 | /// Resumes a thread waiting on WaitSynchronization | ||
| 596 | static Result CancelSynchronization(Core::System& system, Handle handle) { | ||
| 597 | LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle); | ||
| 598 | |||
| 599 | // Get the thread from its handle. | ||
| 600 | KScopedAutoObject thread = | ||
| 601 | system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle); | ||
| 602 | R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); | ||
| 603 | |||
| 604 | // Cancel the thread's wait. | ||
| 605 | thread->WaitCancel(); | ||
| 606 | return ResultSuccess; | ||
| 607 | } | ||
| 608 | |||
| 609 | static Result CancelSynchronization32(Core::System& system, Handle handle) { | ||
| 610 | return CancelSynchronization(system, handle); | ||
| 611 | } | ||
| 612 | |||
| 613 | /// Attempts to locks a mutex | ||
| 614 | static Result ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, u32 tag) { | ||
| 615 | LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}", | ||
| 616 | thread_handle, address, tag); | ||
| 617 | |||
| 618 | // Validate the input address. | ||
| 619 | if (IsKernelAddress(address)) { | ||
| 620 | LOG_ERROR(Kernel_SVC, "Attempting to arbitrate a lock on a kernel address (address={:08X})", | ||
| 621 | address); | ||
| 622 | return ResultInvalidCurrentMemory; | ||
| 623 | } | ||
| 624 | if (!Common::IsAligned(address, sizeof(u32))) { | ||
| 625 | LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address); | ||
| 626 | return ResultInvalidAddress; | ||
| 627 | } | ||
| 628 | |||
| 629 | return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag); | ||
| 630 | } | ||
| 631 | |||
| 632 | static Result ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, u32 tag) { | ||
| 633 | return ArbitrateLock(system, thread_handle, address, tag); | ||
| 634 | } | ||
| 635 | |||
| 636 | /// Unlock a mutex | ||
| 637 | static Result ArbitrateUnlock(Core::System& system, VAddr address) { | ||
| 638 | LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address); | ||
| 639 | |||
| 640 | // Validate the input address. | ||
| 641 | if (IsKernelAddress(address)) { | ||
| 642 | LOG_ERROR(Kernel_SVC, | ||
| 643 | "Attempting to arbitrate an unlock on a kernel address (address={:08X})", | ||
| 644 | address); | ||
| 645 | return ResultInvalidCurrentMemory; | ||
| 646 | } | ||
| 647 | if (!Common::IsAligned(address, sizeof(u32))) { | ||
| 648 | LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address); | ||
| 649 | return ResultInvalidAddress; | ||
| 650 | } | ||
| 651 | |||
| 652 | return system.Kernel().CurrentProcess()->SignalToAddress(address); | ||
| 653 | } | ||
| 654 | |||
| 655 | static Result ArbitrateUnlock32(Core::System& system, u32 address) { | ||
| 656 | return ArbitrateUnlock(system, address); | ||
| 657 | } | ||
| 658 | |||
| 659 | /// Break program execution | ||
| 660 | static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { | ||
| 661 | BreakReason break_reason = | ||
| 662 | static_cast<BreakReason>(reason & ~static_cast<u32>(BreakReason::NotificationOnlyFlag)); | ||
| 663 | bool notification_only = (reason & static_cast<u32>(BreakReason::NotificationOnlyFlag)) != 0; | ||
| 664 | |||
| 665 | bool has_dumped_buffer{}; | ||
| 666 | std::vector<u8> debug_buffer; | ||
| 667 | |||
| 668 | const auto handle_debug_buffer = [&](VAddr addr, u64 sz) { | ||
| 669 | if (sz == 0 || addr == 0 || has_dumped_buffer) { | ||
| 670 | return; | ||
| 671 | } | ||
| 672 | |||
| 673 | auto& memory = system.Memory(); | ||
| 674 | |||
| 675 | // This typically is an error code so we're going to assume this is the case | ||
| 676 | if (sz == sizeof(u32)) { | ||
| 677 | LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", memory.Read32(addr)); | ||
| 678 | } else { | ||
| 679 | // We don't know what's in here so we'll hexdump it | ||
| 680 | debug_buffer.resize(sz); | ||
| 681 | memory.ReadBlock(addr, debug_buffer.data(), sz); | ||
| 682 | std::string hexdump; | ||
| 683 | for (std::size_t i = 0; i < debug_buffer.size(); i++) { | ||
| 684 | hexdump += fmt::format("{:02X} ", debug_buffer[i]); | ||
| 685 | if (i != 0 && i % 16 == 0) { | ||
| 686 | hexdump += '\n'; | ||
| 687 | } | ||
| 688 | } | ||
| 689 | LOG_CRITICAL(Debug_Emulated, "debug_buffer=\n{}", hexdump); | ||
| 690 | } | ||
| 691 | has_dumped_buffer = true; | ||
| 692 | }; | ||
| 693 | switch (break_reason) { | ||
| 694 | case BreakReason::Panic: | ||
| 695 | LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1, | ||
| 696 | info2); | ||
| 697 | handle_debug_buffer(info1, info2); | ||
| 698 | break; | ||
| 699 | case BreakReason::Assert: | ||
| 700 | LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}", | ||
| 701 | info1, info2); | ||
| 702 | handle_debug_buffer(info1, info2); | ||
| 703 | break; | ||
| 704 | case BreakReason::User: | ||
| 705 | LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2); | ||
| 706 | handle_debug_buffer(info1, info2); | ||
| 707 | break; | ||
| 708 | case BreakReason::PreLoadDll: | ||
| 709 | LOG_INFO(Debug_Emulated, | ||
| 710 | "Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1, | ||
| 711 | info2); | ||
| 712 | break; | ||
| 713 | case BreakReason::PostLoadDll: | ||
| 714 | LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1, | ||
| 715 | info2); | ||
| 716 | break; | ||
| 717 | case BreakReason::PreUnloadDll: | ||
| 718 | LOG_INFO(Debug_Emulated, | ||
| 719 | "Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1, | ||
| 720 | info2); | ||
| 721 | break; | ||
| 722 | case BreakReason::PostUnloadDll: | ||
| 723 | LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}", | ||
| 724 | info1, info2); | ||
| 725 | break; | ||
| 726 | case BreakReason::CppException: | ||
| 727 | LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered."); | ||
| 728 | break; | ||
| 729 | default: | ||
| 730 | LOG_WARNING( | ||
| 731 | Debug_Emulated, | ||
| 732 | "Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}", | ||
| 733 | reason, info1, info2); | ||
| 734 | handle_debug_buffer(info1, info2); | ||
| 735 | break; | ||
| 736 | } | ||
| 737 | |||
| 738 | system.GetReporter().SaveSvcBreakReport(reason, notification_only, info1, info2, | ||
| 739 | has_dumped_buffer ? std::make_optional(debug_buffer) | ||
| 740 | : std::nullopt); | ||
| 741 | |||
| 742 | if (!notification_only) { | ||
| 743 | LOG_CRITICAL( | ||
| 744 | Debug_Emulated, | ||
| 745 | "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", | ||
| 746 | reason, info1, info2); | ||
| 747 | |||
| 748 | handle_debug_buffer(info1, info2); | ||
| 749 | |||
| 750 | auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); | ||
| 751 | const auto thread_processor_id = current_thread->GetActiveCore(); | ||
| 752 | system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace(); | ||
| 753 | } | ||
| 754 | |||
| 755 | if (system.DebuggerEnabled()) { | ||
| 756 | auto* thread = system.Kernel().GetCurrentEmuThread(); | ||
| 757 | system.GetDebugger().NotifyThreadStopped(thread); | ||
| 758 | thread->RequestSuspend(Kernel::SuspendType::Debug); | ||
| 759 | } | ||
| 760 | } | ||
| 761 | |||
| 762 | static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) { | ||
| 763 | Break(system, reason, info1, info2); | ||
| 764 | } | ||
| 765 | |||
| 766 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit | ||
| 767 | static void OutputDebugString(Core::System& system, VAddr address, u64 len) { | ||
| 768 | if (len == 0) { | ||
| 769 | return; | ||
| 770 | } | ||
| 771 | |||
| 772 | std::string str(len, '\0'); | ||
| 773 | system.Memory().ReadBlock(address, str.data(), str.size()); | ||
| 774 | LOG_DEBUG(Debug_Emulated, "{}", str); | ||
| 775 | } | ||
| 776 | |||
| 777 | static void OutputDebugString32(Core::System& system, u32 address, u32 len) { | ||
| 778 | OutputDebugString(system, address, len); | ||
| 779 | } | ||
| 780 | |||
| 781 | /// Gets system/memory information for the current process | ||
| 782 | static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle, | ||
| 783 | u64 info_sub_id) { | ||
| 784 | LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, | ||
| 785 | info_sub_id, handle); | ||
| 786 | |||
| 787 | const auto info_id_type = static_cast<InfoType>(info_id); | ||
| 788 | |||
| 789 | switch (info_id_type) { | ||
| 790 | case InfoType::CoreMask: | ||
| 791 | case InfoType::PriorityMask: | ||
| 792 | case InfoType::AliasRegionAddress: | ||
| 793 | case InfoType::AliasRegionSize: | ||
| 794 | case InfoType::HeapRegionAddress: | ||
| 795 | case InfoType::HeapRegionSize: | ||
| 796 | case InfoType::AslrRegionAddress: | ||
| 797 | case InfoType::AslrRegionSize: | ||
| 798 | case InfoType::StackRegionAddress: | ||
| 799 | case InfoType::StackRegionSize: | ||
| 800 | case InfoType::TotalMemorySize: | ||
| 801 | case InfoType::UsedMemorySize: | ||
| 802 | case InfoType::SystemResourceSizeTotal: | ||
| 803 | case InfoType::SystemResourceSizeUsed: | ||
| 804 | case InfoType::ProgramId: | ||
| 805 | case InfoType::UserExceptionContextAddress: | ||
| 806 | case InfoType::TotalNonSystemMemorySize: | ||
| 807 | case InfoType::UsedNonSystemMemorySize: | ||
| 808 | case InfoType::IsApplication: | ||
| 809 | case InfoType::FreeThreadCount: { | ||
| 810 | if (info_sub_id != 0) { | ||
| 811 | LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id, | ||
| 812 | info_sub_id); | ||
| 813 | return ResultInvalidEnumValue; | ||
| 814 | } | ||
| 815 | |||
| 816 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 817 | KScopedAutoObject process = handle_table.GetObject<KProcess>(handle); | ||
| 818 | if (process.IsNull()) { | ||
| 819 | LOG_ERROR(Kernel_SVC, "Process is not valid! info_id={}, info_sub_id={}, handle={:08X}", | ||
| 820 | info_id, info_sub_id, handle); | ||
| 821 | return ResultInvalidHandle; | ||
| 822 | } | ||
| 823 | |||
| 824 | switch (info_id_type) { | ||
| 825 | case InfoType::CoreMask: | ||
| 826 | *result = process->GetCoreMask(); | ||
| 827 | return ResultSuccess; | ||
| 828 | |||
| 829 | case InfoType::PriorityMask: | ||
| 830 | *result = process->GetPriorityMask(); | ||
| 831 | return ResultSuccess; | ||
| 832 | |||
| 833 | case InfoType::AliasRegionAddress: | ||
| 834 | *result = process->PageTable().GetAliasRegionStart(); | ||
| 835 | return ResultSuccess; | ||
| 836 | |||
| 837 | case InfoType::AliasRegionSize: | ||
| 838 | *result = process->PageTable().GetAliasRegionSize(); | ||
| 839 | return ResultSuccess; | ||
| 840 | |||
| 841 | case InfoType::HeapRegionAddress: | ||
| 842 | *result = process->PageTable().GetHeapRegionStart(); | ||
| 843 | return ResultSuccess; | ||
| 844 | |||
| 845 | case InfoType::HeapRegionSize: | ||
| 846 | *result = process->PageTable().GetHeapRegionSize(); | ||
| 847 | return ResultSuccess; | ||
| 848 | |||
| 849 | case InfoType::AslrRegionAddress: | ||
| 850 | *result = process->PageTable().GetAliasCodeRegionStart(); | ||
| 851 | return ResultSuccess; | ||
| 852 | |||
| 853 | case InfoType::AslrRegionSize: | ||
| 854 | *result = process->PageTable().GetAliasCodeRegionSize(); | ||
| 855 | return ResultSuccess; | ||
| 856 | |||
| 857 | case InfoType::StackRegionAddress: | ||
| 858 | *result = process->PageTable().GetStackRegionStart(); | ||
| 859 | return ResultSuccess; | ||
| 860 | |||
| 861 | case InfoType::StackRegionSize: | ||
| 862 | *result = process->PageTable().GetStackRegionSize(); | ||
| 863 | return ResultSuccess; | ||
| 864 | |||
| 865 | case InfoType::TotalMemorySize: | ||
| 866 | *result = process->GetTotalPhysicalMemoryAvailable(); | ||
| 867 | return ResultSuccess; | ||
| 868 | |||
| 869 | case InfoType::UsedMemorySize: | ||
| 870 | *result = process->GetTotalPhysicalMemoryUsed(); | ||
| 871 | return ResultSuccess; | ||
| 872 | |||
| 873 | case InfoType::SystemResourceSizeTotal: | ||
| 874 | *result = process->GetSystemResourceSize(); | ||
| 875 | return ResultSuccess; | ||
| 876 | |||
| 877 | case InfoType::SystemResourceSizeUsed: | ||
| 878 | LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage"); | ||
| 879 | *result = process->GetSystemResourceUsage(); | ||
| 880 | return ResultSuccess; | ||
| 881 | |||
| 882 | case InfoType::ProgramId: | ||
| 883 | *result = process->GetProgramID(); | ||
| 884 | return ResultSuccess; | ||
| 885 | |||
| 886 | case InfoType::UserExceptionContextAddress: | ||
| 887 | *result = process->GetProcessLocalRegionAddress(); | ||
| 888 | return ResultSuccess; | ||
| 889 | |||
| 890 | case InfoType::TotalNonSystemMemorySize: | ||
| 891 | *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource(); | ||
| 892 | return ResultSuccess; | ||
| 893 | |||
| 894 | case InfoType::UsedNonSystemMemorySize: | ||
| 895 | *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource(); | ||
| 896 | return ResultSuccess; | ||
| 897 | |||
| 898 | case InfoType::FreeThreadCount: | ||
| 899 | *result = process->GetFreeThreadCount(); | ||
| 900 | return ResultSuccess; | ||
| 901 | |||
| 902 | default: | ||
| 903 | break; | ||
| 904 | } | ||
| 905 | |||
| 906 | LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id); | ||
| 907 | return ResultInvalidEnumValue; | ||
| 908 | } | ||
| 909 | |||
| 910 | case InfoType::DebuggerAttached: | ||
| 911 | *result = 0; | ||
| 912 | return ResultSuccess; | ||
| 913 | |||
| 914 | case InfoType::ResourceLimit: { | ||
| 915 | if (handle != 0) { | ||
| 916 | LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle); | ||
| 917 | return ResultInvalidHandle; | ||
| 918 | } | ||
| 919 | |||
| 920 | if (info_sub_id != 0) { | ||
| 921 | LOG_ERROR(Kernel, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id, | ||
| 922 | info_sub_id); | ||
| 923 | return ResultInvalidCombination; | ||
| 924 | } | ||
| 925 | |||
| 926 | KProcess* const current_process = system.Kernel().CurrentProcess(); | ||
| 927 | KHandleTable& handle_table = current_process->GetHandleTable(); | ||
| 928 | const auto resource_limit = current_process->GetResourceLimit(); | ||
| 929 | if (!resource_limit) { | ||
| 930 | *result = Svc::InvalidHandle; | ||
| 931 | // Yes, the kernel considers this a successful operation. | ||
| 932 | return ResultSuccess; | ||
| 933 | } | ||
| 934 | |||
| 935 | Handle resource_handle{}; | ||
| 936 | R_TRY(handle_table.Add(&resource_handle, resource_limit)); | ||
| 937 | |||
| 938 | *result = resource_handle; | ||
| 939 | return ResultSuccess; | ||
| 940 | } | ||
| 941 | |||
| 942 | case InfoType::RandomEntropy: | ||
| 943 | if (handle != 0) { | ||
| 944 | LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}", | ||
| 945 | handle); | ||
| 946 | return ResultInvalidHandle; | ||
| 947 | } | ||
| 948 | |||
| 949 | if (info_sub_id >= KProcess::RANDOM_ENTROPY_SIZE) { | ||
| 950 | LOG_ERROR(Kernel_SVC, "Entropy size is out of range, expected {} but got {}", | ||
| 951 | KProcess::RANDOM_ENTROPY_SIZE, info_sub_id); | ||
| 952 | return ResultInvalidCombination; | ||
| 953 | } | ||
| 954 | |||
| 955 | *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id); | ||
| 956 | return ResultSuccess; | ||
| 957 | |||
| 958 | case InfoType::InitialProcessIdRange: | ||
| 959 | LOG_WARNING(Kernel_SVC, | ||
| 960 | "(STUBBED) Attempted to query privileged process id bounds, returned 0"); | ||
| 961 | *result = 0; | ||
| 962 | return ResultSuccess; | ||
| 963 | |||
| 964 | case InfoType::ThreadTickCount: { | ||
| 965 | constexpr u64 num_cpus = 4; | ||
| 966 | if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) { | ||
| 967 | LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus, | ||
| 968 | info_sub_id); | ||
| 969 | return ResultInvalidCombination; | ||
| 970 | } | ||
| 971 | |||
| 972 | KScopedAutoObject thread = | ||
| 973 | system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>( | ||
| 974 | static_cast<Handle>(handle)); | ||
| 975 | if (thread.IsNull()) { | ||
| 976 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", | ||
| 977 | static_cast<Handle>(handle)); | ||
| 978 | return ResultInvalidHandle; | ||
| 979 | } | ||
| 980 | |||
| 981 | const auto& core_timing = system.CoreTiming(); | ||
| 982 | const auto& scheduler = *system.Kernel().CurrentScheduler(); | ||
| 983 | const auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); | ||
| 984 | const bool same_thread = current_thread == thread.GetPointerUnsafe(); | ||
| 985 | |||
| 986 | const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTime(); | ||
| 987 | u64 out_ticks = 0; | ||
| 988 | if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { | ||
| 989 | const u64 thread_ticks = current_thread->GetCpuTime(); | ||
| 990 | |||
| 991 | out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); | ||
| 992 | } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) { | ||
| 993 | out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks; | ||
| 994 | } | ||
| 995 | |||
| 996 | *result = out_ticks; | ||
| 997 | return ResultSuccess; | ||
| 998 | } | ||
| 999 | case InfoType::IdleTickCount: { | ||
| 1000 | // Verify the input handle is invalid. | ||
| 1001 | R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); | ||
| 1002 | |||
| 1003 | // Verify the requested core is valid. | ||
| 1004 | const bool core_valid = | ||
| 1005 | (info_sub_id == 0xFFFFFFFFFFFFFFFF) || | ||
| 1006 | (info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex())); | ||
| 1007 | R_UNLESS(core_valid, ResultInvalidCombination); | ||
| 1008 | |||
| 1009 | // Get the idle tick count. | ||
| 1010 | *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime(); | ||
| 1011 | return ResultSuccess; | ||
| 1012 | } | ||
| 1013 | case InfoType::MesosphereCurrentProcess: { | ||
| 1014 | // Verify the input handle is invalid. | ||
| 1015 | R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); | ||
| 1016 | |||
| 1017 | // Verify the sub-type is valid. | ||
| 1018 | R_UNLESS(info_sub_id == 0, ResultInvalidCombination); | ||
| 1019 | |||
| 1020 | // Get the handle table. | ||
| 1021 | KProcess* current_process = system.Kernel().CurrentProcess(); | ||
| 1022 | KHandleTable& handle_table = current_process->GetHandleTable(); | ||
| 1023 | |||
| 1024 | // Get a new handle for the current process. | ||
| 1025 | Handle tmp; | ||
| 1026 | R_TRY(handle_table.Add(&tmp, current_process)); | ||
| 1027 | |||
| 1028 | // Set the output. | ||
| 1029 | *result = tmp; | ||
| 1030 | |||
| 1031 | // We succeeded. | ||
| 1032 | return ResultSuccess; | ||
| 1033 | } | ||
| 1034 | default: | ||
| 1035 | LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id); | ||
| 1036 | return ResultInvalidEnumValue; | ||
| 1037 | } | ||
| 1038 | } | ||
| 1039 | |||
| 1040 | static Result GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low, | ||
| 1041 | u32 info_id, u32 handle, u32 sub_id_high) { | ||
| 1042 | const u64 sub_id{u64{sub_id_low} | (u64{sub_id_high} << 32)}; | ||
| 1043 | u64 res_value{}; | ||
| 1044 | |||
| 1045 | const Result result{GetInfo(system, &res_value, info_id, handle, sub_id)}; | ||
| 1046 | *result_high = static_cast<u32>(res_value >> 32); | ||
| 1047 | *result_low = static_cast<u32>(res_value & std::numeric_limits<u32>::max()); | ||
| 1048 | |||
| 1049 | return result; | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | /// Maps memory at a desired address | ||
| 1053 | static Result MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { | ||
| 1054 | LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); | ||
| 1055 | |||
| 1056 | if (!Common::Is4KBAligned(addr)) { | ||
| 1057 | LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr); | ||
| 1058 | return ResultInvalidAddress; | ||
| 1059 | } | ||
| 1060 | |||
| 1061 | if (!Common::Is4KBAligned(size)) { | ||
| 1062 | LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size); | ||
| 1063 | return ResultInvalidSize; | ||
| 1064 | } | ||
| 1065 | |||
| 1066 | if (size == 0) { | ||
| 1067 | LOG_ERROR(Kernel_SVC, "Size is zero"); | ||
| 1068 | return ResultInvalidSize; | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | if (!(addr < addr + size)) { | ||
| 1072 | LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address"); | ||
| 1073 | return ResultInvalidMemoryRegion; | ||
| 1074 | } | ||
| 1075 | |||
| 1076 | KProcess* const current_process{system.Kernel().CurrentProcess()}; | ||
| 1077 | auto& page_table{current_process->PageTable()}; | ||
| 1078 | |||
| 1079 | if (current_process->GetSystemResourceSize() == 0) { | ||
| 1080 | LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); | ||
| 1081 | return ResultInvalidState; | ||
| 1082 | } | ||
| 1083 | |||
| 1084 | if (!page_table.IsInsideAddressSpace(addr, size)) { | ||
| 1085 | LOG_ERROR(Kernel_SVC, | ||
| 1086 | "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, | ||
| 1087 | size); | ||
| 1088 | return ResultInvalidMemoryRegion; | ||
| 1089 | } | ||
| 1090 | |||
| 1091 | if (page_table.IsOutsideAliasRegion(addr, size)) { | ||
| 1092 | LOG_ERROR(Kernel_SVC, | ||
| 1093 | "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr, | ||
| 1094 | size); | ||
| 1095 | return ResultInvalidMemoryRegion; | ||
| 1096 | } | ||
| 1097 | |||
| 1098 | return page_table.MapPhysicalMemory(addr, size); | ||
| 1099 | } | ||
| 1100 | |||
| 1101 | static Result MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { | ||
| 1102 | return MapPhysicalMemory(system, addr, size); | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | /// Unmaps memory previously mapped via MapPhysicalMemory | ||
| 1106 | static Result UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { | ||
| 1107 | LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); | ||
| 1108 | |||
| 1109 | if (!Common::Is4KBAligned(addr)) { | ||
| 1110 | LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr); | ||
| 1111 | return ResultInvalidAddress; | ||
| 1112 | } | ||
| 1113 | |||
| 1114 | if (!Common::Is4KBAligned(size)) { | ||
| 1115 | LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size); | ||
| 1116 | return ResultInvalidSize; | ||
| 1117 | } | ||
| 1118 | |||
| 1119 | if (size == 0) { | ||
| 1120 | LOG_ERROR(Kernel_SVC, "Size is zero"); | ||
| 1121 | return ResultInvalidSize; | ||
| 1122 | } | ||
| 1123 | |||
| 1124 | if (!(addr < addr + size)) { | ||
| 1125 | LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address"); | ||
| 1126 | return ResultInvalidMemoryRegion; | ||
| 1127 | } | ||
| 1128 | |||
| 1129 | KProcess* const current_process{system.Kernel().CurrentProcess()}; | ||
| 1130 | auto& page_table{current_process->PageTable()}; | ||
| 1131 | |||
| 1132 | if (current_process->GetSystemResourceSize() == 0) { | ||
| 1133 | LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); | ||
| 1134 | return ResultInvalidState; | ||
| 1135 | } | ||
| 1136 | |||
| 1137 | if (!page_table.IsInsideAddressSpace(addr, size)) { | ||
| 1138 | LOG_ERROR(Kernel_SVC, | ||
| 1139 | "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, | ||
| 1140 | size); | ||
| 1141 | return ResultInvalidMemoryRegion; | ||
| 1142 | } | ||
| 1143 | |||
| 1144 | if (page_table.IsOutsideAliasRegion(addr, size)) { | ||
| 1145 | LOG_ERROR(Kernel_SVC, | ||
| 1146 | "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr, | ||
| 1147 | size); | ||
| 1148 | return ResultInvalidMemoryRegion; | ||
| 1149 | } | ||
| 1150 | |||
| 1151 | return page_table.UnmapPhysicalMemory(addr, size); | ||
| 1152 | } | ||
| 1153 | |||
| 1154 | static Result UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { | ||
| 1155 | return UnmapPhysicalMemory(system, addr, size); | ||
| 1156 | } | ||
| 1157 | |||
| 1158 | /// Sets the thread activity | ||
| 1159 | static Result SetThreadActivity(Core::System& system, Handle thread_handle, | ||
| 1160 | ThreadActivity thread_activity) { | ||
| 1161 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle, | ||
| 1162 | thread_activity); | ||
| 1163 | |||
| 1164 | // Validate the activity. | ||
| 1165 | constexpr auto IsValidThreadActivity = [](ThreadActivity activity) { | ||
| 1166 | return activity == ThreadActivity::Runnable || activity == ThreadActivity::Paused; | ||
| 1167 | }; | ||
| 1168 | R_UNLESS(IsValidThreadActivity(thread_activity), ResultInvalidEnumValue); | ||
| 1169 | |||
| 1170 | // Get the thread from its handle. | ||
| 1171 | KScopedAutoObject thread = | ||
| 1172 | system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); | ||
| 1173 | R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); | ||
| 1174 | |||
| 1175 | // Check that the activity is being set on a non-current thread for the current process. | ||
| 1176 | R_UNLESS(thread->GetOwnerProcess() == system.Kernel().CurrentProcess(), ResultInvalidHandle); | ||
| 1177 | R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(system.Kernel()), ResultBusy); | ||
| 1178 | |||
| 1179 | // Set the activity. | ||
| 1180 | R_TRY(thread->SetActivity(thread_activity)); | ||
| 1181 | |||
| 1182 | return ResultSuccess; | ||
| 1183 | } | ||
| 1184 | |||
| 1185 | static Result SetThreadActivity32(Core::System& system, Handle thread_handle, | ||
| 1186 | Svc::ThreadActivity thread_activity) { | ||
| 1187 | return SetThreadActivity(system, thread_handle, thread_activity); | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | /// Gets the thread context | ||
| 1191 | static Result GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) { | ||
| 1192 | LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context, | ||
| 1193 | thread_handle); | ||
| 1194 | |||
| 1195 | auto& kernel = system.Kernel(); | ||
| 1196 | |||
| 1197 | // Get the thread from its handle. | ||
| 1198 | KScopedAutoObject thread = | ||
| 1199 | kernel.CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); | ||
| 1200 | R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); | ||
| 1201 | |||
| 1202 | // Require the handle be to a non-current thread in the current process. | ||
| 1203 | const auto* current_process = kernel.CurrentProcess(); | ||
| 1204 | R_UNLESS(current_process == thread->GetOwnerProcess(), ResultInvalidId); | ||
| 1205 | |||
| 1206 | // Verify that the thread isn't terminated. | ||
| 1207 | R_UNLESS(thread->GetState() != ThreadState::Terminated, ResultTerminationRequested); | ||
| 1208 | |||
| 1209 | /// Check that the thread is not the current one. | ||
| 1210 | /// NOTE: Nintendo does not check this, and thus the following loop will deadlock. | ||
| 1211 | R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(kernel), ResultInvalidId); | ||
| 1212 | |||
| 1213 | // Try to get the thread context until the thread isn't current on any core. | ||
| 1214 | while (true) { | ||
| 1215 | KScopedSchedulerLock sl{kernel}; | ||
| 1216 | |||
| 1217 | // TODO(bunnei): Enforce that thread is suspended for debug here. | ||
| 1218 | |||
| 1219 | // If the thread's raw state isn't runnable, check if it's current on some core. | ||
| 1220 | if (thread->GetRawState() != ThreadState::Runnable) { | ||
| 1221 | bool current = false; | ||
| 1222 | for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { | ||
| 1223 | if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetSchedulerCurrentThread()) { | ||
| 1224 | current = true; | ||
| 1225 | break; | ||
| 1226 | } | ||
| 1227 | } | ||
| 1228 | |||
| 1229 | // If the thread is current, retry until it isn't. | ||
| 1230 | if (current) { | ||
| 1231 | continue; | ||
| 1232 | } | ||
| 1233 | } | ||
| 1234 | |||
| 1235 | // Get the thread context. | ||
| 1236 | std::vector<u8> context; | ||
| 1237 | R_TRY(thread->GetThreadContext3(context)); | ||
| 1238 | |||
| 1239 | // Copy the thread context to user space. | ||
| 1240 | system.Memory().WriteBlock(out_context, context.data(), context.size()); | ||
| 1241 | |||
| 1242 | return ResultSuccess; | ||
| 1243 | } | ||
| 1244 | |||
| 1245 | return ResultSuccess; | ||
| 1246 | } | ||
| 1247 | |||
| 1248 | static Result GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) { | ||
| 1249 | return GetThreadContext(system, out_context, thread_handle); | ||
| 1250 | } | ||
| 1251 | |||
| 1252 | /// Gets the priority for the specified thread | ||
| 1253 | static Result GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) { | ||
| 1254 | LOG_TRACE(Kernel_SVC, "called"); | ||
| 1255 | |||
| 1256 | // Get the thread from its handle. | ||
| 1257 | KScopedAutoObject thread = | ||
| 1258 | system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle); | ||
| 1259 | R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); | ||
| 1260 | |||
| 1261 | // Get the thread's priority. | ||
| 1262 | *out_priority = thread->GetPriority(); | ||
| 1263 | return ResultSuccess; | ||
| 1264 | } | ||
| 1265 | |||
| 1266 | static Result GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) { | ||
| 1267 | return GetThreadPriority(system, out_priority, handle); | ||
| 1268 | } | ||
| 1269 | |||
| 1270 | /// Sets the priority for the specified thread | ||
| 1271 | static Result SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority) { | ||
| 1272 | // Get the current process. | ||
| 1273 | KProcess& process = *system.Kernel().CurrentProcess(); | ||
| 1274 | |||
| 1275 | // Validate the priority. | ||
| 1276 | R_UNLESS(HighestThreadPriority <= priority && priority <= LowestThreadPriority, | ||
| 1277 | ResultInvalidPriority); | ||
| 1278 | R_UNLESS(process.CheckThreadPriority(priority), ResultInvalidPriority); | ||
| 1279 | |||
| 1280 | // Get the thread from its handle. | ||
| 1281 | KScopedAutoObject thread = process.GetHandleTable().GetObject<KThread>(thread_handle); | ||
| 1282 | R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); | ||
| 1283 | |||
| 1284 | // Set the thread priority. | ||
| 1285 | thread->SetBasePriority(priority); | ||
| 1286 | return ResultSuccess; | ||
| 1287 | } | ||
| 1288 | |||
| 1289 | static Result SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority) { | ||
| 1290 | return SetThreadPriority(system, thread_handle, priority); | ||
| 1291 | } | ||
| 1292 | |||
| 1293 | /// Get which CPU core is executing the current thread | ||
| 1294 | static u32 GetCurrentProcessorNumber(Core::System& system) { | ||
| 1295 | LOG_TRACE(Kernel_SVC, "called"); | ||
| 1296 | return static_cast<u32>(system.CurrentPhysicalCore().CoreIndex()); | ||
| 1297 | } | ||
| 1298 | |||
| 1299 | static u32 GetCurrentProcessorNumber32(Core::System& system) { | ||
| 1300 | return GetCurrentProcessorNumber(system); | ||
| 1301 | } | ||
| 1302 | |||
| 1303 | namespace { | ||
| 1304 | |||
| 1305 | constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) { | ||
| 1306 | switch (perm) { | ||
| 1307 | case Svc::MemoryPermission::Read: | ||
| 1308 | case Svc::MemoryPermission::ReadWrite: | ||
| 1309 | return true; | ||
| 1310 | default: | ||
| 1311 | return false; | ||
| 1312 | } | ||
| 1313 | } | ||
| 1314 | |||
| 1315 | [[maybe_unused]] constexpr bool IsValidRemoteSharedMemoryPermission(Svc::MemoryPermission perm) { | ||
| 1316 | return IsValidSharedMemoryPermission(perm) || perm == Svc::MemoryPermission::DontCare; | ||
| 1317 | } | ||
| 1318 | |||
| 1319 | constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) { | ||
| 1320 | switch (perm) { | ||
| 1321 | case Svc::MemoryPermission::None: | ||
| 1322 | case Svc::MemoryPermission::Read: | ||
| 1323 | case Svc::MemoryPermission::ReadWrite: | ||
| 1324 | case Svc::MemoryPermission::ReadExecute: | ||
| 1325 | return true; | ||
| 1326 | default: | ||
| 1327 | return false; | ||
| 1328 | } | ||
| 1329 | } | ||
| 1330 | |||
| 1331 | constexpr bool IsValidMapCodeMemoryPermission(Svc::MemoryPermission perm) { | ||
| 1332 | return perm == Svc::MemoryPermission::ReadWrite; | ||
| 1333 | } | ||
| 1334 | |||
| 1335 | constexpr bool IsValidMapToOwnerCodeMemoryPermission(Svc::MemoryPermission perm) { | ||
| 1336 | return perm == Svc::MemoryPermission::Read || perm == Svc::MemoryPermission::ReadExecute; | ||
| 1337 | } | ||
| 1338 | |||
| 1339 | constexpr bool IsValidUnmapCodeMemoryPermission(Svc::MemoryPermission perm) { | ||
| 1340 | return perm == Svc::MemoryPermission::None; | ||
| 1341 | } | ||
| 1342 | |||
| 1343 | constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(Svc::MemoryPermission perm) { | ||
| 1344 | return perm == Svc::MemoryPermission::None; | ||
| 1345 | } | ||
| 1346 | |||
| 1347 | } // Anonymous namespace | ||
| 1348 | |||
| 1349 | static Result MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size, | ||
| 1350 | Svc::MemoryPermission map_perm) { | ||
| 1351 | LOG_TRACE(Kernel_SVC, | ||
| 1352 | "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", | ||
| 1353 | shmem_handle, address, size, map_perm); | ||
| 1354 | |||
| 1355 | // Validate the address/size. | ||
| 1356 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 1357 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 1358 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 1359 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 1360 | |||
| 1361 | // Validate the permission. | ||
| 1362 | R_UNLESS(IsValidSharedMemoryPermission(map_perm), ResultInvalidNewMemoryPermission); | ||
| 1363 | |||
| 1364 | // Get the current process. | ||
| 1365 | auto& process = *system.Kernel().CurrentProcess(); | ||
| 1366 | auto& page_table = process.PageTable(); | ||
| 1367 | |||
| 1368 | // Get the shared memory. | ||
| 1369 | KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle); | ||
| 1370 | R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle); | ||
| 1371 | |||
| 1372 | // Verify that the mapping is in range. | ||
| 1373 | R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion); | ||
| 1374 | |||
| 1375 | // Add the shared memory to the process. | ||
| 1376 | R_TRY(process.AddSharedMemory(shmem.GetPointerUnsafe(), address, size)); | ||
| 1377 | |||
| 1378 | // Ensure that we clean up the shared memory if we fail to map it. | ||
| 1379 | auto guard = | ||
| 1380 | SCOPE_GUARD({ process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size); }); | ||
| 1381 | |||
| 1382 | // Map the shared memory. | ||
| 1383 | R_TRY(shmem->Map(process, address, size, map_perm)); | ||
| 1384 | |||
| 1385 | // We succeeded. | ||
| 1386 | guard.Cancel(); | ||
| 1387 | return ResultSuccess; | ||
| 1388 | } | ||
| 1389 | |||
| 1390 | static Result MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size, | ||
| 1391 | Svc::MemoryPermission map_perm) { | ||
| 1392 | return MapSharedMemory(system, shmem_handle, address, size, map_perm); | ||
| 1393 | } | ||
| 1394 | |||
| 1395 | static Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, | ||
| 1396 | u64 size) { | ||
| 1397 | // Validate the address/size. | ||
| 1398 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 1399 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 1400 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 1401 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 1402 | |||
| 1403 | // Get the current process. | ||
| 1404 | auto& process = *system.Kernel().CurrentProcess(); | ||
| 1405 | auto& page_table = process.PageTable(); | ||
| 1406 | |||
| 1407 | // Get the shared memory. | ||
| 1408 | KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle); | ||
| 1409 | R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle); | ||
| 1410 | |||
| 1411 | // Verify that the mapping is in range. | ||
| 1412 | R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion); | ||
| 1413 | |||
| 1414 | // Unmap the shared memory. | ||
| 1415 | R_TRY(shmem->Unmap(process, address, size)); | ||
| 1416 | |||
| 1417 | // Remove the shared memory from the process. | ||
| 1418 | process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size); | ||
| 1419 | |||
| 1420 | return ResultSuccess; | ||
| 1421 | } | ||
| 1422 | |||
| 1423 | static Result UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, | ||
| 1424 | u32 size) { | ||
| 1425 | return UnmapSharedMemory(system, shmem_handle, address, size); | ||
| 1426 | } | ||
| 1427 | |||
| 1428 | static Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, VAddr address, | ||
| 1429 | u64 size, Svc::MemoryPermission perm) { | ||
| 1430 | LOG_TRACE(Kernel_SVC, | ||
| 1431 | "called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", | ||
| 1432 | process_handle, address, size, perm); | ||
| 1433 | |||
| 1434 | // Validate the address/size. | ||
| 1435 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 1436 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 1437 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 1438 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 1439 | R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory); | ||
| 1440 | R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory); | ||
| 1441 | |||
| 1442 | // Validate the memory permission. | ||
| 1443 | R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 1444 | |||
| 1445 | // Get the process from its handle. | ||
| 1446 | KScopedAutoObject process = | ||
| 1447 | system.CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle); | ||
| 1448 | R_UNLESS(process.IsNotNull(), ResultInvalidHandle); | ||
| 1449 | |||
| 1450 | // Validate that the address is in range. | ||
| 1451 | auto& page_table = process->PageTable(); | ||
| 1452 | R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); | ||
| 1453 | |||
| 1454 | // Set the memory permission. | ||
| 1455 | return page_table.SetProcessMemoryPermission(address, size, perm); | ||
| 1456 | } | ||
| 1457 | |||
| 1458 | static Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, | ||
| 1459 | VAddr src_address, u64 size) { | ||
| 1460 | LOG_TRACE(Kernel_SVC, | ||
| 1461 | "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}", | ||
| 1462 | dst_address, process_handle, src_address, size); | ||
| 1463 | |||
| 1464 | // Validate the address/size. | ||
| 1465 | R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress); | ||
| 1466 | R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress); | ||
| 1467 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 1468 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 1469 | R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory); | ||
| 1470 | R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory); | ||
| 1471 | |||
| 1472 | // Get the processes. | ||
| 1473 | KProcess* dst_process = system.CurrentProcess(); | ||
| 1474 | KScopedAutoObject src_process = | ||
| 1475 | dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle); | ||
| 1476 | R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); | ||
| 1477 | |||
| 1478 | // Get the page tables. | ||
| 1479 | auto& dst_pt = dst_process->PageTable(); | ||
| 1480 | auto& src_pt = src_process->PageTable(); | ||
| 1481 | |||
| 1482 | // Validate that the mapping is in range. | ||
| 1483 | R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory); | ||
| 1484 | R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode), | ||
| 1485 | ResultInvalidMemoryRegion); | ||
| 1486 | |||
| 1487 | // Create a new page group. | ||
| 1488 | KPageGroup pg{system.Kernel(), dst_pt.GetBlockInfoManager()}; | ||
| 1489 | R_TRY(src_pt.MakeAndOpenPageGroup( | ||
| 1490 | std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess, | ||
| 1491 | KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None, | ||
| 1492 | KMemoryAttribute::All, KMemoryAttribute::None)); | ||
| 1493 | |||
| 1494 | // Map the group. | ||
| 1495 | R_TRY(dst_pt.MapPageGroup(dst_address, pg, KMemoryState::SharedCode, | ||
| 1496 | KMemoryPermission::UserReadWrite)); | ||
| 1497 | |||
| 1498 | return ResultSuccess; | ||
| 1499 | } | ||
| 1500 | |||
| 1501 | static Result UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, | ||
| 1502 | VAddr src_address, u64 size) { | ||
| 1503 | LOG_TRACE(Kernel_SVC, | ||
| 1504 | "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}", | ||
| 1505 | dst_address, process_handle, src_address, size); | ||
| 1506 | |||
| 1507 | // Validate the address/size. | ||
| 1508 | R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress); | ||
| 1509 | R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress); | ||
| 1510 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 1511 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 1512 | R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory); | ||
| 1513 | R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory); | ||
| 1514 | |||
| 1515 | // Get the processes. | ||
| 1516 | KProcess* dst_process = system.CurrentProcess(); | ||
| 1517 | KScopedAutoObject src_process = | ||
| 1518 | dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle); | ||
| 1519 | R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); | ||
| 1520 | |||
| 1521 | // Get the page tables. | ||
| 1522 | auto& dst_pt = dst_process->PageTable(); | ||
| 1523 | auto& src_pt = src_process->PageTable(); | ||
| 1524 | |||
| 1525 | // Validate that the mapping is in range. | ||
| 1526 | R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory); | ||
| 1527 | R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode), | ||
| 1528 | ResultInvalidMemoryRegion); | ||
| 1529 | |||
| 1530 | // Unmap the memory. | ||
| 1531 | R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address)); | ||
| 1532 | |||
| 1533 | return ResultSuccess; | ||
| 1534 | } | ||
| 1535 | |||
| 1536 | static Result CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) { | ||
| 1537 | LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size); | ||
| 1538 | |||
| 1539 | // Get kernel instance. | ||
| 1540 | auto& kernel = system.Kernel(); | ||
| 1541 | |||
| 1542 | // Validate address / size. | ||
| 1543 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 1544 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 1545 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 1546 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 1547 | |||
| 1548 | // Create the code memory. | ||
| 1549 | |||
| 1550 | KCodeMemory* code_mem = KCodeMemory::Create(kernel); | ||
| 1551 | R_UNLESS(code_mem != nullptr, ResultOutOfResource); | ||
| 1552 | |||
| 1553 | // Verify that the region is in range. | ||
| 1554 | R_UNLESS(system.CurrentProcess()->PageTable().Contains(address, size), | ||
| 1555 | ResultInvalidCurrentMemory); | ||
| 1556 | |||
| 1557 | // Initialize the code memory. | ||
| 1558 | R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size)); | ||
| 1559 | |||
| 1560 | // Register the code memory. | ||
| 1561 | KCodeMemory::Register(kernel, code_mem); | ||
| 1562 | |||
| 1563 | // Add the code memory to the handle table. | ||
| 1564 | R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, code_mem)); | ||
| 1565 | |||
| 1566 | code_mem->Close(); | ||
| 1567 | |||
| 1568 | return ResultSuccess; | ||
| 1569 | } | ||
| 1570 | |||
| 1571 | static Result CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size) { | ||
| 1572 | return CreateCodeMemory(system, out, address, size); | ||
| 1573 | } | ||
| 1574 | |||
| 1575 | static Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation, | ||
| 1576 | VAddr address, size_t size, Svc::MemoryPermission perm) { | ||
| 1577 | |||
| 1578 | LOG_TRACE(Kernel_SVC, | ||
| 1579 | "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, " | ||
| 1580 | "permission=0x{:X}", | ||
| 1581 | code_memory_handle, operation, address, size, perm); | ||
| 1582 | |||
| 1583 | // Validate the address / size. | ||
| 1584 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 1585 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 1586 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 1587 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 1588 | |||
| 1589 | // Get the code memory from its handle. | ||
| 1590 | KScopedAutoObject code_mem = | ||
| 1591 | system.CurrentProcess()->GetHandleTable().GetObject<KCodeMemory>(code_memory_handle); | ||
| 1592 | R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle); | ||
| 1593 | |||
| 1594 | // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process. | ||
| 1595 | // This enables homebrew usage of these SVCs for JIT. | ||
| 1596 | |||
| 1597 | // Perform the operation. | ||
| 1598 | switch (static_cast<CodeMemoryOperation>(operation)) { | ||
| 1599 | case CodeMemoryOperation::Map: { | ||
| 1600 | // Check that the region is in range. | ||
| 1601 | R_UNLESS( | ||
| 1602 | system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut), | ||
| 1603 | ResultInvalidMemoryRegion); | ||
| 1604 | |||
| 1605 | // Check the memory permission. | ||
| 1606 | R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 1607 | |||
| 1608 | // Map the memory. | ||
| 1609 | R_TRY(code_mem->Map(address, size)); | ||
| 1610 | } break; | ||
| 1611 | case CodeMemoryOperation::Unmap: { | ||
| 1612 | // Check that the region is in range. | ||
| 1613 | R_UNLESS( | ||
| 1614 | system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut), | ||
| 1615 | ResultInvalidMemoryRegion); | ||
| 1616 | |||
| 1617 | // Check the memory permission. | ||
| 1618 | R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 1619 | |||
| 1620 | // Unmap the memory. | ||
| 1621 | R_TRY(code_mem->Unmap(address, size)); | ||
| 1622 | } break; | ||
| 1623 | case CodeMemoryOperation::MapToOwner: { | ||
| 1624 | // Check that the region is in range. | ||
| 1625 | R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size, | ||
| 1626 | KMemoryState::GeneratedCode), | ||
| 1627 | ResultInvalidMemoryRegion); | ||
| 1628 | |||
| 1629 | // Check the memory permission. | ||
| 1630 | R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 1631 | |||
| 1632 | // Map the memory to its owner. | ||
| 1633 | R_TRY(code_mem->MapToOwner(address, size, perm)); | ||
| 1634 | } break; | ||
| 1635 | case CodeMemoryOperation::UnmapFromOwner: { | ||
| 1636 | // Check that the region is in range. | ||
| 1637 | R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size, | ||
| 1638 | KMemoryState::GeneratedCode), | ||
| 1639 | ResultInvalidMemoryRegion); | ||
| 1640 | |||
| 1641 | // Check the memory permission. | ||
| 1642 | R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 1643 | |||
| 1644 | // Unmap the memory from its owner. | ||
| 1645 | R_TRY(code_mem->UnmapFromOwner(address, size)); | ||
| 1646 | } break; | ||
| 1647 | default: | ||
| 1648 | return ResultInvalidEnumValue; | ||
| 1649 | } | ||
| 1650 | |||
| 1651 | return ResultSuccess; | ||
| 1652 | } | ||
| 1653 | |||
| 1654 | static Result ControlCodeMemory32(Core::System& system, Handle code_memory_handle, u32 operation, | ||
| 1655 | u64 address, u64 size, Svc::MemoryPermission perm) { | ||
| 1656 | return ControlCodeMemory(system, code_memory_handle, operation, address, size, perm); | ||
| 1657 | } | ||
| 1658 | |||
| 1659 | static Result QueryProcessMemory(Core::System& system, VAddr memory_info_address, | ||
| 1660 | VAddr page_info_address, Handle process_handle, VAddr address) { | ||
| 1661 | LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address); | ||
| 1662 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 1663 | KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); | ||
| 1664 | if (process.IsNull()) { | ||
| 1665 | LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", | ||
| 1666 | process_handle); | ||
| 1667 | return ResultInvalidHandle; | ||
| 1668 | } | ||
| 1669 | |||
| 1670 | auto& memory{system.Memory()}; | ||
| 1671 | const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()}; | ||
| 1672 | |||
| 1673 | memory.Write64(memory_info_address + 0x00, memory_info.base_address); | ||
| 1674 | memory.Write64(memory_info_address + 0x08, memory_info.size); | ||
| 1675 | memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff); | ||
| 1676 | memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attribute)); | ||
| 1677 | memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.permission)); | ||
| 1678 | memory.Write32(memory_info_address + 0x1c, memory_info.ipc_count); | ||
| 1679 | memory.Write32(memory_info_address + 0x20, memory_info.device_count); | ||
| 1680 | memory.Write32(memory_info_address + 0x24, 0); | ||
| 1681 | |||
| 1682 | // Page info appears to be currently unused by the kernel and is always set to zero. | ||
| 1683 | memory.Write32(page_info_address, 0); | ||
| 1684 | |||
| 1685 | return ResultSuccess; | ||
| 1686 | } | ||
| 1687 | |||
| 1688 | static Result QueryMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address, | ||
| 1689 | VAddr query_address) { | ||
| 1690 | LOG_TRACE(Kernel_SVC, | ||
| 1691 | "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, " | ||
| 1692 | "query_address=0x{:016X}", | ||
| 1693 | memory_info_address, page_info_address, query_address); | ||
| 1694 | |||
| 1695 | return QueryProcessMemory(system, memory_info_address, page_info_address, CurrentProcess, | ||
| 1696 | query_address); | ||
| 1697 | } | ||
| 1698 | |||
| 1699 | static Result QueryMemory32(Core::System& system, u32 memory_info_address, u32 page_info_address, | ||
| 1700 | u32 query_address) { | ||
| 1701 | return QueryMemory(system, memory_info_address, page_info_address, query_address); | ||
| 1702 | } | ||
| 1703 | |||
| 1704 | static Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, | ||
| 1705 | u64 src_address, u64 size) { | ||
| 1706 | LOG_DEBUG(Kernel_SVC, | ||
| 1707 | "called. process_handle=0x{:08X}, dst_address=0x{:016X}, " | ||
| 1708 | "src_address=0x{:016X}, size=0x{:016X}", | ||
| 1709 | process_handle, dst_address, src_address, size); | ||
| 1710 | |||
| 1711 | if (!Common::Is4KBAligned(src_address)) { | ||
| 1712 | LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", | ||
| 1713 | src_address); | ||
| 1714 | return ResultInvalidAddress; | ||
| 1715 | } | ||
| 1716 | |||
| 1717 | if (!Common::Is4KBAligned(dst_address)) { | ||
| 1718 | LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", | ||
| 1719 | dst_address); | ||
| 1720 | return ResultInvalidAddress; | ||
| 1721 | } | ||
| 1722 | |||
| 1723 | if (size == 0 || !Common::Is4KBAligned(size)) { | ||
| 1724 | LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size); | ||
| 1725 | return ResultInvalidSize; | ||
| 1726 | } | ||
| 1727 | |||
| 1728 | if (!IsValidAddressRange(dst_address, size)) { | ||
| 1729 | LOG_ERROR(Kernel_SVC, | ||
| 1730 | "Destination address range overflows the address space (dst_address=0x{:016X}, " | ||
| 1731 | "size=0x{:016X}).", | ||
| 1732 | dst_address, size); | ||
| 1733 | return ResultInvalidCurrentMemory; | ||
| 1734 | } | ||
| 1735 | |||
| 1736 | if (!IsValidAddressRange(src_address, size)) { | ||
| 1737 | LOG_ERROR(Kernel_SVC, | ||
| 1738 | "Source address range overflows the address space (src_address=0x{:016X}, " | ||
| 1739 | "size=0x{:016X}).", | ||
| 1740 | src_address, size); | ||
| 1741 | return ResultInvalidCurrentMemory; | ||
| 1742 | } | ||
| 1743 | |||
| 1744 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 1745 | KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); | ||
| 1746 | if (process.IsNull()) { | ||
| 1747 | LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", | ||
| 1748 | process_handle); | ||
| 1749 | return ResultInvalidHandle; | ||
| 1750 | } | ||
| 1751 | |||
| 1752 | auto& page_table = process->PageTable(); | ||
| 1753 | if (!page_table.IsInsideAddressSpace(src_address, size)) { | ||
| 1754 | LOG_ERROR(Kernel_SVC, | ||
| 1755 | "Source address range is not within the address space (src_address=0x{:016X}, " | ||
| 1756 | "size=0x{:016X}).", | ||
| 1757 | src_address, size); | ||
| 1758 | return ResultInvalidCurrentMemory; | ||
| 1759 | } | ||
| 1760 | |||
| 1761 | if (!page_table.IsInsideASLRRegion(dst_address, size)) { | ||
| 1762 | LOG_ERROR(Kernel_SVC, | ||
| 1763 | "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " | ||
| 1764 | "size=0x{:016X}).", | ||
| 1765 | dst_address, size); | ||
| 1766 | return ResultInvalidMemoryRegion; | ||
| 1767 | } | ||
| 1768 | |||
| 1769 | return page_table.MapCodeMemory(dst_address, src_address, size); | ||
| 1770 | } | ||
| 1771 | |||
| 1772 | static Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, | ||
| 1773 | u64 src_address, u64 size) { | ||
| 1774 | LOG_DEBUG(Kernel_SVC, | ||
| 1775 | "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, " | ||
| 1776 | "size=0x{:016X}", | ||
| 1777 | process_handle, dst_address, src_address, size); | ||
| 1778 | |||
| 1779 | if (!Common::Is4KBAligned(dst_address)) { | ||
| 1780 | LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", | ||
| 1781 | dst_address); | ||
| 1782 | return ResultInvalidAddress; | ||
| 1783 | } | ||
| 1784 | |||
| 1785 | if (!Common::Is4KBAligned(src_address)) { | ||
| 1786 | LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", | ||
| 1787 | src_address); | ||
| 1788 | return ResultInvalidAddress; | ||
| 1789 | } | ||
| 1790 | |||
| 1791 | if (size == 0 || !Common::Is4KBAligned(size)) { | ||
| 1792 | LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size); | ||
| 1793 | return ResultInvalidSize; | ||
| 1794 | } | ||
| 1795 | |||
| 1796 | if (!IsValidAddressRange(dst_address, size)) { | ||
| 1797 | LOG_ERROR(Kernel_SVC, | ||
| 1798 | "Destination address range overflows the address space (dst_address=0x{:016X}, " | ||
| 1799 | "size=0x{:016X}).", | ||
| 1800 | dst_address, size); | ||
| 1801 | return ResultInvalidCurrentMemory; | ||
| 1802 | } | ||
| 1803 | |||
| 1804 | if (!IsValidAddressRange(src_address, size)) { | ||
| 1805 | LOG_ERROR(Kernel_SVC, | ||
| 1806 | "Source address range overflows the address space (src_address=0x{:016X}, " | ||
| 1807 | "size=0x{:016X}).", | ||
| 1808 | src_address, size); | ||
| 1809 | return ResultInvalidCurrentMemory; | ||
| 1810 | } | ||
| 1811 | |||
| 1812 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 1813 | KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); | ||
| 1814 | if (process.IsNull()) { | ||
| 1815 | LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", | ||
| 1816 | process_handle); | ||
| 1817 | return ResultInvalidHandle; | ||
| 1818 | } | ||
| 1819 | |||
| 1820 | auto& page_table = process->PageTable(); | ||
| 1821 | if (!page_table.IsInsideAddressSpace(src_address, size)) { | ||
| 1822 | LOG_ERROR(Kernel_SVC, | ||
| 1823 | "Source address range is not within the address space (src_address=0x{:016X}, " | ||
| 1824 | "size=0x{:016X}).", | ||
| 1825 | src_address, size); | ||
| 1826 | return ResultInvalidCurrentMemory; | ||
| 1827 | } | ||
| 1828 | |||
| 1829 | if (!page_table.IsInsideASLRRegion(dst_address, size)) { | ||
| 1830 | LOG_ERROR(Kernel_SVC, | ||
| 1831 | "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " | ||
| 1832 | "size=0x{:016X}).", | ||
| 1833 | dst_address, size); | ||
| 1834 | return ResultInvalidMemoryRegion; | ||
| 1835 | } | ||
| 1836 | |||
| 1837 | return page_table.UnmapCodeMemory(dst_address, src_address, size, | ||
| 1838 | KPageTable::ICacheInvalidationStrategy::InvalidateAll); | ||
| 1839 | } | ||
| 1840 | |||
| 1841 | /// Exits the current process | ||
| 1842 | static void ExitProcess(Core::System& system) { | ||
| 1843 | auto* current_process = system.Kernel().CurrentProcess(); | ||
| 1844 | |||
| 1845 | LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); | ||
| 1846 | ASSERT_MSG(current_process->GetState() == KProcess::State::Running, | ||
| 1847 | "Process has already exited"); | ||
| 1848 | |||
| 1849 | system.Exit(); | ||
| 1850 | } | ||
| 1851 | |||
| 1852 | static void ExitProcess32(Core::System& system) { | ||
| 1853 | ExitProcess(system); | ||
| 1854 | } | ||
| 1855 | |||
| 1856 | namespace { | ||
| 1857 | |||
| 1858 | constexpr bool IsValidVirtualCoreId(int32_t core_id) { | ||
| 1859 | return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES)); | ||
| 1860 | } | ||
| 1861 | |||
| 1862 | } // Anonymous namespace | ||
| 1863 | |||
| 1864 | /// Creates a new thread | ||
| 1865 | static Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, | ||
| 1866 | VAddr stack_bottom, u32 priority, s32 core_id) { | ||
| 1867 | LOG_DEBUG(Kernel_SVC, | ||
| 1868 | "called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, " | ||
| 1869 | "priority=0x{:08X}, core_id=0x{:08X}", | ||
| 1870 | entry_point, arg, stack_bottom, priority, core_id); | ||
| 1871 | |||
| 1872 | // Adjust core id, if it's the default magic. | ||
| 1873 | auto& kernel = system.Kernel(); | ||
| 1874 | auto& process = *kernel.CurrentProcess(); | ||
| 1875 | if (core_id == IdealCoreUseProcessValue) { | ||
| 1876 | core_id = process.GetIdealCoreId(); | ||
| 1877 | } | ||
| 1878 | |||
| 1879 | // Validate arguments. | ||
| 1880 | if (!IsValidVirtualCoreId(core_id)) { | ||
| 1881 | LOG_ERROR(Kernel_SVC, "Invalid Core ID specified (id={})", core_id); | ||
| 1882 | return ResultInvalidCoreId; | ||
| 1883 | } | ||
| 1884 | if (((1ULL << core_id) & process.GetCoreMask()) == 0) { | ||
| 1885 | LOG_ERROR(Kernel_SVC, "Core ID doesn't fall within allowable cores (id={})", core_id); | ||
| 1886 | return ResultInvalidCoreId; | ||
| 1887 | } | ||
| 1888 | |||
| 1889 | if (HighestThreadPriority > priority || priority > LowestThreadPriority) { | ||
| 1890 | LOG_ERROR(Kernel_SVC, "Invalid priority specified (priority={})", priority); | ||
| 1891 | return ResultInvalidPriority; | ||
| 1892 | } | ||
| 1893 | if (!process.CheckThreadPriority(priority)) { | ||
| 1894 | LOG_ERROR(Kernel_SVC, "Invalid allowable thread priority (priority={})", priority); | ||
| 1895 | return ResultInvalidPriority; | ||
| 1896 | } | ||
| 1897 | |||
| 1898 | // Reserve a new thread from the process resource limit (waiting up to 100ms). | ||
| 1899 | KScopedResourceReservation thread_reservation( | ||
| 1900 | kernel.CurrentProcess(), LimitableResource::ThreadCountMax, 1, | ||
| 1901 | system.CoreTiming().GetGlobalTimeNs().count() + 100000000); | ||
| 1902 | if (!thread_reservation.Succeeded()) { | ||
| 1903 | LOG_ERROR(Kernel_SVC, "Could not reserve a new thread"); | ||
| 1904 | return ResultLimitReached; | ||
| 1905 | } | ||
| 1906 | |||
| 1907 | // Create the thread. | ||
| 1908 | KThread* thread = KThread::Create(kernel); | ||
| 1909 | if (!thread) { | ||
| 1910 | LOG_ERROR(Kernel_SVC, "Unable to create new threads. Thread creation limit reached."); | ||
| 1911 | return ResultOutOfResource; | ||
| 1912 | } | ||
| 1913 | SCOPE_EXIT({ thread->Close(); }); | ||
| 1914 | |||
| 1915 | // Initialize the thread. | ||
| 1916 | { | ||
| 1917 | KScopedLightLock lk{process.GetStateLock()}; | ||
| 1918 | R_TRY(KThread::InitializeUserThread(system, thread, entry_point, arg, stack_bottom, | ||
| 1919 | priority, core_id, &process)); | ||
| 1920 | } | ||
| 1921 | |||
| 1922 | // Set the thread name for debugging purposes. | ||
| 1923 | thread->SetName(fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *out_handle)); | ||
| 1924 | |||
| 1925 | // Commit the thread reservation. | ||
| 1926 | thread_reservation.Commit(); | ||
| 1927 | |||
| 1928 | // Register the new thread. | ||
| 1929 | KThread::Register(kernel, thread); | ||
| 1930 | |||
| 1931 | // Add the thread to the handle table. | ||
| 1932 | R_TRY(process.GetHandleTable().Add(out_handle, thread)); | ||
| 1933 | |||
| 1934 | return ResultSuccess; | ||
| 1935 | } | ||
| 1936 | |||
| 1937 | static Result CreateThread32(Core::System& system, Handle* out_handle, u32 priority, | ||
| 1938 | u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) { | ||
| 1939 | return CreateThread(system, out_handle, entry_point, arg, stack_top, priority, processor_id); | ||
| 1940 | } | ||
| 1941 | |||
| 1942 | /// Starts the thread for the provided handle | ||
| 1943 | static Result StartThread(Core::System& system, Handle thread_handle) { | ||
| 1944 | LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle); | ||
| 1945 | |||
| 1946 | // Get the thread from its handle. | ||
| 1947 | KScopedAutoObject thread = | ||
| 1948 | system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); | ||
| 1949 | R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); | ||
| 1950 | |||
| 1951 | // Try to start the thread. | ||
| 1952 | R_TRY(thread->Run()); | ||
| 1953 | |||
| 1954 | // If we succeeded, persist a reference to the thread. | ||
| 1955 | thread->Open(); | ||
| 1956 | system.Kernel().RegisterInUseObject(thread.GetPointerUnsafe()); | ||
| 1957 | |||
| 1958 | return ResultSuccess; | ||
| 1959 | } | ||
| 1960 | |||
| 1961 | static Result StartThread32(Core::System& system, Handle thread_handle) { | ||
| 1962 | return StartThread(system, thread_handle); | ||
| 1963 | } | ||
| 1964 | |||
| 1965 | /// Called when a thread exits | ||
| 1966 | static void ExitThread(Core::System& system) { | ||
| 1967 | LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); | ||
| 1968 | |||
| 1969 | auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); | ||
| 1970 | system.GlobalSchedulerContext().RemoveThread(current_thread); | ||
| 1971 | current_thread->Exit(); | ||
| 1972 | system.Kernel().UnregisterInUseObject(current_thread); | ||
| 1973 | } | ||
| 1974 | |||
| 1975 | static void ExitThread32(Core::System& system) { | ||
| 1976 | ExitThread(system); | ||
| 1977 | } | ||
| 1978 | |||
| 1979 | /// Sleep the current thread | ||
| 1980 | static void SleepThread(Core::System& system, s64 nanoseconds) { | ||
| 1981 | auto& kernel = system.Kernel(); | ||
| 1982 | const auto yield_type = static_cast<Svc::YieldType>(nanoseconds); | ||
| 1983 | |||
| 1984 | LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); | ||
| 1985 | |||
| 1986 | // When the input tick is positive, sleep. | ||
| 1987 | if (nanoseconds > 0) { | ||
| 1988 | // Convert the timeout from nanoseconds to ticks. | ||
| 1989 | // NOTE: Nintendo does not use this conversion logic in WaitSynchronization... | ||
| 1990 | |||
| 1991 | // Sleep. | ||
| 1992 | // NOTE: Nintendo does not check the result of this sleep. | ||
| 1993 | static_cast<void>(GetCurrentThread(kernel).Sleep(nanoseconds)); | ||
| 1994 | } else if (yield_type == Svc::YieldType::WithoutCoreMigration) { | ||
| 1995 | KScheduler::YieldWithoutCoreMigration(kernel); | ||
| 1996 | } else if (yield_type == Svc::YieldType::WithCoreMigration) { | ||
| 1997 | KScheduler::YieldWithCoreMigration(kernel); | ||
| 1998 | } else if (yield_type == Svc::YieldType::ToAnyThread) { | ||
| 1999 | KScheduler::YieldToAnyThread(kernel); | ||
| 2000 | } else { | ||
| 2001 | // Nintendo does nothing at all if an otherwise invalid value is passed. | ||
| 2002 | ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds); | ||
| 2003 | } | ||
| 2004 | } | ||
| 2005 | |||
| 2006 | static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanoseconds_high) { | ||
| 2007 | const auto nanoseconds = static_cast<s64>(u64{nanoseconds_low} | (u64{nanoseconds_high} << 32)); | ||
| 2008 | SleepThread(system, nanoseconds); | ||
| 2009 | } | ||
| 2010 | |||
| 2011 | /// Wait process wide key atomic | ||
| 2012 | static Result WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, u32 tag, | ||
| 2013 | s64 timeout_ns) { | ||
| 2014 | LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address, | ||
| 2015 | cv_key, tag, timeout_ns); | ||
| 2016 | |||
| 2017 | // Validate input. | ||
| 2018 | if (IsKernelAddress(address)) { | ||
| 2019 | LOG_ERROR(Kernel_SVC, "Attempted to wait on kernel address (address={:08X})", address); | ||
| 2020 | return ResultInvalidCurrentMemory; | ||
| 2021 | } | ||
| 2022 | if (!Common::IsAligned(address, sizeof(s32))) { | ||
| 2023 | LOG_ERROR(Kernel_SVC, "Address must be 4 byte aligned (address={:08X})", address); | ||
| 2024 | return ResultInvalidAddress; | ||
| 2025 | } | ||
| 2026 | |||
| 2027 | // Convert timeout from nanoseconds to ticks. | ||
| 2028 | s64 timeout{}; | ||
| 2029 | if (timeout_ns > 0) { | ||
| 2030 | const s64 offset_tick(timeout_ns); | ||
| 2031 | if (offset_tick > 0) { | ||
| 2032 | timeout = offset_tick + 2; | ||
| 2033 | if (timeout <= 0) { | ||
| 2034 | timeout = std::numeric_limits<s64>::max(); | ||
| 2035 | } | ||
| 2036 | } else { | ||
| 2037 | timeout = std::numeric_limits<s64>::max(); | ||
| 2038 | } | ||
| 2039 | } else { | ||
| 2040 | timeout = timeout_ns; | ||
| 2041 | } | ||
| 2042 | |||
| 2043 | // Wait on the condition variable. | ||
| 2044 | return system.Kernel().CurrentProcess()->WaitConditionVariable( | ||
| 2045 | address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout); | ||
| 2046 | } | ||
| 2047 | |||
| 2048 | static Result WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag, | ||
| 2049 | u32 timeout_ns_low, u32 timeout_ns_high) { | ||
| 2050 | const auto timeout_ns = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32)); | ||
| 2051 | return WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns); | ||
| 2052 | } | ||
| 2053 | |||
| 2054 | /// Signal process wide key | ||
| 2055 | static void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count) { | ||
| 2056 | LOG_TRACE(Kernel_SVC, "called, cv_key=0x{:X}, count=0x{:08X}", cv_key, count); | ||
| 2057 | |||
| 2058 | // Signal the condition variable. | ||
| 2059 | return system.Kernel().CurrentProcess()->SignalConditionVariable( | ||
| 2060 | Common::AlignDown(cv_key, sizeof(u32)), count); | ||
| 2061 | } | ||
| 2062 | |||
| 2063 | static void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count) { | ||
| 2064 | SignalProcessWideKey(system, cv_key, count); | ||
| 2065 | } | ||
| 2066 | |||
| 2067 | namespace { | ||
| 2068 | |||
| 2069 | constexpr bool IsValidSignalType(Svc::SignalType type) { | ||
| 2070 | switch (type) { | ||
| 2071 | case Svc::SignalType::Signal: | ||
| 2072 | case Svc::SignalType::SignalAndIncrementIfEqual: | ||
| 2073 | case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual: | ||
| 2074 | return true; | ||
| 2075 | default: | ||
| 2076 | return false; | ||
| 2077 | } | ||
| 2078 | } | ||
| 2079 | |||
| 2080 | constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) { | ||
| 2081 | switch (type) { | ||
| 2082 | case Svc::ArbitrationType::WaitIfLessThan: | ||
| 2083 | case Svc::ArbitrationType::DecrementAndWaitIfLessThan: | ||
| 2084 | case Svc::ArbitrationType::WaitIfEqual: | ||
| 2085 | return true; | ||
| 2086 | default: | ||
| 2087 | return false; | ||
| 2088 | } | ||
| 2089 | } | ||
| 2090 | |||
| 2091 | } // namespace | ||
| 2092 | |||
| 2093 | // Wait for an address (via Address Arbiter) | ||
| 2094 | static Result WaitForAddress(Core::System& system, VAddr address, Svc::ArbitrationType arb_type, | ||
| 2095 | s32 value, s64 timeout_ns) { | ||
| 2096 | LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}", | ||
| 2097 | address, arb_type, value, timeout_ns); | ||
| 2098 | |||
| 2099 | // Validate input. | ||
| 2100 | if (IsKernelAddress(address)) { | ||
| 2101 | LOG_ERROR(Kernel_SVC, "Attempting to wait on kernel address (address={:08X})", address); | ||
| 2102 | return ResultInvalidCurrentMemory; | ||
| 2103 | } | ||
| 2104 | if (!Common::IsAligned(address, sizeof(s32))) { | ||
| 2105 | LOG_ERROR(Kernel_SVC, "Wait address must be 4 byte aligned (address={:08X})", address); | ||
| 2106 | return ResultInvalidAddress; | ||
| 2107 | } | ||
| 2108 | if (!IsValidArbitrationType(arb_type)) { | ||
| 2109 | LOG_ERROR(Kernel_SVC, "Invalid arbitration type specified (type={})", arb_type); | ||
| 2110 | return ResultInvalidEnumValue; | ||
| 2111 | } | ||
| 2112 | |||
| 2113 | // Convert timeout from nanoseconds to ticks. | ||
| 2114 | s64 timeout{}; | ||
| 2115 | if (timeout_ns > 0) { | ||
| 2116 | const s64 offset_tick(timeout_ns); | ||
| 2117 | if (offset_tick > 0) { | ||
| 2118 | timeout = offset_tick + 2; | ||
| 2119 | if (timeout <= 0) { | ||
| 2120 | timeout = std::numeric_limits<s64>::max(); | ||
| 2121 | } | ||
| 2122 | } else { | ||
| 2123 | timeout = std::numeric_limits<s64>::max(); | ||
| 2124 | } | ||
| 2125 | } else { | ||
| 2126 | timeout = timeout_ns; | ||
| 2127 | } | ||
| 2128 | |||
| 2129 | return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout); | ||
| 2130 | } | ||
| 2131 | |||
| 2132 | static Result WaitForAddress32(Core::System& system, u32 address, Svc::ArbitrationType arb_type, | ||
| 2133 | s32 value, u32 timeout_ns_low, u32 timeout_ns_high) { | ||
| 2134 | const auto timeout = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32)); | ||
| 2135 | return WaitForAddress(system, address, arb_type, value, timeout); | ||
| 2136 | } | ||
| 2137 | |||
| 2138 | // Signals to an address (via Address Arbiter) | ||
| 2139 | static Result SignalToAddress(Core::System& system, VAddr address, Svc::SignalType signal_type, | ||
| 2140 | s32 value, s32 count) { | ||
| 2141 | LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}", | ||
| 2142 | address, signal_type, value, count); | ||
| 2143 | |||
| 2144 | // Validate input. | ||
| 2145 | if (IsKernelAddress(address)) { | ||
| 2146 | LOG_ERROR(Kernel_SVC, "Attempting to signal to a kernel address (address={:08X})", address); | ||
| 2147 | return ResultInvalidCurrentMemory; | ||
| 2148 | } | ||
| 2149 | if (!Common::IsAligned(address, sizeof(s32))) { | ||
| 2150 | LOG_ERROR(Kernel_SVC, "Signaled address must be 4 byte aligned (address={:08X})", address); | ||
| 2151 | return ResultInvalidAddress; | ||
| 2152 | } | ||
| 2153 | if (!IsValidSignalType(signal_type)) { | ||
| 2154 | LOG_ERROR(Kernel_SVC, "Invalid signal type specified (type={})", signal_type); | ||
| 2155 | return ResultInvalidEnumValue; | ||
| 2156 | } | ||
| 2157 | |||
| 2158 | return system.Kernel().CurrentProcess()->SignalAddressArbiter(address, signal_type, value, | ||
| 2159 | count); | ||
| 2160 | } | ||
| 2161 | |||
| 2162 | static void SynchronizePreemptionState(Core::System& system) { | ||
| 2163 | auto& kernel = system.Kernel(); | ||
| 2164 | |||
| 2165 | // Lock the scheduler. | ||
| 2166 | KScopedSchedulerLock sl{kernel}; | ||
| 2167 | |||
| 2168 | // If the current thread is pinned, unpin it. | ||
| 2169 | KProcess* cur_process = system.Kernel().CurrentProcess(); | ||
| 2170 | const auto core_id = GetCurrentCoreId(kernel); | ||
| 2171 | |||
| 2172 | if (cur_process->GetPinnedThread(core_id) == GetCurrentThreadPointer(kernel)) { | ||
| 2173 | // Clear the current thread's interrupt flag. | ||
| 2174 | GetCurrentThread(kernel).ClearInterruptFlag(); | ||
| 2175 | |||
| 2176 | // Unpin the current thread. | ||
| 2177 | cur_process->UnpinCurrentThread(core_id); | ||
| 2178 | } | ||
| 2179 | } | ||
| 2180 | |||
| 2181 | static Result SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type, | ||
| 2182 | s32 value, s32 count) { | ||
| 2183 | return SignalToAddress(system, address, signal_type, value, count); | ||
| 2184 | } | ||
| 2185 | |||
| 2186 | static void KernelDebug([[maybe_unused]] Core::System& system, | ||
| 2187 | [[maybe_unused]] u32 kernel_debug_type, [[maybe_unused]] u64 param1, | ||
| 2188 | [[maybe_unused]] u64 param2, [[maybe_unused]] u64 param3) { | ||
| 2189 | // Intentionally do nothing, as this does nothing in released kernel binaries. | ||
| 2190 | } | ||
| 2191 | |||
| 2192 | static void ChangeKernelTraceState([[maybe_unused]] Core::System& system, | ||
| 2193 | [[maybe_unused]] u32 trace_state) { | ||
| 2194 | // Intentionally do nothing, as this does nothing in released kernel binaries. | ||
| 2195 | } | ||
| 2196 | |||
| 2197 | /// This returns the total CPU ticks elapsed since the CPU was powered-on | ||
| 2198 | static u64 GetSystemTick(Core::System& system) { | ||
| 2199 | LOG_TRACE(Kernel_SVC, "called"); | ||
| 2200 | |||
| 2201 | auto& core_timing = system.CoreTiming(); | ||
| 2202 | |||
| 2203 | // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick) | ||
| 2204 | const u64 result{core_timing.GetClockTicks()}; | ||
| 2205 | |||
| 2206 | if (!system.Kernel().IsMulticore()) { | ||
| 2207 | core_timing.AddTicks(400U); | ||
| 2208 | } | ||
| 2209 | |||
| 2210 | return result; | ||
| 2211 | } | ||
| 2212 | |||
| 2213 | static void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high) { | ||
| 2214 | const auto time = GetSystemTick(system); | ||
| 2215 | *time_low = static_cast<u32>(time); | ||
| 2216 | *time_high = static_cast<u32>(time >> 32); | ||
| 2217 | } | ||
| 2218 | |||
| 2219 | /// Close a handle | ||
| 2220 | static Result CloseHandle(Core::System& system, Handle handle) { | ||
| 2221 | LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); | ||
| 2222 | |||
| 2223 | // Remove the handle. | ||
| 2224 | R_UNLESS(system.Kernel().CurrentProcess()->GetHandleTable().Remove(handle), | ||
| 2225 | ResultInvalidHandle); | ||
| 2226 | |||
| 2227 | return ResultSuccess; | ||
| 2228 | } | ||
| 2229 | |||
| 2230 | static Result CloseHandle32(Core::System& system, Handle handle) { | ||
| 2231 | return CloseHandle(system, handle); | ||
| 2232 | } | ||
| 2233 | |||
| 2234 | /// Clears the signaled state of an event or process. | ||
| 2235 | static Result ResetSignal(Core::System& system, Handle handle) { | ||
| 2236 | LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle); | ||
| 2237 | |||
| 2238 | // Get the current handle table. | ||
| 2239 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 2240 | |||
| 2241 | // Try to reset as readable event. | ||
| 2242 | { | ||
| 2243 | KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(handle); | ||
| 2244 | if (readable_event.IsNotNull()) { | ||
| 2245 | return readable_event->Reset(); | ||
| 2246 | } | ||
| 2247 | } | ||
| 2248 | |||
| 2249 | // Try to reset as process. | ||
| 2250 | { | ||
| 2251 | KScopedAutoObject process = handle_table.GetObject<KProcess>(handle); | ||
| 2252 | if (process.IsNotNull()) { | ||
| 2253 | return process->Reset(); | ||
| 2254 | } | ||
| 2255 | } | ||
| 2256 | |||
| 2257 | LOG_ERROR(Kernel_SVC, "invalid handle (0x{:08X})", handle); | ||
| 2258 | |||
| 2259 | return ResultInvalidHandle; | ||
| 2260 | } | ||
| 2261 | |||
| 2262 | static Result ResetSignal32(Core::System& system, Handle handle) { | ||
| 2263 | return ResetSignal(system, handle); | ||
| 2264 | } | ||
| 2265 | |||
| 2266 | namespace { | ||
| 2267 | |||
| 2268 | constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) { | ||
| 2269 | switch (perm) { | ||
| 2270 | case MemoryPermission::None: | ||
| 2271 | case MemoryPermission::Read: | ||
| 2272 | case MemoryPermission::ReadWrite: | ||
| 2273 | return true; | ||
| 2274 | default: | ||
| 2275 | return false; | ||
| 2276 | } | ||
| 2277 | } | ||
| 2278 | |||
| 2279 | } // Anonymous namespace | ||
| 2280 | |||
| 2281 | /// Creates a TransferMemory object | ||
| 2282 | static Result CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size, | ||
| 2283 | MemoryPermission map_perm) { | ||
| 2284 | auto& kernel = system.Kernel(); | ||
| 2285 | |||
| 2286 | // Validate the size. | ||
| 2287 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 2288 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 2289 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 2290 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 2291 | |||
| 2292 | // Validate the permissions. | ||
| 2293 | R_UNLESS(IsValidTransferMemoryPermission(map_perm), ResultInvalidNewMemoryPermission); | ||
| 2294 | |||
| 2295 | // Get the current process and handle table. | ||
| 2296 | auto& process = *kernel.CurrentProcess(); | ||
| 2297 | auto& handle_table = process.GetHandleTable(); | ||
| 2298 | |||
| 2299 | // Reserve a new transfer memory from the process resource limit. | ||
| 2300 | KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(), | ||
| 2301 | LimitableResource::TransferMemoryCountMax); | ||
| 2302 | R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached); | ||
| 2303 | |||
| 2304 | // Create the transfer memory. | ||
| 2305 | KTransferMemory* trmem = KTransferMemory::Create(kernel); | ||
| 2306 | R_UNLESS(trmem != nullptr, ResultOutOfResource); | ||
| 2307 | |||
| 2308 | // Ensure the only reference is in the handle table when we're done. | ||
| 2309 | SCOPE_EXIT({ trmem->Close(); }); | ||
| 2310 | |||
| 2311 | // Ensure that the region is in range. | ||
| 2312 | R_UNLESS(process.PageTable().Contains(address, size), ResultInvalidCurrentMemory); | ||
| 2313 | |||
| 2314 | // Initialize the transfer memory. | ||
| 2315 | R_TRY(trmem->Initialize(address, size, map_perm)); | ||
| 2316 | |||
| 2317 | // Commit the reservation. | ||
| 2318 | trmem_reservation.Commit(); | ||
| 2319 | |||
| 2320 | // Register the transfer memory. | ||
| 2321 | KTransferMemory::Register(kernel, trmem); | ||
| 2322 | |||
| 2323 | // Add the transfer memory to the handle table. | ||
| 2324 | R_TRY(handle_table.Add(out, trmem)); | ||
| 2325 | |||
| 2326 | return ResultSuccess; | ||
| 2327 | } | ||
| 2328 | |||
| 2329 | static Result CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size, | ||
| 2330 | MemoryPermission map_perm) { | ||
| 2331 | return CreateTransferMemory(system, out, address, size, map_perm); | ||
| 2332 | } | ||
| 2333 | |||
| 2334 | static Result GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id, | ||
| 2335 | u64* out_affinity_mask) { | ||
| 2336 | LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); | ||
| 2337 | |||
| 2338 | // Get the thread from its handle. | ||
| 2339 | KScopedAutoObject thread = | ||
| 2340 | system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); | ||
| 2341 | R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); | ||
| 2342 | |||
| 2343 | // Get the core mask. | ||
| 2344 | R_TRY(thread->GetCoreMask(out_core_id, out_affinity_mask)); | ||
| 2345 | |||
| 2346 | return ResultSuccess; | ||
| 2347 | } | ||
| 2348 | |||
| 2349 | static Result GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id, | ||
| 2350 | u32* out_affinity_mask_low, u32* out_affinity_mask_high) { | ||
| 2351 | u64 out_affinity_mask{}; | ||
| 2352 | const auto result = GetThreadCoreMask(system, thread_handle, out_core_id, &out_affinity_mask); | ||
| 2353 | *out_affinity_mask_high = static_cast<u32>(out_affinity_mask >> 32); | ||
| 2354 | *out_affinity_mask_low = static_cast<u32>(out_affinity_mask); | ||
| 2355 | return result; | ||
| 2356 | } | ||
| 2357 | |||
| 2358 | static Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id, | ||
| 2359 | u64 affinity_mask) { | ||
| 2360 | // Determine the core id/affinity mask. | ||
| 2361 | if (core_id == IdealCoreUseProcessValue) { | ||
| 2362 | core_id = system.Kernel().CurrentProcess()->GetIdealCoreId(); | ||
| 2363 | affinity_mask = (1ULL << core_id); | ||
| 2364 | } else { | ||
| 2365 | // Validate the affinity mask. | ||
| 2366 | const u64 process_core_mask = system.Kernel().CurrentProcess()->GetCoreMask(); | ||
| 2367 | R_UNLESS((affinity_mask | process_core_mask) == process_core_mask, ResultInvalidCoreId); | ||
| 2368 | R_UNLESS(affinity_mask != 0, ResultInvalidCombination); | ||
| 2369 | |||
| 2370 | // Validate the core id. | ||
| 2371 | if (IsValidVirtualCoreId(core_id)) { | ||
| 2372 | R_UNLESS(((1ULL << core_id) & affinity_mask) != 0, ResultInvalidCombination); | ||
| 2373 | } else { | ||
| 2374 | R_UNLESS(core_id == IdealCoreNoUpdate || core_id == IdealCoreDontCare, | ||
| 2375 | ResultInvalidCoreId); | ||
| 2376 | } | ||
| 2377 | } | ||
| 2378 | |||
| 2379 | // Get the thread from its handle. | ||
| 2380 | KScopedAutoObject thread = | ||
| 2381 | system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); | ||
| 2382 | R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); | ||
| 2383 | |||
| 2384 | // Set the core mask. | ||
| 2385 | R_TRY(thread->SetCoreMask(core_id, affinity_mask)); | ||
| 2386 | |||
| 2387 | return ResultSuccess; | ||
| 2388 | } | ||
| 2389 | |||
| 2390 | static Result SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id, | ||
| 2391 | u32 affinity_mask_low, u32 affinity_mask_high) { | ||
| 2392 | const auto affinity_mask = u64{affinity_mask_low} | (u64{affinity_mask_high} << 32); | ||
| 2393 | return SetThreadCoreMask(system, thread_handle, core_id, affinity_mask); | ||
| 2394 | } | ||
| 2395 | |||
| 2396 | static Result SignalEvent(Core::System& system, Handle event_handle) { | ||
| 2397 | LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle); | ||
| 2398 | |||
| 2399 | // Get the current handle table. | ||
| 2400 | const KHandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 2401 | |||
| 2402 | // Get the event. | ||
| 2403 | KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle); | ||
| 2404 | R_UNLESS(event.IsNotNull(), ResultInvalidHandle); | ||
| 2405 | |||
| 2406 | return event->Signal(); | ||
| 2407 | } | ||
| 2408 | |||
| 2409 | static Result SignalEvent32(Core::System& system, Handle event_handle) { | ||
| 2410 | return SignalEvent(system, event_handle); | ||
| 2411 | } | ||
| 2412 | |||
| 2413 | static Result ClearEvent(Core::System& system, Handle event_handle) { | ||
| 2414 | LOG_TRACE(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle); | ||
| 2415 | |||
| 2416 | // Get the current handle table. | ||
| 2417 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 2418 | |||
| 2419 | // Try to clear the writable event. | ||
| 2420 | { | ||
| 2421 | KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle); | ||
| 2422 | if (event.IsNotNull()) { | ||
| 2423 | return event->Clear(); | ||
| 2424 | } | ||
| 2425 | } | ||
| 2426 | |||
| 2427 | // Try to clear the readable event. | ||
| 2428 | { | ||
| 2429 | KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(event_handle); | ||
| 2430 | if (readable_event.IsNotNull()) { | ||
| 2431 | return readable_event->Clear(); | ||
| 2432 | } | ||
| 2433 | } | ||
| 2434 | |||
| 2435 | LOG_ERROR(Kernel_SVC, "Event handle does not exist, event_handle=0x{:08X}", event_handle); | ||
| 2436 | |||
| 2437 | return ResultInvalidHandle; | ||
| 2438 | } | ||
| 2439 | |||
| 2440 | static Result ClearEvent32(Core::System& system, Handle event_handle) { | ||
| 2441 | return ClearEvent(system, event_handle); | ||
| 2442 | } | ||
| 2443 | |||
| 2444 | static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) { | ||
| 2445 | LOG_DEBUG(Kernel_SVC, "called"); | ||
| 2446 | |||
| 2447 | // Get the kernel reference and handle table. | ||
| 2448 | auto& kernel = system.Kernel(); | ||
| 2449 | auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); | ||
| 2450 | |||
| 2451 | // Reserve a new event from the process resource limit | ||
| 2452 | KScopedResourceReservation event_reservation(kernel.CurrentProcess(), | ||
| 2453 | LimitableResource::EventCountMax); | ||
| 2454 | R_UNLESS(event_reservation.Succeeded(), ResultLimitReached); | ||
| 2455 | |||
| 2456 | // Create a new event. | ||
| 2457 | KEvent* event = KEvent::Create(kernel); | ||
| 2458 | R_UNLESS(event != nullptr, ResultOutOfResource); | ||
| 2459 | |||
| 2460 | // Initialize the event. | ||
| 2461 | event->Initialize(kernel.CurrentProcess()); | ||
| 2462 | |||
| 2463 | // Commit the thread reservation. | ||
| 2464 | event_reservation.Commit(); | ||
| 2465 | |||
| 2466 | // Ensure that we clean up the event (and its only references are handle table) on function end. | ||
| 2467 | SCOPE_EXIT({ | ||
| 2468 | event->GetReadableEvent().Close(); | ||
| 2469 | event->Close(); | ||
| 2470 | }); | ||
| 2471 | |||
| 2472 | // Register the event. | ||
| 2473 | KEvent::Register(kernel, event); | ||
| 2474 | |||
| 2475 | // Add the event to the handle table. | ||
| 2476 | R_TRY(handle_table.Add(out_write, event)); | ||
| 2477 | |||
| 2478 | // Ensure that we maintaing a clean handle state on exit. | ||
| 2479 | auto handle_guard = SCOPE_GUARD({ handle_table.Remove(*out_write); }); | ||
| 2480 | |||
| 2481 | // Add the readable event to the handle table. | ||
| 2482 | R_TRY(handle_table.Add(out_read, std::addressof(event->GetReadableEvent()))); | ||
| 2483 | |||
| 2484 | // We succeeded. | ||
| 2485 | handle_guard.Cancel(); | ||
| 2486 | return ResultSuccess; | ||
| 2487 | } | ||
| 2488 | |||
| 2489 | static Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read) { | ||
| 2490 | return CreateEvent(system, out_write, out_read); | ||
| 2491 | } | ||
| 2492 | |||
| 2493 | static Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { | ||
| 2494 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); | ||
| 2495 | |||
| 2496 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 2497 | KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); | ||
| 2498 | if (process.IsNull()) { | ||
| 2499 | LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", | ||
| 2500 | process_handle); | ||
| 2501 | return ResultInvalidHandle; | ||
| 2502 | } | ||
| 2503 | |||
| 2504 | const auto info_type = static_cast<ProcessInfoType>(type); | ||
| 2505 | if (info_type != ProcessInfoType::ProcessState) { | ||
| 2506 | LOG_ERROR(Kernel_SVC, "Expected info_type to be ProcessState but got {} instead", type); | ||
| 2507 | return ResultInvalidEnumValue; | ||
| 2508 | } | ||
| 2509 | |||
| 2510 | *out = static_cast<u64>(process->GetState()); | ||
| 2511 | return ResultSuccess; | ||
| 2512 | } | ||
| 2513 | |||
| 2514 | static Result CreateResourceLimit(Core::System& system, Handle* out_handle) { | ||
| 2515 | LOG_DEBUG(Kernel_SVC, "called"); | ||
| 2516 | |||
| 2517 | // Create a new resource limit. | ||
| 2518 | auto& kernel = system.Kernel(); | ||
| 2519 | KResourceLimit* resource_limit = KResourceLimit::Create(kernel); | ||
| 2520 | R_UNLESS(resource_limit != nullptr, ResultOutOfResource); | ||
| 2521 | |||
| 2522 | // Ensure we don't leak a reference to the limit. | ||
| 2523 | SCOPE_EXIT({ resource_limit->Close(); }); | ||
| 2524 | |||
| 2525 | // Initialize the resource limit. | ||
| 2526 | resource_limit->Initialize(&system.CoreTiming()); | ||
| 2527 | |||
| 2528 | // Register the limit. | ||
| 2529 | KResourceLimit::Register(kernel, resource_limit); | ||
| 2530 | |||
| 2531 | // Add the limit to the handle table. | ||
| 2532 | R_TRY(kernel.CurrentProcess()->GetHandleTable().Add(out_handle, resource_limit)); | ||
| 2533 | |||
| 2534 | return ResultSuccess; | ||
| 2535 | } | ||
| 2536 | |||
| 2537 | static Result GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value, | ||
| 2538 | Handle resource_limit_handle, LimitableResource which) { | ||
| 2539 | LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle, | ||
| 2540 | which); | ||
| 2541 | |||
| 2542 | // Validate the resource. | ||
| 2543 | R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue); | ||
| 2544 | |||
| 2545 | // Get the resource limit. | ||
| 2546 | auto& kernel = system.Kernel(); | ||
| 2547 | KScopedAutoObject resource_limit = | ||
| 2548 | kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle); | ||
| 2549 | R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle); | ||
| 2550 | |||
| 2551 | // Get the limit value. | ||
| 2552 | *out_limit_value = resource_limit->GetLimitValue(which); | ||
| 2553 | |||
| 2554 | return ResultSuccess; | ||
| 2555 | } | ||
| 2556 | |||
| 2557 | static Result GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value, | ||
| 2558 | Handle resource_limit_handle, LimitableResource which) { | ||
| 2559 | LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle, | ||
| 2560 | which); | ||
| 2561 | |||
| 2562 | // Validate the resource. | ||
| 2563 | R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue); | ||
| 2564 | |||
| 2565 | // Get the resource limit. | ||
| 2566 | auto& kernel = system.Kernel(); | ||
| 2567 | KScopedAutoObject resource_limit = | ||
| 2568 | kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle); | ||
| 2569 | R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle); | ||
| 2570 | |||
| 2571 | // Get the current value. | ||
| 2572 | *out_current_value = resource_limit->GetCurrentValue(which); | ||
| 2573 | |||
| 2574 | return ResultSuccess; | ||
| 2575 | } | ||
| 2576 | |||
| 2577 | static Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle, | ||
| 2578 | LimitableResource which, u64 limit_value) { | ||
| 2579 | LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}, limit_value={}", | ||
| 2580 | resource_limit_handle, which, limit_value); | ||
| 2581 | |||
| 2582 | // Validate the resource. | ||
| 2583 | R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue); | ||
| 2584 | |||
| 2585 | // Get the resource limit. | ||
| 2586 | auto& kernel = system.Kernel(); | ||
| 2587 | KScopedAutoObject resource_limit = | ||
| 2588 | kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle); | ||
| 2589 | R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle); | ||
| 2590 | |||
| 2591 | // Set the limit value. | ||
| 2592 | R_TRY(resource_limit->SetLimitValue(which, limit_value)); | ||
| 2593 | |||
| 2594 | return ResultSuccess; | ||
| 2595 | } | ||
| 2596 | |||
| 2597 | static Result GetProcessList(Core::System& system, u32* out_num_processes, VAddr out_process_ids, | ||
| 2598 | u32 out_process_ids_size) { | ||
| 2599 | LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}", | ||
| 2600 | out_process_ids, out_process_ids_size); | ||
| 2601 | |||
| 2602 | // If the supplied size is negative or greater than INT32_MAX / sizeof(u64), bail. | ||
| 2603 | if ((out_process_ids_size & 0xF0000000) != 0) { | ||
| 2604 | LOG_ERROR(Kernel_SVC, | ||
| 2605 | "Supplied size outside [0, 0x0FFFFFFF] range. out_process_ids_size={}", | ||
| 2606 | out_process_ids_size); | ||
| 2607 | return ResultOutOfRange; | ||
| 2608 | } | ||
| 2609 | |||
| 2610 | const auto& kernel = system.Kernel(); | ||
| 2611 | const auto total_copy_size = out_process_ids_size * sizeof(u64); | ||
| 2612 | |||
| 2613 | if (out_process_ids_size > 0 && !kernel.CurrentProcess()->PageTable().IsInsideAddressSpace( | ||
| 2614 | out_process_ids, total_copy_size)) { | ||
| 2615 | LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}", | ||
| 2616 | out_process_ids, out_process_ids + total_copy_size); | ||
| 2617 | return ResultInvalidCurrentMemory; | ||
| 2618 | } | ||
| 2619 | |||
| 2620 | auto& memory = system.Memory(); | ||
| 2621 | const auto& process_list = kernel.GetProcessList(); | ||
| 2622 | const auto num_processes = process_list.size(); | ||
| 2623 | const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes); | ||
| 2624 | |||
| 2625 | for (std::size_t i = 0; i < copy_amount; ++i) { | ||
| 2626 | memory.Write64(out_process_ids, process_list[i]->GetProcessID()); | ||
| 2627 | out_process_ids += sizeof(u64); | ||
| 2628 | } | ||
| 2629 | |||
| 2630 | *out_num_processes = static_cast<u32>(num_processes); | ||
| 2631 | return ResultSuccess; | ||
| 2632 | } | ||
| 2633 | |||
| 2634 | static Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids, | ||
| 2635 | u32 out_thread_ids_size, Handle debug_handle) { | ||
| 2636 | // TODO: Handle this case when debug events are supported. | ||
| 2637 | UNIMPLEMENTED_IF(debug_handle != InvalidHandle); | ||
| 2638 | |||
| 2639 | LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{:016X}, out_thread_ids_size={}", | ||
| 2640 | out_thread_ids, out_thread_ids_size); | ||
| 2641 | |||
| 2642 | // If the size is negative or larger than INT32_MAX / sizeof(u64) | ||
| 2643 | if ((out_thread_ids_size & 0xF0000000) != 0) { | ||
| 2644 | LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}", | ||
| 2645 | out_thread_ids_size); | ||
| 2646 | return ResultOutOfRange; | ||
| 2647 | } | ||
| 2648 | |||
| 2649 | auto* const current_process = system.Kernel().CurrentProcess(); | ||
| 2650 | const auto total_copy_size = out_thread_ids_size * sizeof(u64); | ||
| 2651 | |||
| 2652 | if (out_thread_ids_size > 0 && | ||
| 2653 | !current_process->PageTable().IsInsideAddressSpace(out_thread_ids, total_copy_size)) { | ||
| 2654 | LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}", | ||
| 2655 | out_thread_ids, out_thread_ids + total_copy_size); | ||
| 2656 | return ResultInvalidCurrentMemory; | ||
| 2657 | } | ||
| 2658 | |||
| 2659 | auto& memory = system.Memory(); | ||
| 2660 | const auto& thread_list = current_process->GetThreadList(); | ||
| 2661 | const auto num_threads = thread_list.size(); | ||
| 2662 | const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads); | ||
| 2663 | |||
| 2664 | auto list_iter = thread_list.cbegin(); | ||
| 2665 | for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) { | ||
| 2666 | memory.Write64(out_thread_ids, (*list_iter)->GetThreadID()); | ||
| 2667 | out_thread_ids += sizeof(u64); | ||
| 2668 | } | ||
| 2669 | |||
| 2670 | *out_num_threads = static_cast<u32>(num_threads); | ||
| 2671 | return ResultSuccess; | ||
| 2672 | } | ||
| 2673 | |||
| 2674 | static Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address, | ||
| 2675 | u64 size) { | ||
| 2676 | // Validate address/size. | ||
| 2677 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 2678 | R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory); | ||
| 2679 | R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory); | ||
| 2680 | |||
| 2681 | // Get the process from its handle. | ||
| 2682 | KScopedAutoObject process = | ||
| 2683 | system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle); | ||
| 2684 | R_UNLESS(process.IsNotNull(), ResultInvalidHandle); | ||
| 2685 | |||
| 2686 | // Verify the region is within range. | ||
| 2687 | auto& page_table = process->PageTable(); | ||
| 2688 | R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); | ||
| 2689 | |||
| 2690 | // Perform the operation. | ||
| 2691 | R_RETURN(system.Memory().FlushDataCache(*process, address, size)); | ||
| 2692 | } | ||
| 2693 | |||
| 2694 | namespace { | ||
| 2695 | struct FunctionDef { | 14 | struct FunctionDef { |
| 2696 | using Func = void(Core::System&); | 15 | using Func = void(Core::System&); |
| 2697 | 16 | ||
| @@ -2699,6 +18,7 @@ struct FunctionDef { | |||
| 2699 | Func* func; | 18 | Func* func; |
| 2700 | const char* name; | 19 | const char* name; |
| 2701 | }; | 20 | }; |
| 21 | |||
| 2702 | } // namespace | 22 | } // namespace |
| 2703 | 23 | ||
| 2704 | static const FunctionDef SVC_Table_32[] = { | 24 | static const FunctionDef SVC_Table_32[] = { |
diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h index 13f061b83..b599f9a3d 100644 --- a/src/core/hle/kernel/svc.h +++ b/src/core/hle/kernel/svc.h | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "core/hle/kernel/svc_types.h" | ||
| 8 | #include "core/hle/result.h" | ||
| 7 | 9 | ||
| 8 | namespace Core { | 10 | namespace Core { |
| 9 | class System; | 11 | class System; |
| @@ -13,4 +15,158 @@ namespace Kernel::Svc { | |||
| 13 | 15 | ||
| 14 | void Call(Core::System& system, u32 immediate); | 16 | void Call(Core::System& system, u32 immediate); |
| 15 | 17 | ||
| 18 | Result SetHeapSize(Core::System& system, VAddr* out_address, u64 size); | ||
| 19 | Result SetMemoryPermission(Core::System& system, VAddr address, u64 size, MemoryPermission perm); | ||
| 20 | Result SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, u32 attr); | ||
| 21 | Result MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size); | ||
| 22 | Result UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size); | ||
| 23 | Result QueryMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address, | ||
| 24 | VAddr query_address); | ||
| 25 | void ExitProcess(Core::System& system); | ||
| 26 | Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, | ||
| 27 | VAddr stack_bottom, u32 priority, s32 core_id); | ||
| 28 | Result StartThread(Core::System& system, Handle thread_handle); | ||
| 29 | void ExitThread(Core::System& system); | ||
| 30 | void SleepThread(Core::System& system, s64 nanoseconds); | ||
| 31 | Result GetThreadPriority(Core::System& system, u32* out_priority, Handle handle); | ||
| 32 | Result SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority); | ||
| 33 | Result GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id, | ||
| 34 | u64* out_affinity_mask); | ||
| 35 | Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id, | ||
| 36 | u64 affinity_mask); | ||
| 37 | u32 GetCurrentProcessorNumber(Core::System& system); | ||
| 38 | Result SignalEvent(Core::System& system, Handle event_handle); | ||
| 39 | Result ClearEvent(Core::System& system, Handle event_handle); | ||
| 40 | Result MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size, | ||
| 41 | MemoryPermission map_perm); | ||
| 42 | Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size); | ||
| 43 | Result CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size, | ||
| 44 | MemoryPermission map_perm); | ||
| 45 | Result CloseHandle(Core::System& system, Handle handle); | ||
| 46 | Result ResetSignal(Core::System& system, Handle handle); | ||
| 47 | Result WaitSynchronization(Core::System& system, s32* index, VAddr handles_address, s32 num_handles, | ||
| 48 | s64 nano_seconds); | ||
| 49 | Result CancelSynchronization(Core::System& system, Handle handle); | ||
| 50 | Result ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, u32 tag); | ||
| 51 | Result ArbitrateUnlock(Core::System& system, VAddr address); | ||
| 52 | Result WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, u32 tag, | ||
| 53 | s64 timeout_ns); | ||
| 54 | void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count); | ||
| 55 | u64 GetSystemTick(Core::System& system); | ||
| 56 | Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address); | ||
| 57 | Result SendSyncRequest(Core::System& system, Handle handle); | ||
| 58 | Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle); | ||
| 59 | Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle); | ||
| 60 | void Break(Core::System& system, u32 reason, u64 info1, u64 info2); | ||
| 61 | void OutputDebugString(Core::System& system, VAddr address, u64 len); | ||
| 62 | Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle, u64 info_sub_id); | ||
| 63 | Result MapPhysicalMemory(Core::System& system, VAddr addr, u64 size); | ||
| 64 | Result UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size); | ||
| 65 | Result GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value, | ||
| 66 | Handle resource_limit_handle, LimitableResource which); | ||
| 67 | Result GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value, | ||
| 68 | Handle resource_limit_handle, LimitableResource which); | ||
| 69 | Result SetThreadActivity(Core::System& system, Handle thread_handle, | ||
| 70 | ThreadActivity thread_activity); | ||
| 71 | Result GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle); | ||
| 72 | Result WaitForAddress(Core::System& system, VAddr address, ArbitrationType arb_type, s32 value, | ||
| 73 | s64 timeout_ns); | ||
| 74 | Result SignalToAddress(Core::System& system, VAddr address, SignalType signal_type, s32 value, | ||
| 75 | s32 count); | ||
| 76 | void SynchronizePreemptionState(Core::System& system); | ||
| 77 | void KernelDebug(Core::System& system, u32 kernel_debug_type, u64 param1, u64 param2, u64 param3); | ||
| 78 | void ChangeKernelTraceState(Core::System& system, u32 trace_state); | ||
| 79 | Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u32 is_light, | ||
| 80 | u64 name); | ||
| 81 | Result ReplyAndReceive(Core::System& system, s32* out_index, Handle* handles, s32 num_handles, | ||
| 82 | Handle reply_target, s64 timeout_ns); | ||
| 83 | Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read); | ||
| 84 | Result CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size); | ||
| 85 | Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation, | ||
| 86 | VAddr address, size_t size, MemoryPermission perm); | ||
| 87 | Result GetProcessList(Core::System& system, u32* out_num_processes, VAddr out_process_ids, | ||
| 88 | u32 out_process_ids_size); | ||
| 89 | Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids, | ||
| 90 | u32 out_thread_ids_size, Handle debug_handle); | ||
| 91 | Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, VAddr address, | ||
| 92 | u64 size, MemoryPermission perm); | ||
| 93 | Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, | ||
| 94 | VAddr src_address, u64 size); | ||
| 95 | Result UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, | ||
| 96 | VAddr src_address, u64 size); | ||
| 97 | Result QueryProcessMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address, | ||
| 98 | Handle process_handle, VAddr address); | ||
| 99 | Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, | ||
| 100 | u64 src_address, u64 size); | ||
| 101 | Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, | ||
| 102 | u64 src_address, u64 size); | ||
| 103 | Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type); | ||
| 104 | Result CreateResourceLimit(Core::System& system, Handle* out_handle); | ||
| 105 | Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle, | ||
| 106 | LimitableResource which, u64 limit_value); | ||
| 107 | |||
| 108 | // | ||
| 109 | |||
| 110 | Result SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size); | ||
| 111 | Result SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask, u32 attr); | ||
| 112 | Result MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size); | ||
| 113 | Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size); | ||
| 114 | Result QueryMemory32(Core::System& system, u32 memory_info_address, u32 page_info_address, | ||
| 115 | u32 query_address); | ||
| 116 | void ExitProcess32(Core::System& system); | ||
| 117 | Result CreateThread32(Core::System& system, Handle* out_handle, u32 priority, u32 entry_point, | ||
| 118 | u32 arg, u32 stack_top, s32 processor_id); | ||
| 119 | Result StartThread32(Core::System& system, Handle thread_handle); | ||
| 120 | void ExitThread32(Core::System& system); | ||
| 121 | void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanoseconds_high); | ||
| 122 | Result GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle); | ||
| 123 | Result SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority); | ||
| 124 | Result GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id, | ||
| 125 | u32* out_affinity_mask_low, u32* out_affinity_mask_high); | ||
| 126 | Result SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id, | ||
| 127 | u32 affinity_mask_low, u32 affinity_mask_high); | ||
| 128 | u32 GetCurrentProcessorNumber32(Core::System& system); | ||
| 129 | Result SignalEvent32(Core::System& system, Handle event_handle); | ||
| 130 | Result ClearEvent32(Core::System& system, Handle event_handle); | ||
| 131 | Result MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size, | ||
| 132 | MemoryPermission map_perm); | ||
| 133 | Result UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size); | ||
| 134 | Result CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size, | ||
| 135 | MemoryPermission map_perm); | ||
| 136 | Result CloseHandle32(Core::System& system, Handle handle); | ||
| 137 | Result ResetSignal32(Core::System& system, Handle handle); | ||
| 138 | Result WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address, | ||
| 139 | s32 num_handles, u32 timeout_high, s32* index); | ||
| 140 | Result CancelSynchronization32(Core::System& system, Handle handle); | ||
| 141 | Result ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, u32 tag); | ||
| 142 | Result ArbitrateUnlock32(Core::System& system, u32 address); | ||
| 143 | Result WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag, | ||
| 144 | u32 timeout_ns_low, u32 timeout_ns_high); | ||
| 145 | void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count); | ||
| 146 | void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high); | ||
| 147 | Result ConnectToNamedPort32(Core::System& system, Handle* out_handle, u32 port_name_address); | ||
| 148 | Result SendSyncRequest32(Core::System& system, Handle handle); | ||
| 149 | Result GetProcessId32(Core::System& system, u32* out_process_id_low, u32* out_process_id_high, | ||
| 150 | Handle handle); | ||
| 151 | Result GetThreadId32(Core::System& system, u32* out_thread_id_low, u32* out_thread_id_high, | ||
| 152 | Handle thread_handle); | ||
| 153 | void Break32(Core::System& system, u32 reason, u32 info1, u32 info2); | ||
| 154 | void OutputDebugString32(Core::System& system, u32 address, u32 len); | ||
| 155 | Result GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low, | ||
| 156 | u32 info_id, u32 handle, u32 sub_id_high); | ||
| 157 | Result MapPhysicalMemory32(Core::System& system, u32 addr, u32 size); | ||
| 158 | Result UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size); | ||
| 159 | Result SetThreadActivity32(Core::System& system, Handle thread_handle, | ||
| 160 | ThreadActivity thread_activity); | ||
| 161 | Result GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle); | ||
| 162 | Result WaitForAddress32(Core::System& system, u32 address, ArbitrationType arb_type, s32 value, | ||
| 163 | u32 timeout_ns_low, u32 timeout_ns_high); | ||
| 164 | Result SignalToAddress32(Core::System& system, u32 address, SignalType signal_type, s32 value, | ||
| 165 | s32 count); | ||
| 166 | Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read); | ||
| 167 | Result CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size); | ||
| 168 | Result ControlCodeMemory32(Core::System& system, Handle code_memory_handle, u32 operation, | ||
| 169 | u64 address, u64 size, MemoryPermission perm); | ||
| 170 | Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address, u64 size); | ||
| 171 | |||
| 16 | } // namespace Kernel::Svc | 172 | } // namespace Kernel::Svc |
diff --git a/src/core/hle/kernel/svc/svc_activity.cpp b/src/core/hle/kernel/svc/svc_activity.cpp new file mode 100644 index 000000000..8774a5c98 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_activity.cpp | |||
| @@ -0,0 +1,44 @@ | |||
| 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 | ||
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 1ea8c7fbc..052be40dd 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h | |||
| @@ -172,11 +172,11 @@ void SvcWrap64(Core::System& system) { | |||
| 172 | } | 172 | } |
| 173 | 173 | ||
| 174 | // Used by GetResourceLimitLimitValue. | 174 | // Used by GetResourceLimitLimitValue. |
| 175 | template <Result func(Core::System&, u64*, Handle, LimitableResource)> | 175 | template <Result func(Core::System&, u64*, Handle, Svc::LimitableResource)> |
| 176 | void SvcWrap64(Core::System& system) { | 176 | void SvcWrap64(Core::System& system) { |
| 177 | u64 param_1 = 0; | 177 | u64 param_1 = 0; |
| 178 | const u32 retval = func(system, ¶m_1, static_cast<Handle>(Param(system, 1)), | 178 | const u32 retval = func(system, ¶m_1, static_cast<Handle>(Param(system, 1)), |
| 179 | static_cast<LimitableResource>(Param(system, 2))) | 179 | static_cast<Svc::LimitableResource>(Param(system, 2))) |
| 180 | .raw; | 180 | .raw; |
| 181 | 181 | ||
| 182 | system.CurrentArmInterface().SetReg(1, param_1); | 182 | system.CurrentArmInterface().SetReg(1, param_1); |
| @@ -189,10 +189,10 @@ void SvcWrap64(Core::System& system) { | |||
| 189 | } | 189 | } |
| 190 | 190 | ||
| 191 | // Used by SetResourceLimitLimitValue | 191 | // Used by SetResourceLimitLimitValue |
| 192 | template <Result func(Core::System&, Handle, LimitableResource, u64)> | 192 | template <Result func(Core::System&, Handle, Svc::LimitableResource, u64)> |
| 193 | void SvcWrap64(Core::System& system) { | 193 | void SvcWrap64(Core::System& system) { |
| 194 | FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)), | 194 | FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)), |
| 195 | static_cast<LimitableResource>(Param(system, 1)), Param(system, 2)) | 195 | static_cast<Svc::LimitableResource>(Param(system, 1)), Param(system, 2)) |
| 196 | .raw); | 196 | .raw); |
| 197 | } | 197 | } |
| 198 | 198 | ||