diff options
| -rw-r--r-- | .gitignore | 4 | ||||
| -rw-r--r-- | src/core/arm/arm_interface.h | 9 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic.cpp | 9 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic.h | 2 | ||||
| -rw-r--r-- | src/core/arm/dyncom/arm_dyncom.cpp | 8 | ||||
| -rw-r--r-- | src/core/arm/dyncom/arm_dyncom.h | 2 | ||||
| -rw-r--r-- | src/core/core_timing.cpp | 36 | ||||
| -rw-r--r-- | src/core/core_timing.h | 6 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 13 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.h | 15 | ||||
| -rw-r--r-- | src/core/hle/kernel/wait_object.cpp | 11 | ||||
| -rw-r--r-- | src/core/hle/service/apt/apt.cpp | 39 | ||||
| -rw-r--r-- | src/core/hle/service/apt/apt.h | 10 | ||||
| -rw-r--r-- | src/core/hle/service/apt/apt_s.cpp | 4 | ||||
| -rw-r--r-- | src/core/hle/service/nim/nim.cpp | 18 | ||||
| -rw-r--r-- | src/core/hle/service/nim/nim.h | 11 | ||||
| -rw-r--r-- | src/core/hle/service/nim/nim_u.cpp | 2 | ||||
| -rw-r--r-- | src/core/hle/service/nwm/nwm_uds.cpp | 121 | ||||
| -rw-r--r-- | src/core/hle/service/nwm/uds_data.cpp | 80 | ||||
| -rw-r--r-- | src/core/hle/service/nwm/uds_data.h | 68 | ||||
| -rw-r--r-- | src/core/hle/svc.cpp | 71 | ||||
| -rw-r--r-- | src/tests/core/arm/dyncom/arm_dyncom_vfp_tests.cpp | 2 |
22 files changed, 452 insertions, 89 deletions
diff --git a/.gitignore b/.gitignore index ec74b0fa4..7999a40e1 100644 --- a/.gitignore +++ b/.gitignore | |||
| @@ -9,12 +9,16 @@ src/common/scm_rev.cpp | |||
| 9 | # Project/editor files | 9 | # Project/editor files |
| 10 | *.swp | 10 | *.swp |
| 11 | .idea/ | 11 | .idea/ |
| 12 | .vs/ | ||
| 12 | .vscode/ | 13 | .vscode/ |
| 13 | 14 | ||
| 14 | # *nix related | 15 | # *nix related |
| 15 | # Common convention for backup or temporary files | 16 | # Common convention for backup or temporary files |
| 16 | *~ | 17 | *~ |
| 17 | 18 | ||
| 19 | # Visual Studio CMake settings | ||
| 20 | CMakeSettings.json | ||
| 21 | |||
| 18 | # OSX global filetypes | 22 | # OSX global filetypes |
| 19 | # Created by Finder or Spotlight in directories for various OS functionality (indexing, etc) | 23 | # Created by Finder or Spotlight in directories for various OS functionality (indexing, etc) |
| 20 | .DS_Store | 24 | .DS_Store |
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 2aa017a54..ba528403c 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h | |||
| @@ -125,12 +125,6 @@ public: | |||
| 125 | virtual void SetCP15Register(CP15Register reg, u32 value) = 0; | 125 | virtual void SetCP15Register(CP15Register reg, u32 value) = 0; |
| 126 | 126 | ||
| 127 | /** | 127 | /** |
| 128 | * Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time) | ||
| 129 | * @param ticks Number of ticks to advance the CPU core | ||
| 130 | */ | ||
| 131 | virtual void AddTicks(u64 ticks) = 0; | ||
| 132 | |||
| 133 | /** | ||
| 134 | * Saves the current CPU context | 128 | * Saves the current CPU context |
| 135 | * @param ctx Thread context to save | 129 | * @param ctx Thread context to save |
| 136 | */ | 130 | */ |
| @@ -150,9 +144,6 @@ public: | |||
| 150 | return num_instructions; | 144 | return num_instructions; |
| 151 | } | 145 | } |
| 152 | 146 | ||
| 153 | s64 down_count = 0; ///< A decreasing counter of remaining cycles before the next event, | ||
| 154 | /// decreased by the cpu run loop | ||
| 155 | |||
| 156 | protected: | 147 | protected: |
| 157 | /** | 148 | /** |
| 158 | * Executes the given number of instructions | 149 | * Executes the given number of instructions |
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 42ae93ae8..2cb56d12f 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp | |||
| @@ -124,13 +124,6 @@ void ARM_Dynarmic::SetCP15Register(CP15Register reg, u32 value) { | |||
| 124 | interpreter_state->CP15[reg] = value; | 124 | interpreter_state->CP15[reg] = value; |
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | void ARM_Dynarmic::AddTicks(u64 ticks) { | ||
| 128 | down_count -= ticks; | ||
| 129 | if (down_count < 0) { | ||
| 130 | CoreTiming::Advance(); | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64)); | 127 | MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64)); |
| 135 | 128 | ||
| 136 | void ARM_Dynarmic::ExecuteInstructions(int num_instructions) { | 129 | void ARM_Dynarmic::ExecuteInstructions(int num_instructions) { |
| @@ -139,7 +132,7 @@ void ARM_Dynarmic::ExecuteInstructions(int num_instructions) { | |||
| 139 | 132 | ||
| 140 | std::size_t ticks_executed = jit->Run(static_cast<unsigned>(num_instructions)); | 133 | std::size_t ticks_executed = jit->Run(static_cast<unsigned>(num_instructions)); |
| 141 | 134 | ||
| 142 | AddTicks(ticks_executed); | 135 | CoreTiming::AddTicks(ticks_executed); |
| 143 | } | 136 | } |
| 144 | 137 | ||
| 145 | void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) { | 138 | void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) { |
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index 96148a1a5..0b00158a5 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h | |||
| @@ -32,8 +32,6 @@ public: | |||
| 32 | u32 GetCP15Register(CP15Register reg) override; | 32 | u32 GetCP15Register(CP15Register reg) override; |
| 33 | void SetCP15Register(CP15Register reg, u32 value) override; | 33 | void SetCP15Register(CP15Register reg, u32 value) override; |
| 34 | 34 | ||
| 35 | void AddTicks(u64 ticks) override; | ||
| 36 | |||
| 37 | void SaveContext(ThreadContext& ctx) override; | 35 | void SaveContext(ThreadContext& ctx) override; |
| 38 | void LoadContext(const ThreadContext& ctx) override; | 36 | void LoadContext(const ThreadContext& ctx) override; |
| 39 | 37 | ||
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp index da955c9b9..4d72aef77 100644 --- a/src/core/arm/dyncom/arm_dyncom.cpp +++ b/src/core/arm/dyncom/arm_dyncom.cpp | |||
| @@ -77,12 +77,6 @@ void ARM_DynCom::SetCP15Register(CP15Register reg, u32 value) { | |||
| 77 | state->CP15[reg] = value; | 77 | state->CP15[reg] = value; |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | void ARM_DynCom::AddTicks(u64 ticks) { | ||
| 81 | down_count -= ticks; | ||
| 82 | if (down_count < 0) | ||
| 83 | CoreTiming::Advance(); | ||
| 84 | } | ||
| 85 | |||
| 86 | void ARM_DynCom::ExecuteInstructions(int num_instructions) { | 80 | void ARM_DynCom::ExecuteInstructions(int num_instructions) { |
| 87 | state->NumInstrsToExecute = num_instructions; | 81 | state->NumInstrsToExecute = num_instructions; |
| 88 | 82 | ||
| @@ -90,7 +84,7 @@ void ARM_DynCom::ExecuteInstructions(int num_instructions) { | |||
| 90 | // executing one instruction at a time. Otherwise, if a block is being executed, more | 84 | // executing one instruction at a time. Otherwise, if a block is being executed, more |
| 91 | // instructions may actually be executed than specified. | 85 | // instructions may actually be executed than specified. |
| 92 | unsigned ticks_executed = InterpreterMainLoop(state.get()); | 86 | unsigned ticks_executed = InterpreterMainLoop(state.get()); |
| 93 | AddTicks(ticks_executed); | 87 | CoreTiming::AddTicks(ticks_executed); |
| 94 | } | 88 | } |
| 95 | 89 | ||
| 96 | void ARM_DynCom::SaveContext(ThreadContext& ctx) { | 90 | void ARM_DynCom::SaveContext(ThreadContext& ctx) { |
diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h index 0ae535671..fc1ffed6a 100644 --- a/src/core/arm/dyncom/arm_dyncom.h +++ b/src/core/arm/dyncom/arm_dyncom.h | |||
| @@ -31,8 +31,6 @@ public: | |||
| 31 | u32 GetCP15Register(CP15Register reg) override; | 31 | u32 GetCP15Register(CP15Register reg) override; |
| 32 | void SetCP15Register(CP15Register reg, u32 value) override; | 32 | void SetCP15Register(CP15Register reg, u32 value) override; |
| 33 | 33 | ||
| 34 | void AddTicks(u64 ticks) override; | ||
| 35 | |||
| 36 | void SaveContext(ThreadContext& ctx) override; | 34 | void SaveContext(ThreadContext& ctx) override; |
| 37 | void LoadContext(const ThreadContext& ctx) override; | 35 | void LoadContext(const ThreadContext& ctx) override; |
| 38 | 36 | ||
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 276ecfdf6..5e2a5d00f 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -57,6 +57,9 @@ static s64 idled_cycles; | |||
| 57 | static s64 last_global_time_ticks; | 57 | static s64 last_global_time_ticks; |
| 58 | static s64 last_global_time_us; | 58 | static s64 last_global_time_us; |
| 59 | 59 | ||
| 60 | static s64 down_count = 0; ///< A decreasing counter of remaining cycles before the next event, | ||
| 61 | /// decreased by the cpu run loop | ||
| 62 | |||
| 60 | static std::recursive_mutex external_event_section; | 63 | static std::recursive_mutex external_event_section; |
| 61 | 64 | ||
| 62 | // Warning: not included in save state. | 65 | // Warning: not included in save state. |
| @@ -146,7 +149,7 @@ void UnregisterAllEvents() { | |||
| 146 | } | 149 | } |
| 147 | 150 | ||
| 148 | void Init() { | 151 | void Init() { |
| 149 | Core::CPU().down_count = INITIAL_SLICE_LENGTH; | 152 | down_count = INITIAL_SLICE_LENGTH; |
| 150 | g_slice_length = INITIAL_SLICE_LENGTH; | 153 | g_slice_length = INITIAL_SLICE_LENGTH; |
| 151 | global_timer = 0; | 154 | global_timer = 0; |
| 152 | idled_cycles = 0; | 155 | idled_cycles = 0; |
| @@ -185,8 +188,15 @@ void Shutdown() { | |||
| 185 | } | 188 | } |
| 186 | } | 189 | } |
| 187 | 190 | ||
| 191 | void AddTicks(u64 ticks) { | ||
| 192 | down_count -= ticks; | ||
| 193 | if (down_count < 0) { | ||
| 194 | Advance(); | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 188 | u64 GetTicks() { | 198 | u64 GetTicks() { |
| 189 | return (u64)global_timer + g_slice_length - Core::CPU().down_count; | 199 | return (u64)global_timer + g_slice_length - down_count; |
| 190 | } | 200 | } |
| 191 | 201 | ||
| 192 | u64 GetIdleTicks() { | 202 | u64 GetIdleTicks() { |
| @@ -460,18 +470,18 @@ void MoveEvents() { | |||
| 460 | } | 470 | } |
| 461 | 471 | ||
| 462 | void ForceCheck() { | 472 | void ForceCheck() { |
| 463 | s64 cycles_executed = g_slice_length - Core::CPU().down_count; | 473 | s64 cycles_executed = g_slice_length - down_count; |
| 464 | global_timer += cycles_executed; | 474 | global_timer += cycles_executed; |
| 465 | // This will cause us to check for new events immediately. | 475 | // This will cause us to check for new events immediately. |
| 466 | Core::CPU().down_count = 0; | 476 | down_count = 0; |
| 467 | // But let's not eat a bunch more time in Advance() because of this. | 477 | // But let's not eat a bunch more time in Advance() because of this. |
| 468 | g_slice_length = 0; | 478 | g_slice_length = 0; |
| 469 | } | 479 | } |
| 470 | 480 | ||
| 471 | void Advance() { | 481 | void Advance() { |
| 472 | s64 cycles_executed = g_slice_length - Core::CPU().down_count; | 482 | s64 cycles_executed = g_slice_length - down_count; |
| 473 | global_timer += cycles_executed; | 483 | global_timer += cycles_executed; |
| 474 | Core::CPU().down_count = g_slice_length; | 484 | down_count = g_slice_length; |
| 475 | 485 | ||
| 476 | if (has_ts_events) | 486 | if (has_ts_events) |
| 477 | MoveEvents(); | 487 | MoveEvents(); |
| @@ -480,7 +490,7 @@ void Advance() { | |||
| 480 | if (!first) { | 490 | if (!first) { |
| 481 | if (g_slice_length < 10000) { | 491 | if (g_slice_length < 10000) { |
| 482 | g_slice_length += 10000; | 492 | g_slice_length += 10000; |
| 483 | Core::CPU().down_count += g_slice_length; | 493 | down_count += g_slice_length; |
| 484 | } | 494 | } |
| 485 | } else { | 495 | } else { |
| 486 | // Note that events can eat cycles as well. | 496 | // Note that events can eat cycles as well. |
| @@ -490,7 +500,7 @@ void Advance() { | |||
| 490 | 500 | ||
| 491 | const int diff = target - g_slice_length; | 501 | const int diff = target - g_slice_length; |
| 492 | g_slice_length += diff; | 502 | g_slice_length += diff; |
| 493 | Core::CPU().down_count += diff; | 503 | down_count += diff; |
| 494 | } | 504 | } |
| 495 | if (advance_callback) | 505 | if (advance_callback) |
| 496 | advance_callback(static_cast<int>(cycles_executed)); | 506 | advance_callback(static_cast<int>(cycles_executed)); |
| @@ -506,12 +516,12 @@ void LogPendingEvents() { | |||
| 506 | } | 516 | } |
| 507 | 517 | ||
| 508 | void Idle(int max_idle) { | 518 | void Idle(int max_idle) { |
| 509 | s64 cycles_down = Core::CPU().down_count; | 519 | s64 cycles_down = down_count; |
| 510 | if (max_idle != 0 && cycles_down > max_idle) | 520 | if (max_idle != 0 && cycles_down > max_idle) |
| 511 | cycles_down = max_idle; | 521 | cycles_down = max_idle; |
| 512 | 522 | ||
| 513 | if (first && cycles_down > 0) { | 523 | if (first && cycles_down > 0) { |
| 514 | s64 cycles_executed = g_slice_length - Core::CPU().down_count; | 524 | s64 cycles_executed = g_slice_length - down_count; |
| 515 | s64 cycles_next_event = first->time - global_timer; | 525 | s64 cycles_next_event = first->time - global_timer; |
| 516 | 526 | ||
| 517 | if (cycles_next_event < cycles_executed + cycles_down) { | 527 | if (cycles_next_event < cycles_executed + cycles_down) { |
| @@ -526,9 +536,9 @@ void Idle(int max_idle) { | |||
| 526 | cycles_down / (float)(g_clock_rate_arm11 * 0.001f)); | 536 | cycles_down / (float)(g_clock_rate_arm11 * 0.001f)); |
| 527 | 537 | ||
| 528 | idled_cycles += cycles_down; | 538 | idled_cycles += cycles_down; |
| 529 | Core::CPU().down_count -= cycles_down; | 539 | down_count -= cycles_down; |
| 530 | if (Core::CPU().down_count == 0) | 540 | if (down_count == 0) |
| 531 | Core::CPU().down_count = -1; | 541 | down_count = -1; |
| 532 | } | 542 | } |
| 533 | 543 | ||
| 534 | std::string GetScheduledEventsSummary() { | 544 | std::string GetScheduledEventsSummary() { |
diff --git a/src/core/core_timing.h b/src/core/core_timing.h index d2f85cd4d..897350801 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h | |||
| @@ -67,6 +67,12 @@ void Shutdown(); | |||
| 67 | typedef void (*MHzChangeCallback)(); | 67 | typedef void (*MHzChangeCallback)(); |
| 68 | typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback; | 68 | typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback; |
| 69 | 69 | ||
| 70 | /** | ||
| 71 | * Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time) | ||
| 72 | * @param ticks Number of ticks to advance the CPU core | ||
| 73 | */ | ||
| 74 | void AddTicks(u64 ticks); | ||
| 75 | |||
| 70 | u64 GetTicks(); | 76 | u64 GetTicks(); |
| 71 | u64 GetIdleTicks(); | 77 | u64 GetIdleTicks(); |
| 72 | u64 GetGlobalTimeUs(); | 78 | u64 GetGlobalTimeUs(); |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 6ebc8c151..0f7970ebe 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -247,12 +247,15 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { | |||
| 247 | 247 | ||
| 248 | if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY || | 248 | if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY || |
| 249 | thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) { | 249 | thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) { |
| 250 | thread->wait_set_output = false; | 250 | |
| 251 | // Invoke the wakeup callback before clearing the wait objects | ||
| 252 | if (thread->wakeup_callback) | ||
| 253 | thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr); | ||
| 254 | |||
| 251 | // Remove the thread from each of its waiting objects' waitlists | 255 | // Remove the thread from each of its waiting objects' waitlists |
| 252 | for (auto& object : thread->wait_objects) | 256 | for (auto& object : thread->wait_objects) |
| 253 | object->RemoveWaitingThread(thread.get()); | 257 | object->RemoveWaitingThread(thread.get()); |
| 254 | thread->wait_objects.clear(); | 258 | thread->wait_objects.clear(); |
| 255 | thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); | ||
| 256 | } | 259 | } |
| 257 | 260 | ||
| 258 | thread->ResumeFromWait(); | 261 | thread->ResumeFromWait(); |
| @@ -278,6 +281,9 @@ void Thread::ResumeFromWait() { | |||
| 278 | break; | 281 | break; |
| 279 | 282 | ||
| 280 | case THREADSTATUS_READY: | 283 | case THREADSTATUS_READY: |
| 284 | // The thread's wakeup callback must have already been cleared when the thread was first | ||
| 285 | // awoken. | ||
| 286 | ASSERT(wakeup_callback == nullptr); | ||
| 281 | // If the thread is waiting on multiple wait objects, it might be awoken more than once | 287 | // If the thread is waiting on multiple wait objects, it might be awoken more than once |
| 282 | // before actually resuming. We can ignore subsequent wakeups if the thread status has | 288 | // before actually resuming. We can ignore subsequent wakeups if the thread status has |
| 283 | // already been set to THREADSTATUS_READY. | 289 | // already been set to THREADSTATUS_READY. |
| @@ -293,6 +299,8 @@ void Thread::ResumeFromWait() { | |||
| 293 | return; | 299 | return; |
| 294 | } | 300 | } |
| 295 | 301 | ||
| 302 | wakeup_callback = nullptr; | ||
| 303 | |||
| 296 | ready_queue.push_back(current_priority, this); | 304 | ready_queue.push_back(current_priority, this); |
| 297 | status = THREADSTATUS_READY; | 305 | status = THREADSTATUS_READY; |
| 298 | Core::System::GetInstance().PrepareReschedule(); | 306 | Core::System::GetInstance().PrepareReschedule(); |
| @@ -395,7 +403,6 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, | |||
| 395 | thread->nominal_priority = thread->current_priority = priority; | 403 | thread->nominal_priority = thread->current_priority = priority; |
| 396 | thread->last_running_ticks = CoreTiming::GetTicks(); | 404 | thread->last_running_ticks = CoreTiming::GetTicks(); |
| 397 | thread->processor_id = processor_id; | 405 | thread->processor_id = processor_id; |
| 398 | thread->wait_set_output = false; | ||
| 399 | thread->wait_objects.clear(); | 406 | thread->wait_objects.clear(); |
| 400 | thread->wait_address = 0; | 407 | thread->wait_address = 0; |
| 401 | thread->name = std::move(name); | 408 | thread->name = std::move(name); |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 520ac22c2..314fba81f 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -41,6 +41,11 @@ enum ThreadStatus { | |||
| 41 | THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated | 41 | THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated |
| 42 | }; | 42 | }; |
| 43 | 43 | ||
| 44 | enum class ThreadWakeupReason { | ||
| 45 | Signal, // The thread was woken up by WakeupAllWaitingThreads due to an object signal. | ||
| 46 | Timeout // The thread was woken up due to a wait timeout. | ||
| 47 | }; | ||
| 48 | |||
| 44 | namespace Kernel { | 49 | namespace Kernel { |
| 45 | 50 | ||
| 46 | class Mutex; | 51 | class Mutex; |
| @@ -205,14 +210,18 @@ public: | |||
| 205 | 210 | ||
| 206 | VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address | 211 | VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address |
| 207 | 212 | ||
| 208 | /// True if the WaitSynchronizationN output parameter should be set on thread wakeup. | ||
| 209 | bool wait_set_output; | ||
| 210 | |||
| 211 | std::string name; | 213 | std::string name; |
| 212 | 214 | ||
| 213 | /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. | 215 | /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. |
| 214 | Handle callback_handle; | 216 | Handle callback_handle; |
| 215 | 217 | ||
| 218 | using WakeupCallback = void(ThreadWakeupReason reason, SharedPtr<Thread> thread, | ||
| 219 | SharedPtr<WaitObject> object); | ||
| 220 | // Callback that will be invoked when the thread is resumed from a waiting state. If the thread | ||
| 221 | // was waiting via WaitSynchronizationN then the object will be the last object that became | ||
| 222 | // available. In case of a timeout, the object will be nullptr. | ||
| 223 | std::function<WakeupCallback> wakeup_callback; | ||
| 224 | |||
| 216 | private: | 225 | private: |
| 217 | Thread(); | 226 | Thread(); |
| 218 | ~Thread() override; | 227 | ~Thread() override; |
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp index 56fdd977f..469554908 100644 --- a/src/core/hle/kernel/wait_object.cpp +++ b/src/core/hle/kernel/wait_object.cpp | |||
| @@ -71,23 +71,20 @@ void WaitObject::WakeupAllWaitingThreads() { | |||
| 71 | while (auto thread = GetHighestPriorityReadyThread()) { | 71 | while (auto thread = GetHighestPriorityReadyThread()) { |
| 72 | if (!thread->IsSleepingOnWaitAll()) { | 72 | if (!thread->IsSleepingOnWaitAll()) { |
| 73 | Acquire(thread.get()); | 73 | Acquire(thread.get()); |
| 74 | // Set the output index of the WaitSynchronizationN call to the index of this object. | ||
| 75 | if (thread->wait_set_output) { | ||
| 76 | thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this)); | ||
| 77 | thread->wait_set_output = false; | ||
| 78 | } | ||
| 79 | } else { | 74 | } else { |
| 80 | for (auto& object : thread->wait_objects) { | 75 | for (auto& object : thread->wait_objects) { |
| 81 | object->Acquire(thread.get()); | 76 | object->Acquire(thread.get()); |
| 82 | } | 77 | } |
| 83 | // Note: This case doesn't update the output index of WaitSynchronizationN. | ||
| 84 | } | 78 | } |
| 85 | 79 | ||
| 80 | // Invoke the wakeup callback before clearing the wait objects | ||
| 81 | if (thread->wakeup_callback) | ||
| 82 | thread->wakeup_callback(ThreadWakeupReason::Signal, thread, this); | ||
| 83 | |||
| 86 | for (auto& object : thread->wait_objects) | 84 | for (auto& object : thread->wait_objects) |
| 87 | object->RemoveWaitingThread(thread.get()); | 85 | object->RemoveWaitingThread(thread.get()); |
| 88 | thread->wait_objects.clear(); | 86 | thread->wait_objects.clear(); |
| 89 | 87 | ||
| 90 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 91 | thread->ResumeFromWait(); | 88 | thread->ResumeFromWait(); |
| 92 | } | 89 | } |
| 93 | } | 90 | } |
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 4c6156345..2f7362748 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp | |||
| @@ -65,6 +65,7 @@ union AppletAttributes { | |||
| 65 | u32 raw; | 65 | u32 raw; |
| 66 | 66 | ||
| 67 | BitField<0, 3, u32> applet_pos; | 67 | BitField<0, 3, u32> applet_pos; |
| 68 | BitField<29, 1, u32> is_home_menu; | ||
| 68 | 69 | ||
| 69 | AppletAttributes() : raw(0) {} | 70 | AppletAttributes() : raw(0) {} |
| 70 | AppletAttributes(u32 attributes) : raw(attributes) {} | 71 | AppletAttributes(u32 attributes) : raw(attributes) {} |
| @@ -158,6 +159,11 @@ static AppletSlotData* GetAppletSlotData(AppletAttributes attributes) { | |||
| 158 | if (slot == AppletSlot::Error) | 159 | if (slot == AppletSlot::Error) |
| 159 | return nullptr; | 160 | return nullptr; |
| 160 | 161 | ||
| 162 | // The Home Menu is a system applet, however, it has its own applet slot so that it can run | ||
| 163 | // concurrently with other system applets. | ||
| 164 | if (slot == AppletSlot::SystemApplet && attributes.is_home_menu) | ||
| 165 | return &applet_slots[static_cast<size_t>(AppletSlot::HomeMenu)]; | ||
| 166 | |||
| 161 | return &applet_slots[static_cast<size_t>(slot)]; | 167 | return &applet_slots[static_cast<size_t>(slot)]; |
| 162 | } | 168 | } |
| 163 | 169 | ||
| @@ -197,6 +203,19 @@ void Initialize(Service::Interface* self) { | |||
| 197 | rb.Push(RESULT_SUCCESS); | 203 | rb.Push(RESULT_SUCCESS); |
| 198 | rb.PushCopyHandles(Kernel::g_handle_table.Create(slot_data->notification_event).Unwrap(), | 204 | rb.PushCopyHandles(Kernel::g_handle_table.Create(slot_data->notification_event).Unwrap(), |
| 199 | Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap()); | 205 | Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap()); |
| 206 | |||
| 207 | if (slot_data->applet_id == AppletId::Application || | ||
| 208 | slot_data->applet_id == AppletId::HomeMenu) { | ||
| 209 | // Initialize the APT parameter to wake up the application. | ||
| 210 | next_parameter.emplace(); | ||
| 211 | next_parameter->signal = static_cast<u32>(SignalType::Wakeup); | ||
| 212 | next_parameter->sender_id = static_cast<u32>(AppletId::None); | ||
| 213 | next_parameter->destination_id = app_id; | ||
| 214 | // Not signaling the parameter event will cause the application (or Home Menu) to hang | ||
| 215 | // during startup. In the real console, it is usually the Kernel and Home Menu who cause NS | ||
| 216 | // to signal the HomeMenu and Application parameter events, respectively. | ||
| 217 | slot_data->parameter_event->Signal(); | ||
| 218 | } | ||
| 200 | } | 219 | } |
| 201 | 220 | ||
| 202 | static u32 DecompressLZ11(const u8* in, u8* out) { | 221 | static u32 DecompressLZ11(const u8* in, u8* out) { |
| @@ -757,6 +776,20 @@ void PrepareToStartLibraryApplet(Service::Interface* self) { | |||
| 757 | LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); | 776 | LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); |
| 758 | } | 777 | } |
| 759 | 778 | ||
| 779 | void PrepareToStartNewestHomeMenu(Service::Interface* self) { | ||
| 780 | IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0); // 0x1A0000 | ||
| 781 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||
| 782 | |||
| 783 | // TODO(Subv): This command can only be called by a System Applet (return 0xC8A0CC04 otherwise). | ||
| 784 | |||
| 785 | // This command must return an error when called, otherwise the Home Menu will try to reboot the | ||
| 786 | // system. | ||
| 787 | rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet, | ||
| 788 | ErrorSummary::InvalidState, ErrorLevel::Status)); | ||
| 789 | |||
| 790 | LOG_DEBUG(Service_APT, "called"); | ||
| 791 | } | ||
| 792 | |||
| 760 | void PreloadLibraryApplet(Service::Interface* self) { | 793 | void PreloadLibraryApplet(Service::Interface* self) { |
| 761 | IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x16, 1, 0); // 0x160040 | 794 | IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x16, 1, 0); // 0x160040 |
| 762 | AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>()); | 795 | AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>()); |
| @@ -1041,12 +1074,6 @@ void Init() { | |||
| 1041 | slot_data.parameter_event = | 1074 | slot_data.parameter_event = |
| 1042 | Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Parameter"); | 1075 | Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Parameter"); |
| 1043 | } | 1076 | } |
| 1044 | |||
| 1045 | // Initialize the parameter to wake up the application. | ||
| 1046 | next_parameter.emplace(); | ||
| 1047 | next_parameter->signal = static_cast<u32>(SignalType::Wakeup); | ||
| 1048 | next_parameter->destination_id = static_cast<u32>(AppletId::Application); | ||
| 1049 | applet_slots[static_cast<size_t>(AppletSlot::Application)].parameter_event->Signal(); | ||
| 1050 | } | 1077 | } |
| 1051 | 1078 | ||
| 1052 | void Shutdown() { | 1079 | void Shutdown() { |
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index 96b28b438..7b79e1f3e 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h | |||
| @@ -420,6 +420,16 @@ void GetAppCpuTimeLimit(Service::Interface* self); | |||
| 420 | void PrepareToStartLibraryApplet(Service::Interface* self); | 420 | void PrepareToStartLibraryApplet(Service::Interface* self); |
| 421 | 421 | ||
| 422 | /** | 422 | /** |
| 423 | * APT::PrepareToStartNewestHomeMenu service function | ||
| 424 | * Inputs: | ||
| 425 | * 0 : Command header [0x001A0000] | ||
| 426 | * Outputs: | ||
| 427 | * 0 : Return header | ||
| 428 | * 1 : Result of function | ||
| 429 | */ | ||
| 430 | void PrepareToStartNewestHomeMenu(Service::Interface* self); | ||
| 431 | |||
| 432 | /** | ||
| 423 | * APT::PreloadLibraryApplet service function | 433 | * APT::PreloadLibraryApplet service function |
| 424 | * Inputs: | 434 | * Inputs: |
| 425 | * 0 : Command header [0x00160040] | 435 | * 0 : Command header [0x00160040] |
diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp index ec5668d05..fe1d21fff 100644 --- a/src/core/hle/service/apt/apt_s.cpp +++ b/src/core/hle/service/apt/apt_s.cpp | |||
| @@ -17,7 +17,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 17 | {0x00060040, GetAppletInfo, "GetAppletInfo"}, | 17 | {0x00060040, GetAppletInfo, "GetAppletInfo"}, |
| 18 | {0x00070000, nullptr, "GetLastSignaledAppletId"}, | 18 | {0x00070000, nullptr, "GetLastSignaledAppletId"}, |
| 19 | {0x00080000, nullptr, "CountRegisteredApplet"}, | 19 | {0x00080000, nullptr, "CountRegisteredApplet"}, |
| 20 | {0x00090040, nullptr, "IsRegistered"}, | 20 | {0x00090040, IsRegistered, "IsRegistered"}, |
| 21 | {0x000A0040, nullptr, "GetAttribute"}, | 21 | {0x000A0040, nullptr, "GetAttribute"}, |
| 22 | {0x000B0040, InquireNotification, "InquireNotification"}, | 22 | {0x000B0040, InquireNotification, "InquireNotification"}, |
| 23 | {0x000C0104, nullptr, "SendParameter"}, | 23 | {0x000C0104, nullptr, "SendParameter"}, |
| @@ -34,7 +34,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 34 | {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, | 34 | {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, |
| 35 | {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, | 35 | {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, |
| 36 | {0x00190040, nullptr, "PrepareToStartSystemApplet"}, | 36 | {0x00190040, nullptr, "PrepareToStartSystemApplet"}, |
| 37 | {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, | 37 | {0x001A0000, PrepareToStartNewestHomeMenu, "PrepareToStartNewestHomeMenu"}, |
| 38 | {0x001B00C4, nullptr, "StartApplication"}, | 38 | {0x001B00C4, nullptr, "StartApplication"}, |
| 39 | {0x001C0000, nullptr, "WakeupApplication"}, | 39 | {0x001C0000, nullptr, "WakeupApplication"}, |
| 40 | {0x001D0000, nullptr, "CancelApplication"}, | 40 | {0x001D0000, nullptr, "CancelApplication"}, |
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index d5624fe54..b10d5852b 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp | |||
| @@ -5,6 +5,8 @@ | |||
| 5 | #include "common/common_types.h" | 5 | #include "common/common_types.h" |
| 6 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 7 | #include "core/hle/ipc.h" | 7 | #include "core/hle/ipc.h" |
| 8 | #include "core/hle/ipc_helpers.h" | ||
| 9 | #include "core/hle/kernel/event.h" | ||
| 8 | #include "core/hle/service/nim/nim.h" | 10 | #include "core/hle/service/nim/nim.h" |
| 9 | #include "core/hle/service/nim/nim_aoc.h" | 11 | #include "core/hle/service/nim/nim_aoc.h" |
| 10 | #include "core/hle/service/nim/nim_s.h" | 12 | #include "core/hle/service/nim/nim_s.h" |
| @@ -14,6 +16,16 @@ | |||
| 14 | namespace Service { | 16 | namespace Service { |
| 15 | namespace NIM { | 17 | namespace NIM { |
| 16 | 18 | ||
| 19 | static Kernel::SharedPtr<Kernel::Event> nim_system_update_event; | ||
| 20 | |||
| 21 | void CheckForSysUpdateEvent(Service::Interface* self) { | ||
| 22 | IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x5, 0, 0); // 0x50000 | ||
| 23 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | ||
| 24 | rb.Push(RESULT_SUCCESS); | ||
| 25 | rb.PushCopyHandles(Kernel::g_handle_table.Create(nim_system_update_event).Unwrap()); | ||
| 26 | LOG_TRACE(Service_NIM, "called"); | ||
| 27 | } | ||
| 28 | |||
| 17 | void CheckSysUpdateAvailable(Service::Interface* self) { | 29 | void CheckSysUpdateAvailable(Service::Interface* self) { |
| 18 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 30 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 19 | 31 | ||
| @@ -29,9 +41,13 @@ void Init() { | |||
| 29 | AddService(new NIM_AOC_Interface); | 41 | AddService(new NIM_AOC_Interface); |
| 30 | AddService(new NIM_S_Interface); | 42 | AddService(new NIM_S_Interface); |
| 31 | AddService(new NIM_U_Interface); | 43 | AddService(new NIM_U_Interface); |
| 44 | |||
| 45 | nim_system_update_event = Kernel::Event::Create(ResetType::OneShot, "NIM System Update Event"); | ||
| 32 | } | 46 | } |
| 33 | 47 | ||
| 34 | void Shutdown() {} | 48 | void Shutdown() { |
| 49 | nim_system_update_event = nullptr; | ||
| 50 | } | ||
| 35 | 51 | ||
| 36 | } // namespace NIM | 52 | } // namespace NIM |
| 37 | 53 | ||
diff --git a/src/core/hle/service/nim/nim.h b/src/core/hle/service/nim/nim.h index c3106f18b..dbf605e5a 100644 --- a/src/core/hle/service/nim/nim.h +++ b/src/core/hle/service/nim/nim.h | |||
| @@ -11,6 +11,17 @@ class Interface; | |||
| 11 | namespace NIM { | 11 | namespace NIM { |
| 12 | 12 | ||
| 13 | /** | 13 | /** |
| 14 | * NIM::CheckForSysUpdateEvent service function | ||
| 15 | * Inputs: | ||
| 16 | * 1 : None | ||
| 17 | * Outputs: | ||
| 18 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 19 | * 2 : Copy handle descriptor | ||
| 20 | * 3 : System Update event handle | ||
| 21 | */ | ||
| 22 | void CheckForSysUpdateEvent(Service::Interface* self); | ||
| 23 | |||
| 24 | /** | ||
| 14 | * NIM::CheckSysUpdateAvailable service function | 25 | * NIM::CheckSysUpdateAvailable service function |
| 15 | * Inputs: | 26 | * Inputs: |
| 16 | * 1 : None | 27 | * 1 : None |
diff --git a/src/core/hle/service/nim/nim_u.cpp b/src/core/hle/service/nim/nim_u.cpp index 7664bad60..569660278 100644 --- a/src/core/hle/service/nim/nim_u.cpp +++ b/src/core/hle/service/nim/nim_u.cpp | |||
| @@ -12,7 +12,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 12 | {0x00010000, nullptr, "StartSysUpdate"}, | 12 | {0x00010000, nullptr, "StartSysUpdate"}, |
| 13 | {0x00020000, nullptr, "GetUpdateDownloadProgress"}, | 13 | {0x00020000, nullptr, "GetUpdateDownloadProgress"}, |
| 14 | {0x00040000, nullptr, "FinishTitlesInstall"}, | 14 | {0x00040000, nullptr, "FinishTitlesInstall"}, |
| 15 | {0x00050000, nullptr, "CheckForSysUpdateEvent"}, | 15 | {0x00050000, CheckForSysUpdateEvent, "CheckForSysUpdateEvent"}, |
| 16 | {0x00090000, CheckSysUpdateAvailable, "CheckSysUpdateAvailable"}, | 16 | {0x00090000, CheckSysUpdateAvailable, "CheckSysUpdateAvailable"}, |
| 17 | {0x000A0000, nullptr, "GetState"}, | 17 | {0x000A0000, nullptr, "GetState"}, |
| 18 | {0x000B0000, nullptr, "GetSystemTitleHash"}, | 18 | {0x000B0000, nullptr, "GetSystemTitleHash"}, |
diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index 8ef0cda09..0aa63cc1e 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "core/hle/ipc_helpers.h" | 15 | #include "core/hle/ipc_helpers.h" |
| 16 | #include "core/hle/kernel/event.h" | 16 | #include "core/hle/kernel/event.h" |
| 17 | #include "core/hle/kernel/shared_memory.h" | 17 | #include "core/hle/kernel/shared_memory.h" |
| 18 | #include "core/hle/lock.h" | ||
| 18 | #include "core/hle/result.h" | 19 | #include "core/hle/result.h" |
| 19 | #include "core/hle/service/nwm/nwm_uds.h" | 20 | #include "core/hle/service/nwm/nwm_uds.h" |
| 20 | #include "core/hle/service/nwm/uds_beacon.h" | 21 | #include "core/hle/service/nwm/uds_beacon.h" |
| @@ -100,6 +101,20 @@ void SendPacket(Network::WifiPacket& packet) { | |||
| 100 | // TODO(Subv): Implement. | 101 | // TODO(Subv): Implement. |
| 101 | } | 102 | } |
| 102 | 103 | ||
| 104 | /* | ||
| 105 | * Returns an available index in the nodes array for the | ||
| 106 | * currently-hosted UDS network. | ||
| 107 | */ | ||
| 108 | static u16 GetNextAvailableNodeId() { | ||
| 109 | for (u16 index = 0; index < connection_status.max_nodes; ++index) { | ||
| 110 | if ((connection_status.node_bitmask & (1 << index)) == 0) | ||
| 111 | return index; | ||
| 112 | } | ||
| 113 | |||
| 114 | // Any connection attempts to an already full network should have been refused. | ||
| 115 | ASSERT_MSG(false, "No available connection slots in the network"); | ||
| 116 | } | ||
| 117 | |||
| 103 | // Inserts the received beacon frame in the beacon queue and removes any older beacons if the size | 118 | // Inserts the received beacon frame in the beacon queue and removes any older beacons if the size |
| 104 | // limit is exceeded. | 119 | // limit is exceeded. |
| 105 | void HandleBeaconFrame(const Network::WifiPacket& packet) { | 120 | void HandleBeaconFrame(const Network::WifiPacket& packet) { |
| @@ -143,18 +158,88 @@ void HandleAssociationResponseFrame(const Network::WifiPacket& packet) { | |||
| 143 | SendPacket(eapol_start); | 158 | SendPacket(eapol_start); |
| 144 | } | 159 | } |
| 145 | 160 | ||
| 146 | /* | 161 | static void HandleEAPoLPacket(const Network::WifiPacket& packet) { |
| 147 | * Returns an available index in the nodes array for the | 162 | std::lock_guard<std::mutex> lock(connection_status_mutex); |
| 148 | * currently-hosted UDS network. | ||
| 149 | */ | ||
| 150 | static u16 GetNextAvailableNodeId() { | ||
| 151 | for (u16 index = 0; index < connection_status.max_nodes; ++index) { | ||
| 152 | if ((connection_status.node_bitmask & (1 << index)) == 0) | ||
| 153 | return index; | ||
| 154 | } | ||
| 155 | 163 | ||
| 156 | // Any connection attempts to an already full network should have been refused. | 164 | if (GetEAPoLFrameType(packet.data) == EAPoLStartMagic) { |
| 157 | ASSERT_MSG(false, "No available connection slots in the network"); | 165 | if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) { |
| 166 | LOG_DEBUG(Service_NWM, "Connection sequence aborted, because connection status is %u", | ||
| 167 | connection_status.status); | ||
| 168 | return; | ||
| 169 | } | ||
| 170 | |||
| 171 | auto node = DeserializeNodeInfoFromFrame(packet.data); | ||
| 172 | |||
| 173 | if (connection_status.max_nodes == connection_status.total_nodes) { | ||
| 174 | // Reject connection attempt | ||
| 175 | LOG_ERROR(Service_NWM, "Reached maximum nodes, but reject packet wasn't sent."); | ||
| 176 | // TODO(B3N30): Figure out what packet is sent here | ||
| 177 | return; | ||
| 178 | } | ||
| 179 | |||
| 180 | // Get an unused network node id | ||
| 181 | u16 node_id = GetNextAvailableNodeId(); | ||
| 182 | node.network_node_id = node_id + 1; | ||
| 183 | |||
| 184 | connection_status.node_bitmask |= 1 << node_id; | ||
| 185 | connection_status.changed_nodes |= 1 << node_id; | ||
| 186 | connection_status.nodes[node_id] = node.network_node_id; | ||
| 187 | connection_status.total_nodes++; | ||
| 188 | |||
| 189 | u8 current_nodes = network_info.total_nodes; | ||
| 190 | node_info[current_nodes] = node; | ||
| 191 | |||
| 192 | network_info.total_nodes++; | ||
| 193 | |||
| 194 | // Send the EAPoL-Logoff packet. | ||
| 195 | using Network::WifiPacket; | ||
| 196 | WifiPacket eapol_logoff; | ||
| 197 | eapol_logoff.channel = network_channel; | ||
| 198 | eapol_logoff.data = | ||
| 199 | GenerateEAPoLLogoffFrame(packet.transmitter_address, node.network_node_id, node_info, | ||
| 200 | network_info.max_nodes, network_info.total_nodes); | ||
| 201 | // TODO(Subv): Encrypt the packet. | ||
| 202 | eapol_logoff.destination_address = packet.transmitter_address; | ||
| 203 | eapol_logoff.type = WifiPacket::PacketType::Data; | ||
| 204 | |||
| 205 | SendPacket(eapol_logoff); | ||
| 206 | // TODO(B3N30): Broadcast updated node list | ||
| 207 | // The 3ds does this presumably to support spectators. | ||
| 208 | std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); | ||
| 209 | connection_status_event->Signal(); | ||
| 210 | } else { | ||
| 211 | if (connection_status.status != static_cast<u32>(NetworkStatus::NotConnected)) { | ||
| 212 | LOG_DEBUG(Service_NWM, "Connection sequence aborted, because connection status is %u", | ||
| 213 | connection_status.status); | ||
| 214 | return; | ||
| 215 | } | ||
| 216 | auto logoff = ParseEAPoLLogoffFrame(packet.data); | ||
| 217 | |||
| 218 | network_info.total_nodes = logoff.connected_nodes; | ||
| 219 | network_info.max_nodes = logoff.max_nodes; | ||
| 220 | |||
| 221 | connection_status.network_node_id = logoff.assigned_node_id; | ||
| 222 | connection_status.total_nodes = logoff.connected_nodes; | ||
| 223 | connection_status.max_nodes = logoff.max_nodes; | ||
| 224 | |||
| 225 | node_info.clear(); | ||
| 226 | node_info.reserve(network_info.max_nodes); | ||
| 227 | for (size_t index = 0; index < logoff.connected_nodes; ++index) { | ||
| 228 | connection_status.node_bitmask |= 1 << index; | ||
| 229 | connection_status.changed_nodes |= 1 << index; | ||
| 230 | connection_status.nodes[index] = logoff.nodes[index].network_node_id; | ||
| 231 | |||
| 232 | node_info.emplace_back(DeserializeNodeInfo(logoff.nodes[index])); | ||
| 233 | } | ||
| 234 | |||
| 235 | // We're now connected, signal the application | ||
| 236 | connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsClient); | ||
| 237 | // Some games require ConnectToNetwork to block, for now it doesn't | ||
| 238 | // If blocking is implemented this lock needs to be changed, | ||
| 239 | // otherwise it might cause deadlocks | ||
| 240 | std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); | ||
| 241 | connection_status_event->Signal(); | ||
| 242 | } | ||
| 158 | } | 243 | } |
| 159 | 244 | ||
| 160 | /* | 245 | /* |
| @@ -238,6 +323,17 @@ void HandleAuthenticationFrame(const Network::WifiPacket& packet) { | |||
| 238 | } | 323 | } |
| 239 | } | 324 | } |
| 240 | 325 | ||
| 326 | static void HandleDataFrame(const Network::WifiPacket& packet) { | ||
| 327 | switch (GetFrameEtherType(packet.data)) { | ||
| 328 | case EtherType::EAPoL: | ||
| 329 | HandleEAPoLPacket(packet); | ||
| 330 | break; | ||
| 331 | case EtherType::SecureData: | ||
| 332 | // TODO(B3N30): Handle SecureData packets | ||
| 333 | break; | ||
| 334 | } | ||
| 335 | } | ||
| 336 | |||
| 241 | /// Callback to parse and handle a received wifi packet. | 337 | /// Callback to parse and handle a received wifi packet. |
| 242 | void OnWifiPacketReceived(const Network::WifiPacket& packet) { | 338 | void OnWifiPacketReceived(const Network::WifiPacket& packet) { |
| 243 | switch (packet.type) { | 339 | switch (packet.type) { |
| @@ -250,6 +346,9 @@ void OnWifiPacketReceived(const Network::WifiPacket& packet) { | |||
| 250 | case Network::WifiPacket::PacketType::AssociationResponse: | 346 | case Network::WifiPacket::PacketType::AssociationResponse: |
| 251 | HandleAssociationResponseFrame(packet); | 347 | HandleAssociationResponseFrame(packet); |
| 252 | break; | 348 | break; |
| 349 | case Network::WifiPacket::PacketType::Data: | ||
| 350 | HandleDataFrame(packet); | ||
| 351 | break; | ||
| 253 | } | 352 | } |
| 254 | } | 353 | } |
| 255 | 354 | ||
diff --git a/src/core/hle/service/nwm/uds_data.cpp b/src/core/hle/service/nwm/uds_data.cpp index 3ef2a84b6..4b389710f 100644 --- a/src/core/hle/service/nwm/uds_data.cpp +++ b/src/core/hle/service/nwm/uds_data.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 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> | ||
| 5 | #include <cstring> | 6 | #include <cstring> |
| 6 | #include <cryptopp/aes.h> | 7 | #include <cryptopp/aes.h> |
| 7 | #include <cryptopp/ccm.h> | 8 | #include <cryptopp/ccm.h> |
| @@ -277,10 +278,10 @@ std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 | |||
| 277 | std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) { | 278 | std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) { |
| 278 | EAPoLStartPacket eapol_start{}; | 279 | EAPoLStartPacket eapol_start{}; |
| 279 | eapol_start.association_id = association_id; | 280 | eapol_start.association_id = association_id; |
| 280 | eapol_start.friend_code_seed = node_info.friend_code_seed; | 281 | eapol_start.node.friend_code_seed = node_info.friend_code_seed; |
| 281 | 282 | ||
| 282 | for (int i = 0; i < node_info.username.size(); ++i) | 283 | std::copy(node_info.username.begin(), node_info.username.end(), |
| 283 | eapol_start.username[i] = node_info.username[i]; | 284 | eapol_start.node.username.begin()); |
| 284 | 285 | ||
| 285 | // Note: The network_node_id and unknown bytes seem to be uninitialized in the NWM module. | 286 | // Note: The network_node_id and unknown bytes seem to be uninitialized in the NWM module. |
| 286 | // TODO(B3N30): The last 8 bytes seem to have a fixed value of 07 88 15 00 04 e9 13 00 in | 287 | // TODO(B3N30): The last 8 bytes seem to have a fixed value of 07 88 15 00 04 e9 13 00 in |
| @@ -295,5 +296,78 @@ std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node | |||
| 295 | return buffer; | 296 | return buffer; |
| 296 | } | 297 | } |
| 297 | 298 | ||
| 299 | EtherType GetFrameEtherType(const std::vector<u8>& frame) { | ||
| 300 | LLCHeader header; | ||
| 301 | std::memcpy(&header, frame.data(), sizeof(header)); | ||
| 302 | |||
| 303 | u16 ethertype = header.protocol; | ||
| 304 | return static_cast<EtherType>(ethertype); | ||
| 305 | } | ||
| 306 | |||
| 307 | u16 GetEAPoLFrameType(const std::vector<u8>& frame) { | ||
| 308 | // Ignore the LLC header | ||
| 309 | u16_be eapol_type; | ||
| 310 | std::memcpy(&eapol_type, frame.data() + sizeof(LLCHeader), sizeof(eapol_type)); | ||
| 311 | return eapol_type; | ||
| 312 | } | ||
| 313 | |||
| 314 | NodeInfo DeserializeNodeInfoFromFrame(const std::vector<u8>& frame) { | ||
| 315 | EAPoLStartPacket eapol_start; | ||
| 316 | |||
| 317 | // Skip the LLC header | ||
| 318 | std::memcpy(&eapol_start, frame.data() + sizeof(LLCHeader), sizeof(eapol_start)); | ||
| 319 | |||
| 320 | NodeInfo node{}; | ||
| 321 | node.friend_code_seed = eapol_start.node.friend_code_seed; | ||
| 322 | |||
| 323 | std::copy(eapol_start.node.username.begin(), eapol_start.node.username.end(), | ||
| 324 | node.username.begin()); | ||
| 325 | |||
| 326 | return node; | ||
| 327 | } | ||
| 328 | |||
| 329 | NodeInfo DeserializeNodeInfo(const EAPoLNodeInfo& node) { | ||
| 330 | NodeInfo node_info{}; | ||
| 331 | node_info.friend_code_seed = node.friend_code_seed; | ||
| 332 | node_info.network_node_id = node.network_node_id; | ||
| 333 | |||
| 334 | std::copy(node.username.begin(), node.username.end(), node_info.username.begin()); | ||
| 335 | |||
| 336 | return node_info; | ||
| 337 | } | ||
| 338 | |||
| 339 | std::vector<u8> GenerateEAPoLLogoffFrame(const MacAddress& mac_address, u16 network_node_id, | ||
| 340 | const NodeList& nodes, u8 max_nodes, u8 total_nodes) { | ||
| 341 | EAPoLLogoffPacket eapol_logoff{}; | ||
| 342 | eapol_logoff.assigned_node_id = network_node_id; | ||
| 343 | eapol_logoff.connected_nodes = total_nodes; | ||
| 344 | eapol_logoff.max_nodes = max_nodes; | ||
| 345 | |||
| 346 | for (size_t index = 0; index < total_nodes; ++index) { | ||
| 347 | const auto& node_info = nodes[index]; | ||
| 348 | auto& node = eapol_logoff.nodes[index]; | ||
| 349 | |||
| 350 | node.friend_code_seed = node_info.friend_code_seed; | ||
| 351 | node.network_node_id = node_info.network_node_id; | ||
| 352 | |||
| 353 | std::copy(node_info.username.begin(), node_info.username.end(), node.username.begin()); | ||
| 354 | } | ||
| 355 | |||
| 356 | std::vector<u8> eapol_buffer(sizeof(EAPoLLogoffPacket)); | ||
| 357 | std::memcpy(eapol_buffer.data(), &eapol_logoff, sizeof(eapol_logoff)); | ||
| 358 | |||
| 359 | std::vector<u8> buffer = GenerateLLCHeader(EtherType::EAPoL); | ||
| 360 | buffer.insert(buffer.end(), eapol_buffer.begin(), eapol_buffer.end()); | ||
| 361 | return buffer; | ||
| 362 | } | ||
| 363 | |||
| 364 | EAPoLLogoffPacket ParseEAPoLLogoffFrame(const std::vector<u8>& frame) { | ||
| 365 | EAPoLLogoffPacket eapol_logoff; | ||
| 366 | |||
| 367 | // Skip the LLC header | ||
| 368 | std::memcpy(&eapol_logoff, frame.data() + sizeof(LLCHeader), sizeof(eapol_logoff)); | ||
| 369 | return eapol_logoff; | ||
| 370 | } | ||
| 371 | |||
| 298 | } // namespace NWM | 372 | } // namespace NWM |
| 299 | } // namespace Service | 373 | } // namespace Service |
diff --git a/src/core/hle/service/nwm/uds_data.h b/src/core/hle/service/nwm/uds_data.h index 76e8f546b..76bccb1bf 100644 --- a/src/core/hle/service/nwm/uds_data.h +++ b/src/core/hle/service/nwm/uds_data.h | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/swap.h" | 10 | #include "common/swap.h" |
| 11 | #include "core/hle/service/nwm/uds_beacon.h" | ||
| 11 | #include "core/hle/service/service.h" | 12 | #include "core/hle/service/service.h" |
| 12 | 13 | ||
| 13 | namespace Service { | 14 | namespace Service { |
| @@ -67,6 +68,16 @@ struct DataFrameCryptoCTR { | |||
| 67 | 68 | ||
| 68 | static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); | 69 | static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); |
| 69 | 70 | ||
| 71 | struct EAPoLNodeInfo { | ||
| 72 | u64_be friend_code_seed; | ||
| 73 | std::array<u16_be, 10> username; | ||
| 74 | INSERT_PADDING_BYTES(4); | ||
| 75 | u16_be network_node_id; | ||
| 76 | INSERT_PADDING_BYTES(6); | ||
| 77 | }; | ||
| 78 | |||
| 79 | static_assert(sizeof(EAPoLNodeInfo) == 0x28, "EAPoLNodeInfo has the wrong size"); | ||
| 80 | |||
| 70 | constexpr u16 EAPoLStartMagic = 0x201; | 81 | constexpr u16 EAPoLStartMagic = 0x201; |
| 71 | 82 | ||
| 72 | /* | 83 | /* |
| @@ -78,15 +89,27 @@ struct EAPoLStartPacket { | |||
| 78 | // This value is hardcoded to 1 in the NWM module. | 89 | // This value is hardcoded to 1 in the NWM module. |
| 79 | u16_be unknown = 1; | 90 | u16_be unknown = 1; |
| 80 | INSERT_PADDING_BYTES(2); | 91 | INSERT_PADDING_BYTES(2); |
| 92 | EAPoLNodeInfo node; | ||
| 93 | }; | ||
| 81 | 94 | ||
| 82 | u64_be friend_code_seed; | 95 | static_assert(sizeof(EAPoLStartPacket) == 0x30, "EAPoLStartPacket has the wrong size"); |
| 83 | std::array<u16_be, 10> username; | 96 | |
| 84 | INSERT_PADDING_BYTES(4); | 97 | constexpr u16 EAPoLLogoffMagic = 0x202; |
| 85 | u16_be network_node_id; | 98 | |
| 99 | struct EAPoLLogoffPacket { | ||
| 100 | u16_be magic = EAPoLLogoffMagic; | ||
| 101 | INSERT_PADDING_BYTES(2); | ||
| 102 | u16_be assigned_node_id; | ||
| 103 | MacAddress client_mac_address; | ||
| 86 | INSERT_PADDING_BYTES(6); | 104 | INSERT_PADDING_BYTES(6); |
| 105 | u8 connected_nodes; | ||
| 106 | u8 max_nodes; | ||
| 107 | INSERT_PADDING_BYTES(4); | ||
| 108 | |||
| 109 | std::array<EAPoLNodeInfo, UDSMaxNodes> nodes; | ||
| 87 | }; | 110 | }; |
| 88 | 111 | ||
| 89 | static_assert(sizeof(EAPoLStartPacket) == 0x30, "EAPoLStartPacket has the wrong size"); | 112 | static_assert(sizeof(EAPoLLogoffPacket) == 0x298, "EAPoLLogoffPacket has the wrong size"); |
| 90 | 113 | ||
| 91 | /** | 114 | /** |
| 92 | * Generates an unencrypted 802.11 data payload. | 115 | * Generates an unencrypted 802.11 data payload. |
| @@ -102,5 +125,40 @@ std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 | |||
| 102 | */ | 125 | */ |
| 103 | std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info); | 126 | std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info); |
| 104 | 127 | ||
| 128 | /* | ||
| 129 | * Returns the EtherType of the specified 802.11 frame. | ||
| 130 | */ | ||
| 131 | EtherType GetFrameEtherType(const std::vector<u8>& frame); | ||
| 132 | |||
| 133 | /* | ||
| 134 | * Returns the EAPoL type (Start / Logoff) of the specified 802.11 frame. | ||
| 135 | * Note: The frame *must* be an EAPoL frame. | ||
| 136 | */ | ||
| 137 | u16 GetEAPoLFrameType(const std::vector<u8>& frame); | ||
| 138 | |||
| 139 | /* | ||
| 140 | * Returns a deserialized NodeInfo structure from the information inside an EAPoL-Start packet | ||
| 141 | * encapsulated in an 802.11 data frame. | ||
| 142 | */ | ||
| 143 | NodeInfo DeserializeNodeInfoFromFrame(const std::vector<u8>& frame); | ||
| 144 | |||
| 145 | /* | ||
| 146 | * Returns a NodeInfo constructed from the data in the specified EAPoLNodeInfo. | ||
| 147 | */ | ||
| 148 | NodeInfo DeserializeNodeInfo(const EAPoLNodeInfo& node); | ||
| 149 | |||
| 150 | /* | ||
| 151 | * Generates an unencrypted 802.11 data frame body with the EAPoL-Logoff format for UDS | ||
| 152 | * communication. | ||
| 153 | * @returns The generated frame body. | ||
| 154 | */ | ||
| 155 | std::vector<u8> GenerateEAPoLLogoffFrame(const MacAddress& mac_address, u16 network_node_id, | ||
| 156 | const NodeList& nodes, u8 max_nodes, u8 total_nodes); | ||
| 157 | |||
| 158 | /* | ||
| 159 | * Returns a EAPoLLogoffPacket representing the specified 802.11-encapsulated data frame. | ||
| 160 | */ | ||
| 161 | EAPoLLogoffPacket ParseEAPoLLogoffFrame(const std::vector<u8>& frame); | ||
| 162 | |||
| 105 | } // namespace NWM | 163 | } // namespace NWM |
| 106 | } // namespace Service | 164 | } // namespace Service |
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 41c82c922..6be5db13f 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp | |||
| @@ -271,6 +271,24 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) | |||
| 271 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 271 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 272 | thread->WakeAfterDelay(nano_seconds); | 272 | thread->WakeAfterDelay(nano_seconds); |
| 273 | 273 | ||
| 274 | thread->wakeup_callback = [](ThreadWakeupReason reason, | ||
| 275 | Kernel::SharedPtr<Kernel::Thread> thread, | ||
| 276 | Kernel::SharedPtr<Kernel::WaitObject> object) { | ||
| 277 | |||
| 278 | ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY); | ||
| 279 | |||
| 280 | if (reason == ThreadWakeupReason::Timeout) { | ||
| 281 | thread->SetWaitSynchronizationResult(Kernel::RESULT_TIMEOUT); | ||
| 282 | return; | ||
| 283 | } | ||
| 284 | |||
| 285 | ASSERT(reason == ThreadWakeupReason::Signal); | ||
| 286 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 287 | |||
| 288 | // WaitSynchronization1 doesn't have an output index like WaitSynchronizationN, so we | ||
| 289 | // don't have to do anything else here. | ||
| 290 | }; | ||
| 291 | |||
| 274 | Core::System::GetInstance().PrepareReschedule(); | 292 | Core::System::GetInstance().PrepareReschedule(); |
| 275 | 293 | ||
| 276 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread | 294 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread |
| @@ -344,6 +362,23 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha | |||
| 344 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 362 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 345 | thread->WakeAfterDelay(nano_seconds); | 363 | thread->WakeAfterDelay(nano_seconds); |
| 346 | 364 | ||
| 365 | thread->wakeup_callback = [](ThreadWakeupReason reason, | ||
| 366 | Kernel::SharedPtr<Kernel::Thread> thread, | ||
| 367 | Kernel::SharedPtr<Kernel::WaitObject> object) { | ||
| 368 | |||
| 369 | ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ALL); | ||
| 370 | |||
| 371 | if (reason == ThreadWakeupReason::Timeout) { | ||
| 372 | thread->SetWaitSynchronizationResult(Kernel::RESULT_TIMEOUT); | ||
| 373 | return; | ||
| 374 | } | ||
| 375 | |||
| 376 | ASSERT(reason == ThreadWakeupReason::Signal); | ||
| 377 | |||
| 378 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 379 | // The wait_all case does not update the output index. | ||
| 380 | }; | ||
| 381 | |||
| 347 | Core::System::GetInstance().PrepareReschedule(); | 382 | Core::System::GetInstance().PrepareReschedule(); |
| 348 | 383 | ||
| 349 | // This value gets set to -1 by default in this case, it is not modified after this. | 384 | // This value gets set to -1 by default in this case, it is not modified after this. |
| @@ -389,12 +424,28 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha | |||
| 389 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 424 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 390 | thread->WakeAfterDelay(nano_seconds); | 425 | thread->WakeAfterDelay(nano_seconds); |
| 391 | 426 | ||
| 427 | thread->wakeup_callback = [](ThreadWakeupReason reason, | ||
| 428 | Kernel::SharedPtr<Kernel::Thread> thread, | ||
| 429 | Kernel::SharedPtr<Kernel::WaitObject> object) { | ||
| 430 | |||
| 431 | ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY); | ||
| 432 | |||
| 433 | if (reason == ThreadWakeupReason::Timeout) { | ||
| 434 | thread->SetWaitSynchronizationResult(Kernel::RESULT_TIMEOUT); | ||
| 435 | return; | ||
| 436 | } | ||
| 437 | |||
| 438 | ASSERT(reason == ThreadWakeupReason::Signal); | ||
| 439 | |||
| 440 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 441 | thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get())); | ||
| 442 | }; | ||
| 443 | |||
| 392 | Core::System::GetInstance().PrepareReschedule(); | 444 | Core::System::GetInstance().PrepareReschedule(); |
| 393 | 445 | ||
| 394 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a | 446 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a |
| 395 | // signal in one of its wait objects. | 447 | // signal in one of its wait objects. |
| 396 | // Otherwise we retain the default value of timeout, and -1 in the out parameter | 448 | // Otherwise we retain the default value of timeout, and -1 in the out parameter |
| 397 | thread->wait_set_output = true; | ||
| 398 | *out = -1; | 449 | *out = -1; |
| 399 | return Kernel::RESULT_TIMEOUT; | 450 | return Kernel::RESULT_TIMEOUT; |
| 400 | } | 451 | } |
| @@ -483,8 +534,6 @@ static ResultCode ReplyAndReceive(s32* index, Kernel::Handle* handles, s32 handl | |||
| 483 | 534 | ||
| 484 | // No objects were ready to be acquired, prepare to suspend the thread. | 535 | // No objects were ready to be acquired, prepare to suspend the thread. |
| 485 | 536 | ||
| 486 | // TODO(Subv): Perform IPC translation upon wakeup. | ||
| 487 | |||
| 488 | // Put the thread to sleep | 537 | // Put the thread to sleep |
| 489 | thread->status = THREADSTATUS_WAIT_SYNCH_ANY; | 538 | thread->status = THREADSTATUS_WAIT_SYNCH_ANY; |
| 490 | 539 | ||
| @@ -496,12 +545,24 @@ static ResultCode ReplyAndReceive(s32* index, Kernel::Handle* handles, s32 handl | |||
| 496 | 545 | ||
| 497 | thread->wait_objects = std::move(objects); | 546 | thread->wait_objects = std::move(objects); |
| 498 | 547 | ||
| 548 | thread->wakeup_callback = [](ThreadWakeupReason reason, | ||
| 549 | Kernel::SharedPtr<Kernel::Thread> thread, | ||
| 550 | Kernel::SharedPtr<Kernel::WaitObject> object) { | ||
| 551 | |||
| 552 | ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY); | ||
| 553 | ASSERT(reason == ThreadWakeupReason::Signal); | ||
| 554 | |||
| 555 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 556 | thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get())); | ||
| 557 | |||
| 558 | // TODO(Subv): Perform IPC translation upon wakeup. | ||
| 559 | }; | ||
| 560 | |||
| 499 | Core::System::GetInstance().PrepareReschedule(); | 561 | Core::System::GetInstance().PrepareReschedule(); |
| 500 | 562 | ||
| 501 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a | 563 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a |
| 502 | // signal in one of its wait objects, or to 0xC8A01836 if there was a translation error. | 564 | // signal in one of its wait objects, or to 0xC8A01836 if there was a translation error. |
| 503 | // By default the index is set to -1. | 565 | // By default the index is set to -1. |
| 504 | thread->wait_set_output = true; | ||
| 505 | *index = -1; | 566 | *index = -1; |
| 506 | return RESULT_SUCCESS; | 567 | return RESULT_SUCCESS; |
| 507 | } | 568 | } |
| @@ -978,7 +1039,7 @@ static void SleepThread(s64 nanoseconds) { | |||
| 978 | static s64 GetSystemTick() { | 1039 | static s64 GetSystemTick() { |
| 979 | s64 result = CoreTiming::GetTicks(); | 1040 | s64 result = CoreTiming::GetTicks(); |
| 980 | // Advance time to defeat dumb games (like Cubic Ninja) that busy-wait for the frame to end. | 1041 | // Advance time to defeat dumb games (like Cubic Ninja) that busy-wait for the frame to end. |
| 981 | Core::CPU().AddTicks(150); // Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b | 1042 | CoreTiming::AddTicks(150); // Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b |
| 982 | return result; | 1043 | return result; |
| 983 | } | 1044 | } |
| 984 | 1045 | ||
diff --git a/src/tests/core/arm/dyncom/arm_dyncom_vfp_tests.cpp b/src/tests/core/arm/dyncom/arm_dyncom_vfp_tests.cpp index 86de41773..83719a58e 100644 --- a/src/tests/core/arm/dyncom/arm_dyncom_vfp_tests.cpp +++ b/src/tests/core/arm/dyncom/arm_dyncom_vfp_tests.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <catch.hpp> | 5 | #include <catch.hpp> |
| 6 | 6 | ||
| 7 | #include "core/arm/dyncom/arm_dyncom.h" | 7 | #include "core/arm/dyncom/arm_dyncom.h" |
| 8 | #include "core/core_timing.h" | ||
| 8 | #include "tests/core/arm/arm_test_common.h" | 9 | #include "tests/core/arm/arm_test_common.h" |
| 9 | 10 | ||
| 10 | namespace ArmTests { | 11 | namespace ArmTests { |
| @@ -29,7 +30,6 @@ TEST_CASE("ARM_DynCom (vfp): vadd", "[arm_dyncom]") { | |||
| 29 | }}; | 30 | }}; |
| 30 | 31 | ||
| 31 | for (const auto& test_case : test_cases) { | 32 | for (const auto& test_case : test_cases) { |
| 32 | dyncom.down_count = 1000; // Ensure that CoreTimeing will not be called. | ||
| 33 | dyncom.SetPC(0); | 33 | dyncom.SetPC(0); |
| 34 | dyncom.SetVFPSystemReg(VFP_FPSCR, test_case.initial_fpscr); | 34 | dyncom.SetVFPSystemReg(VFP_FPSCR, test_case.initial_fpscr); |
| 35 | dyncom.SetVFPReg(4, test_case.a); | 35 | dyncom.SetVFPReg(4, test_case.a); |