diff options
| author | 2018-01-02 20:40:30 -0500 | |
|---|---|---|
| committer | 2018-01-02 20:40:30 -0500 | |
| commit | 480906fe1b31a8830aec80fbea04ec941894003f (patch) | |
| tree | cef091503185cde98d5a8a6cd64bda83d6b8ff38 /src/core/hle/kernel/svc.cpp | |
| parent | svc: Improve svcGetInfo. (diff) | |
| download | yuzu-480906fe1b31a8830aec80fbea04ec941894003f.tar.gz yuzu-480906fe1b31a8830aec80fbea04ec941894003f.tar.xz yuzu-480906fe1b31a8830aec80fbea04ec941894003f.zip | |
hle: Move SVC code to kernel namespace.
Diffstat (limited to 'src/core/hle/kernel/svc.cpp')
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 612 |
1 files changed, 612 insertions, 0 deletions
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp new file mode 100644 index 000000000..3dae8b38b --- /dev/null +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -0,0 +1,612 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "common/microprofile.h" | ||
| 7 | #include "core/core_timing.h" | ||
| 8 | #include "core/hle/kernel/client_port.h" | ||
| 9 | #include "core/hle/kernel/client_session.h" | ||
| 10 | #include "core/hle/kernel/handle_table.h" | ||
| 11 | #include "core/hle/kernel/mutex.h" | ||
| 12 | #include "core/hle/kernel/object_address_table.h" | ||
| 13 | #include "core/hle/kernel/process.h" | ||
| 14 | #include "core/hle/kernel/resource_limit.h" | ||
| 15 | #include "core/hle/kernel/svc.h" | ||
| 16 | #include "core/hle/kernel/svc_wrap.h" | ||
| 17 | #include "core/hle/kernel/sync_object.h" | ||
| 18 | #include "core/hle/kernel/thread.h" | ||
| 19 | #include "core/hle/lock.h" | ||
| 20 | #include "core/hle/result.h" | ||
| 21 | #include "core/hle/service/service.h" | ||
| 22 | |||
| 23 | namespace Kernel { | ||
| 24 | |||
| 25 | /// Set the process heap to a given Size. It can both extend and shrink the heap. | ||
| 26 | static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { | ||
| 27 | LOG_TRACE(Kernel_SVC, "called, heap_size=0x%llx", heap_size); | ||
| 28 | auto& process = *g_current_process; | ||
| 29 | CASCADE_RESULT(*heap_addr, process.HeapAllocate(Memory::HEAP_VADDR, heap_size, | ||
| 30 | VMAPermission::ReadWrite)); | ||
| 31 | return RESULT_SUCCESS; | ||
| 32 | } | ||
| 33 | |||
| 34 | /// Maps a memory range into a different range. | ||
| 35 | static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | ||
| 36 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x%llx, src_addr=0x%llx, size=0x%llx", dst_addr, | ||
| 37 | src_addr, size); | ||
| 38 | return g_current_process->MirrorMemory(dst_addr, src_addr, size); | ||
| 39 | } | ||
| 40 | |||
| 41 | /// Unmaps a region that was previously mapped with svcMapMemory | ||
| 42 | static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | ||
| 43 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x%llx, src_addr=0x%llx, size=0x%llx", dst_addr, | ||
| 44 | src_addr, size); | ||
| 45 | return g_current_process->UnmapMemory(dst_addr, src_addr, size); | ||
| 46 | } | ||
| 47 | |||
| 48 | /// Connect to an OS service given the port name, returns the handle to the port to out | ||
| 49 | static ResultCode ConnectToPort(Handle* out_handle, VAddr port_name_address) { | ||
| 50 | if (!Memory::IsValidVirtualAddress(port_name_address)) | ||
| 51 | return ERR_NOT_FOUND; | ||
| 52 | |||
| 53 | static constexpr std::size_t PortNameMaxLength = 11; | ||
| 54 | // Read 1 char beyond the max allowed port name to detect names that are too long. | ||
| 55 | std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1); | ||
| 56 | if (port_name.size() > PortNameMaxLength) | ||
| 57 | return ERR_PORT_NAME_TOO_LONG; | ||
| 58 | |||
| 59 | LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name.c_str()); | ||
| 60 | |||
| 61 | auto it = Service::g_kernel_named_ports.find(port_name); | ||
| 62 | if (it == Service::g_kernel_named_ports.end()) { | ||
| 63 | LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: %s", port_name.c_str()); | ||
| 64 | return ERR_NOT_FOUND; | ||
| 65 | } | ||
| 66 | |||
| 67 | auto client_port = it->second; | ||
| 68 | |||
| 69 | SharedPtr<ClientSession> client_session; | ||
| 70 | CASCADE_RESULT(client_session, client_port->Connect()); | ||
| 71 | |||
| 72 | // Return the client session | ||
| 73 | CASCADE_RESULT(*out_handle, g_handle_table.Create(client_session)); | ||
| 74 | return RESULT_SUCCESS; | ||
| 75 | } | ||
| 76 | |||
| 77 | /// Makes a blocking IPC call to an OS service. | ||
| 78 | static ResultCode SendSyncRequest(Handle handle) { | ||
| 79 | SharedPtr<SyncObject> session = g_handle_table.Get<SyncObject>(handle); | ||
| 80 | if (!session) { | ||
| 81 | LOG_ERROR(Kernel_SVC, "called with invalid handle=0x%08X", handle); | ||
| 82 | return ERR_INVALID_HANDLE; | ||
| 83 | } | ||
| 84 | |||
| 85 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str()); | ||
| 86 | |||
| 87 | Core::System::GetInstance().PrepareReschedule(); | ||
| 88 | |||
| 89 | // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server | ||
| 90 | // responds and cause a reschedule. | ||
| 91 | return session->SendSyncRequest(GetCurrentThread()); | ||
| 92 | } | ||
| 93 | |||
| 94 | /// Get the ID for the specified thread. | ||
| 95 | static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) { | ||
| 96 | LOG_TRACE(Kernel_SVC, "called thread=0x%08X", thread_handle); | ||
| 97 | |||
| 98 | const SharedPtr<Thread> thread = | ||
| 99 | g_handle_table.Get<Thread>(thread_handle); | ||
| 100 | if (!thread) { | ||
| 101 | return ERR_INVALID_HANDLE; | ||
| 102 | } | ||
| 103 | |||
| 104 | *thread_id = thread->GetThreadId(); | ||
| 105 | return RESULT_SUCCESS; | ||
| 106 | } | ||
| 107 | |||
| 108 | /// Get the ID of the specified process | ||
| 109 | static ResultCode GetProcessId(u32* process_id, Handle process_handle) { | ||
| 110 | LOG_TRACE(Kernel_SVC, "called process=0x%08X", process_handle); | ||
| 111 | |||
| 112 | const SharedPtr<Process> process = | ||
| 113 | g_handle_table.Get<Process>(process_handle); | ||
| 114 | if (!process) { | ||
| 115 | return ERR_INVALID_HANDLE; | ||
| 116 | } | ||
| 117 | |||
| 118 | *process_id = process->process_id; | ||
| 119 | return RESULT_SUCCESS; | ||
| 120 | } | ||
| 121 | |||
| 122 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds | ||
| 123 | static ResultCode WaitSynchronization(VAddr handles_address, u64 handle_count, s64 nano_seconds) { | ||
| 124 | LOG_WARNING(Kernel_SVC, | ||
| 125 | "(STUBBED) called handles_address=0x%llx, handle_count=%d, nano_seconds=%d", | ||
| 126 | handles_address, handle_count, nano_seconds); | ||
| 127 | return RESULT_SUCCESS; | ||
| 128 | } | ||
| 129 | |||
| 130 | /// Attempts to locks a mutex, creating it if it does not already exist | ||
| 131 | static ResultCode LockMutex(Handle holding_thread_handle, VAddr mutex_addr, | ||
| 132 | Handle requesting_thread_handle) { | ||
| 133 | LOG_TRACE(Kernel_SVC, | ||
| 134 | "called holding_thread_handle=0x%08X, mutex_addr=0x%llx, " | ||
| 135 | "requesting_current_thread_handle=0x%08X", | ||
| 136 | holding_thread_handle, mutex_addr, requesting_thread_handle); | ||
| 137 | |||
| 138 | SharedPtr<Thread> holding_thread = | ||
| 139 | g_handle_table.Get<Thread>(holding_thread_handle); | ||
| 140 | SharedPtr<Thread> requesting_thread = | ||
| 141 | g_handle_table.Get<Thread>(requesting_thread_handle); | ||
| 142 | |||
| 143 | ASSERT(holding_thread); | ||
| 144 | ASSERT(requesting_thread); | ||
| 145 | |||
| 146 | SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(mutex_addr); | ||
| 147 | if (!mutex) { | ||
| 148 | // Create a new mutex for the specified address if one does not already exist | ||
| 149 | mutex = Mutex::Create(holding_thread, mutex_addr); | ||
| 150 | mutex->name = Common::StringFromFormat("mutex-%llx", mutex_addr); | ||
| 151 | } | ||
| 152 | |||
| 153 | if (mutex->ShouldWait(requesting_thread.get())) { | ||
| 154 | // If we cannot lock the mutex, then put the thread too sleep and trigger a reschedule | ||
| 155 | requesting_thread->wait_objects = {mutex}; | ||
| 156 | mutex->AddWaitingThread(requesting_thread); | ||
| 157 | requesting_thread->status = THREADSTATUS_WAIT_SYNCH_ANY; | ||
| 158 | |||
| 159 | Core::System::GetInstance().PrepareReschedule(); | ||
| 160 | } else { | ||
| 161 | // The mutex is available, lock it | ||
| 162 | mutex->Acquire(requesting_thread.get()); | ||
| 163 | } | ||
| 164 | |||
| 165 | return RESULT_SUCCESS; | ||
| 166 | } | ||
| 167 | |||
| 168 | /// Unlock a mutex | ||
| 169 | static ResultCode UnlockMutex(VAddr mutex_addr) { | ||
| 170 | LOG_TRACE(Kernel_SVC, "called mutex_addr=0x%llx", mutex_addr); | ||
| 171 | |||
| 172 | SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(mutex_addr); | ||
| 173 | ASSERT(mutex); | ||
| 174 | |||
| 175 | return mutex->Release(GetCurrentThread()); | ||
| 176 | } | ||
| 177 | |||
| 178 | /// Break program execution | ||
| 179 | static void Break(u64 unk_0, u64 unk_1, u64 unk_2) { | ||
| 180 | LOG_CRITICAL(Debug_Emulated, "Emulated program broke execution!"); | ||
| 181 | ASSERT(false); | ||
| 182 | } | ||
| 183 | |||
| 184 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit | ||
| 185 | static void OutputDebugString(VAddr address, s32 len) { | ||
| 186 | std::vector<char> string(len); | ||
| 187 | Memory::ReadBlock(address, string.data(), len); | ||
| 188 | LOG_DEBUG(Debug_Emulated, "%.*s", len, string.data()); | ||
| 189 | } | ||
| 190 | |||
| 191 | /// Gets system/memory information for the current process | ||
| 192 | static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) { | ||
| 193 | LOG_TRACE(Kernel_SVC, "called info_id=0x%X, info_sub_id=0x%X, handle=0x%08X", info_id, | ||
| 194 | info_sub_id, handle); | ||
| 195 | |||
| 196 | auto& vm_manager = g_current_process->vm_manager; | ||
| 197 | switch (static_cast<GetInfoType>(info_id)) { | ||
| 198 | case GetInfoType::TotalMemoryUsage: | ||
| 199 | *result = vm_manager.GetTotalMemoryUsage(); | ||
| 200 | break; | ||
| 201 | case GetInfoType::TotalHeapUsage: | ||
| 202 | *result = vm_manager.GetTotalHeapUsage(); | ||
| 203 | break; | ||
| 204 | case GetInfoType::RandomEntropy: | ||
| 205 | *result = 0; | ||
| 206 | break; | ||
| 207 | case GetInfoType::AddressSpaceBaseAddr: | ||
| 208 | *result = vm_manager.GetAddressSpaceBaseAddr(); | ||
| 209 | break; | ||
| 210 | case GetInfoType::AddressSpaceSize: | ||
| 211 | *result = vm_manager.GetAddressSpaceSize(); | ||
| 212 | break; | ||
| 213 | case GetInfoType::NewMapRegionBaseAddr: | ||
| 214 | *result = vm_manager.GetNewMapRegionBaseAddr(); | ||
| 215 | break; | ||
| 216 | case GetInfoType::NewMapRegionSize: | ||
| 217 | *result = vm_manager.GetNewMapRegionSize(); | ||
| 218 | break; | ||
| 219 | default: | ||
| 220 | UNIMPLEMENTED(); | ||
| 221 | } | ||
| 222 | |||
| 223 | return RESULT_SUCCESS; | ||
| 224 | } | ||
| 225 | |||
| 226 | /// Gets the priority for the specified thread | ||
| 227 | static ResultCode GetThreadPriority(u32* priority, Handle handle) { | ||
| 228 | const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(handle); | ||
| 229 | if (!thread) | ||
| 230 | return ERR_INVALID_HANDLE; | ||
| 231 | |||
| 232 | *priority = thread->GetPriority(); | ||
| 233 | return RESULT_SUCCESS; | ||
| 234 | } | ||
| 235 | |||
| 236 | /// Sets the priority for the specified thread | ||
| 237 | static ResultCode SetThreadPriority(Handle handle, u32 priority) { | ||
| 238 | if (priority > THREADPRIO_LOWEST) { | ||
| 239 | return ERR_OUT_OF_RANGE; | ||
| 240 | } | ||
| 241 | |||
| 242 | SharedPtr<Thread> thread = g_handle_table.Get<Thread>(handle); | ||
| 243 | if (!thread) | ||
| 244 | return ERR_INVALID_HANDLE; | ||
| 245 | |||
| 246 | // Note: The kernel uses the current process's resource limit instead of | ||
| 247 | // the one from the thread owner's resource limit. | ||
| 248 | SharedPtr<ResourceLimit>& resource_limit = g_current_process->resource_limit; | ||
| 249 | if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) { | ||
| 250 | return ERR_NOT_AUTHORIZED; | ||
| 251 | } | ||
| 252 | |||
| 253 | thread->SetPriority(priority); | ||
| 254 | thread->UpdatePriority(); | ||
| 255 | |||
| 256 | // Update the mutexes that this thread is waiting for | ||
| 257 | for (auto& mutex : thread->pending_mutexes) | ||
| 258 | mutex->UpdatePriority(); | ||
| 259 | |||
| 260 | Core::System::GetInstance().PrepareReschedule(); | ||
| 261 | return RESULT_SUCCESS; | ||
| 262 | } | ||
| 263 | |||
| 264 | /// Get which CPU core is executing the current thread | ||
| 265 | static u32 GetCurrentProcessorNumber() { | ||
| 266 | LOG_WARNING(Kernel_SVC, "(STUBBED) called, defaulting to processor 0"); | ||
| 267 | return 0; | ||
| 268 | } | ||
| 269 | |||
| 270 | /// Query process memory | ||
| 271 | static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/, | ||
| 272 | Handle process_handle, u64 addr) { | ||
| 273 | SharedPtr<Process> process = g_handle_table.Get<Process>(process_handle); | ||
| 274 | if (!process) { | ||
| 275 | return ERR_INVALID_HANDLE; | ||
| 276 | } | ||
| 277 | auto vma = process->vm_manager.FindVMA(addr); | ||
| 278 | memory_info->attributes = 0; | ||
| 279 | if (vma == g_current_process->vm_manager.vma_map.end()) { | ||
| 280 | memory_info->base_address = 0; | ||
| 281 | memory_info->permission = static_cast<u32>(VMAPermission::None); | ||
| 282 | memory_info->size = 0; | ||
| 283 | memory_info->type = static_cast<u32>(MemoryState::Free); | ||
| 284 | } else { | ||
| 285 | memory_info->base_address = vma->second.base; | ||
| 286 | memory_info->permission = static_cast<u32>(vma->second.permissions); | ||
| 287 | memory_info->size = vma->second.size; | ||
| 288 | memory_info->type = static_cast<u32>(vma->second.meminfo_state); | ||
| 289 | } | ||
| 290 | |||
| 291 | LOG_TRACE(Kernel_SVC, "called process=0x%08X addr=%llx", process_handle, addr); | ||
| 292 | return RESULT_SUCCESS; | ||
| 293 | } | ||
| 294 | |||
| 295 | /// Query memory | ||
| 296 | static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAddr addr) { | ||
| 297 | LOG_TRACE(Kernel_SVC, "called, addr=%llx", addr); | ||
| 298 | return QueryProcessMemory(memory_info, page_info, CurrentProcess, addr); | ||
| 299 | } | ||
| 300 | |||
| 301 | /// Exits the current process | ||
| 302 | static void ExitProcess() { | ||
| 303 | LOG_INFO(Kernel_SVC, "Process %u exiting", g_current_process->process_id); | ||
| 304 | |||
| 305 | ASSERT_MSG(g_current_process->status == ProcessStatus::Running, | ||
| 306 | "Process has already exited"); | ||
| 307 | |||
| 308 | g_current_process->status = ProcessStatus::Exited; | ||
| 309 | |||
| 310 | // Stop all the process threads that are currently waiting for objects. | ||
| 311 | auto& thread_list = GetThreadList(); | ||
| 312 | for (auto& thread : thread_list) { | ||
| 313 | if (thread->owner_process != g_current_process) | ||
| 314 | continue; | ||
| 315 | |||
| 316 | if (thread == GetCurrentThread()) | ||
| 317 | continue; | ||
| 318 | |||
| 319 | // TODO(Subv): When are the other running/ready threads terminated? | ||
| 320 | ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY || | ||
| 321 | thread->status == THREADSTATUS_WAIT_SYNCH_ALL, | ||
| 322 | "Exiting processes with non-waiting threads is currently unimplemented"); | ||
| 323 | |||
| 324 | thread->Stop(); | ||
| 325 | } | ||
| 326 | |||
| 327 | // Kill the current thread | ||
| 328 | GetCurrentThread()->Stop(); | ||
| 329 | |||
| 330 | Core::System::GetInstance().PrepareReschedule(); | ||
| 331 | } | ||
| 332 | |||
| 333 | /// Creates a new thread | ||
| 334 | static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, | ||
| 335 | u32 priority, s32 processor_id) { | ||
| 336 | std::string name = Common::StringFromFormat("unknown-%llx", entry_point); | ||
| 337 | |||
| 338 | if (priority > THREADPRIO_LOWEST) { | ||
| 339 | return ERR_OUT_OF_RANGE; | ||
| 340 | } | ||
| 341 | |||
| 342 | SharedPtr<ResourceLimit>& resource_limit = g_current_process->resource_limit; | ||
| 343 | if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) { | ||
| 344 | return ERR_NOT_AUTHORIZED; | ||
| 345 | } | ||
| 346 | |||
| 347 | if (processor_id == THREADPROCESSORID_DEFAULT) { | ||
| 348 | // Set the target CPU to the one specified in the process' exheader. | ||
| 349 | processor_id = g_current_process->ideal_processor; | ||
| 350 | ASSERT(processor_id != THREADPROCESSORID_DEFAULT); | ||
| 351 | } | ||
| 352 | |||
| 353 | switch (processor_id) { | ||
| 354 | case THREADPROCESSORID_0: | ||
| 355 | break; | ||
| 356 | case THREADPROCESSORID_ALL: | ||
| 357 | LOG_INFO(Kernel_SVC, | ||
| 358 | "Newly created thread is allowed to be run in any Core, unimplemented."); | ||
| 359 | break; | ||
| 360 | case THREADPROCESSORID_1: | ||
| 361 | LOG_ERROR(Kernel_SVC, | ||
| 362 | "Newly created thread must run in the SysCore (Core1), unimplemented."); | ||
| 363 | break; | ||
| 364 | default: | ||
| 365 | // TODO(bunnei): Implement support for other processor IDs | ||
| 366 | ASSERT_MSG(false, "Unsupported thread processor ID: %d", processor_id); | ||
| 367 | break; | ||
| 368 | } | ||
| 369 | |||
| 370 | CASCADE_RESULT(SharedPtr<Thread> thread, | ||
| 371 | Thread::Create(name, entry_point, priority, arg, processor_id, stack_top, | ||
| 372 | g_current_process)); | ||
| 373 | |||
| 374 | thread->context.fpscr = | ||
| 375 | FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO; // 0x03C00000 | ||
| 376 | |||
| 377 | CASCADE_RESULT(thread->guest_handle, g_handle_table.Create(thread)); | ||
| 378 | *out_handle = thread->guest_handle; | ||
| 379 | |||
| 380 | Core::System::GetInstance().PrepareReschedule(); | ||
| 381 | |||
| 382 | LOG_TRACE(Kernel_SVC, | ||
| 383 | "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " | ||
| 384 | "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", | ||
| 385 | entry_point, name.c_str(), arg, stack_top, priority, processor_id, *out_handle); | ||
| 386 | |||
| 387 | return RESULT_SUCCESS; | ||
| 388 | } | ||
| 389 | |||
| 390 | /// Starts the thread for the provided handle | ||
| 391 | static ResultCode StartThread(Handle thread_handle) { | ||
| 392 | LOG_TRACE(Kernel_SVC, "called thread=0x%08X", thread_handle); | ||
| 393 | |||
| 394 | const SharedPtr<Thread> thread = | ||
| 395 | g_handle_table.Get<Thread>(thread_handle); | ||
| 396 | if (!thread) { | ||
| 397 | return ERR_INVALID_HANDLE; | ||
| 398 | } | ||
| 399 | |||
| 400 | thread->ResumeFromWait(); | ||
| 401 | |||
| 402 | return RESULT_SUCCESS; | ||
| 403 | } | ||
| 404 | |||
| 405 | /// Called when a thread exits | ||
| 406 | static void ExitThread() { | ||
| 407 | LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::CPU().GetPC()); | ||
| 408 | |||
| 409 | ExitCurrentThread(); | ||
| 410 | Core::System::GetInstance().PrepareReschedule(); | ||
| 411 | } | ||
| 412 | |||
| 413 | /// Sleep the current thread | ||
| 414 | static void SleepThread(s64 nanoseconds) { | ||
| 415 | LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds); | ||
| 416 | |||
| 417 | // Don't attempt to yield execution if there are no available threads to run, | ||
| 418 | // this way we avoid a useless reschedule to the idle thread. | ||
| 419 | if (nanoseconds == 0 && !HaveReadyThreads()) | ||
| 420 | return; | ||
| 421 | |||
| 422 | // Sleep current thread and check for next thread to schedule | ||
| 423 | WaitCurrentThread_Sleep(); | ||
| 424 | |||
| 425 | // Create an event to wake the thread up after the specified nanosecond delay has passed | ||
| 426 | GetCurrentThread()->WakeAfterDelay(nanoseconds); | ||
| 427 | |||
| 428 | Core::System::GetInstance().PrepareReschedule(); | ||
| 429 | } | ||
| 430 | |||
| 431 | /// Signal process wide key | ||
| 432 | static ResultCode SignalProcessWideKey(VAddr addr, u32 target) { | ||
| 433 | LOG_WARNING(Kernel_SVC, "(STUBBED) called, address=0x%llx, target=0x%08x", addr, target); | ||
| 434 | return RESULT_SUCCESS; | ||
| 435 | } | ||
| 436 | |||
| 437 | /// Close a handle | ||
| 438 | static ResultCode CloseHandle(Handle handle) { | ||
| 439 | LOG_TRACE(Kernel_SVC, "Closing handle 0x%08X", handle); | ||
| 440 | return g_handle_table.Close(handle); | ||
| 441 | } | ||
| 442 | |||
| 443 | namespace { | ||
| 444 | struct FunctionDef { | ||
| 445 | using Func = void(); | ||
| 446 | |||
| 447 | u32 id; | ||
| 448 | Func* func; | ||
| 449 | const char* name; | ||
| 450 | }; | ||
| 451 | } // namespace | ||
| 452 | |||
| 453 | static const FunctionDef SVC_Table[] = { | ||
| 454 | {0x00, nullptr, "Unknown"}, | ||
| 455 | {0x01, SvcWrap<SetHeapSize>, "svcSetHeapSize"}, | ||
| 456 | {0x02, nullptr, "svcSetMemoryPermission"}, | ||
| 457 | {0x03, nullptr, "svcSetMemoryAttribute"}, | ||
| 458 | {0x04, SvcWrap<MapMemory>, "svcMapMemory"}, | ||
| 459 | {0x05, SvcWrap<UnmapMemory>, "svcUnmapMemory"}, | ||
| 460 | {0x06, SvcWrap<QueryMemory>, "svcQueryMemory"}, | ||
| 461 | {0x07, SvcWrap<ExitProcess>, "svcExitProcess"}, | ||
| 462 | {0x08, SvcWrap<CreateThread>, "svcCreateThread"}, | ||
| 463 | {0x09, SvcWrap<StartThread>, "svcStartThread"}, | ||
| 464 | {0x0A, SvcWrap<ExitThread>, "svcExitThread"}, | ||
| 465 | {0x0B, SvcWrap<SleepThread>, "svcSleepThread"}, | ||
| 466 | {0x0C, SvcWrap<GetThreadPriority>, "svcGetThreadPriority"}, | ||
| 467 | {0x0D, SvcWrap<SetThreadPriority>, "svcSetThreadPriority"}, | ||
| 468 | {0x0E, nullptr, "svcGetThreadCoreMask"}, | ||
| 469 | {0x0F, nullptr, "svcSetThreadCoreMask"}, | ||
| 470 | {0x10, SvcWrap<GetCurrentProcessorNumber>, "svcGetCurrentProcessorNumber"}, | ||
| 471 | {0x11, nullptr, "svcSignalEvent"}, | ||
| 472 | {0x12, nullptr, "svcClearEvent"}, | ||
| 473 | {0x13, nullptr, "svcMapSharedMemory"}, | ||
| 474 | {0x14, nullptr, "svcUnmapSharedMemory"}, | ||
| 475 | {0x15, nullptr, "svcCreateTransferMemory"}, | ||
| 476 | {0x16, SvcWrap<CloseHandle>, "svcCloseHandle"}, | ||
| 477 | {0x17, nullptr, "svcResetSignal"}, | ||
| 478 | {0x18, SvcWrap<WaitSynchronization>, "svcWaitSynchronization"}, | ||
| 479 | {0x19, nullptr, "svcCancelSynchronization"}, | ||
| 480 | {0x1A, SvcWrap<LockMutex>, "svcLockMutex"}, | ||
| 481 | {0x1B, SvcWrap<UnlockMutex>, "svcUnlockMutex"}, | ||
| 482 | {0x1C, nullptr, "svcWaitProcessWideKeyAtomic"}, | ||
| 483 | {0x1D, SvcWrap<SignalProcessWideKey>, "svcSignalProcessWideKey"}, | ||
| 484 | {0x1E, nullptr, "svcGetSystemTick"}, | ||
| 485 | {0x1F, SvcWrap<ConnectToPort>, "svcConnectToPort"}, | ||
| 486 | {0x20, nullptr, "svcSendSyncRequestLight"}, | ||
| 487 | {0x21, SvcWrap<SendSyncRequest>, "svcSendSyncRequest"}, | ||
| 488 | {0x22, nullptr, "svcSendSyncRequestWithUserBuffer"}, | ||
| 489 | {0x23, nullptr, "svcSendAsyncRequestWithUserBuffer"}, | ||
| 490 | {0x24, SvcWrap<GetProcessId>, "svcGetProcessId"}, | ||
| 491 | {0x25, SvcWrap<GetThreadId>, "svcGetThreadId"}, | ||
| 492 | {0x26, SvcWrap<Break>, "svcBreak"}, | ||
| 493 | {0x27, SvcWrap<OutputDebugString>, "svcOutputDebugString"}, | ||
| 494 | {0x28, nullptr, "svcReturnFromException"}, | ||
| 495 | {0x29, SvcWrap<GetInfo>, "svcGetInfo"}, | ||
| 496 | {0x2A, nullptr, "svcFlushEntireDataCache"}, | ||
| 497 | {0x2B, nullptr, "svcFlushDataCache"}, | ||
| 498 | {0x2C, nullptr, "svcMapPhysicalMemory"}, | ||
| 499 | {0x2D, nullptr, "svcUnmapPhysicalMemory"}, | ||
| 500 | {0x2E, nullptr, "Unknown"}, | ||
| 501 | {0x2F, nullptr, "svcGetLastThreadInfo"}, | ||
| 502 | {0x30, nullptr, "svcGetResourceLimitLimitValue"}, | ||
| 503 | {0x31, nullptr, "svcGetResourceLimitCurrentValue"}, | ||
| 504 | {0x32, nullptr, "svcSetThreadActivity"}, | ||
| 505 | {0x33, nullptr, "svcGetThreadContext"}, | ||
| 506 | {0x34, nullptr, "Unknown"}, | ||
| 507 | {0x35, nullptr, "Unknown"}, | ||
| 508 | {0x36, nullptr, "Unknown"}, | ||
| 509 | {0x37, nullptr, "Unknown"}, | ||
| 510 | {0x38, nullptr, "Unknown"}, | ||
| 511 | {0x39, nullptr, "Unknown"}, | ||
| 512 | {0x3A, nullptr, "Unknown"}, | ||
| 513 | {0x3B, nullptr, "Unknown"}, | ||
| 514 | {0x3C, nullptr, "svcDumpInfo"}, | ||
| 515 | {0x3D, nullptr, "Unknown"}, | ||
| 516 | {0x3E, nullptr, "Unknown"}, | ||
| 517 | {0x3F, nullptr, "Unknown"}, | ||
| 518 | {0x40, nullptr, "svcCreateSession"}, | ||
| 519 | {0x41, nullptr, "svcAcceptSession"}, | ||
| 520 | {0x42, nullptr, "svcReplyAndReceiveLight"}, | ||
| 521 | {0x43, nullptr, "svcReplyAndReceive"}, | ||
| 522 | {0x44, nullptr, "svcReplyAndReceiveWithUserBuffer"}, | ||
| 523 | {0x45, nullptr, "svcCreateEvent"}, | ||
| 524 | {0x46, nullptr, "Unknown"}, | ||
| 525 | {0x47, nullptr, "Unknown"}, | ||
| 526 | {0x48, nullptr, "Unknown"}, | ||
| 527 | {0x49, nullptr, "Unknown"}, | ||
| 528 | {0x4A, nullptr, "Unknown"}, | ||
| 529 | {0x4B, nullptr, "Unknown"}, | ||
| 530 | {0x4C, nullptr, "Unknown"}, | ||
| 531 | {0x4D, nullptr, "svcSleepSystem"}, | ||
| 532 | {0x4E, nullptr, "svcReadWriteRegister"}, | ||
| 533 | {0x4F, nullptr, "svcSetProcessActivity"}, | ||
| 534 | {0x50, nullptr, "svcCreateSharedMemory"}, | ||
| 535 | {0x51, nullptr, "svcMapTransferMemory"}, | ||
| 536 | {0x52, nullptr, "svcUnmapTransferMemory"}, | ||
| 537 | {0x53, nullptr, "svcCreateInterruptEvent"}, | ||
| 538 | {0x54, nullptr, "svcQueryPhysicalAddress"}, | ||
| 539 | {0x55, nullptr, "svcQueryIoMapping"}, | ||
| 540 | {0x56, nullptr, "svcCreateDeviceAddressSpace"}, | ||
| 541 | {0x57, nullptr, "svcAttachDeviceAddressSpace"}, | ||
| 542 | {0x58, nullptr, "svcDetachDeviceAddressSpace"}, | ||
| 543 | {0x59, nullptr, "svcMapDeviceAddressSpaceByForce"}, | ||
| 544 | {0x5A, nullptr, "svcMapDeviceAddressSpaceAligned"}, | ||
| 545 | {0x5B, nullptr, "svcMapDeviceAddressSpace"}, | ||
| 546 | {0x5C, nullptr, "svcUnmapDeviceAddressSpace"}, | ||
| 547 | {0x5D, nullptr, "svcInvalidateProcessDataCache"}, | ||
| 548 | {0x5E, nullptr, "svcStoreProcessDataCache"}, | ||
| 549 | {0x5F, nullptr, "svcFlushProcessDataCache"}, | ||
| 550 | {0x60, nullptr, "svcDebugActiveProcess"}, | ||
| 551 | {0x61, nullptr, "svcBreakDebugProcess"}, | ||
| 552 | {0x62, nullptr, "svcTerminateDebugProcess"}, | ||
| 553 | {0x63, nullptr, "svcGetDebugEvent"}, | ||
| 554 | {0x64, nullptr, "svcContinueDebugEvent"}, | ||
| 555 | {0x65, nullptr, "svcGetProcessList"}, | ||
| 556 | {0x66, nullptr, "svcGetThreadList"}, | ||
| 557 | {0x67, nullptr, "svcGetDebugThreadContext"}, | ||
| 558 | {0x68, nullptr, "svcSetDebugThreadContext"}, | ||
| 559 | {0x69, nullptr, "svcQueryDebugProcessMemory"}, | ||
| 560 | {0x6A, nullptr, "svcReadDebugProcessMemory"}, | ||
| 561 | {0x6B, nullptr, "svcWriteDebugProcessMemory"}, | ||
| 562 | {0x6C, nullptr, "svcSetHardwareBreakPoint"}, | ||
| 563 | {0x6D, nullptr, "svcGetDebugThreadParam"}, | ||
| 564 | {0x6E, nullptr, "Unknown"}, | ||
| 565 | {0x6F, nullptr, "Unknown"}, | ||
| 566 | {0x70, nullptr, "svcCreatePort"}, | ||
| 567 | {0x71, nullptr, "svcManageNamedPort"}, | ||
| 568 | {0x72, nullptr, "svcConnectToPort"}, | ||
| 569 | {0x73, nullptr, "svcSetProcessMemoryPermission"}, | ||
| 570 | {0x74, nullptr, "svcMapProcessMemory"}, | ||
| 571 | {0x75, nullptr, "svcUnmapProcessMemory"}, | ||
| 572 | {0x76, nullptr, "svcQueryProcessMemory"}, | ||
| 573 | {0x77, nullptr, "svcMapProcessCodeMemory"}, | ||
| 574 | {0x78, nullptr, "svcUnmapProcessCodeMemory"}, | ||
| 575 | {0x79, nullptr, "svcCreateProcess"}, | ||
| 576 | {0x7A, nullptr, "svcStartProcess"}, | ||
| 577 | {0x7B, nullptr, "svcTerminateProcess"}, | ||
| 578 | {0x7C, nullptr, "svcGetProcessInfo"}, | ||
| 579 | {0x7D, nullptr, "svcCreateResourceLimit"}, | ||
| 580 | {0x7E, nullptr, "svcSetResourceLimitLimitValue"}, | ||
| 581 | {0x7F, nullptr, "svcCallSecureMonitor"}, | ||
| 582 | }; | ||
| 583 | |||
| 584 | static const FunctionDef* GetSVCInfo(u32 func_num) { | ||
| 585 | if (func_num >= ARRAY_SIZE(SVC_Table)) { | ||
| 586 | LOG_ERROR(Kernel_SVC, "unknown svc=0x%02X", func_num); | ||
| 587 | return nullptr; | ||
| 588 | } | ||
| 589 | return &SVC_Table[func_num]; | ||
| 590 | } | ||
| 591 | |||
| 592 | MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); | ||
| 593 | |||
| 594 | void CallSVC(u32 immediate) { | ||
| 595 | MICROPROFILE_SCOPE(Kernel_SVC); | ||
| 596 | |||
| 597 | // Lock the global kernel mutex when we enter the kernel HLE. | ||
| 598 | std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); | ||
| 599 | |||
| 600 | const FunctionDef* info = GetSVCInfo(immediate); | ||
| 601 | if (info) { | ||
| 602 | if (info->func) { | ||
| 603 | info->func(); | ||
| 604 | } else { | ||
| 605 | LOG_CRITICAL(Kernel_SVC, "unimplemented SVC function %s(..)", info->name); | ||
| 606 | } | ||
| 607 | } else { | ||
| 608 | LOG_CRITICAL(Kernel_SVC, "unknown SVC function 0x%x", immediate); | ||
| 609 | } | ||
| 610 | } | ||
| 611 | |||
| 612 | } // namespace Kernel | ||