summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--src/core/arm/arm_interface.h9
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp9
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h2
-rw-r--r--src/core/arm/dyncom/arm_dyncom.cpp8
-rw-r--r--src/core/arm/dyncom/arm_dyncom.h2
-rw-r--r--src/core/core_timing.cpp36
-rw-r--r--src/core/core_timing.h6
-rw-r--r--src/core/hle/kernel/thread.cpp13
-rw-r--r--src/core/hle/kernel/thread.h15
-rw-r--r--src/core/hle/kernel/wait_object.cpp11
-rw-r--r--src/core/hle/service/apt/apt.cpp39
-rw-r--r--src/core/hle/service/apt/apt.h10
-rw-r--r--src/core/hle/service/apt/apt_s.cpp4
-rw-r--r--src/core/hle/service/nim/nim.cpp18
-rw-r--r--src/core/hle/service/nim/nim.h11
-rw-r--r--src/core/hle/service/nim/nim_u.cpp2
-rw-r--r--src/core/hle/service/nwm/nwm_uds.cpp121
-rw-r--r--src/core/hle/service/nwm/uds_data.cpp80
-rw-r--r--src/core/hle/service/nwm/uds_data.h68
-rw-r--r--src/core/hle/svc.cpp71
-rw-r--r--src/tests/core/arm/dyncom/arm_dyncom_vfp_tests.cpp2
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
20CMakeSettings.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
156protected: 147protected:
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
127void ARM_Dynarmic::AddTicks(u64 ticks) {
128 down_count -= ticks;
129 if (down_count < 0) {
130 CoreTiming::Advance();
131 }
132}
133
134MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64)); 127MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64));
135 128
136void ARM_Dynarmic::ExecuteInstructions(int num_instructions) { 129void 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
145void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) { 138void 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
80void ARM_DynCom::AddTicks(u64 ticks) {
81 down_count -= ticks;
82 if (down_count < 0)
83 CoreTiming::Advance();
84}
85
86void ARM_DynCom::ExecuteInstructions(int num_instructions) { 80void 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
96void ARM_DynCom::SaveContext(ThreadContext& ctx) { 90void 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;
57static s64 last_global_time_ticks; 57static s64 last_global_time_ticks;
58static s64 last_global_time_us; 58static s64 last_global_time_us;
59 59
60static s64 down_count = 0; ///< A decreasing counter of remaining cycles before the next event,
61 /// decreased by the cpu run loop
62
60static std::recursive_mutex external_event_section; 63static 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
148void Init() { 151void 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
191void AddTicks(u64 ticks) {
192 down_count -= ticks;
193 if (down_count < 0) {
194 Advance();
195 }
196}
197
188u64 GetTicks() { 198u64 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
192u64 GetIdleTicks() { 202u64 GetIdleTicks() {
@@ -460,18 +470,18 @@ void MoveEvents() {
460} 470}
461 471
462void ForceCheck() { 472void 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
471void Advance() { 481void 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
508void Idle(int max_idle) { 518void 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
534std::string GetScheduledEventsSummary() { 544std::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();
67typedef void (*MHzChangeCallback)(); 67typedef void (*MHzChangeCallback)();
68typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback; 68typedef 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*/
74void AddTicks(u64 ticks);
75
70u64 GetTicks(); 76u64 GetTicks();
71u64 GetIdleTicks(); 77u64 GetIdleTicks();
72u64 GetGlobalTimeUs(); 78u64 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
44enum 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
44namespace Kernel { 49namespace Kernel {
45 50
46class Mutex; 51class 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
216private: 225private:
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
202static u32 DecompressLZ11(const u8* in, u8* out) { 221static 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
779void 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
760void PreloadLibraryApplet(Service::Interface* self) { 793void 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
1052void Shutdown() { 1079void 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);
420void PrepareToStartLibraryApplet(Service::Interface* self); 420void 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 */
430void 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 @@
14namespace Service { 16namespace Service {
15namespace NIM { 17namespace NIM {
16 18
19static Kernel::SharedPtr<Kernel::Event> nim_system_update_event;
20
21void 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
17void CheckSysUpdateAvailable(Service::Interface* self) { 29void 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
34void Shutdown() {} 48void 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;
11namespace NIM { 11namespace 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 */
22void 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 */
108static 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.
105void HandleBeaconFrame(const Network::WifiPacket& packet) { 120void 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/* 161static 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 */
150static 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
326static 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.
242void OnWifiPacketReceived(const Network::WifiPacket& packet) { 338void 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
277std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) { 278std::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
299EtherType 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
307u16 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
314NodeInfo 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
329NodeInfo 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
339std::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
364EAPoLLogoffPacket 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
13namespace Service { 14namespace Service {
@@ -67,6 +68,16 @@ struct DataFrameCryptoCTR {
67 68
68static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); 69static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size");
69 70
71struct 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
79static_assert(sizeof(EAPoLNodeInfo) == 0x28, "EAPoLNodeInfo has the wrong size");
80
70constexpr u16 EAPoLStartMagic = 0x201; 81constexpr 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; 95static_assert(sizeof(EAPoLStartPacket) == 0x30, "EAPoLStartPacket has the wrong size");
83 std::array<u16_be, 10> username; 96
84 INSERT_PADDING_BYTES(4); 97constexpr u16 EAPoLLogoffMagic = 0x202;
85 u16_be network_node_id; 98
99struct 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
89static_assert(sizeof(EAPoLStartPacket) == 0x30, "EAPoLStartPacket has the wrong size"); 112static_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 */
103std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info); 126std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info);
104 127
128/*
129 * Returns the EtherType of the specified 802.11 frame.
130 */
131EtherType 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 */
137u16 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 */
143NodeInfo DeserializeNodeInfoFromFrame(const std::vector<u8>& frame);
144
145/*
146 * Returns a NodeInfo constructed from the data in the specified EAPoLNodeInfo.
147 */
148NodeInfo 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 */
155std::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 */
161EAPoLLogoffPacket 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) {
978static s64 GetSystemTick() { 1039static 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
10namespace ArmTests { 11namespace 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);