diff options
| author | 2017-10-14 17:30:07 -0400 | |
|---|---|---|
| committer | 2017-10-14 17:30:07 -0400 | |
| commit | cb889f9220720ce32cf757afd5b317e506eee3c2 (patch) | |
| tree | 523369b251d231f733beff840f82b726570f5283 /src/core | |
| parent | Remove more 3DS-specific code. (diff) | |
| download | yuzu-cb889f9220720ce32cf757afd5b317e506eee3c2.tar.gz yuzu-cb889f9220720ce32cf757afd5b317e506eee3c2.tar.xz yuzu-cb889f9220720ce32cf757afd5b317e506eee3c2.zip | |
svc: Initial nx impl. for QueryMemory, ConnectToPort, SendSyncRequest, etc.
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/hle/svc.cpp | 1370 |
1 files changed, 185 insertions, 1185 deletions
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index e4b803046..c0481ea6f 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp | |||
| @@ -2,35 +2,15 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 6 | #include <cinttypes> | ||
| 7 | #include <map> | ||
| 8 | #include "common/logging/log.h" | 5 | #include "common/logging/log.h" |
| 9 | #include "common/microprofile.h" | 6 | #include "common/microprofile.h" |
| 10 | #include "common/scope_exit.h" | ||
| 11 | #include "common/string_util.h" | ||
| 12 | #include "core/arm/arm_interface.h" | ||
| 13 | #include "core/core_timing.h" | 7 | #include "core/core_timing.h" |
| 14 | #include "core/hle/function_wrappers.h" | 8 | #include "core/hle/function_wrappers.h" |
| 15 | #include "core/hle/kernel/address_arbiter.h" | ||
| 16 | #include "core/hle/kernel/client_port.h" | 9 | #include "core/hle/kernel/client_port.h" |
| 17 | #include "core/hle/kernel/client_session.h" | 10 | #include "core/hle/kernel/client_session.h" |
| 18 | #include "core/hle/kernel/errors.h" | ||
| 19 | #include "core/hle/kernel/event.h" | ||
| 20 | #include "core/hle/kernel/handle_table.h" | 11 | #include "core/hle/kernel/handle_table.h" |
| 21 | #include "core/hle/kernel/memory.h" | ||
| 22 | #include "core/hle/kernel/mutex.h" | ||
| 23 | #include "core/hle/kernel/process.h" | 12 | #include "core/hle/kernel/process.h" |
| 24 | #include "core/hle/kernel/resource_limit.h" | 13 | #include "core/hle/lock.h" |
| 25 | #include "core/hle/kernel/semaphore.h" | ||
| 26 | #include "core/hle/kernel/server_port.h" | ||
| 27 | #include "core/hle/kernel/server_session.h" | ||
| 28 | #include "core/hle/kernel/session.h" | ||
| 29 | #include "core/hle/kernel/shared_memory.h" | ||
| 30 | #include "core/hle/kernel/thread.h" | ||
| 31 | #include "core/hle/kernel/timer.h" | ||
| 32 | #include "core/hle/kernel/vm_manager.h" | ||
| 33 | #include "core/hle/kernel/wait_object.h" | ||
| 34 | #include "core/hle/result.h" | 14 | #include "core/hle/result.h" |
| 35 | #include "core/hle/service/service.h" | 15 | #include "core/hle/service/service.h" |
| 36 | 16 | ||
| @@ -43,174 +23,22 @@ using Kernel::SharedPtr; | |||
| 43 | 23 | ||
| 44 | namespace SVC { | 24 | namespace SVC { |
| 45 | 25 | ||
| 46 | enum ControlMemoryOperation { | ||
| 47 | MEMOP_FREE = 1, | ||
| 48 | MEMOP_RESERVE = 2, // This operation seems to be unsupported in the kernel | ||
| 49 | MEMOP_COMMIT = 3, | ||
| 50 | MEMOP_MAP = 4, | ||
| 51 | MEMOP_UNMAP = 5, | ||
| 52 | MEMOP_PROTECT = 6, | ||
| 53 | MEMOP_OPERATION_MASK = 0xFF, | ||
| 54 | |||
| 55 | MEMOP_REGION_APP = 0x100, | ||
| 56 | MEMOP_REGION_SYSTEM = 0x200, | ||
| 57 | MEMOP_REGION_BASE = 0x300, | ||
| 58 | MEMOP_REGION_MASK = 0xF00, | ||
| 59 | |||
| 60 | MEMOP_LINEAR = 0x10000, | ||
| 61 | }; | ||
| 62 | |||
| 63 | /// Map application or GSP heap memory | ||
| 64 | static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, | ||
| 65 | u32 permissions) { | ||
| 66 | using namespace Kernel; | ||
| 67 | |||
| 68 | LOG_DEBUG(Kernel_SVC, | ||
| 69 | "called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=0x%X, permissions=0x%08X", | ||
| 70 | operation, addr0, addr1, size, permissions); | ||
| 71 | |||
| 72 | if ((addr0 & Memory::PAGE_MASK) != 0 || (addr1 & Memory::PAGE_MASK) != 0) { | ||
| 73 | return ERR_MISALIGNED_ADDRESS; | ||
| 74 | } | ||
| 75 | if ((size & Memory::PAGE_MASK) != 0) { | ||
| 76 | return ERR_MISALIGNED_SIZE; | ||
| 77 | } | ||
| 78 | |||
| 79 | u32 region = operation & MEMOP_REGION_MASK; | ||
| 80 | operation &= ~MEMOP_REGION_MASK; | ||
| 81 | |||
| 82 | if (region != 0) { | ||
| 83 | LOG_WARNING(Kernel_SVC, "ControlMemory with specified region not supported, region=%X", | ||
| 84 | region); | ||
| 85 | } | ||
| 86 | |||
| 87 | if ((permissions & (u32)MemoryPermission::ReadWrite) != permissions) { | ||
| 88 | return ERR_INVALID_COMBINATION; | ||
| 89 | } | ||
| 90 | VMAPermission vma_permissions = (VMAPermission)permissions; | ||
| 91 | |||
| 92 | auto& process = *g_current_process; | ||
| 93 | |||
| 94 | switch (operation & MEMOP_OPERATION_MASK) { | ||
| 95 | case MEMOP_FREE: { | ||
| 96 | // TODO(Subv): What happens if an application tries to FREE a block of memory that has a | ||
| 97 | // SharedMemory pointing to it? | ||
| 98 | if (addr0 >= Memory::HEAP_VADDR && addr0 < Memory::HEAP_VADDR_END) { | ||
| 99 | ResultCode result = process.HeapFree(addr0, size); | ||
| 100 | if (result.IsError()) | ||
| 101 | return result; | ||
| 102 | } else if (addr0 >= process.GetLinearHeapBase() && addr0 < process.GetLinearHeapLimit()) { | ||
| 103 | ResultCode result = process.LinearFree(addr0, size); | ||
| 104 | if (result.IsError()) | ||
| 105 | return result; | ||
| 106 | } else { | ||
| 107 | return ERR_INVALID_ADDRESS; | ||
| 108 | } | ||
| 109 | *out_addr = addr0; | ||
| 110 | break; | ||
| 111 | } | ||
| 112 | |||
| 113 | case MEMOP_COMMIT: { | ||
| 114 | if (operation & MEMOP_LINEAR) { | ||
| 115 | CASCADE_RESULT(*out_addr, process.LinearAllocate(addr0, size, vma_permissions)); | ||
| 116 | } else { | ||
| 117 | CASCADE_RESULT(*out_addr, process.HeapAllocate(addr0, size, vma_permissions)); | ||
| 118 | } | ||
| 119 | break; | ||
| 120 | } | ||
| 121 | |||
| 122 | case MEMOP_MAP: // TODO: This is just a hack to avoid regressions until memory aliasing is | ||
| 123 | // implemented | ||
| 124 | { | ||
| 125 | CASCADE_RESULT(*out_addr, process.HeapAllocate(addr0, size, vma_permissions)); | ||
| 126 | break; | ||
| 127 | } | ||
| 128 | |||
| 129 | case MEMOP_UNMAP: // TODO: This is just a hack to avoid regressions until memory aliasing is | ||
| 130 | // implemented | ||
| 131 | { | ||
| 132 | ResultCode result = process.HeapFree(addr0, size); | ||
| 133 | if (result.IsError()) | ||
| 134 | return result; | ||
| 135 | break; | ||
| 136 | } | ||
| 137 | |||
| 138 | case MEMOP_PROTECT: { | ||
| 139 | ResultCode result = process.vm_manager.ReprotectRange(addr0, size, vma_permissions); | ||
| 140 | if (result.IsError()) | ||
| 141 | return result; | ||
| 142 | break; | ||
| 143 | } | ||
| 144 | |||
| 145 | default: | ||
| 146 | LOG_ERROR(Kernel_SVC, "unknown operation=0x%08X", operation); | ||
| 147 | return ERR_INVALID_COMBINATION; | ||
| 148 | } | ||
| 149 | |||
| 150 | process.vm_manager.LogLayout(Log::Level::Trace); | ||
| 151 | |||
| 152 | return RESULT_SUCCESS; | ||
| 153 | } | ||
| 154 | |||
| 155 | /// Maps a memory block to specified address | ||
| 156 | static ResultCode MapMemoryBlock(Kernel::Handle handle, u32 addr, u32 permissions, | ||
| 157 | u32 other_permissions) { | ||
| 158 | using Kernel::SharedMemory; | ||
| 159 | using Kernel::MemoryPermission; | ||
| 160 | |||
| 161 | LOG_TRACE(Kernel_SVC, | ||
| 162 | "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d", | ||
| 163 | handle, addr, permissions, other_permissions); | ||
| 164 | |||
| 165 | SharedPtr<SharedMemory> shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle); | ||
| 166 | if (shared_memory == nullptr) | ||
| 167 | return ERR_INVALID_HANDLE; | ||
| 168 | |||
| 169 | MemoryPermission permissions_type = static_cast<MemoryPermission>(permissions); | ||
| 170 | switch (permissions_type) { | ||
| 171 | case MemoryPermission::Read: | ||
| 172 | case MemoryPermission::Write: | ||
| 173 | case MemoryPermission::ReadWrite: | ||
| 174 | case MemoryPermission::Execute: | ||
| 175 | case MemoryPermission::ReadExecute: | ||
| 176 | case MemoryPermission::WriteExecute: | ||
| 177 | case MemoryPermission::ReadWriteExecute: | ||
| 178 | case MemoryPermission::DontCare: | ||
| 179 | return shared_memory->Map(Kernel::g_current_process.get(), addr, permissions_type, | ||
| 180 | static_cast<MemoryPermission>(other_permissions)); | ||
| 181 | default: | ||
| 182 | LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions); | ||
| 183 | } | ||
| 184 | |||
| 185 | return Kernel::ERR_INVALID_COMBINATION; | ||
| 186 | } | ||
| 187 | |||
| 188 | static ResultCode UnmapMemoryBlock(Kernel::Handle handle, u32 addr) { | ||
| 189 | using Kernel::SharedMemory; | ||
| 190 | |||
| 191 | LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X", handle, addr); | ||
| 192 | |||
| 193 | // TODO(Subv): Return E0A01BF5 if the address is not in the application's heap | ||
| 194 | |||
| 195 | SharedPtr<SharedMemory> shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle); | ||
| 196 | if (shared_memory == nullptr) | ||
| 197 | return ERR_INVALID_HANDLE; | ||
| 198 | |||
| 199 | return shared_memory->Unmap(Kernel::g_current_process.get(), addr); | ||
| 200 | } | ||
| 201 | |||
| 202 | /// Connect to an OS service given the port name, returns the handle to the port to out | 26 | /// Connect to an OS service given the port name, returns the handle to the port to out |
| 203 | static ResultCode ConnectToPort(Kernel::Handle* out_handle, const char* port_name) { | 27 | static ResultCode ConnectToPort(Kernel::Handle* out_handle, VAddr port_name_address) { |
| 204 | if (port_name == nullptr) | 28 | if (!Memory::IsValidVirtualAddress(port_name_address)) |
| 205 | return Kernel::ERR_NOT_FOUND; | 29 | return Kernel::ERR_NOT_FOUND; |
| 206 | if (std::strlen(port_name) > 11) | 30 | |
| 31 | static constexpr std::size_t PortNameMaxLength = 11; | ||
| 32 | // Read 1 char beyond the max allowed port name to detect names that are too long. | ||
| 33 | std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1); | ||
| 34 | if (port_name.size() > PortNameMaxLength) | ||
| 207 | return Kernel::ERR_PORT_NAME_TOO_LONG; | 35 | return Kernel::ERR_PORT_NAME_TOO_LONG; |
| 208 | 36 | ||
| 209 | LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name); | 37 | LOG_INFO(Kernel_SVC, "called port_name=%s", port_name.c_str()); |
| 210 | 38 | ||
| 211 | auto it = Service::g_kernel_named_ports.find(port_name); | 39 | auto it = Service::g_kernel_named_ports.find(port_name); |
| 212 | if (it == Service::g_kernel_named_ports.end()) { | 40 | if (it == Service::g_kernel_named_ports.end()) { |
| 213 | LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: %s", port_name); | 41 | LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: %s", port_name.c_str()); |
| 214 | return Kernel::ERR_NOT_FOUND; | 42 | return Kernel::ERR_NOT_FOUND; |
| 215 | } | 43 | } |
| 216 | 44 | ||
| @@ -229,6 +57,7 @@ static ResultCode SendSyncRequest(Kernel::Handle handle) { | |||
| 229 | SharedPtr<Kernel::ClientSession> session = | 57 | SharedPtr<Kernel::ClientSession> session = |
| 230 | Kernel::g_handle_table.Get<Kernel::ClientSession>(handle); | 58 | Kernel::g_handle_table.Get<Kernel::ClientSession>(handle); |
| 231 | if (session == nullptr) { | 59 | if (session == nullptr) { |
| 60 | LOG_ERROR(Kernel_SVC, "called with invalid handle=0x%08X", handle); | ||
| 232 | return ERR_INVALID_HANDLE; | 61 | return ERR_INVALID_HANDLE; |
| 233 | } | 62 | } |
| 234 | 63 | ||
| @@ -241,586 +70,43 @@ static ResultCode SendSyncRequest(Kernel::Handle handle) { | |||
| 241 | return session->SendSyncRequest(Kernel::GetCurrentThread()); | 70 | return session->SendSyncRequest(Kernel::GetCurrentThread()); |
| 242 | } | 71 | } |
| 243 | 72 | ||
| 244 | /// Close a handle | 73 | /// Break program execution |
| 245 | static ResultCode CloseHandle(Kernel::Handle handle) { | 74 | static void Break(u64 unk_0, u64 unk_1, u64 unk_2) { |
| 246 | LOG_TRACE(Kernel_SVC, "Closing handle 0x%08X", handle); | 75 | LOG_CRITICAL(Debug_Emulated, "Emulated program broke execution!"); |
| 247 | return Kernel::g_handle_table.Close(handle); | 76 | ASSERT(false); |
| 248 | } | 77 | } |
| 249 | 78 | ||
| 250 | /// Wait for a handle to synchronize, timeout after the specified nanoseconds | 79 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit |
| 251 | static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) { | 80 | static void OutputDebugString(VAddr address, int len) { |
| 252 | auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handle); | 81 | std::vector<char> string(len); |
| 253 | Kernel::Thread* thread = Kernel::GetCurrentThread(); | 82 | Memory::ReadBlock(address, string.data(), len); |
| 254 | 83 | LOG_DEBUG(Debug_Emulated, "%.*s", len, string.data()); | |
| 255 | if (object == nullptr) | ||
| 256 | return ERR_INVALID_HANDLE; | ||
| 257 | |||
| 258 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, | ||
| 259 | object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); | ||
| 260 | |||
| 261 | if (object->ShouldWait(thread)) { | ||
| 262 | |||
| 263 | if (nano_seconds == 0) | ||
| 264 | return Kernel::RESULT_TIMEOUT; | ||
| 265 | |||
| 266 | thread->wait_objects = {object}; | ||
| 267 | object->AddWaitingThread(thread); | ||
| 268 | thread->status = THREADSTATUS_WAIT_SYNCH_ANY; | ||
| 269 | |||
| 270 | // Create an event to wake the thread up after the specified nanosecond delay has passed | ||
| 271 | thread->WakeAfterDelay(nano_seconds); | ||
| 272 | |||
| 273 | Core::System::GetInstance().PrepareReschedule(); | ||
| 274 | |||
| 275 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread | ||
| 276 | // resumes due to a signal in its wait objects. | ||
| 277 | // Otherwise we retain the default value of timeout. | ||
| 278 | return Kernel::RESULT_TIMEOUT; | ||
| 279 | } | ||
| 280 | |||
| 281 | object->Acquire(thread); | ||
| 282 | |||
| 283 | return RESULT_SUCCESS; | ||
| 284 | } | 84 | } |
| 285 | 85 | ||
| 286 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds | 86 | static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) { |
| 287 | static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 handle_count, | 87 | LOG_INFO(Kernel_SVC, "called, info_id=0x%X, info_sub_id=0x%X, handle=0x%08X", info_id, info_sub_id, handle); |
| 288 | bool wait_all, s64 nano_seconds) { | ||
| 289 | Kernel::Thread* thread = Kernel::GetCurrentThread(); | ||
| 290 | |||
| 291 | // Check if 'handles' is invalid | ||
| 292 | if (handles == nullptr) | ||
| 293 | return Kernel::ERR_INVALID_POINTER; | ||
| 294 | |||
| 295 | // NOTE: on real hardware, there is no nullptr check for 'out' (tested with firmware 4.4). If | ||
| 296 | // this happens, the running application will crash. | ||
| 297 | ASSERT_MSG(out != nullptr, "invalid output pointer specified!"); | ||
| 298 | 88 | ||
| 299 | // Check if 'handle_count' is invalid | 89 | if (!handle) { |
| 300 | if (handle_count < 0) | 90 | switch (info_id) { |
| 301 | return Kernel::ERR_OUT_OF_RANGE; | 91 | case 0xB: |
| 302 | 92 | *result = 0; // Used for PRNG seed | |
| 303 | using ObjectPtr = Kernel::SharedPtr<Kernel::WaitObject>; | ||
| 304 | std::vector<ObjectPtr> objects(handle_count); | ||
| 305 | |||
| 306 | for (int i = 0; i < handle_count; ++i) { | ||
| 307 | auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handles[i]); | ||
| 308 | if (object == nullptr) | ||
| 309 | return ERR_INVALID_HANDLE; | ||
| 310 | objects[i] = object; | ||
| 311 | } | ||
| 312 | |||
| 313 | if (wait_all) { | ||
| 314 | bool all_available = | ||
| 315 | std::all_of(objects.begin(), objects.end(), | ||
| 316 | [thread](const ObjectPtr& object) { return !object->ShouldWait(thread); }); | ||
| 317 | if (all_available) { | ||
| 318 | // We can acquire all objects right now, do so. | ||
| 319 | for (auto& object : objects) | ||
| 320 | object->Acquire(thread); | ||
| 321 | // Note: In this case, the `out` parameter is not set, | ||
| 322 | // and retains whatever value it had before. | ||
| 323 | return RESULT_SUCCESS; | ||
| 324 | } | ||
| 325 | |||
| 326 | // Not all objects were available right now, prepare to suspend the thread. | ||
| 327 | |||
| 328 | // If a timeout value of 0 was provided, just return the Timeout error code instead of | ||
| 329 | // suspending the thread. | ||
| 330 | if (nano_seconds == 0) | ||
| 331 | return Kernel::RESULT_TIMEOUT; | ||
| 332 | |||
| 333 | // Put the thread to sleep | ||
| 334 | thread->status = THREADSTATUS_WAIT_SYNCH_ALL; | ||
| 335 | |||
| 336 | // Add the thread to each of the objects' waiting threads. | ||
| 337 | for (auto& object : objects) { | ||
| 338 | object->AddWaitingThread(thread); | ||
| 339 | } | ||
| 340 | |||
| 341 | thread->wait_objects = std::move(objects); | ||
| 342 | |||
| 343 | // Create an event to wake the thread up after the specified nanosecond delay has passed | ||
| 344 | thread->WakeAfterDelay(nano_seconds); | ||
| 345 | |||
| 346 | Core::System::GetInstance().PrepareReschedule(); | ||
| 347 | |||
| 348 | // This value gets set to -1 by default in this case, it is not modified after this. | ||
| 349 | *out = -1; | ||
| 350 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to | ||
| 351 | // a signal in one of its wait objects. | ||
| 352 | return Kernel::RESULT_TIMEOUT; | ||
| 353 | } else { | ||
| 354 | // Find the first object that is acquirable in the provided list of objects | ||
| 355 | auto itr = std::find_if(objects.begin(), objects.end(), [thread](const ObjectPtr& object) { | ||
| 356 | return !object->ShouldWait(thread); | ||
| 357 | }); | ||
| 358 | |||
| 359 | if (itr != objects.end()) { | ||
| 360 | // We found a ready object, acquire it and set the result value | ||
| 361 | Kernel::WaitObject* object = itr->get(); | ||
| 362 | object->Acquire(thread); | ||
| 363 | *out = std::distance(objects.begin(), itr); | ||
| 364 | return RESULT_SUCCESS; | 93 | return RESULT_SUCCESS; |
| 365 | } | 94 | } |
| 366 | |||
| 367 | // No objects were ready to be acquired, prepare to suspend the thread. | ||
| 368 | |||
| 369 | // If a timeout value of 0 was provided, just return the Timeout error code instead of | ||
| 370 | // suspending the thread. | ||
| 371 | if (nano_seconds == 0) | ||
| 372 | return Kernel::RESULT_TIMEOUT; | ||
| 373 | |||
| 374 | // Put the thread to sleep | ||
| 375 | thread->status = THREADSTATUS_WAIT_SYNCH_ANY; | ||
| 376 | |||
| 377 | // Add the thread to each of the objects' waiting threads. | ||
| 378 | for (size_t i = 0; i < objects.size(); ++i) { | ||
| 379 | Kernel::WaitObject* object = objects[i].get(); | ||
| 380 | object->AddWaitingThread(thread); | ||
| 381 | } | ||
| 382 | |||
| 383 | thread->wait_objects = std::move(objects); | ||
| 384 | |||
| 385 | // Note: If no handles and no timeout were given, then the thread will deadlock, this is | ||
| 386 | // consistent with hardware behavior. | ||
| 387 | |||
| 388 | // Create an event to wake the thread up after the specified nanosecond delay has passed | ||
| 389 | thread->WakeAfterDelay(nano_seconds); | ||
| 390 | |||
| 391 | Core::System::GetInstance().PrepareReschedule(); | ||
| 392 | |||
| 393 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a | ||
| 394 | // signal in one of its wait objects. | ||
| 395 | // Otherwise we retain the default value of timeout, and -1 in the out parameter | ||
| 396 | thread->wait_set_output = true; | ||
| 397 | *out = -1; | ||
| 398 | return Kernel::RESULT_TIMEOUT; | ||
| 399 | } | ||
| 400 | } | ||
| 401 | |||
| 402 | /// In a single operation, sends a IPC reply and waits for a new request. | ||
| 403 | static ResultCode ReplyAndReceive(s32* index, Kernel::Handle* handles, s32 handle_count, | ||
| 404 | Kernel::Handle reply_target) { | ||
| 405 | // 'handles' has to be a valid pointer even if 'handle_count' is 0. | ||
| 406 | if (handles == nullptr) | ||
| 407 | return Kernel::ERR_INVALID_POINTER; | ||
| 408 | |||
| 409 | // Check if 'handle_count' is invalid | ||
| 410 | if (handle_count < 0) | ||
| 411 | return Kernel::ERR_OUT_OF_RANGE; | ||
| 412 | |||
| 413 | using ObjectPtr = SharedPtr<Kernel::WaitObject>; | ||
| 414 | std::vector<ObjectPtr> objects(handle_count); | ||
| 415 | |||
| 416 | for (int i = 0; i < handle_count; ++i) { | ||
| 417 | auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handles[i]); | ||
| 418 | if (object == nullptr) | ||
| 419 | return ERR_INVALID_HANDLE; | ||
| 420 | objects[i] = object; | ||
| 421 | } | ||
| 422 | |||
| 423 | // We are also sending a command reply. | ||
| 424 | // Do not send a reply if the command id in the command buffer is 0xFFFF. | ||
| 425 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 426 | IPC::Header header{cmd_buff[0]}; | ||
| 427 | if (reply_target != 0 && header.command_id != 0xFFFF) { | ||
| 428 | auto session = Kernel::g_handle_table.Get<Kernel::ServerSession>(reply_target); | ||
| 429 | if (session == nullptr) | ||
| 430 | return ERR_INVALID_HANDLE; | ||
| 431 | |||
| 432 | auto request_thread = std::move(session->currently_handling); | ||
| 433 | |||
| 434 | // Mark the request as "handled". | ||
| 435 | session->currently_handling = nullptr; | ||
| 436 | |||
| 437 | // Error out if there's no request thread or the session was closed. | ||
| 438 | // TODO(Subv): Is the same error code (ClosedByRemote) returned for both of these cases? | ||
| 439 | if (request_thread == nullptr || session->parent->client == nullptr) { | ||
| 440 | *index = -1; | ||
| 441 | return Kernel::ERR_SESSION_CLOSED_BY_REMOTE; | ||
| 442 | } | ||
| 443 | |||
| 444 | // TODO(Subv): Perform IPC translation from the current thread to request_thread. | ||
| 445 | |||
| 446 | // Note: The scheduler is not invoked here. | ||
| 447 | request_thread->ResumeFromWait(); | ||
| 448 | } | ||
| 449 | |||
| 450 | if (handle_count == 0) { | ||
| 451 | *index = 0; | ||
| 452 | // The kernel uses this value as a placeholder for the real error, and returns it when we | ||
| 453 | // pass no handles and do not perform any reply. | ||
| 454 | if (reply_target == 0 || header.command_id == 0xFFFF) | ||
| 455 | return ResultCode(0xE7E3FFFF); | ||
| 456 | |||
| 457 | return RESULT_SUCCESS; | ||
| 458 | } | ||
| 459 | |||
| 460 | auto thread = Kernel::GetCurrentThread(); | ||
| 461 | |||
| 462 | // Find the first object that is acquirable in the provided list of objects | ||
| 463 | auto itr = std::find_if(objects.begin(), objects.end(), [thread](const ObjectPtr& object) { | ||
| 464 | return !object->ShouldWait(thread); | ||
| 465 | }); | ||
| 466 | |||
| 467 | if (itr != objects.end()) { | ||
| 468 | // We found a ready object, acquire it and set the result value | ||
| 469 | Kernel::WaitObject* object = itr->get(); | ||
| 470 | object->Acquire(thread); | ||
| 471 | *index = std::distance(objects.begin(), itr); | ||
| 472 | |||
| 473 | if (object->GetHandleType() == Kernel::HandleType::ServerSession) { | ||
| 474 | auto server_session = static_cast<Kernel::ServerSession*>(object); | ||
| 475 | if (server_session->parent->client == nullptr) | ||
| 476 | return Kernel::ERR_SESSION_CLOSED_BY_REMOTE; | ||
| 477 | |||
| 478 | // TODO(Subv): Perform IPC translation from the ServerSession to the current thread. | ||
| 479 | } | ||
| 480 | return RESULT_SUCCESS; | ||
| 481 | } | ||
| 482 | |||
| 483 | // No objects were ready to be acquired, prepare to suspend the thread. | ||
| 484 | |||
| 485 | // TODO(Subv): Perform IPC translation upon wakeup. | ||
| 486 | |||
| 487 | // Put the thread to sleep | ||
| 488 | thread->status = THREADSTATUS_WAIT_SYNCH_ANY; | ||
| 489 | |||
| 490 | // Add the thread to each of the objects' waiting threads. | ||
| 491 | for (size_t i = 0; i < objects.size(); ++i) { | ||
| 492 | Kernel::WaitObject* object = objects[i].get(); | ||
| 493 | object->AddWaitingThread(thread); | ||
| 494 | } | ||
| 495 | |||
| 496 | thread->wait_objects = std::move(objects); | ||
| 497 | |||
| 498 | Core::System::GetInstance().PrepareReschedule(); | ||
| 499 | |||
| 500 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a | ||
| 501 | // signal in one of its wait objects, or to 0xC8A01836 if there was a translation error. | ||
| 502 | // By default the index is set to -1. | ||
| 503 | thread->wait_set_output = true; | ||
| 504 | *index = -1; | ||
| 505 | return RESULT_SUCCESS; | ||
| 506 | } | ||
| 507 | |||
| 508 | /// Create an address arbiter (to allocate access to shared resources) | ||
| 509 | static ResultCode CreateAddressArbiter(Kernel::Handle* out_handle) { | ||
| 510 | using Kernel::AddressArbiter; | ||
| 511 | |||
| 512 | SharedPtr<AddressArbiter> arbiter = AddressArbiter::Create(); | ||
| 513 | CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(arbiter))); | ||
| 514 | LOG_TRACE(Kernel_SVC, "returned handle=0x%08X", *out_handle); | ||
| 515 | return RESULT_SUCCESS; | ||
| 516 | } | ||
| 517 | |||
| 518 | /// Arbitrate address | ||
| 519 | static ResultCode ArbitrateAddress(Kernel::Handle handle, u32 address, u32 type, u32 value, | ||
| 520 | s64 nanoseconds) { | ||
| 521 | using Kernel::AddressArbiter; | ||
| 522 | |||
| 523 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X, address=0x%08X, type=0x%08X, value=0x%08X", handle, | ||
| 524 | address, type, value); | ||
| 525 | |||
| 526 | SharedPtr<AddressArbiter> arbiter = Kernel::g_handle_table.Get<AddressArbiter>(handle); | ||
| 527 | if (arbiter == nullptr) | ||
| 528 | return ERR_INVALID_HANDLE; | ||
| 529 | |||
| 530 | auto res = arbiter->ArbitrateAddress(static_cast<Kernel::ArbitrationType>(type), address, value, | ||
| 531 | nanoseconds); | ||
| 532 | |||
| 533 | // TODO(Subv): Identify in which specific cases this call should cause a reschedule. | ||
| 534 | Core::System::GetInstance().PrepareReschedule(); | ||
| 535 | |||
| 536 | return res; | ||
| 537 | } | ||
| 538 | |||
| 539 | static void Break(u8 break_reason) { | ||
| 540 | LOG_CRITICAL(Debug_Emulated, "Emulated program broke execution!"); | ||
| 541 | std::string reason_str; | ||
| 542 | switch (break_reason) { | ||
| 543 | case 0: | ||
| 544 | reason_str = "PANIC"; | ||
| 545 | break; | ||
| 546 | case 1: | ||
| 547 | reason_str = "ASSERT"; | ||
| 548 | break; | ||
| 549 | case 2: | ||
| 550 | reason_str = "USER"; | ||
| 551 | break; | ||
| 552 | default: | ||
| 553 | reason_str = "UNKNOWN"; | ||
| 554 | break; | ||
| 555 | } | 95 | } |
| 556 | LOG_CRITICAL(Debug_Emulated, "Break reason: %s", reason_str.c_str()); | ||
| 557 | } | ||
| 558 | |||
| 559 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit | ||
| 560 | static void OutputDebugString(const char* string, int len) { | ||
| 561 | LOG_DEBUG(Debug_Emulated, "%.*s", len, string); | ||
| 562 | } | ||
| 563 | |||
| 564 | /// Get resource limit | ||
| 565 | static ResultCode GetResourceLimit(Kernel::Handle* resource_limit, Kernel::Handle process_handle) { | ||
| 566 | LOG_TRACE(Kernel_SVC, "called process=0x%08X", process_handle); | ||
| 567 | |||
| 568 | SharedPtr<Kernel::Process> process = | ||
| 569 | Kernel::g_handle_table.Get<Kernel::Process>(process_handle); | ||
| 570 | if (process == nullptr) | ||
| 571 | return ERR_INVALID_HANDLE; | ||
| 572 | |||
| 573 | CASCADE_RESULT(*resource_limit, Kernel::g_handle_table.Create(process->resource_limit)); | ||
| 574 | |||
| 575 | return RESULT_SUCCESS; | ||
| 576 | } | ||
| 577 | |||
| 578 | /// Get resource limit current values | ||
| 579 | static ResultCode GetResourceLimitCurrentValues(s64* values, Kernel::Handle resource_limit_handle, | ||
| 580 | u32* names, u32 name_count) { | ||
| 581 | LOG_TRACE(Kernel_SVC, "called resource_limit=%08X, names=%p, name_count=%d", | ||
| 582 | resource_limit_handle, names, name_count); | ||
| 583 | |||
| 584 | SharedPtr<Kernel::ResourceLimit> resource_limit = | ||
| 585 | Kernel::g_handle_table.Get<Kernel::ResourceLimit>(resource_limit_handle); | ||
| 586 | if (resource_limit == nullptr) | ||
| 587 | return ERR_INVALID_HANDLE; | ||
| 588 | |||
| 589 | for (unsigned int i = 0; i < name_count; ++i) | ||
| 590 | values[i] = resource_limit->GetCurrentResourceValue(names[i]); | ||
| 591 | |||
| 592 | return RESULT_SUCCESS; | ||
| 593 | } | ||
| 594 | |||
| 595 | /// Get resource limit max values | ||
| 596 | static ResultCode GetResourceLimitLimitValues(s64* values, Kernel::Handle resource_limit_handle, | ||
| 597 | u32* names, u32 name_count) { | ||
| 598 | LOG_TRACE(Kernel_SVC, "called resource_limit=%08X, names=%p, name_count=%d", | ||
| 599 | resource_limit_handle, names, name_count); | ||
| 600 | |||
| 601 | SharedPtr<Kernel::ResourceLimit> resource_limit = | ||
| 602 | Kernel::g_handle_table.Get<Kernel::ResourceLimit>(resource_limit_handle); | ||
| 603 | if (resource_limit == nullptr) | ||
| 604 | return ERR_INVALID_HANDLE; | ||
| 605 | |||
| 606 | for (unsigned int i = 0; i < name_count; ++i) | ||
| 607 | values[i] = resource_limit->GetMaxResourceValue(names[i]); | ||
| 608 | |||
| 609 | return RESULT_SUCCESS; | 96 | return RESULT_SUCCESS; |
| 610 | } | 97 | } |
| 611 | 98 | ||
| 612 | /// Creates a new thread | ||
| 613 | static ResultCode CreateThread(Kernel::Handle* out_handle, u32 priority, u32 entry_point, u32 arg, | ||
| 614 | u32 stack_top, s32 processor_id) { | ||
| 615 | using Kernel::Thread; | ||
| 616 | |||
| 617 | std::string name = Common::StringFromFormat("unknown-%08" PRIX32, entry_point); | ||
| 618 | |||
| 619 | if (priority > THREADPRIO_LOWEST) { | ||
| 620 | return Kernel::ERR_OUT_OF_RANGE; | ||
| 621 | } | ||
| 622 | |||
| 623 | using Kernel::ResourceLimit; | ||
| 624 | Kernel::SharedPtr<ResourceLimit>& resource_limit = Kernel::g_current_process->resource_limit; | ||
| 625 | if (resource_limit->GetMaxResourceValue(Kernel::ResourceTypes::PRIORITY) > priority) { | ||
| 626 | return Kernel::ERR_NOT_AUTHORIZED; | ||
| 627 | } | ||
| 628 | |||
| 629 | switch (processor_id) { | ||
| 630 | case THREADPROCESSORID_ALL: | ||
| 631 | case THREADPROCESSORID_DEFAULT: | ||
| 632 | case THREADPROCESSORID_0: | ||
| 633 | case THREADPROCESSORID_1: | ||
| 634 | break; | ||
| 635 | default: | ||
| 636 | // TODO(bunnei): Implement support for other processor IDs | ||
| 637 | ASSERT_MSG(false, "Unsupported thread processor ID: %d", processor_id); | ||
| 638 | break; | ||
| 639 | } | ||
| 640 | |||
| 641 | if (processor_id == THREADPROCESSORID_ALL) { | ||
| 642 | LOG_INFO(Kernel_SVC, | ||
| 643 | "Newly created thread is allowed to be run in any Core, unimplemented."); | ||
| 644 | } | ||
| 645 | |||
| 646 | if (processor_id == THREADPROCESSORID_DEFAULT && | ||
| 647 | Kernel::g_current_process->ideal_processor == THREADPROCESSORID_1) { | ||
| 648 | LOG_WARNING( | ||
| 649 | Kernel_SVC, | ||
| 650 | "Newly created thread is allowed to be run in the SysCore (Core1), unimplemented."); | ||
| 651 | } | ||
| 652 | |||
| 653 | if (processor_id == THREADPROCESSORID_1) { | ||
| 654 | LOG_ERROR(Kernel_SVC, | ||
| 655 | "Newly created thread must run in the SysCore (Core1), unimplemented."); | ||
| 656 | } | ||
| 657 | |||
| 658 | CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create(name, entry_point, priority, | ||
| 659 | arg, processor_id, stack_top)); | ||
| 660 | |||
| 661 | thread->context.fpscr = | ||
| 662 | FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO; // 0x03C00000 | ||
| 663 | |||
| 664 | CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread))); | ||
| 665 | |||
| 666 | Core::System::GetInstance().PrepareReschedule(); | ||
| 667 | |||
| 668 | LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " | ||
| 669 | "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", | ||
| 670 | entry_point, name.c_str(), arg, stack_top, priority, processor_id, *out_handle); | ||
| 671 | |||
| 672 | return RESULT_SUCCESS; | ||
| 673 | } | ||
| 674 | |||
| 675 | /// Called when a thread exits | ||
| 676 | static void ExitThread() { | ||
| 677 | LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::CPU().GetPC()); | ||
| 678 | |||
| 679 | Kernel::ExitCurrentThread(); | ||
| 680 | Core::System::GetInstance().PrepareReschedule(); | ||
| 681 | } | ||
| 682 | |||
| 683 | /// Gets the priority for the specified thread | 99 | /// Gets the priority for the specified thread |
| 684 | static ResultCode GetThreadPriority(s32* priority, Kernel::Handle handle) { | 100 | static ResultCode GetThreadPriority(s32* priority, Kernel::Handle handle) { |
| 101 | LOG_INFO(Kernel_SVC, "called, handle=0x%08X", handle); | ||
| 685 | const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); | 102 | const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); |
| 686 | if (thread == nullptr) | 103 | *priority = thread ? thread->GetPriority() : 0; |
| 687 | return ERR_INVALID_HANDLE; | ||
| 688 | |||
| 689 | *priority = thread->GetPriority(); | ||
| 690 | return RESULT_SUCCESS; | ||
| 691 | } | ||
| 692 | |||
| 693 | /// Sets the priority for the specified thread | ||
| 694 | static ResultCode SetThreadPriority(Kernel::Handle handle, s32 priority) { | ||
| 695 | if (priority > THREADPRIO_LOWEST) { | ||
| 696 | return Kernel::ERR_OUT_OF_RANGE; | ||
| 697 | } | ||
| 698 | |||
| 699 | SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); | ||
| 700 | if (thread == nullptr) | ||
| 701 | return ERR_INVALID_HANDLE; | ||
| 702 | |||
| 703 | using Kernel::ResourceLimit; | ||
| 704 | // Note: The kernel uses the current process's resource limit instead of | ||
| 705 | // the one from the thread owner's resource limit. | ||
| 706 | Kernel::SharedPtr<ResourceLimit>& resource_limit = Kernel::g_current_process->resource_limit; | ||
| 707 | if (resource_limit->GetMaxResourceValue(Kernel::ResourceTypes::PRIORITY) > priority) { | ||
| 708 | return Kernel::ERR_NOT_AUTHORIZED; | ||
| 709 | } | ||
| 710 | |||
| 711 | thread->SetPriority(priority); | ||
| 712 | thread->UpdatePriority(); | ||
| 713 | |||
| 714 | // Update the mutexes that this thread is waiting for | ||
| 715 | for (auto& mutex : thread->pending_mutexes) | ||
| 716 | mutex->UpdatePriority(); | ||
| 717 | |||
| 718 | Core::System::GetInstance().PrepareReschedule(); | ||
| 719 | return RESULT_SUCCESS; | ||
| 720 | } | ||
| 721 | |||
| 722 | /// Create a mutex | ||
| 723 | static ResultCode CreateMutex(Kernel::Handle* out_handle, u32 initial_locked) { | ||
| 724 | using Kernel::Mutex; | ||
| 725 | |||
| 726 | SharedPtr<Mutex> mutex = Mutex::Create(initial_locked != 0); | ||
| 727 | mutex->name = Common::StringFromFormat("mutex-%08x", Core::CPU().GetReg(14)); | ||
| 728 | CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(mutex))); | ||
| 729 | |||
| 730 | LOG_TRACE(Kernel_SVC, "called initial_locked=%s : created handle=0x%08X", | ||
| 731 | initial_locked ? "true" : "false", *out_handle); | ||
| 732 | |||
| 733 | return RESULT_SUCCESS; | ||
| 734 | } | ||
| 735 | |||
| 736 | /// Release a mutex | ||
| 737 | static ResultCode ReleaseMutex(Kernel::Handle handle) { | ||
| 738 | using Kernel::Mutex; | ||
| 739 | |||
| 740 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X", handle); | ||
| 741 | |||
| 742 | SharedPtr<Mutex> mutex = Kernel::g_handle_table.Get<Mutex>(handle); | ||
| 743 | if (mutex == nullptr) | ||
| 744 | return ERR_INVALID_HANDLE; | ||
| 745 | |||
| 746 | mutex->Release(); | ||
| 747 | |||
| 748 | return RESULT_SUCCESS; | ||
| 749 | } | ||
| 750 | |||
| 751 | /// Get the ID of the specified process | ||
| 752 | static ResultCode GetProcessId(u32* process_id, Kernel::Handle process_handle) { | ||
| 753 | LOG_TRACE(Kernel_SVC, "called process=0x%08X", process_handle); | ||
| 754 | |||
| 755 | const SharedPtr<Kernel::Process> process = | ||
| 756 | Kernel::g_handle_table.Get<Kernel::Process>(process_handle); | ||
| 757 | if (process == nullptr) | ||
| 758 | return ERR_INVALID_HANDLE; | ||
| 759 | |||
| 760 | *process_id = process->process_id; | ||
| 761 | return RESULT_SUCCESS; | ||
| 762 | } | ||
| 763 | |||
| 764 | /// Get the ID of the process that owns the specified thread | ||
| 765 | static ResultCode GetProcessIdOfThread(u32* process_id, Kernel::Handle thread_handle) { | ||
| 766 | LOG_TRACE(Kernel_SVC, "called thread=0x%08X", thread_handle); | ||
| 767 | |||
| 768 | const SharedPtr<Kernel::Thread> thread = | ||
| 769 | Kernel::g_handle_table.Get<Kernel::Thread>(thread_handle); | ||
| 770 | if (thread == nullptr) | ||
| 771 | return ERR_INVALID_HANDLE; | ||
| 772 | |||
| 773 | const SharedPtr<Kernel::Process> process = thread->owner_process; | ||
| 774 | |||
| 775 | ASSERT_MSG(process != nullptr, "Invalid parent process for thread=0x%08X", thread_handle); | ||
| 776 | |||
| 777 | *process_id = process->process_id; | ||
| 778 | return RESULT_SUCCESS; | ||
| 779 | } | ||
| 780 | |||
| 781 | /// Get the ID for the specified thread. | ||
| 782 | static ResultCode GetThreadId(u32* thread_id, Kernel::Handle handle) { | ||
| 783 | LOG_TRACE(Kernel_SVC, "called thread=0x%08X", handle); | ||
| 784 | |||
| 785 | const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); | ||
| 786 | if (thread == nullptr) | ||
| 787 | return ERR_INVALID_HANDLE; | ||
| 788 | |||
| 789 | *thread_id = thread->GetThreadId(); | ||
| 790 | return RESULT_SUCCESS; | ||
| 791 | } | ||
| 792 | |||
| 793 | /// Creates a semaphore | ||
| 794 | static ResultCode CreateSemaphore(Kernel::Handle* out_handle, s32 initial_count, s32 max_count) { | ||
| 795 | using Kernel::Semaphore; | ||
| 796 | |||
| 797 | CASCADE_RESULT(SharedPtr<Semaphore> semaphore, Semaphore::Create(initial_count, max_count)); | ||
| 798 | semaphore->name = Common::StringFromFormat("semaphore-%08x", Core::CPU().GetReg(14)); | ||
| 799 | CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(semaphore))); | ||
| 800 | |||
| 801 | LOG_TRACE(Kernel_SVC, "called initial_count=%d, max_count=%d, created handle=0x%08X", | ||
| 802 | initial_count, max_count, *out_handle); | ||
| 803 | return RESULT_SUCCESS; | ||
| 804 | } | ||
| 805 | |||
| 806 | /// Releases a certain number of slots in a semaphore | ||
| 807 | static ResultCode ReleaseSemaphore(s32* count, Kernel::Handle handle, s32 release_count) { | ||
| 808 | using Kernel::Semaphore; | ||
| 809 | |||
| 810 | LOG_TRACE(Kernel_SVC, "called release_count=%d, handle=0x%08X", release_count, handle); | ||
| 811 | |||
| 812 | SharedPtr<Semaphore> semaphore = Kernel::g_handle_table.Get<Semaphore>(handle); | ||
| 813 | if (semaphore == nullptr) | ||
| 814 | return ERR_INVALID_HANDLE; | ||
| 815 | |||
| 816 | CASCADE_RESULT(*count, semaphore->Release(release_count)); | ||
| 817 | |||
| 818 | return RESULT_SUCCESS; | 104 | return RESULT_SUCCESS; |
| 819 | } | 105 | } |
| 820 | 106 | ||
| 821 | /// Query process memory | 107 | /// Query process memory |
| 822 | static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* page_info, | 108 | static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/, |
| 823 | Kernel::Handle process_handle, u32 addr) { | 109 | Kernel::Handle process_handle, u64 addr) { |
| 824 | using Kernel::Process; | 110 | using Kernel::Process; |
| 825 | Kernel::SharedPtr<Process> process = Kernel::g_handle_table.Get<Process>(process_handle); | 111 | Kernel::SharedPtr<Process> process = Kernel::g_handle_table.Get<Process>(process_handle); |
| 826 | if (process == nullptr) | 112 | if (process == nullptr) |
| @@ -829,131 +115,32 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* page_inf | |||
| 829 | auto vma = process->vm_manager.FindVMA(addr); | 115 | auto vma = process->vm_manager.FindVMA(addr); |
| 830 | 116 | ||
| 831 | if (vma == Kernel::g_current_process->vm_manager.vma_map.end()) | 117 | if (vma == Kernel::g_current_process->vm_manager.vma_map.end()) |
| 832 | return Kernel::ERR_INVALID_ADDRESS; | 118 | { |
| 119 | //return Kernel::ERR_INVALID_ADDRESS; | ||
| 120 | |||
| 121 | memory_info->base_address = 0; | ||
| 122 | memory_info->permission = static_cast<u64>(Kernel::VMAPermission::None); | ||
| 123 | memory_info->size = 0; | ||
| 124 | memory_info->state = static_cast<u64>(Kernel::MemoryState::Free); | ||
| 125 | |||
| 126 | return RESULT_SUCCESS; | ||
| 127 | } | ||
| 833 | 128 | ||
| 834 | memory_info->base_address = vma->second.base; | 129 | memory_info->base_address = vma->second.base; |
| 835 | memory_info->permission = static_cast<u32>(vma->second.permissions); | 130 | memory_info->permission = static_cast<u64>(vma->second.permissions); |
| 836 | memory_info->size = vma->second.size; | 131 | memory_info->size = vma->second.size; |
| 837 | memory_info->state = static_cast<u32>(vma->second.meminfo_state); | 132 | memory_info->state = static_cast<u64>(vma->second.meminfo_state); |
| 838 | 133 | ||
| 839 | page_info->flags = 0; | 134 | LOG_TRACE(Kernel_SVC, "called process=0x%08X addr=%llx", process_handle, addr); |
| 840 | LOG_TRACE(Kernel_SVC, "called process=0x%08X addr=0x%08X", process_handle, addr); | ||
| 841 | return RESULT_SUCCESS; | 135 | return RESULT_SUCCESS; |
| 842 | } | 136 | } |
| 843 | 137 | ||
| 844 | /// Query memory | 138 | /// Query memory |
| 845 | static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, u32 addr) { | 139 | static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAddr addr) { |
| 140 | LOG_INFO(Kernel_SVC, "called, addr=%llx", addr); | ||
| 846 | return QueryProcessMemory(memory_info, page_info, Kernel::CurrentProcess, addr); | 141 | return QueryProcessMemory(memory_info, page_info, Kernel::CurrentProcess, addr); |
| 847 | } | 142 | } |
| 848 | 143 | ||
| 849 | /// Create an event | ||
| 850 | static ResultCode CreateEvent(Kernel::Handle* out_handle, u32 reset_type) { | ||
| 851 | using Kernel::Event; | ||
| 852 | |||
| 853 | SharedPtr<Event> evt = Event::Create(static_cast<Kernel::ResetType>(reset_type)); | ||
| 854 | evt->name = Common::StringFromFormat("event-%08x", Core::CPU().GetReg(14)); | ||
| 855 | CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(evt))); | ||
| 856 | |||
| 857 | LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X", reset_type, | ||
| 858 | *out_handle); | ||
| 859 | return RESULT_SUCCESS; | ||
| 860 | } | ||
| 861 | |||
| 862 | /// Duplicates a kernel handle | ||
| 863 | static ResultCode DuplicateHandle(Kernel::Handle* out, Kernel::Handle handle) { | ||
| 864 | CASCADE_RESULT(*out, Kernel::g_handle_table.Duplicate(handle)); | ||
| 865 | LOG_TRACE(Kernel_SVC, "duplicated 0x%08X to 0x%08X", handle, *out); | ||
| 866 | return RESULT_SUCCESS; | ||
| 867 | } | ||
| 868 | |||
| 869 | /// Signals an event | ||
| 870 | static ResultCode SignalEvent(Kernel::Handle handle) { | ||
| 871 | using Kernel::Event; | ||
| 872 | LOG_TRACE(Kernel_SVC, "called event=0x%08X", handle); | ||
| 873 | |||
| 874 | SharedPtr<Event> evt = Kernel::g_handle_table.Get<Kernel::Event>(handle); | ||
| 875 | if (evt == nullptr) | ||
| 876 | return ERR_INVALID_HANDLE; | ||
| 877 | |||
| 878 | evt->Signal(); | ||
| 879 | |||
| 880 | return RESULT_SUCCESS; | ||
| 881 | } | ||
| 882 | |||
| 883 | /// Clears an event | ||
| 884 | static ResultCode ClearEvent(Kernel::Handle handle) { | ||
| 885 | using Kernel::Event; | ||
| 886 | LOG_TRACE(Kernel_SVC, "called event=0x%08X", handle); | ||
| 887 | |||
| 888 | SharedPtr<Event> evt = Kernel::g_handle_table.Get<Kernel::Event>(handle); | ||
| 889 | if (evt == nullptr) | ||
| 890 | return ERR_INVALID_HANDLE; | ||
| 891 | |||
| 892 | evt->Clear(); | ||
| 893 | return RESULT_SUCCESS; | ||
| 894 | } | ||
| 895 | |||
| 896 | /// Creates a timer | ||
| 897 | static ResultCode CreateTimer(Kernel::Handle* out_handle, u32 reset_type) { | ||
| 898 | using Kernel::Timer; | ||
| 899 | |||
| 900 | SharedPtr<Timer> timer = Timer::Create(static_cast<Kernel::ResetType>(reset_type)); | ||
| 901 | timer->name = Common::StringFromFormat("timer-%08x", Core::CPU().GetReg(14)); | ||
| 902 | CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(timer))); | ||
| 903 | |||
| 904 | LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X", reset_type, | ||
| 905 | *out_handle); | ||
| 906 | return RESULT_SUCCESS; | ||
| 907 | } | ||
| 908 | |||
| 909 | /// Clears a timer | ||
| 910 | static ResultCode ClearTimer(Kernel::Handle handle) { | ||
| 911 | using Kernel::Timer; | ||
| 912 | |||
| 913 | LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); | ||
| 914 | |||
| 915 | SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle); | ||
| 916 | if (timer == nullptr) | ||
| 917 | return ERR_INVALID_HANDLE; | ||
| 918 | |||
| 919 | timer->Clear(); | ||
| 920 | return RESULT_SUCCESS; | ||
| 921 | } | ||
| 922 | |||
| 923 | /// Starts a timer | ||
| 924 | static ResultCode SetTimer(Kernel::Handle handle, s64 initial, s64 interval) { | ||
| 925 | using Kernel::Timer; | ||
| 926 | |||
| 927 | LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); | ||
| 928 | |||
| 929 | if (initial < 0 || interval < 0) { | ||
| 930 | return Kernel::ERR_OUT_OF_RANGE_KERNEL; | ||
| 931 | } | ||
| 932 | |||
| 933 | SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle); | ||
| 934 | if (timer == nullptr) | ||
| 935 | return ERR_INVALID_HANDLE; | ||
| 936 | |||
| 937 | timer->Set(initial, interval); | ||
| 938 | |||
| 939 | return RESULT_SUCCESS; | ||
| 940 | } | ||
| 941 | |||
| 942 | /// Cancels a timer | ||
| 943 | static ResultCode CancelTimer(Kernel::Handle handle) { | ||
| 944 | using Kernel::Timer; | ||
| 945 | |||
| 946 | LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); | ||
| 947 | |||
| 948 | SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle); | ||
| 949 | if (timer == nullptr) | ||
| 950 | return ERR_INVALID_HANDLE; | ||
| 951 | |||
| 952 | timer->Cancel(); | ||
| 953 | |||
| 954 | return RESULT_SUCCESS; | ||
| 955 | } | ||
| 956 | |||
| 957 | /// Sleep the current thread | 144 | /// Sleep the current thread |
| 958 | static void SleepThread(s64 nanoseconds) { | 145 | static void SleepThread(s64 nanoseconds) { |
| 959 | LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds); | 146 | LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds); |
| @@ -972,212 +159,16 @@ static void SleepThread(s64 nanoseconds) { | |||
| 972 | Core::System::GetInstance().PrepareReschedule(); | 159 | Core::System::GetInstance().PrepareReschedule(); |
| 973 | } | 160 | } |
| 974 | 161 | ||
| 975 | /// This returns the total CPU ticks elapsed since the CPU was powered-on | 162 | /// Signal process wide key |
| 976 | static s64 GetSystemTick() { | 163 | static ResultCode SignalProcessWideKey(VAddr address, u32 target) { |
| 977 | s64 result = CoreTiming::GetTicks(); | 164 | LOG_INFO(Kernel_SVC, "called, address=0x%llx, target=0x%08x", address, target); |
| 978 | // Advance time to defeat dumb games (like Cubic Ninja) that busy-wait for the frame to end. | ||
| 979 | Core::CPU().AddTicks(150); // Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b | ||
| 980 | return result; | ||
| 981 | } | ||
| 982 | |||
| 983 | /// Creates a memory block at the specified address with the specified permissions and size | ||
| 984 | static ResultCode CreateMemoryBlock(Kernel::Handle* out_handle, u32 addr, u32 size, | ||
| 985 | u32 my_permission, u32 other_permission) { | ||
| 986 | using Kernel::SharedMemory; | ||
| 987 | |||
| 988 | if (size % Memory::PAGE_SIZE != 0) | ||
| 989 | return Kernel::ERR_MISALIGNED_SIZE; | ||
| 990 | |||
| 991 | SharedPtr<SharedMemory> shared_memory = nullptr; | ||
| 992 | |||
| 993 | using Kernel::MemoryPermission; | ||
| 994 | auto VerifyPermissions = [](MemoryPermission permission) { | ||
| 995 | // SharedMemory blocks can not be created with Execute permissions | ||
| 996 | switch (permission) { | ||
| 997 | case MemoryPermission::None: | ||
| 998 | case MemoryPermission::Read: | ||
| 999 | case MemoryPermission::Write: | ||
| 1000 | case MemoryPermission::ReadWrite: | ||
| 1001 | case MemoryPermission::DontCare: | ||
| 1002 | return true; | ||
| 1003 | default: | ||
| 1004 | return false; | ||
| 1005 | } | ||
| 1006 | }; | ||
| 1007 | |||
| 1008 | if (!VerifyPermissions(static_cast<MemoryPermission>(my_permission)) || | ||
| 1009 | !VerifyPermissions(static_cast<MemoryPermission>(other_permission))) | ||
| 1010 | return Kernel::ERR_INVALID_COMBINATION; | ||
| 1011 | |||
| 1012 | // TODO(Subv): Processes with memory type APPLICATION are not allowed | ||
| 1013 | // to create memory blocks with addr = 0, any attempts to do so | ||
| 1014 | // should return error 0xD92007EA. | ||
| 1015 | if ((addr < Memory::PROCESS_IMAGE_VADDR || addr + size > Memory::SHARED_MEMORY_VADDR_END) && | ||
| 1016 | addr != 0) { | ||
| 1017 | return Kernel::ERR_INVALID_ADDRESS; | ||
| 1018 | } | ||
| 1019 | |||
| 1020 | // When trying to create a memory block with address = 0, | ||
| 1021 | // if the process has the Shared Device Memory flag in the exheader, | ||
| 1022 | // then we have to allocate from the same region as the caller process instead of the BASE | ||
| 1023 | // region. | ||
| 1024 | Kernel::MemoryRegion region = Kernel::MemoryRegion::BASE; | ||
| 1025 | if (addr == 0 && Kernel::g_current_process->flags.shared_device_mem) | ||
| 1026 | region = Kernel::g_current_process->flags.memory_region; | ||
| 1027 | |||
| 1028 | shared_memory = SharedMemory::Create( | ||
| 1029 | Kernel::g_current_process, size, static_cast<MemoryPermission>(my_permission), | ||
| 1030 | static_cast<MemoryPermission>(other_permission), addr, region); | ||
| 1031 | CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(shared_memory))); | ||
| 1032 | |||
| 1033 | LOG_WARNING(Kernel_SVC, "called addr=0x%08X", addr); | ||
| 1034 | return RESULT_SUCCESS; | 165 | return RESULT_SUCCESS; |
| 1035 | } | 166 | } |
| 1036 | 167 | ||
| 1037 | static ResultCode CreatePort(Kernel::Handle* server_port, Kernel::Handle* client_port, | 168 | /// Close a handle |
| 1038 | const char* name, u32 max_sessions) { | 169 | static ResultCode CloseHandle(Kernel::Handle handle) { |
| 1039 | // TODO(Subv): Implement named ports. | 170 | LOG_TRACE(Kernel_SVC, "Closing handle 0x%08X", handle); |
| 1040 | ASSERT_MSG(name == nullptr, "Named ports are currently unimplemented"); | 171 | return Kernel::g_handle_table.Close(handle); |
| 1041 | |||
| 1042 | using Kernel::ServerPort; | ||
| 1043 | using Kernel::ClientPort; | ||
| 1044 | |||
| 1045 | auto ports = ServerPort::CreatePortPair(max_sessions); | ||
| 1046 | CASCADE_RESULT(*client_port, Kernel::g_handle_table.Create( | ||
| 1047 | std::move(std::get<SharedPtr<ClientPort>>(ports)))); | ||
| 1048 | // Note: The 3DS kernel also leaks the client port handle if the server port handle fails to be | ||
| 1049 | // created. | ||
| 1050 | CASCADE_RESULT(*server_port, Kernel::g_handle_table.Create( | ||
| 1051 | std::move(std::get<SharedPtr<ServerPort>>(ports)))); | ||
| 1052 | |||
| 1053 | LOG_TRACE(Kernel_SVC, "called max_sessions=%u", max_sessions); | ||
| 1054 | return RESULT_SUCCESS; | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | static ResultCode CreateSessionToPort(Handle* out_client_session, Handle client_port_handle) { | ||
| 1058 | using Kernel::ClientPort; | ||
| 1059 | SharedPtr<ClientPort> client_port = Kernel::g_handle_table.Get<ClientPort>(client_port_handle); | ||
| 1060 | if (client_port == nullptr) | ||
| 1061 | return ERR_INVALID_HANDLE; | ||
| 1062 | |||
| 1063 | CASCADE_RESULT(auto session, client_port->Connect()); | ||
| 1064 | CASCADE_RESULT(*out_client_session, Kernel::g_handle_table.Create(std::move(session))); | ||
| 1065 | return RESULT_SUCCESS; | ||
| 1066 | } | ||
| 1067 | |||
| 1068 | static ResultCode CreateSession(Handle* server_session, Handle* client_session) { | ||
| 1069 | auto sessions = Kernel::ServerSession::CreateSessionPair(); | ||
| 1070 | |||
| 1071 | auto& server = std::get<SharedPtr<Kernel::ServerSession>>(sessions); | ||
| 1072 | CASCADE_RESULT(*server_session, Kernel::g_handle_table.Create(std::move(server))); | ||
| 1073 | |||
| 1074 | auto& client = std::get<SharedPtr<Kernel::ClientSession>>(sessions); | ||
| 1075 | CASCADE_RESULT(*client_session, Kernel::g_handle_table.Create(std::move(client))); | ||
| 1076 | |||
| 1077 | LOG_TRACE(Kernel_SVC, "called"); | ||
| 1078 | return RESULT_SUCCESS; | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | static ResultCode AcceptSession(Handle* out_server_session, Handle server_port_handle) { | ||
| 1082 | using Kernel::ServerPort; | ||
| 1083 | SharedPtr<ServerPort> server_port = Kernel::g_handle_table.Get<ServerPort>(server_port_handle); | ||
| 1084 | if (server_port == nullptr) | ||
| 1085 | return ERR_INVALID_HANDLE; | ||
| 1086 | |||
| 1087 | CASCADE_RESULT(auto session, server_port->Accept()); | ||
| 1088 | CASCADE_RESULT(*out_server_session, Kernel::g_handle_table.Create(std::move(session))); | ||
| 1089 | return RESULT_SUCCESS; | ||
| 1090 | } | ||
| 1091 | |||
| 1092 | static ResultCode GetSystemInfo(s64* out, u32 type, s32 param) { | ||
| 1093 | using Kernel::MemoryRegion; | ||
| 1094 | |||
| 1095 | LOG_TRACE(Kernel_SVC, "called type=%u param=%d", type, param); | ||
| 1096 | |||
| 1097 | switch ((SystemInfoType)type) { | ||
| 1098 | case SystemInfoType::REGION_MEMORY_USAGE: | ||
| 1099 | switch ((SystemInfoMemUsageRegion)param) { | ||
| 1100 | case SystemInfoMemUsageRegion::ALL: | ||
| 1101 | *out = Kernel::GetMemoryRegion(Kernel::MemoryRegion::APPLICATION)->used + | ||
| 1102 | Kernel::GetMemoryRegion(Kernel::MemoryRegion::SYSTEM)->used + | ||
| 1103 | Kernel::GetMemoryRegion(Kernel::MemoryRegion::BASE)->used; | ||
| 1104 | break; | ||
| 1105 | case SystemInfoMemUsageRegion::APPLICATION: | ||
| 1106 | *out = Kernel::GetMemoryRegion(Kernel::MemoryRegion::APPLICATION)->used; | ||
| 1107 | break; | ||
| 1108 | case SystemInfoMemUsageRegion::SYSTEM: | ||
| 1109 | *out = Kernel::GetMemoryRegion(Kernel::MemoryRegion::SYSTEM)->used; | ||
| 1110 | break; | ||
| 1111 | case SystemInfoMemUsageRegion::BASE: | ||
| 1112 | *out = Kernel::GetMemoryRegion(Kernel::MemoryRegion::BASE)->used; | ||
| 1113 | break; | ||
| 1114 | default: | ||
| 1115 | LOG_ERROR(Kernel_SVC, "unknown GetSystemInfo type=0 region: param=%d", param); | ||
| 1116 | *out = 0; | ||
| 1117 | break; | ||
| 1118 | } | ||
| 1119 | break; | ||
| 1120 | case SystemInfoType::KERNEL_ALLOCATED_PAGES: | ||
| 1121 | LOG_ERROR(Kernel_SVC, "unimplemented GetSystemInfo type=2 param=%d", param); | ||
| 1122 | *out = 0; | ||
| 1123 | break; | ||
| 1124 | case SystemInfoType::KERNEL_SPAWNED_PIDS: | ||
| 1125 | *out = 5; | ||
| 1126 | break; | ||
| 1127 | default: | ||
| 1128 | LOG_ERROR(Kernel_SVC, "unknown GetSystemInfo type=%u param=%d", type, param); | ||
| 1129 | *out = 0; | ||
| 1130 | break; | ||
| 1131 | } | ||
| 1132 | |||
| 1133 | // This function never returns an error, even if invalid parameters were passed. | ||
| 1134 | return RESULT_SUCCESS; | ||
| 1135 | } | ||
| 1136 | |||
| 1137 | static ResultCode GetProcessInfo(s64* out, Kernel::Handle process_handle, u32 type) { | ||
| 1138 | LOG_TRACE(Kernel_SVC, "called process=0x%08X type=%u", process_handle, type); | ||
| 1139 | |||
| 1140 | using Kernel::Process; | ||
| 1141 | Kernel::SharedPtr<Process> process = Kernel::g_handle_table.Get<Process>(process_handle); | ||
| 1142 | if (process == nullptr) | ||
| 1143 | return ERR_INVALID_HANDLE; | ||
| 1144 | |||
| 1145 | switch (type) { | ||
| 1146 | case 0: | ||
| 1147 | case 2: | ||
| 1148 | // TODO(yuriks): Type 0 returns a slightly higher number than type 2, but I'm not sure | ||
| 1149 | // what's the difference between them. | ||
| 1150 | *out = process->heap_used + process->linear_heap_used + process->misc_memory_used; | ||
| 1151 | if (*out % Memory::PAGE_SIZE != 0) { | ||
| 1152 | LOG_ERROR(Kernel_SVC, "called, memory size not page-aligned"); | ||
| 1153 | return Kernel::ERR_MISALIGNED_SIZE; | ||
| 1154 | } | ||
| 1155 | break; | ||
| 1156 | case 1: | ||
| 1157 | case 3: | ||
| 1158 | case 4: | ||
| 1159 | case 5: | ||
| 1160 | case 6: | ||
| 1161 | case 7: | ||
| 1162 | case 8: | ||
| 1163 | // These are valid, but not implemented yet | ||
| 1164 | LOG_ERROR(Kernel_SVC, "unimplemented GetProcessInfo type=%u", type); | ||
| 1165 | break; | ||
| 1166 | case 20: | ||
| 1167 | *out = Memory::FCRAM_PADDR - process->GetLinearHeapBase(); | ||
| 1168 | break; | ||
| 1169 | case 21: | ||
| 1170 | case 22: | ||
| 1171 | case 23: | ||
| 1172 | // These return a different error value than higher invalid values | ||
| 1173 | LOG_ERROR(Kernel_SVC, "unknown GetProcessInfo type=%u", type); | ||
| 1174 | return Kernel::ERR_NOT_IMPLEMENTED; | ||
| 1175 | default: | ||
| 1176 | LOG_ERROR(Kernel_SVC, "unknown GetProcessInfo type=%u", type); | ||
| 1177 | return Kernel::ERR_INVALID_ENUM_VALUE; | ||
| 1178 | } | ||
| 1179 | |||
| 1180 | return RESULT_SUCCESS; | ||
| 1181 | } | 172 | } |
| 1182 | 173 | ||
| 1183 | namespace { | 174 | namespace { |
| @@ -1188,135 +179,137 @@ struct FunctionDef { | |||
| 1188 | Func* func; | 179 | Func* func; |
| 1189 | const char* name; | 180 | const char* name; |
| 1190 | }; | 181 | }; |
| 1191 | } | 182 | } // namespace |
| 1192 | 183 | ||
| 1193 | static const FunctionDef SVC_Table[] = { | 184 | static const FunctionDef SVC_Table[] = { |
| 1194 | {0x00, nullptr, "Unknown"}, | 185 | {0x00, nullptr, "Unknown"}, |
| 1195 | {0x01, HLE::Wrap<ControlMemory>, "ControlMemory"}, | 186 | {0x01, nullptr, "svcSetHeapSize"}, |
| 1196 | {0x02, HLE::Wrap<QueryMemory>, "QueryMemory"}, | 187 | {0x02, nullptr, "svcSetMemoryPermission"}, |
| 1197 | {0x03, nullptr, "ExitProcess"}, | 188 | {0x03, nullptr, "svcSetMemoryAttribute"}, |
| 1198 | {0x04, nullptr, "GetProcessAffinityMask"}, | 189 | {0x04, nullptr, "svcMapMemory"}, |
| 1199 | {0x05, nullptr, "SetProcessAffinityMask"}, | 190 | {0x05, nullptr, "svcUnmapMemory"}, |
| 1200 | {0x06, nullptr, "GetProcessIdealProcessor"}, | 191 | {0x06, HLE::Wrap<QueryMemory>, "svcQueryMemory"}, |
| 1201 | {0x07, nullptr, "SetProcessIdealProcessor"}, | 192 | {0x07, nullptr, "svcExitProcess"}, |
| 1202 | {0x08, HLE::Wrap<CreateThread>, "CreateThread"}, | 193 | {0x08, nullptr, "svcCreateThread"}, |
| 1203 | {0x09, ExitThread, "ExitThread"}, | 194 | {0x09, nullptr, "svcStartThread"}, |
| 1204 | {0x0A, HLE::Wrap<SleepThread>, "SleepThread"}, | 195 | {0x0A, nullptr, "svcExitThread"}, |
| 1205 | {0x0B, HLE::Wrap<GetThreadPriority>, "GetThreadPriority"}, | 196 | {0x0B, HLE::Wrap<SleepThread>, "svcSleepThread"}, |
| 1206 | {0x0C, HLE::Wrap<SetThreadPriority>, "SetThreadPriority"}, | 197 | {0x0C, HLE::Wrap<GetThreadPriority>, "svcGetThreadPriority"}, |
| 1207 | {0x0D, nullptr, "GetThreadAffinityMask"}, | 198 | {0x0D, nullptr, "svcSetThreadPriority"}, |
| 1208 | {0x0E, nullptr, "SetThreadAffinityMask"}, | 199 | {0x0E, nullptr, "svcGetThreadCoreMask"}, |
| 1209 | {0x0F, nullptr, "GetThreadIdealProcessor"}, | 200 | {0x0F, nullptr, "svcSetThreadCoreMask"}, |
| 1210 | {0x10, nullptr, "SetThreadIdealProcessor"}, | 201 | {0x10, nullptr, "svcGetCurrentProcessorNumber"}, |
| 1211 | {0x11, nullptr, "GetCurrentProcessorNumber"}, | 202 | {0x11, nullptr, "svcSignalEvent"}, |
| 1212 | {0x12, nullptr, "Run"}, | 203 | {0x12, nullptr, "svcClearEvent"}, |
| 1213 | {0x13, HLE::Wrap<CreateMutex>, "CreateMutex"}, | 204 | {0x13, nullptr, "svcMapSharedMemory"}, |
| 1214 | {0x14, HLE::Wrap<ReleaseMutex>, "ReleaseMutex"}, | 205 | {0x14, nullptr, "svcUnmapSharedMemory"}, |
| 1215 | {0x15, HLE::Wrap<CreateSemaphore>, "CreateSemaphore"}, | 206 | {0x15, nullptr, "svcCreateTransferMemory"}, |
| 1216 | {0x16, HLE::Wrap<ReleaseSemaphore>, "ReleaseSemaphore"}, | 207 | {0x16, HLE::Wrap<CloseHandle>, "svcCloseHandle"}, |
| 1217 | {0x17, HLE::Wrap<CreateEvent>, "CreateEvent"}, | 208 | {0x17, nullptr, "svcResetSignal"}, |
| 1218 | {0x18, HLE::Wrap<SignalEvent>, "SignalEvent"}, | 209 | {0x18, nullptr, "svcWaitSynchronization"}, |
| 1219 | {0x19, HLE::Wrap<ClearEvent>, "ClearEvent"}, | 210 | {0x19, nullptr, "svcCancelSynchronization"}, |
| 1220 | {0x1A, HLE::Wrap<CreateTimer>, "CreateTimer"}, | 211 | {0x1A, nullptr, "svcLockMutex"}, |
| 1221 | {0x1B, HLE::Wrap<SetTimer>, "SetTimer"}, | 212 | {0x1B, nullptr, "svcUnlockMutex"}, |
| 1222 | {0x1C, HLE::Wrap<CancelTimer>, "CancelTimer"}, | 213 | {0x1C, nullptr, "svcWaitProcessWideKeyAtomic"}, |
| 1223 | {0x1D, HLE::Wrap<ClearTimer>, "ClearTimer"}, | 214 | {0x1D, HLE::Wrap<SignalProcessWideKey>, "svcSignalProcessWideKey"}, |
| 1224 | {0x1E, HLE::Wrap<CreateMemoryBlock>, "CreateMemoryBlock"}, | 215 | {0x1E, nullptr, "svcGetSystemTick"}, |
| 1225 | {0x1F, HLE::Wrap<MapMemoryBlock>, "MapMemoryBlock"}, | 216 | {0x1F, HLE::Wrap<ConnectToPort>, "svcConnectToPort"}, |
| 1226 | {0x20, HLE::Wrap<UnmapMemoryBlock>, "UnmapMemoryBlock"}, | 217 | {0x20, nullptr, "svcSendSyncRequestLight"}, |
| 1227 | {0x21, HLE::Wrap<CreateAddressArbiter>, "CreateAddressArbiter"}, | 218 | {0x21, HLE::Wrap<SendSyncRequest>, "svcSendSyncRequest"}, |
| 1228 | {0x22, HLE::Wrap<ArbitrateAddress>, "ArbitrateAddress"}, | 219 | {0x22, nullptr, "svcSendSyncRequestWithUserBuffer"}, |
| 1229 | {0x23, HLE::Wrap<CloseHandle>, "CloseHandle"}, | 220 | {0x23, nullptr, "svcSendAsyncRequestWithUserBuffer"}, |
| 1230 | {0x24, HLE::Wrap<WaitSynchronization1>, "WaitSynchronization1"}, | 221 | {0x24, nullptr, "svcGetProcessId"}, |
| 1231 | {0x25, HLE::Wrap<WaitSynchronizationN>, "WaitSynchronizationN"}, | 222 | {0x25, nullptr, "svcGetThreadId"}, |
| 1232 | {0x26, nullptr, "SignalAndWait"}, | 223 | {0x26, HLE::Wrap<Break>, "svcBreak"}, |
| 1233 | {0x27, HLE::Wrap<DuplicateHandle>, "DuplicateHandle"}, | 224 | {0x27, HLE::Wrap<OutputDebugString>, "svcOutputDebugString"}, |
| 1234 | {0x28, HLE::Wrap<GetSystemTick>, "GetSystemTick"}, | 225 | {0x28, nullptr, "svcReturnFromException"}, |
| 1235 | {0x29, nullptr, "GetHandleInfo"}, | 226 | {0x29, HLE::Wrap<GetInfo>, "svcGetInfo"}, |
| 1236 | {0x2A, HLE::Wrap<GetSystemInfo>, "GetSystemInfo"}, | 227 | {0x2A, nullptr, "svcFlushEntireDataCache"}, |
| 1237 | {0x2B, HLE::Wrap<GetProcessInfo>, "GetProcessInfo"}, | 228 | {0x2B, nullptr, "svcFlushDataCache"}, |
| 1238 | {0x2C, nullptr, "GetThreadInfo"}, | 229 | {0x2C, nullptr, "svcMapPhysicalMemory"}, |
| 1239 | {0x2D, HLE::Wrap<ConnectToPort>, "ConnectToPort"}, | 230 | {0x2D, nullptr, "svcUnmapPhysicalMemory"}, |
| 1240 | {0x2E, nullptr, "SendSyncRequest1"}, | 231 | {0x2E, nullptr, "Unknown"}, |
| 1241 | {0x2F, nullptr, "SendSyncRequest2"}, | 232 | {0x2F, nullptr, "svcGetLastThreadInfo"}, |
| 1242 | {0x30, nullptr, "SendSyncRequest3"}, | 233 | {0x30, nullptr, "svcGetResourceLimitLimitValue"}, |
| 1243 | {0x31, nullptr, "SendSyncRequest4"}, | 234 | {0x31, nullptr, "svcGetResourceLimitCurrentValue"}, |
| 1244 | {0x32, HLE::Wrap<SendSyncRequest>, "SendSyncRequest"}, | 235 | {0x32, nullptr, "svcSetThreadActivity"}, |
| 1245 | {0x33, nullptr, "OpenProcess"}, | 236 | {0x33, nullptr, "svcGetThreadContext"}, |
| 1246 | {0x34, nullptr, "OpenThread"}, | 237 | {0x34, nullptr, "Unknown"}, |
| 1247 | {0x35, HLE::Wrap<GetProcessId>, "GetProcessId"}, | 238 | {0x35, nullptr, "Unknown"}, |
| 1248 | {0x36, HLE::Wrap<GetProcessIdOfThread>, "GetProcessIdOfThread"}, | 239 | {0x36, nullptr, "Unknown"}, |
| 1249 | {0x37, HLE::Wrap<GetThreadId>, "GetThreadId"}, | 240 | {0x37, nullptr, "Unknown"}, |
| 1250 | {0x38, HLE::Wrap<GetResourceLimit>, "GetResourceLimit"}, | 241 | {0x38, nullptr, "Unknown"}, |
| 1251 | {0x39, HLE::Wrap<GetResourceLimitLimitValues>, "GetResourceLimitLimitValues"}, | 242 | {0x39, nullptr, "Unknown"}, |
| 1252 | {0x3A, HLE::Wrap<GetResourceLimitCurrentValues>, "GetResourceLimitCurrentValues"}, | 243 | {0x3A, nullptr, "Unknown"}, |
| 1253 | {0x3B, nullptr, "GetThreadContext"}, | 244 | {0x3B, nullptr, "Unknown"}, |
| 1254 | {0x3C, HLE::Wrap<Break>, "Break"}, | 245 | {0x3C, nullptr, "svcDumpInfo"}, |
| 1255 | {0x3D, HLE::Wrap<OutputDebugString>, "OutputDebugString"}, | 246 | {0x3D, nullptr, "Unknown"}, |
| 1256 | {0x3E, nullptr, "ControlPerformanceCounter"}, | 247 | {0x3E, nullptr, "Unknown"}, |
| 1257 | {0x3F, nullptr, "Unknown"}, | 248 | {0x3F, nullptr, "Unknown"}, |
| 1258 | {0x40, nullptr, "Unknown"}, | 249 | {0x40, nullptr, "svcCreateSession"}, |
| 1259 | {0x41, nullptr, "Unknown"}, | 250 | {0x41, nullptr, "svcAcceptSession"}, |
| 1260 | {0x42, nullptr, "Unknown"}, | 251 | {0x42, nullptr, "svcReplyAndReceiveLight"}, |
| 1261 | {0x43, nullptr, "Unknown"}, | 252 | {0x43, nullptr, "svcReplyAndReceive"}, |
| 1262 | {0x44, nullptr, "Unknown"}, | 253 | {0x44, nullptr, "svcReplyAndReceiveWithUserBuffer"}, |
| 1263 | {0x45, nullptr, "Unknown"}, | 254 | {0x45, nullptr, "svcCreateEvent"}, |
| 1264 | {0x46, nullptr, "Unknown"}, | 255 | {0x46, nullptr, "Unknown"}, |
| 1265 | {0x47, HLE::Wrap<CreatePort>, "CreatePort"}, | 256 | {0x47, nullptr, "Unknown"}, |
| 1266 | {0x48, HLE::Wrap<CreateSessionToPort>, "CreateSessionToPort"}, | 257 | {0x48, nullptr, "Unknown"}, |
| 1267 | {0x49, HLE::Wrap<CreateSession>, "CreateSession"}, | 258 | {0x49, nullptr, "Unknown"}, |
| 1268 | {0x4A, HLE::Wrap<AcceptSession>, "AcceptSession"}, | 259 | {0x4A, nullptr, "Unknown"}, |
| 1269 | {0x4B, nullptr, "ReplyAndReceive1"}, | 260 | {0x4B, nullptr, "Unknown"}, |
| 1270 | {0x4C, nullptr, "ReplyAndReceive2"}, | 261 | {0x4C, nullptr, "Unknown"}, |
| 1271 | {0x4D, nullptr, "ReplyAndReceive3"}, | 262 | {0x4D, nullptr, "svcSleepSystem"}, |
| 1272 | {0x4E, nullptr, "ReplyAndReceive4"}, | 263 | {0x4E, nullptr, "svcReadWriteRegister"}, |
| 1273 | {0x4F, HLE::Wrap<ReplyAndReceive>, "ReplyAndReceive"}, | 264 | {0x4F, nullptr, "svcSetProcessActivity"}, |
| 1274 | {0x50, nullptr, "BindInterrupt"}, | 265 | {0x50, nullptr, "svcCreateSharedMemory"}, |
| 1275 | {0x51, nullptr, "UnbindInterrupt"}, | 266 | {0x51, nullptr, "svcMapTransferMemory"}, |
| 1276 | {0x52, nullptr, "InvalidateProcessDataCache"}, | 267 | {0x52, nullptr, "svcUnmapTransferMemory"}, |
| 1277 | {0x53, nullptr, "StoreProcessDataCache"}, | 268 | {0x53, nullptr, "svcCreateInterruptEvent"}, |
| 1278 | {0x54, nullptr, "FlushProcessDataCache"}, | 269 | {0x54, nullptr, "svcQueryPhysicalAddress"}, |
| 1279 | {0x55, nullptr, "StartInterProcessDma"}, | 270 | {0x55, nullptr, "svcQueryIoMapping"}, |
| 1280 | {0x56, nullptr, "StopDma"}, | 271 | {0x56, nullptr, "svcCreateDeviceAddressSpace"}, |
| 1281 | {0x57, nullptr, "GetDmaState"}, | 272 | {0x57, nullptr, "svcAttachDeviceAddressSpace"}, |
| 1282 | {0x58, nullptr, "RestartDma"}, | 273 | {0x58, nullptr, "svcDetachDeviceAddressSpace"}, |
| 1283 | {0x59, nullptr, "Unknown"}, | 274 | {0x59, nullptr, "svcMapDeviceAddressSpaceByForce"}, |
| 1284 | {0x5A, nullptr, "Unknown"}, | 275 | {0x5A, nullptr, "svcMapDeviceAddressSpaceAligned"}, |
| 1285 | {0x5B, nullptr, "Unknown"}, | 276 | {0x5B, nullptr, "svcMapDeviceAddressSpace"}, |
| 1286 | {0x5C, nullptr, "Unknown"}, | 277 | {0x5C, nullptr, "svcUnmapDeviceAddressSpace"}, |
| 1287 | {0x5D, nullptr, "Unknown"}, | 278 | {0x5D, nullptr, "svcInvalidateProcessDataCache"}, |
| 1288 | {0x5E, nullptr, "Unknown"}, | 279 | {0x5E, nullptr, "svcStoreProcessDataCache"}, |
| 1289 | {0x5F, nullptr, "Unknown"}, | 280 | {0x5F, nullptr, "svcFlushProcessDataCache"}, |
| 1290 | {0x60, nullptr, "DebugActiveProcess"}, | 281 | {0x60, nullptr, "svcDebugActiveProcess"}, |
| 1291 | {0x61, nullptr, "BreakDebugProcess"}, | 282 | {0x61, nullptr, "svcBreakDebugProcess"}, |
| 1292 | {0x62, nullptr, "TerminateDebugProcess"}, | 283 | {0x62, nullptr, "svcTerminateDebugProcess"}, |
| 1293 | {0x63, nullptr, "GetProcessDebugEvent"}, | 284 | {0x63, nullptr, "svcGetDebugEvent"}, |
| 1294 | {0x64, nullptr, "ContinueDebugEvent"}, | 285 | {0x64, nullptr, "svcContinueDebugEvent"}, |
| 1295 | {0x65, nullptr, "GetProcessList"}, | 286 | {0x65, nullptr, "svcGetProcessList"}, |
| 1296 | {0x66, nullptr, "GetThreadList"}, | 287 | {0x66, nullptr, "svcGetThreadList"}, |
| 1297 | {0x67, nullptr, "GetDebugThreadContext"}, | 288 | {0x67, nullptr, "svcGetDebugThreadContext"}, |
| 1298 | {0x68, nullptr, "SetDebugThreadContext"}, | 289 | {0x68, nullptr, "svcSetDebugThreadContext"}, |
| 1299 | {0x69, nullptr, "QueryDebugProcessMemory"}, | 290 | {0x69, nullptr, "svcQueryDebugProcessMemory"}, |
| 1300 | {0x6A, nullptr, "ReadProcessMemory"}, | 291 | {0x6A, nullptr, "svcReadDebugProcessMemory"}, |
| 1301 | {0x6B, nullptr, "WriteProcessMemory"}, | 292 | {0x6B, nullptr, "svcWriteDebugProcessMemory"}, |
| 1302 | {0x6C, nullptr, "SetHardwareBreakPoint"}, | 293 | {0x6C, nullptr, "svcSetHardwareBreakPoint"}, |
| 1303 | {0x6D, nullptr, "GetDebugThreadParam"}, | 294 | {0x6D, nullptr, "svcGetDebugThreadParam"}, |
| 1304 | {0x6E, nullptr, "Unknown"}, | 295 | {0x6E, nullptr, "Unknown"}, |
| 1305 | {0x6F, nullptr, "Unknown"}, | 296 | {0x6F, nullptr, "Unknown"}, |
| 1306 | {0x70, nullptr, "ControlProcessMemory"}, | 297 | {0x70, nullptr, "svcCreatePort"}, |
| 1307 | {0x71, nullptr, "MapProcessMemory"}, | 298 | {0x71, nullptr, "svcManageNamedPort"}, |
| 1308 | {0x72, nullptr, "UnmapProcessMemory"}, | 299 | {0x72, nullptr, "svcConnectToPort"}, |
| 1309 | {0x73, nullptr, "CreateCodeSet"}, | 300 | {0x73, nullptr, "svcSetProcessMemoryPermission"}, |
| 1310 | {0x74, nullptr, "RandomStub"}, | 301 | {0x74, nullptr, "svcMapProcessMemory"}, |
| 1311 | {0x75, nullptr, "CreateProcess"}, | 302 | {0x75, nullptr, "svcUnmapProcessMemory"}, |
| 1312 | {0x76, nullptr, "TerminateProcess"}, | 303 | {0x76, nullptr, "svcQueryProcessMemory"}, |
| 1313 | {0x77, nullptr, "SetProcessResourceLimits"}, | 304 | {0x77, nullptr, "svcMapProcessCodeMemory"}, |
| 1314 | {0x78, nullptr, "CreateResourceLimit"}, | 305 | {0x78, nullptr, "svcUnmapProcessCodeMemory"}, |
| 1315 | {0x79, nullptr, "SetResourceLimitValues"}, | 306 | {0x79, nullptr, "svcCreateProcess"}, |
| 1316 | {0x7A, nullptr, "AddCodeSegment"}, | 307 | {0x7A, nullptr, "svcStartProcess"}, |
| 1317 | {0x7B, nullptr, "Backdoor"}, | 308 | {0x7B, nullptr, "svcTerminateProcess"}, |
| 1318 | {0x7C, nullptr, "KernelSetState"}, | 309 | {0x7C, nullptr, "svcGetProcessInfo"}, |
| 1319 | {0x7D, HLE::Wrap<QueryProcessMemory>, "QueryProcessMemory"}, | 310 | {0x7D, nullptr, "svcCreateResourceLimit"}, |
| 311 | {0x7E, nullptr, "svcSetResourceLimitLimitValue"}, | ||
| 312 | {0x7F, nullptr, "svcCallSecureMonitor"}, | ||
| 1320 | }; | 313 | }; |
| 1321 | 314 | ||
| 1322 | static const FunctionDef* GetSVCInfo(u32 func_num) { | 315 | static const FunctionDef* GetSVCInfo(u32 func_num) { |
| @@ -1332,14 +325,21 @@ MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); | |||
| 1332 | void CallSVC(u32 immediate) { | 325 | void CallSVC(u32 immediate) { |
| 1333 | MICROPROFILE_SCOPE(Kernel_SVC); | 326 | MICROPROFILE_SCOPE(Kernel_SVC); |
| 1334 | 327 | ||
| 328 | // Lock the global kernel mutex when we enter the kernel HLE. | ||
| 329 | std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); | ||
| 330 | |||
| 1335 | const FunctionDef* info = GetSVCInfo(immediate); | 331 | const FunctionDef* info = GetSVCInfo(immediate); |
| 1336 | if (info) { | 332 | if (info) { |
| 1337 | if (info->func) { | 333 | if (info->func) { |
| 1338 | info->func(); | 334 | info->func(); |
| 1339 | } else { | 335 | } else { |
| 1340 | LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name); | 336 | LOG_CRITICAL(Kernel_SVC, "unimplemented SVC function %s(..)", info->name); |
| 1341 | } | 337 | } |
| 338 | } else { | ||
| 339 | LOG_CRITICAL(Kernel_SVC, "unknown SVC function 0x%x", immediate); | ||
| 1342 | } | 340 | } |
| 341 | |||
| 342 | LOG_CRITICAL(Kernel_SVC, "PC = 0x%08X", Core::CPU().GetPC()); | ||
| 1343 | } | 343 | } |
| 1344 | 344 | ||
| 1345 | } // namespace | 345 | } // namespace SVC |