diff options
Diffstat (limited to 'src/core/hle')
35 files changed, 820 insertions, 220 deletions
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index f7f96125a..87ed85df6 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h @@ -122,11 +122,11 @@ union StaticBufferDescInfo { BitField<14, 18, u32> size; }; -inline u32 StaticBufferDesc(u32 size, u8 buffer_id) { +inline u32 StaticBufferDesc(size_t size, u8 buffer_id) { StaticBufferDescInfo info{}; info.descriptor_type.Assign(StaticBuffer); info.buffer_id.Assign(buffer_id); - info.size.Assign(size); + info.size.Assign(static_cast<u32>(size)); return info.raw; } @@ -160,11 +160,11 @@ union MappedBufferDescInfo { BitField<4, 28, u32> size; }; -inline u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) { +inline u32 MappedBufferDesc(size_t size, MappedBufferPermissions perms) { MappedBufferDescInfo info{}; info.flags.Assign(MappedBuffer); info.perms.Assign(perms); - info.size.Assign(size); + info.size.Assign(static_cast<u32>(size)); return info.raw; } diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index f0d89cffe..7cb95cbac 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -117,9 +117,9 @@ public: void PushCurrentPIDHandle(); - void PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id); + void PushStaticBuffer(VAddr buffer_vaddr, size_t size, u8 buffer_id); - void PushMappedBuffer(VAddr buffer_vaddr, u32 size, MappedBufferPermissions perms); + void PushMappedBuffer(VAddr buffer_vaddr, size_t size, MappedBufferPermissions perms); }; /// Push /// @@ -190,12 +190,12 @@ inline void RequestBuilder::PushCurrentPIDHandle() { Push(u32(0)); } -inline void RequestBuilder::PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id) { +inline void RequestBuilder::PushStaticBuffer(VAddr buffer_vaddr, size_t size, u8 buffer_id) { Push(StaticBufferDesc(size, buffer_id)); Push(buffer_vaddr); } -inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, u32 size, +inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, size_t size, MappedBufferPermissions perms) { Push(MappedBufferDesc(size, perms)); Push(buffer_vaddr); @@ -227,8 +227,8 @@ public: bool validateHeader = true) { if (validateHeader) ValidateHeader(); - Header builderHeader{ - MakeHeader(header.command_id, normal_params_size, translate_params_size)}; + Header builderHeader{MakeHeader(static_cast<u16>(header.command_id), normal_params_size, + translate_params_size)}; if (context != nullptr) return {*context, builderHeader}; else diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 5ebe2eca4..6020e9764 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -37,7 +37,7 @@ SharedPtr<Object> HLERequestContext::GetIncomingHandle(u32 id_from_cmdbuf) const u32 HLERequestContext::AddOutgoingHandle(SharedPtr<Object> object) { request_handles.push_back(std::move(object)); - return request_handles.size() - 1; + return static_cast<u32>(request_handles.size() - 1); } void HLERequestContext::ClearIncomingObjects() { diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index cef961289..2cbca5e5b 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -90,7 +90,7 @@ void Mutex::UpdatePriority() { if (!holding_thread) return; - s32 best_priority = THREADPRIO_LOWEST; + u32 best_priority = THREADPRIO_LOWEST; for (auto& waiter : GetWaitingThreads()) { if (waiter->current_priority < best_priority) best_priority = waiter->current_priority; diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 522ad2333..cf3163e0f 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -147,7 +147,7 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) { } vm_manager.LogLayout(Log::Level::Debug); - Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority); + Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority, this); } VAddr Process::GetLinearHeapAreaAddress() const { diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp index a8f10a3ee..517dc47a8 100644 --- a/src/core/hle/kernel/resource_limit.cpp +++ b/src/core/hle/kernel/resource_limit.cpp @@ -61,7 +61,7 @@ s32 ResourceLimit::GetCurrentResourceValue(u32 resource) const { } } -s32 ResourceLimit::GetMaxResourceValue(u32 resource) const { +u32 ResourceLimit::GetMaxResourceValue(u32 resource) const { switch (resource) { case PRIORITY: return max_priority; diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h index 6cdfbcf8d..42874eb8d 100644 --- a/src/core/hle/kernel/resource_limit.h +++ b/src/core/hle/kernel/resource_limit.h @@ -67,7 +67,7 @@ public: * @param resource Requested resource type * @returns The max value of the resource type */ - s32 GetMaxResourceValue(u32 resource) const; + u32 GetMaxResourceValue(u32 resource) const; /// Name of resource limit object. std::string name; diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index a7b66142f..d45daca35 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -42,7 +42,8 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u memory_region->used += size; shared_memory->linear_heap_phys_address = - Memory::FCRAM_PADDR + memory_region->base + shared_memory->backing_block_offset; + Memory::FCRAM_PADDR + memory_region->base + + static_cast<PAddr>(shared_memory->backing_block_offset); // Increase the amount of used linear heap memory for the owner process. if (shared_memory->owner_process != nullptr) { @@ -54,22 +55,19 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u Kernel::g_current_process->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); } } else { - // TODO(Subv): What happens if an application tries to create multiple memory blocks - // pointing to the same address? auto& vm_manager = shared_memory->owner_process->vm_manager; // The memory is already available and mapped in the owner process. - auto vma = vm_manager.FindVMA(address)->second; - // Copy it over to our own storage - shared_memory->backing_block = std::make_shared<std::vector<u8>>( - vma.backing_block->data() + vma.offset, vma.backing_block->data() + vma.offset + size); - shared_memory->backing_block_offset = 0; - // Unmap the existing pages - vm_manager.UnmapRange(address, size); - // Map our own block into the address space - vm_manager.MapMemoryBlock(address, shared_memory->backing_block, 0, size, - MemoryState::Shared); - // Reprotect the block with the new permissions - vm_manager.ReprotectRange(address, size, ConvertPermissions(permissions)); + auto vma = vm_manager.FindVMA(address); + ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address"); + ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address"); + + // The returned VMA might be a bigger one encompassing the desired address. + auto vma_offset = address - vma->first; + ASSERT_MSG(vma_offset + size <= vma->second.size, + "Shared memory exceeds bounds of mapped block"); + + shared_memory->backing_block = vma->second.backing_block; + shared_memory->backing_block_offset = vma->second.offset + vma_offset; } shared_memory->base_address = address; @@ -183,4 +181,4 @@ u8* SharedMemory::GetPointer(u32 offset) { return backing_block->data() + backing_block_offset + offset; } -} // namespace +} // namespace Kernel diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 94b335ed1..93a6f2182 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -114,7 +114,7 @@ public: /// Backing memory for this shared memory block. std::shared_ptr<std::vector<u8>> backing_block; /// Offset into the backing block for this shared memory. - u32 backing_block_offset; + size_t backing_block_offset; /// Size of the memory block. Page-aligned. u32 size; /// Permission restrictions applied to the process which created the block. diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 324415a36..0f7970ebe 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -111,7 +111,7 @@ void Thread::Stop() { Thread* ArbitrateHighestPriorityThread(u32 address) { Thread* highest_priority_thread = nullptr; - s32 priority = THREADPRIO_LOWEST; + u32 priority = THREADPRIO_LOWEST; // Iterate through threads, find highest priority thread that is waiting to be arbitrated... for (auto& thread : thread_list) { @@ -178,16 +178,13 @@ static void SwitchContext(Thread* new_thread) { ready_queue.remove(new_thread->current_priority, new_thread); new_thread->status = THREADSTATUS_RUNNING; - Core::CPU().LoadContext(new_thread->context); - Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress()); - if (previous_process != current_thread->owner_process) { Kernel::g_current_process = current_thread->owner_process; - Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table; - // We have switched processes and thus, page tables, clear the instruction cache so we - // don't keep stale data from the previous process. - Core::CPU().ClearInstructionCache(); + SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); } + + Core::CPU().LoadContext(new_thread->context); + Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress()); } else { current_thread = nullptr; // Note: We do not reset the current process and current page table when idling because @@ -250,12 +247,15 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY || thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) { - thread->wait_set_output = false; + + // Invoke the wakeup callback before clearing the wait objects + if (thread->wakeup_callback) + thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr); + // Remove the thread from each of its waiting objects' waitlists for (auto& object : thread->wait_objects) object->RemoveWaitingThread(thread.get()); thread->wait_objects.clear(); - thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); } thread->ResumeFromWait(); @@ -281,6 +281,9 @@ void Thread::ResumeFromWait() { break; case THREADSTATUS_READY: + // The thread's wakeup callback must have already been cleared when the thread was first + // awoken. + ASSERT(wakeup_callback == nullptr); // If the thread is waiting on multiple wait objects, it might be awoken more than once // before actually resuming. We can ignore subsequent wakeups if the thread status has // already been set to THREADSTATUS_READY. @@ -296,6 +299,8 @@ void Thread::ResumeFromWait() { return; } + wakeup_callback = nullptr; + ready_queue.push_back(current_priority, this); status = THREADSTATUS_READY; Core::System::GetInstance().PrepareReschedule(); @@ -314,7 +319,7 @@ static void DebugThreadQueue() { } for (auto& t : thread_list) { - s32 priority = ready_queue.contains(t.get()); + u32 priority = ready_queue.contains(t.get()); if (priority != -1) { LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId()); } @@ -364,7 +369,8 @@ static void ResetThreadContext(ARM_Interface::ThreadContext& context, u32 stack_ } ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, u32 priority, - u32 arg, s32 processor_id, VAddr stack_top) { + u32 arg, s32 processor_id, VAddr stack_top, + SharedPtr<Process> owner_process) { // Check if priority is in ranged. Lowest priority -> highest priority id. if (priority > THREADPRIO_LOWEST) { LOG_ERROR(Kernel_SVC, "Invalid thread priority: %d", priority); @@ -378,7 +384,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, // TODO(yuriks): Other checks, returning 0xD9001BEA - if (!Memory::IsValidVirtualAddress(entry_point)) { + if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) { LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point); // TODO: Verify error return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, @@ -397,15 +403,14 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, thread->nominal_priority = thread->current_priority = priority; thread->last_running_ticks = CoreTiming::GetTicks(); thread->processor_id = processor_id; - thread->wait_set_output = false; thread->wait_objects.clear(); thread->wait_address = 0; thread->name = std::move(name); thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap(); - thread->owner_process = g_current_process; + thread->owner_process = owner_process; // Find the next available TLS index, and mark it as used - auto& tls_slots = Kernel::g_current_process->tls_slots; + auto& tls_slots = owner_process->tls_slots; bool needs_allocation = true; u32 available_page; // Which allocated page has free space u32 available_slot; // Which slot within the page is free @@ -424,18 +429,18 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, return ERR_OUT_OF_MEMORY; } - u32 offset = linheap_memory->size(); + size_t offset = linheap_memory->size(); // Allocate some memory from the end of the linear heap for this region. linheap_memory->insert(linheap_memory->end(), Memory::PAGE_SIZE, 0); memory_region->used += Memory::PAGE_SIZE; - Kernel::g_current_process->linear_heap_used += Memory::PAGE_SIZE; + owner_process->linear_heap_used += Memory::PAGE_SIZE; tls_slots.emplace_back(0); // The page is completely available at the start - available_page = tls_slots.size() - 1; + available_page = static_cast<u32>(tls_slots.size() - 1); available_slot = 0; // Use the first slot in the new page - auto& vm_manager = Kernel::g_current_process->vm_manager; + auto& vm_manager = owner_process->vm_manager; vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); // Map the page to the current process' address space. @@ -459,7 +464,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, return MakeResult<SharedPtr<Thread>>(std::move(thread)); } -void Thread::SetPriority(s32 priority) { +void Thread::SetPriority(u32 priority) { ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, "Invalid priority value."); // If thread was ready, adjust queues @@ -472,7 +477,7 @@ void Thread::SetPriority(s32 priority) { } void Thread::UpdatePriority() { - s32 best_priority = nominal_priority; + u32 best_priority = nominal_priority; for (auto& mutex : held_mutexes) { if (mutex->priority < best_priority) best_priority = mutex->priority; @@ -480,7 +485,7 @@ void Thread::UpdatePriority() { BoostPriority(best_priority); } -void Thread::BoostPriority(s32 priority) { +void Thread::BoostPriority(u32 priority) { // If thread was ready, adjust queues if (status == THREADSTATUS_READY) ready_queue.move(this, current_priority, priority); @@ -489,10 +494,10 @@ void Thread::BoostPriority(s32 priority) { current_priority = priority; } -SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority) { +SharedPtr<Thread> SetupMainThread(u32 entry_point, u32 priority, SharedPtr<Process> owner_process) { // Initialize new "main" thread auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0, - Memory::HEAP_VADDR_END); + Memory::HEAP_VADDR_END, owner_process); SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); @@ -533,7 +538,13 @@ void Thread::SetWaitSynchronizationOutput(s32 output) { s32 Thread::GetWaitObjectIndex(WaitObject* object) const { ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything"); auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object); - return std::distance(match, wait_objects.rend()) - 1; + return static_cast<s32>(std::distance(match, wait_objects.rend()) - 1); +} + +VAddr Thread::GetCommandBufferAddress() const { + // Offset from the start of TLS at which the IPC command buffer begins. + static constexpr int CommandHeaderOffset = 0x80; + return GetTLSAddress() + CommandHeaderOffset; } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 6a3566f15..314fba81f 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -15,7 +15,7 @@ #include "core/hle/kernel/wait_object.h" #include "core/hle/result.h" -enum ThreadPriority : s32 { +enum ThreadPriority : u32 { THREADPRIO_HIGHEST = 0, ///< Highest thread priority THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps THREADPRIO_DEFAULT = 48, ///< Default thread priority for userland apps @@ -41,6 +41,11 @@ enum ThreadStatus { THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated }; +enum class ThreadWakeupReason { + Signal, // The thread was woken up by WakeupAllWaitingThreads due to an object signal. + Timeout // The thread was woken up due to a wait timeout. +}; + namespace Kernel { class Mutex; @@ -56,10 +61,12 @@ public: * @param arg User data to pass to the thread * @param processor_id The ID(s) of the processors on which the thread is desired to be run * @param stack_top The address of the thread's stack top + * @param owner_process The parent process for the thread * @return A shared pointer to the newly created thread */ static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, u32 priority, - u32 arg, s32 processor_id, VAddr stack_top); + u32 arg, s32 processor_id, VAddr stack_top, + SharedPtr<Process> owner_process); std::string GetName() const override { return name; @@ -80,7 +87,7 @@ public: * Gets the thread's current priority * @return The current thread's priority */ - s32 GetPriority() const { + u32 GetPriority() const { return current_priority; } @@ -88,7 +95,7 @@ public: * Sets the thread's current priority * @param priority The new priority */ - void SetPriority(s32 priority); + void SetPriority(u32 priority); /** * Boost's a thread's priority to the best priority among the thread's held mutexes. @@ -100,7 +107,7 @@ public: * Temporarily boosts the thread's priority until the next time it is scheduled * @param priority The new priority */ - void BoostPriority(s32 priority); + void BoostPriority(u32 priority); /** * Gets the thread's thread ID @@ -116,9 +123,9 @@ public: void ResumeFromWait(); /** - * Schedules an event to wake up the specified thread after the specified delay - * @param nanoseconds The time this thread will be allowed to sleep for - */ + * Schedules an event to wake up the specified thread after the specified delay + * @param nanoseconds The time this thread will be allowed to sleep for + */ void WakeAfterDelay(s64 nanoseconds); /** @@ -157,6 +164,12 @@ public: return tls_address; } + /* + * Returns the address of the current thread's command buffer, located in the TLS. + * @returns VAddr of the thread's command buffer. + */ + VAddr GetCommandBufferAddress() const; + /** * Returns whether this thread is waiting for all the objects in * its wait list to become ready, as a result of a WaitSynchronizationN call @@ -174,8 +187,8 @@ public: u32 entry_point; u32 stack_top; - s32 nominal_priority; ///< Nominal thread priority, as set by the emulated application - s32 current_priority; ///< Current thread priority, can be temporarily changed + u32 nominal_priority; ///< Nominal thread priority, as set by the emulated application + u32 current_priority; ///< Current thread priority, can be temporarily changed u64 last_running_ticks; ///< CPU tick when thread was last running @@ -197,14 +210,18 @@ public: VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address - /// True if the WaitSynchronizationN output parameter should be set on thread wakeup. - bool wait_set_output; - std::string name; /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. Handle callback_handle; + using WakeupCallback = void(ThreadWakeupReason reason, SharedPtr<Thread> thread, + SharedPtr<WaitObject> object); + // Callback that will be invoked when the thread is resumed from a waiting state. If the thread + // was waiting via WaitSynchronizationN then the object will be the last object that became + // available. In case of a timeout, the object will be nullptr. + std::function<WakeupCallback> wakeup_callback; + private: Thread(); ~Thread() override; @@ -214,9 +231,10 @@ private: * Sets up the primary application thread * @param entry_point The address at which the thread should start execution * @param priority The priority to give the main thread + * @param owner_process The parent process for the main thread * @return A shared pointer to the main thread */ -SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority); +SharedPtr<Thread> SetupMainThread(u32 entry_point, u32 priority, SharedPtr<Process> owner_process); /** * Returns whether there are any threads that are ready to run. @@ -276,4 +294,4 @@ void ThreadingShutdown(); */ const std::vector<SharedPtr<Thread>>& GetThreadList(); -} // namespace +} // namespace Kernel diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp index f245eda6c..469554908 100644 --- a/src/core/hle/kernel/wait_object.cpp +++ b/src/core/hle/kernel/wait_object.cpp @@ -34,7 +34,7 @@ void WaitObject::RemoveWaitingThread(Thread* thread) { SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { Thread* candidate = nullptr; - s32 candidate_priority = THREADPRIO_LOWEST + 1; + u32 candidate_priority = THREADPRIO_LOWEST + 1; for (const auto& thread : waiting_threads) { // The list of waiting threads must not contain threads that are not waiting to be awakened. @@ -71,23 +71,20 @@ void WaitObject::WakeupAllWaitingThreads() { while (auto thread = GetHighestPriorityReadyThread()) { if (!thread->IsSleepingOnWaitAll()) { Acquire(thread.get()); - // Set the output index of the WaitSynchronizationN call to the index of this object. - if (thread->wait_set_output) { - thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this)); - thread->wait_set_output = false; - } } else { for (auto& object : thread->wait_objects) { object->Acquire(thread.get()); } - // Note: This case doesn't update the output index of WaitSynchronizationN. } + // Invoke the wakeup callback before clearing the wait objects + if (thread->wakeup_callback) + thread->wakeup_callback(ThreadWakeupReason::Signal, thread, this); + for (auto& object : thread->wait_objects) object->RemoveWaitingThread(thread.get()); thread->wait_objects.clear(); - thread->SetWaitSynchronizationResult(RESULT_SUCCESS); thread->ResumeFromWait(); } } diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index c36775473..59ea9823d 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -65,6 +65,7 @@ union AppletAttributes { u32 raw; BitField<0, 3, u32> applet_pos; + BitField<29, 1, u32> is_home_menu; AppletAttributes() : raw(0) {} AppletAttributes(u32 attributes) : raw(attributes) {} @@ -158,6 +159,11 @@ static AppletSlotData* GetAppletSlotData(AppletAttributes attributes) { if (slot == AppletSlot::Error) return nullptr; + // The Home Menu is a system applet, however, it has its own applet slot so that it can run + // concurrently with other system applets. + if (slot == AppletSlot::SystemApplet && attributes.is_home_menu) + return &applet_slots[static_cast<size_t>(AppletSlot::HomeMenu)]; + return &applet_slots[static_cast<size_t>(slot)]; } @@ -201,6 +207,19 @@ void Initialize(Service::Interface* self) { rb.Push(RESULT_SUCCESS); rb.PushCopyHandles(Kernel::g_handle_table.Create(slot_data->notification_event).Unwrap(), Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap()); + + if (slot_data->applet_id == AppletId::Application || + slot_data->applet_id == AppletId::HomeMenu) { + // Initialize the APT parameter to wake up the application. + next_parameter.emplace(); + next_parameter->signal = static_cast<u32>(SignalType::Wakeup); + next_parameter->sender_id = static_cast<u32>(AppletId::None); + next_parameter->destination_id = app_id; + // Not signaling the parameter event will cause the application (or Home Menu) to hang + // during startup. In the real console, it is usually the Kernel and Home Menu who cause NS + // to signal the HomeMenu and Application parameter events, respectively. + slot_data->parameter_event->Signal(); + } } static u32 DecompressLZ11(const u8* in, u8* out) { @@ -563,7 +582,7 @@ void ReceiveParameter(Service::Interface* self) { ? Kernel::g_handle_table.Create(next_parameter->object).Unwrap() : 0); - rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter->buffer.size()), 0); + rb.PushStaticBuffer(buffer, next_parameter->buffer.size(), 0); Memory::WriteBlock(buffer, next_parameter->buffer.data(), next_parameter->buffer.size()); @@ -611,7 +630,7 @@ void GlanceParameter(Service::Interface* self) { ? Kernel::g_handle_table.Create(next_parameter->object).Unwrap() : 0); - rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter->buffer.size()), 0); + rb.PushStaticBuffer(buffer, next_parameter->buffer.size(), 0); Memory::WriteBlock(buffer, next_parameter->buffer.data(), next_parameter->buffer.size()); @@ -763,6 +782,20 @@ void PrepareToStartLibraryApplet(Service::Interface* self) { } } +void PrepareToStartNewestHomeMenu(Service::Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0); // 0x1A0000 + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + + // TODO(Subv): This command can only be called by a System Applet (return 0xC8A0CC04 otherwise). + + // This command must return an error when called, otherwise the Home Menu will try to reboot the + // system. + rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet, + ErrorSummary::InvalidState, ErrorLevel::Status)); + + LOG_DEBUG(Service_APT, "called"); +} + void PreloadLibraryApplet(Service::Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x16, 1, 0); // 0x160040 AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>()); @@ -1058,12 +1091,6 @@ void Init() { slot_data.parameter_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Parameter"); } - - // Initialize the parameter to wake up the application. - next_parameter.emplace(); - next_parameter->signal = static_cast<u32>(SignalType::Wakeup); - next_parameter->destination_id = static_cast<u32>(AppletId::Application); - applet_slots[static_cast<size_t>(AppletSlot::Application)].parameter_event->Signal(); } 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); void PrepareToStartLibraryApplet(Service::Interface* self); /** + * APT::PrepareToStartNewestHomeMenu service function + * Inputs: + * 0 : Command header [0x001A0000] + * Outputs: + * 0 : Return header + * 1 : Result of function + */ +void PrepareToStartNewestHomeMenu(Service::Interface* self); + +/** * APT::PreloadLibraryApplet service function * Inputs: * 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 cf74c2a36..bb78ee7d7 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[] = { {0x00060040, GetAppletInfo, "GetAppletInfo"}, {0x00070000, nullptr, "GetLastSignaledAppletId"}, {0x00080000, nullptr, "CountRegisteredApplet"}, - {0x00090040, nullptr, "IsRegistered"}, + {0x00090040, IsRegistered, "IsRegistered"}, {0x000A0040, nullptr, "GetAttribute"}, {0x000B0040, InquireNotification, "InquireNotification"}, {0x000C0104, SendParameter, "SendParameter"}, @@ -34,7 +34,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, {0x00190040, nullptr, "PrepareToStartSystemApplet"}, - {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, + {0x001A0000, PrepareToStartNewestHomeMenu, "PrepareToStartNewestHomeMenu"}, {0x001B00C4, nullptr, "StartApplication"}, {0x001C0000, nullptr, "WakeupApplication"}, {0x001D0000, nullptr, "CancelApplication"}, diff --git a/src/core/hle/service/cam/cam.cpp b/src/core/hle/service/cam/cam.cpp index c9f9e9d95..8172edae8 100644 --- a/src/core/hle/service/cam/cam.cpp +++ b/src/core/hle/service/cam/cam.cpp @@ -177,7 +177,7 @@ void CompletionEventCallBack(u64 port_id, int) { LOG_ERROR(Service_CAM, "The destination size (%u) doesn't match the source (%zu)!", port.dest_size, buffer_size); } - Memory::WriteBlock(port.dest, buffer.data(), std::min<u32>(port.dest_size, buffer_size)); + Memory::WriteBlock(port.dest, buffer.data(), std::min<size_t>(port.dest_size, buffer_size)); } port.is_receiving = false; diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index f26a1f65f..f78c25fb2 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -141,7 +141,7 @@ void GetCountryCodeString(Service::Interface* self) { void GetCountryCodeID(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u16 country_code = cmd_buff[1]; + u16 country_code = static_cast<u16>(cmd_buff[1]); u16 country_code_id = 0; // The following algorithm will fail if the first country code isn't 0. diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 033fbc9aa..4ee7df73c 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -20,6 +20,7 @@ #include "core/file_sys/archive_savedata.h" #include "core/file_sys/archive_sdmc.h" #include "core/file_sys/archive_sdmcwriteonly.h" +#include "core/file_sys/archive_selfncch.h" #include "core/file_sys/archive_systemsavedata.h" #include "core/file_sys/directory_backend.h" #include "core/file_sys/errors.h" @@ -48,7 +49,7 @@ struct hash<Service::FS::ArchiveIdCode> { return std::hash<Type>()(static_cast<Type>(id_code)); } }; -} +} // namespace std static constexpr Kernel::Handle INVALID_HANDLE{}; @@ -216,7 +217,7 @@ void Directory::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> serve LOG_TRACE(Service_FS, "Read %s: count=%d", GetName().c_str(), count); // Number of entries actually read - u32 read = backend->Read(entries.size(), entries.data()); + u32 read = backend->Read(static_cast<u32>(entries.size()), entries.data()); cmd_buff[2] = read; Memory::WriteBlock(address, entries.data(), read * sizeof(FileSys::Entry)); break; @@ -564,6 +565,21 @@ void RegisterArchiveTypes() { auto systemsavedata_factory = std::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory); RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData); + + auto selfncch_factory = std::make_unique<FileSys::ArchiveFactory_SelfNCCH>(); + RegisterArchiveType(std::move(selfncch_factory), ArchiveIdCode::SelfNCCH); +} + +void RegisterSelfNCCH(Loader::AppLoader& app_loader) { + auto itr = id_code_map.find(ArchiveIdCode::SelfNCCH); + if (itr == id_code_map.end()) { + LOG_ERROR(Service_FS, + "Could not register a new NCCH because the SelfNCCH archive hasn't been created"); + return; + } + + auto* factory = static_cast<FileSys::ArchiveFactory_SelfNCCH*>(itr->second.get()); + factory->Register(app_loader); } void UnregisterArchiveTypes() { diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index 3a3371c88..e3c8fc2ef 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -21,6 +21,10 @@ static constexpr char SYSTEM_ID[]{"00000000000000000000000000000000"}; /// The scrambled SD card CID, also known as ID1 static constexpr char SDCARD_ID[]{"00000000000000000000000000000000"}; +namespace Loader { +class AppLoader; +} + namespace Service { namespace FS { @@ -259,6 +263,9 @@ void ArchiveInit(); /// Shutdown archives void ArchiveShutdown(); +/// Registers a new NCCH file with the SelfNCCH archive factory +void RegisterSelfNCCH(Loader::AppLoader& app_loader); + /// Register all archive types void RegisterArchiveTypes(); diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index aa5d821f9..379fbd71c 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -251,7 +251,7 @@ static void UpdateGyroscopeCallback(u64 userdata, int cycles_late) { Math::Vec3<float> gyro; std::tie(std::ignore, gyro) = motion_device->GetStatus(); double stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale(); - gyro *= gyroscope_coef * stretch; + gyro *= gyroscope_coef * static_cast<float>(stretch); gyroscope_entry.x = static_cast<s16>(gyro.x); gyroscope_entry.y = static_cast<s16>(gyro.y); gyroscope_entry.z = static_cast<s16>(gyro.z); diff --git a/src/core/hle/service/ldr_ro/cro_helper.h b/src/core/hle/service/ldr_ro/cro_helper.h index 3bc10dbdc..57b4fb6df 100644 --- a/src/core/hle/service/ldr_ro/cro_helper.h +++ b/src/core/hle/service/ldr_ro/cro_helper.h @@ -413,7 +413,8 @@ private: */ template <typename T> void GetEntry(std::size_t index, T& data) const { - Memory::ReadBlock(GetField(T::TABLE_OFFSET_FIELD) + index * sizeof(T), &data, sizeof(T)); + Memory::ReadBlock(GetField(T::TABLE_OFFSET_FIELD) + static_cast<u32>(index * sizeof(T)), + &data, sizeof(T)); } /** @@ -425,7 +426,8 @@ private: */ template <typename T> void SetEntry(std::size_t index, const T& data) { - Memory::WriteBlock(GetField(T::TABLE_OFFSET_FIELD) + index * sizeof(T), &data, sizeof(T)); + Memory::WriteBlock(GetField(T::TABLE_OFFSET_FIELD) + static_cast<u32>(index * sizeof(T)), + &data, sizeof(T)); } /** 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 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/hle/ipc.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/event.h" #include "core/hle/service/nim/nim.h" #include "core/hle/service/nim/nim_aoc.h" #include "core/hle/service/nim/nim_s.h" @@ -14,6 +16,16 @@ namespace Service { namespace NIM { +static Kernel::SharedPtr<Kernel::Event> nim_system_update_event; + +void CheckForSysUpdateEvent(Service::Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x5, 0, 0); // 0x50000 + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushCopyHandles(Kernel::g_handle_table.Create(nim_system_update_event).Unwrap()); + LOG_TRACE(Service_NIM, "called"); +} + void CheckSysUpdateAvailable(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); @@ -29,9 +41,13 @@ void Init() { AddService(new NIM_AOC_Interface); AddService(new NIM_S_Interface); AddService(new NIM_U_Interface); + + nim_system_update_event = Kernel::Event::Create(ResetType::OneShot, "NIM System Update Event"); } -void Shutdown() {} +void Shutdown() { + nim_system_update_event = nullptr; +} } // namespace NIM 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; namespace NIM { /** + * NIM::CheckForSysUpdateEvent service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Copy handle descriptor + * 3 : System Update event handle + */ +void CheckForSysUpdateEvent(Service::Interface* self); + +/** * NIM::CheckSysUpdateAvailable service function * Inputs: * 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[] = { {0x00010000, nullptr, "StartSysUpdate"}, {0x00020000, nullptr, "GetUpdateDownloadProgress"}, {0x00040000, nullptr, "FinishTitlesInstall"}, - {0x00050000, nullptr, "CheckForSysUpdateEvent"}, + {0x00050000, CheckForSysUpdateEvent, "CheckForSysUpdateEvent"}, {0x00090000, CheckSysUpdateAvailable, "CheckSysUpdateAvailable"}, {0x000A0000, nullptr, "GetState"}, {0x000B0000, nullptr, "GetSystemTitleHash"}, diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index 893bbb1e7..0aa63cc1e 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp @@ -2,8 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <array> #include <cstring> +#include <list> #include <mutex> #include <unordered_map> #include <vector> @@ -13,6 +15,7 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" +#include "core/hle/lock.h" #include "core/hle/result.h" #include "core/hle/service/nwm/nwm_uds.h" #include "core/hle/service/nwm/uds_beacon.h" @@ -37,9 +40,12 @@ static ConnectionStatus connection_status{}; /* Node information about the current network. * The amount of elements in this vector is always the maximum number * of nodes specified in the network configuration. - * The first node is always the host, so this always contains at least 1 entry. + * The first node is always the host. */ -static NodeList node_info(1); +static NodeList node_info; + +// Node information about our own system. +static NodeInfo current_node; // Mapping of bind node ids to their respective events. static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events; @@ -54,6 +60,10 @@ static NetworkInfo network_info; // Event that will generate and send the 802.11 beacon frames. static int beacon_broadcast_event; +// Mutex to synchronize access to the connection status between the emulation thread and the +// network thread. +static std::mutex connection_status_mutex; + // Mutex to synchronize access to the list of received beacons between the emulation thread and the // network thread. static std::mutex beacon_mutex; @@ -63,14 +73,26 @@ static std::mutex beacon_mutex; constexpr size_t MaxBeaconFrames = 15; // List of the last <MaxBeaconFrames> beacons received from the network. -static std::deque<Network::WifiPacket> received_beacons; +static std::list<Network::WifiPacket> received_beacons; /** * Returns a list of received 802.11 beacon frames from the specified sender since the last call. */ -std::deque<Network::WifiPacket> GetReceivedBeacons(const MacAddress& sender) { +std::list<Network::WifiPacket> GetReceivedBeacons(const MacAddress& sender) { std::lock_guard<std::mutex> lock(beacon_mutex); - // TODO(Subv): Filter by sender. + if (sender != Network::BroadcastMac) { + std::list<Network::WifiPacket> filtered_list; + const auto beacon = std::find_if(received_beacons.begin(), received_beacons.end(), + [&sender](const Network::WifiPacket& packet) { + return packet.transmitter_address == sender; + }); + if (beacon != received_beacons.end()) { + filtered_list.push_back(*beacon); + // TODO(B3N30): Check if the complete deque is cleared or just the fetched entries + received_beacons.erase(beacon); + } + return filtered_list; + } return std::move(received_beacons); } @@ -79,10 +101,33 @@ void SendPacket(Network::WifiPacket& packet) { // TODO(Subv): Implement. } +/* + * Returns an available index in the nodes array for the + * currently-hosted UDS network. + */ +static u16 GetNextAvailableNodeId() { + for (u16 index = 0; index < connection_status.max_nodes; ++index) { + if ((connection_status.node_bitmask & (1 << index)) == 0) + return index; + } + + // Any connection attempts to an already full network should have been refused. + ASSERT_MSG(false, "No available connection slots in the network"); +} + // Inserts the received beacon frame in the beacon queue and removes any older beacons if the size // limit is exceeded. void HandleBeaconFrame(const Network::WifiPacket& packet) { std::lock_guard<std::mutex> lock(beacon_mutex); + const auto unique_beacon = + std::find_if(received_beacons.begin(), received_beacons.end(), + [&packet](const Network::WifiPacket& new_packet) { + return new_packet.transmitter_address == packet.transmitter_address; + }); + if (unique_beacon != received_beacons.end()) { + // We already have a beacon from the same mac in the deque, remove the old one; + received_beacons.erase(unique_beacon); + } received_beacons.emplace_back(packet); @@ -91,21 +136,110 @@ void HandleBeaconFrame(const Network::WifiPacket& packet) { received_beacons.pop_front(); } -/* - * Returns an available index in the nodes array for the - * currently-hosted UDS network. - */ -static u16 GetNextAvailableNodeId() { - ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost), - "Can not accept clients if we're not hosting a network"); +void HandleAssociationResponseFrame(const Network::WifiPacket& packet) { + auto assoc_result = GetAssociationResult(packet.data); - for (u16 index = 0; index < connection_status.max_nodes; ++index) { - if ((connection_status.node_bitmask & (1 << index)) == 0) - return index; + ASSERT_MSG(std::get<AssocStatus>(assoc_result) == AssocStatus::Successful, + "Could not join network"); + { + std::lock_guard<std::mutex> lock(connection_status_mutex); + ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::Connecting)); } - // Any connection attempts to an already full network should have been refused. - ASSERT_MSG(false, "No available connection slots in the network"); + // Send the EAPoL-Start packet to the server. + using Network::WifiPacket; + WifiPacket eapol_start; + eapol_start.channel = network_channel; + eapol_start.data = GenerateEAPoLStartFrame(std::get<u16>(assoc_result), current_node); + // TODO(B3N30): Encrypt the packet. + eapol_start.destination_address = packet.transmitter_address; + eapol_start.type = WifiPacket::PacketType::Data; + + SendPacket(eapol_start); +} + +static void HandleEAPoLPacket(const Network::WifiPacket& packet) { + std::lock_guard<std::mutex> lock(connection_status_mutex); + + if (GetEAPoLFrameType(packet.data) == EAPoLStartMagic) { + if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) { + LOG_DEBUG(Service_NWM, "Connection sequence aborted, because connection status is %u", + connection_status.status); + return; + } + + auto node = DeserializeNodeInfoFromFrame(packet.data); + + if (connection_status.max_nodes == connection_status.total_nodes) { + // Reject connection attempt + LOG_ERROR(Service_NWM, "Reached maximum nodes, but reject packet wasn't sent."); + // TODO(B3N30): Figure out what packet is sent here + return; + } + + // Get an unused network node id + u16 node_id = GetNextAvailableNodeId(); + node.network_node_id = node_id + 1; + + connection_status.node_bitmask |= 1 << node_id; + connection_status.changed_nodes |= 1 << node_id; + connection_status.nodes[node_id] = node.network_node_id; + connection_status.total_nodes++; + + u8 current_nodes = network_info.total_nodes; + node_info[current_nodes] = node; + + network_info.total_nodes++; + + // Send the EAPoL-Logoff packet. + using Network::WifiPacket; + WifiPacket eapol_logoff; + eapol_logoff.channel = network_channel; + eapol_logoff.data = + GenerateEAPoLLogoffFrame(packet.transmitter_address, node.network_node_id, node_info, + network_info.max_nodes, network_info.total_nodes); + // TODO(Subv): Encrypt the packet. + eapol_logoff.destination_address = packet.transmitter_address; + eapol_logoff.type = WifiPacket::PacketType::Data; + + SendPacket(eapol_logoff); + // TODO(B3N30): Broadcast updated node list + // The 3ds does this presumably to support spectators. + std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + connection_status_event->Signal(); + } else { + if (connection_status.status != static_cast<u32>(NetworkStatus::NotConnected)) { + LOG_DEBUG(Service_NWM, "Connection sequence aborted, because connection status is %u", + connection_status.status); + return; + } + auto logoff = ParseEAPoLLogoffFrame(packet.data); + + network_info.total_nodes = logoff.connected_nodes; + network_info.max_nodes = logoff.max_nodes; + + connection_status.network_node_id = logoff.assigned_node_id; + connection_status.total_nodes = logoff.connected_nodes; + connection_status.max_nodes = logoff.max_nodes; + + node_info.clear(); + node_info.reserve(network_info.max_nodes); + for (size_t index = 0; index < logoff.connected_nodes; ++index) { + connection_status.node_bitmask |= 1 << index; + connection_status.changed_nodes |= 1 << index; + connection_status.nodes[index] = logoff.nodes[index].network_node_id; + + node_info.emplace_back(DeserializeNodeInfo(logoff.nodes[index])); + } + + // We're now connected, signal the application + connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsClient); + // Some games require ConnectToNetwork to block, for now it doesn't + // If blocking is implemented this lock needs to be changed, + // otherwise it might cause deadlocks + std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + connection_status_event->Signal(); + } } /* @@ -113,35 +247,46 @@ static u16 GetNextAvailableNodeId() { * authentication frame with SEQ1. */ void StartConnectionSequence(const MacAddress& server) { - ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::NotConnected)); - - // TODO(Subv): Handle timeout. - - // Send an authentication frame with SEQ1 using Network::WifiPacket; WifiPacket auth_request; - auth_request.channel = network_channel; - auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ1); - auth_request.destination_address = server; - auth_request.type = WifiPacket::PacketType::Authentication; + { + std::lock_guard<std::mutex> lock(connection_status_mutex); + ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::NotConnected)); + + // TODO(Subv): Handle timeout. + + // Send an authentication frame with SEQ1 + auth_request.channel = network_channel; + auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ1); + auth_request.destination_address = server; + auth_request.type = WifiPacket::PacketType::Authentication; + } SendPacket(auth_request); } /// Sends an Association Response frame to the specified mac address void SendAssociationResponseFrame(const MacAddress& address) { - ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost)); - using Network::WifiPacket; WifiPacket assoc_response; - assoc_response.channel = network_channel; - // TODO(Subv): This will cause multiple clients to end up with the same association id, but - // we're not using that for anything. - u16 association_id = 1; - assoc_response.data = GenerateAssocResponseFrame(AssocStatus::Successful, association_id, - network_info.network_id); - assoc_response.destination_address = address; - assoc_response.type = WifiPacket::PacketType::AssociationResponse; + + { + std::lock_guard<std::mutex> lock(connection_status_mutex); + if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) { + LOG_ERROR(Service_NWM, "Connection sequence aborted, because connection status is %u", + connection_status.status); + return; + } + + assoc_response.channel = network_channel; + // TODO(Subv): This will cause multiple clients to end up with the same association id, but + // we're not using that for anything. + u16 association_id = 1; + assoc_response.data = GenerateAssocResponseFrame(AssocStatus::Successful, association_id, + network_info.network_id); + assoc_response.destination_address = address; + assoc_response.type = WifiPacket::PacketType::AssociationResponse; + } SendPacket(assoc_response); } @@ -155,22 +300,40 @@ void SendAssociationResponseFrame(const MacAddress& address) { void HandleAuthenticationFrame(const Network::WifiPacket& packet) { // Only the SEQ1 auth frame is handled here, the SEQ2 frame doesn't need any special behavior if (GetAuthenticationSeqNumber(packet.data) == AuthenticationSeq::SEQ1) { - ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost)); - - // Respond with an authentication response frame with SEQ2 using Network::WifiPacket; WifiPacket auth_request; - auth_request.channel = network_channel; - auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2); - auth_request.destination_address = packet.transmitter_address; - auth_request.type = WifiPacket::PacketType::Authentication; - + { + std::lock_guard<std::mutex> lock(connection_status_mutex); + if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) { + LOG_ERROR(Service_NWM, + "Connection sequence aborted, because connection status is %u", + connection_status.status); + return; + } + + // Respond with an authentication response frame with SEQ2 + auth_request.channel = network_channel; + auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2); + auth_request.destination_address = packet.transmitter_address; + auth_request.type = WifiPacket::PacketType::Authentication; + } SendPacket(auth_request); SendAssociationResponseFrame(packet.transmitter_address); } } +static void HandleDataFrame(const Network::WifiPacket& packet) { + switch (GetFrameEtherType(packet.data)) { + case EtherType::EAPoL: + HandleEAPoLPacket(packet); + break; + case EtherType::SecureData: + // TODO(B3N30): Handle SecureData packets + break; + } +} + /// Callback to parse and handle a received wifi packet. void OnWifiPacketReceived(const Network::WifiPacket& packet) { switch (packet.type) { @@ -180,6 +343,12 @@ void OnWifiPacketReceived(const Network::WifiPacket& packet) { case Network::WifiPacket::PacketType::Authentication: HandleAuthenticationFrame(packet); break; + case Network::WifiPacket::PacketType::AssociationResponse: + HandleAssociationResponseFrame(packet); + break; + case Network::WifiPacket::PacketType::Data: + HandleDataFrame(packet); + break; } } @@ -246,7 +415,7 @@ static void RecvBeaconBroadcastData(Interface* self) { auto beacons = GetReceivedBeacons(mac_address); BeaconDataReplyHeader data_reply_header{}; - data_reply_header.total_entries = beacons.size(); + data_reply_header.total_entries = static_cast<u32>(beacons.size()); data_reply_header.max_output_size = out_buffer_size; Memory::WriteBlock(current_buffer_pos, &data_reply_header, sizeof(BeaconDataReplyHeader)); @@ -256,8 +425,8 @@ static void RecvBeaconBroadcastData(Interface* self) { for (const auto& beacon : beacons) { BeaconEntryHeader entry{}; // TODO(Subv): Figure out what this size is used for. - entry.unk_size = sizeof(BeaconEntryHeader) + beacon.data.size(); - entry.total_size = sizeof(BeaconEntryHeader) + beacon.data.size(); + entry.unk_size = static_cast<u32>(sizeof(BeaconEntryHeader) + beacon.data.size()); + entry.total_size = static_cast<u32>(sizeof(BeaconEntryHeader) + beacon.data.size()); entry.wifi_channel = beacon.channel; entry.header_size = sizeof(BeaconEntryHeader); entry.mac_address = beacon.transmitter_address; @@ -268,9 +437,9 @@ static void RecvBeaconBroadcastData(Interface* self) { current_buffer_pos += sizeof(BeaconEntryHeader); Memory::WriteBlock(current_buffer_pos, beacon.data.data(), beacon.data.size()); - current_buffer_pos += beacon.data.size(); + current_buffer_pos += static_cast<VAddr>(beacon.data.size()); - total_size += sizeof(BeaconEntryHeader) + beacon.data.size(); + total_size += static_cast<u32>(sizeof(BeaconEntryHeader) + beacon.data.size()); } // Update the total size in the structure and write it to the buffer again. @@ -305,7 +474,7 @@ static void InitializeWithVersion(Interface* self) { u32 sharedmem_size = rp.Pop<u32>(); // Update the node information with the data the game gave us. - rp.PopRaw(node_info[0]); + rp.PopRaw(current_node); u16 version = rp.Pop<u16>(); @@ -315,10 +484,14 @@ static void InitializeWithVersion(Interface* self) { ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size."); - // Reset the connection status, it contains all zeros after initialization, - // except for the actual status value. - connection_status = {}; - connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); + { + std::lock_guard<std::mutex> lock(connection_status_mutex); + + // Reset the connection status, it contains all zeros after initialization, + // except for the actual status value. + connection_status = {}; + connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); + } IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(RESULT_SUCCESS); @@ -348,12 +521,16 @@ static void GetConnectionStatus(Interface* self) { IPC::RequestBuilder rb = rp.MakeBuilder(13, 0); rb.Push(RESULT_SUCCESS); - rb.PushRaw(connection_status); - - // Reset the bitmask of changed nodes after each call to this - // function to prevent falsely informing games of outstanding - // changes in subsequent calls. - connection_status.changed_nodes = 0; + { + std::lock_guard<std::mutex> lock(connection_status_mutex); + rb.PushRaw(connection_status); + + // Reset the bitmask of changed nodes after each call to this + // function to prevent falsely informing games of outstanding + // changes in subsequent calls. + // TODO(Subv): Find exactly where the NWM module resets this value. + connection_status.changed_nodes = 0; + } LOG_DEBUG(Service_NWM, "called"); } @@ -434,31 +611,36 @@ static void BeginHostingNetwork(Interface* self) { // The real UDS module throws a fatal error if this assert fails. ASSERT_MSG(network_info.max_nodes > 1, "Trying to host a network of only one member."); - connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost); - - // Ensure the application data size is less than the maximum value. - ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize, "Data size is too big."); - - // Set up basic information for this network. - network_info.oui_value = NintendoOUI; - network_info.oui_type = static_cast<u8>(NintendoTagId::NetworkInfo); - - connection_status.max_nodes = network_info.max_nodes; - - // Resize the nodes list to hold max_nodes. - node_info.resize(network_info.max_nodes); - - // There's currently only one node in the network (the host). - connection_status.total_nodes = 1; - network_info.total_nodes = 1; - // The host is always the first node - connection_status.network_node_id = 1; - node_info[0].network_node_id = 1; - connection_status.nodes[0] = connection_status.network_node_id; - // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken. - connection_status.node_bitmask |= 1; - // Notify the application that the first node was set. - connection_status.changed_nodes |= 1; + { + std::lock_guard<std::mutex> lock(connection_status_mutex); + connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost); + + // Ensure the application data size is less than the maximum value. + ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize, + "Data size is too big."); + + // Set up basic information for this network. + network_info.oui_value = NintendoOUI; + network_info.oui_type = static_cast<u8>(NintendoTagId::NetworkInfo); + + connection_status.max_nodes = network_info.max_nodes; + + // Resize the nodes list to hold max_nodes. + node_info.resize(network_info.max_nodes); + + // There's currently only one node in the network (the host). + connection_status.total_nodes = 1; + network_info.total_nodes = 1; + // The host is always the first node + connection_status.network_node_id = 1; + current_node.network_node_id = 1; + connection_status.nodes[0] = connection_status.network_node_id; + // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken. + connection_status.node_bitmask |= 1; + // Notify the application that the first node was set. + connection_status.changed_nodes |= 1; + node_info[0] = current_node; + } // If the game has a preferred channel, use that instead. if (network_info.channel != 0) @@ -495,9 +677,13 @@ static void DestroyNetwork(Interface* self) { // Unschedule the beacon broadcast event. CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); - // TODO(Subv): Check if connection_status is indeed reset after this call. - connection_status = {}; - connection_status.status = static_cast<u8>(NetworkStatus::NotConnected); + { + std::lock_guard<std::mutex> lock(connection_status_mutex); + + // TODO(Subv): Check if connection_status is indeed reset after this call. + connection_status = {}; + connection_status.status = static_cast<u8>(NetworkStatus::NotConnected); + } connection_status_event->Signal(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); @@ -540,17 +726,24 @@ static void SendTo(Interface* self) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) && - connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) { - rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, - ErrorSummary::InvalidState, ErrorLevel::Status)); - return; - } + u16 network_node_id; - if (dest_node_id == connection_status.network_node_id) { - rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS, - ErrorSummary::WrongArgument, ErrorLevel::Status)); - return; + { + std::lock_guard<std::mutex> lock(connection_status_mutex); + if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) && + connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) { + rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } + + if (dest_node_id == connection_status.network_node_id) { + rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS, + ErrorSummary::WrongArgument, ErrorLevel::Status)); + return; + } + + network_node_id = connection_status.network_node_id; } // TODO(Subv): Do something with the flags. @@ -567,8 +760,8 @@ static void SendTo(Interface* self) { // TODO(Subv): Increment the sequence number after each sent packet. u16 sequence_number = 0; - std::vector<u8> data_payload = GenerateDataPayload( - data, data_channel, dest_node_id, connection_status.network_node_id, sequence_number); + std::vector<u8> data_payload = + GenerateDataPayload(data, data_channel, dest_node_id, network_node_id, sequence_number); // TODO(Subv): Retrieve the MAC address of the dest_node_id and our own to encrypt // and encapsulate the payload. @@ -595,6 +788,7 @@ static void GetChannel(Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + std::lock_guard<std::mutex> lock(connection_status_mutex); bool is_connected = connection_status.status != static_cast<u32>(NetworkStatus::NotConnected); u8 channel = is_connected ? network_channel : 0; @@ -766,6 +960,7 @@ static void BeaconBroadcastCallback(u64 userdata, int cycles_late) { * @param network_node_id Network Node Id of the connecting client. */ void OnClientConnected(u16 network_node_id) { + std::lock_guard<std::mutex> lock(connection_status_mutex); ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost), "Can not accept clients if we're not hosting a network"); ASSERT_MSG(connection_status.total_nodes < connection_status.max_nodes, @@ -827,8 +1022,11 @@ NWM_UDS::~NWM_UDS() { connection_status_event = nullptr; recv_buffer_memory = nullptr; - connection_status = {}; - connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); + { + std::lock_guard<std::mutex> lock(connection_status_mutex); + connection_status = {}; + connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); + } CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); } diff --git a/src/core/hle/service/nwm/uds_beacon.cpp b/src/core/hle/service/nwm/uds_beacon.cpp index 552eaf65e..73a80d940 100644 --- a/src/core/hle/service/nwm/uds_beacon.cpp +++ b/src/core/hle/service/nwm/uds_beacon.cpp @@ -243,7 +243,7 @@ std::vector<u8> GenerateNintendoFirstEncryptedDataTag(const NetworkInfo& network EncryptedDataTag tag{}; tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific); - tag.header.length = sizeof(tag) - sizeof(TagHeader) + payload_size; + tag.header.length = static_cast<u8>(sizeof(tag) - sizeof(TagHeader) + payload_size); tag.oui_type = static_cast<u8>(NintendoTagId::EncryptedData0); tag.oui = NintendoOUI; @@ -279,7 +279,7 @@ std::vector<u8> GenerateNintendoSecondEncryptedDataTag(const NetworkInfo& networ EncryptedDataTag tag{}; tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific); - tag.header.length = tag_length; + tag.header.length = static_cast<u8>(tag_length); tag.oui_type = static_cast<u8>(NintendoTagId::EncryptedData1); tag.oui = NintendoOUI; diff --git a/src/core/hle/service/nwm/uds_connection.cpp b/src/core/hle/service/nwm/uds_connection.cpp index c8a76ec2a..c74f51253 100644 --- a/src/core/hle/service/nwm/uds_connection.cpp +++ b/src/core/hle/service/nwm/uds_connection.cpp @@ -75,5 +75,14 @@ std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_i return data; } +std::tuple<AssocStatus, u16> GetAssociationResult(const std::vector<u8>& body) { + AssociationResponseFrame frame; + memcpy(&frame, body.data(), sizeof(frame)); + + constexpr u16 AssociationIdMask = 0x3FFF; + return std::make_tuple(static_cast<AssocStatus>(frame.status_code), + frame.assoc_id & AssociationIdMask); +} + } // namespace NWM } // namespace Service diff --git a/src/core/hle/service/nwm/uds_connection.h b/src/core/hle/service/nwm/uds_connection.h index 73f55a4fd..a664f8471 100644 --- a/src/core/hle/service/nwm/uds_connection.h +++ b/src/core/hle/service/nwm/uds_connection.h @@ -4,6 +4,7 @@ #pragma once +#include <tuple> #include <vector> #include "common/common_types.h" #include "common/swap.h" @@ -47,5 +48,9 @@ AuthenticationSeq GetAuthenticationSeqNumber(const std::vector<u8>& body); /// network id, starting at the frame body. std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id); +/// Returns a tuple of (association status, association id) from the body of an AssociationResponse +/// frame. +std::tuple<AssocStatus, u16> GetAssociationResult(const std::vector<u8>& body); + } // namespace NWM } // namespace Service diff --git a/src/core/hle/service/nwm/uds_data.cpp b/src/core/hle/service/nwm/uds_data.cpp index 8c6742dba..4b389710f 100644 --- a/src/core/hle/service/nwm/uds_data.cpp +++ b/src/core/hle/service/nwm/uds_data.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <cstring> #include <cryptopp/aes.h> #include <cryptopp/ccm.h> @@ -197,7 +198,7 @@ static std::vector<u8> DecryptDataFrame(const std::vector<u8>& encrypted_payload df.ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL); df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL); - int size = df.MaxRetrievable(); + size_t size = df.MaxRetrievable(); std::vector<u8> pdata(size); df.Get(pdata.data(), size); @@ -251,7 +252,7 @@ static std::vector<u8> EncryptDataFrame(const std::vector<u8>& payload, df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL); - int size = df.MaxRetrievable(); + size_t size = df.MaxRetrievable(); std::vector<u8> cipher(size); df.Get(cipher.data(), size); @@ -266,13 +267,107 @@ static std::vector<u8> EncryptDataFrame(const std::vector<u8>& payload, std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, u16 src_node, u16 sequence_number) { std::vector<u8> buffer = GenerateLLCHeader(EtherType::SecureData); - std::vector<u8> securedata_header = - GenerateSecureDataHeader(data.size(), channel, dest_node, src_node, sequence_number); + std::vector<u8> securedata_header = GenerateSecureDataHeader( + static_cast<u16>(data.size()), channel, dest_node, src_node, sequence_number); buffer.insert(buffer.end(), securedata_header.begin(), securedata_header.end()); buffer.insert(buffer.end(), data.begin(), data.end()); return buffer; } +std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) { + EAPoLStartPacket eapol_start{}; + eapol_start.association_id = association_id; + eapol_start.node.friend_code_seed = node_info.friend_code_seed; + + std::copy(node_info.username.begin(), node_info.username.end(), + eapol_start.node.username.begin()); + + // Note: The network_node_id and unknown bytes seem to be uninitialized in the NWM module. + // TODO(B3N30): The last 8 bytes seem to have a fixed value of 07 88 15 00 04 e9 13 00 in + // EAPoL-Start packets from different 3DSs to the same host during a Super Smash Bros. 4 game. + // Find out what that means. + + std::vector<u8> eapol_buffer(sizeof(EAPoLStartPacket)); + std::memcpy(eapol_buffer.data(), &eapol_start, sizeof(eapol_start)); + + std::vector<u8> buffer = GenerateLLCHeader(EtherType::EAPoL); + buffer.insert(buffer.end(), eapol_buffer.begin(), eapol_buffer.end()); + return buffer; +} + +EtherType GetFrameEtherType(const std::vector<u8>& frame) { + LLCHeader header; + std::memcpy(&header, frame.data(), sizeof(header)); + + u16 ethertype = header.protocol; + return static_cast<EtherType>(ethertype); +} + +u16 GetEAPoLFrameType(const std::vector<u8>& frame) { + // Ignore the LLC header + u16_be eapol_type; + std::memcpy(&eapol_type, frame.data() + sizeof(LLCHeader), sizeof(eapol_type)); + return eapol_type; +} + +NodeInfo DeserializeNodeInfoFromFrame(const std::vector<u8>& frame) { + EAPoLStartPacket eapol_start; + + // Skip the LLC header + std::memcpy(&eapol_start, frame.data() + sizeof(LLCHeader), sizeof(eapol_start)); + + NodeInfo node{}; + node.friend_code_seed = eapol_start.node.friend_code_seed; + + std::copy(eapol_start.node.username.begin(), eapol_start.node.username.end(), + node.username.begin()); + + return node; +} + +NodeInfo DeserializeNodeInfo(const EAPoLNodeInfo& node) { + NodeInfo node_info{}; + node_info.friend_code_seed = node.friend_code_seed; + node_info.network_node_id = node.network_node_id; + + std::copy(node.username.begin(), node.username.end(), node_info.username.begin()); + + return node_info; +} + +std::vector<u8> GenerateEAPoLLogoffFrame(const MacAddress& mac_address, u16 network_node_id, + const NodeList& nodes, u8 max_nodes, u8 total_nodes) { + EAPoLLogoffPacket eapol_logoff{}; + eapol_logoff.assigned_node_id = network_node_id; + eapol_logoff.connected_nodes = total_nodes; + eapol_logoff.max_nodes = max_nodes; + + for (size_t index = 0; index < total_nodes; ++index) { + const auto& node_info = nodes[index]; + auto& node = eapol_logoff.nodes[index]; + + node.friend_code_seed = node_info.friend_code_seed; + node.network_node_id = node_info.network_node_id; + + std::copy(node_info.username.begin(), node_info.username.end(), node.username.begin()); + } + + std::vector<u8> eapol_buffer(sizeof(EAPoLLogoffPacket)); + std::memcpy(eapol_buffer.data(), &eapol_logoff, sizeof(eapol_logoff)); + + std::vector<u8> buffer = GenerateLLCHeader(EtherType::EAPoL); + buffer.insert(buffer.end(), eapol_buffer.begin(), eapol_buffer.end()); + return buffer; +} + +EAPoLLogoffPacket ParseEAPoLLogoffFrame(const std::vector<u8>& frame) { + EAPoLLogoffPacket eapol_logoff; + + // Skip the LLC header + std::memcpy(&eapol_logoff, frame.data() + sizeof(LLCHeader), sizeof(eapol_logoff)); + return eapol_logoff; +} + } // namespace NWM } // namespace Service diff --git a/src/core/hle/service/nwm/uds_data.h b/src/core/hle/service/nwm/uds_data.h index a23520a41..76bccb1bf 100644 --- a/src/core/hle/service/nwm/uds_data.h +++ b/src/core/hle/service/nwm/uds_data.h @@ -8,6 +8,7 @@ #include <vector> #include "common/common_types.h" #include "common/swap.h" +#include "core/hle/service/nwm/uds_beacon.h" #include "core/hle/service/service.h" namespace Service { @@ -67,6 +68,49 @@ struct DataFrameCryptoCTR { static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); +struct EAPoLNodeInfo { + u64_be friend_code_seed; + std::array<u16_be, 10> username; + INSERT_PADDING_BYTES(4); + u16_be network_node_id; + INSERT_PADDING_BYTES(6); +}; + +static_assert(sizeof(EAPoLNodeInfo) == 0x28, "EAPoLNodeInfo has the wrong size"); + +constexpr u16 EAPoLStartMagic = 0x201; + +/* + * Nintendo EAPoLStartPacket, is used to initaliaze a connection between client and host + */ +struct EAPoLStartPacket { + u16_be magic = EAPoLStartMagic; + u16_be association_id; + // This value is hardcoded to 1 in the NWM module. + u16_be unknown = 1; + INSERT_PADDING_BYTES(2); + EAPoLNodeInfo node; +}; + +static_assert(sizeof(EAPoLStartPacket) == 0x30, "EAPoLStartPacket has the wrong size"); + +constexpr u16 EAPoLLogoffMagic = 0x202; + +struct EAPoLLogoffPacket { + u16_be magic = EAPoLLogoffMagic; + INSERT_PADDING_BYTES(2); + u16_be assigned_node_id; + MacAddress client_mac_address; + INSERT_PADDING_BYTES(6); + u8 connected_nodes; + u8 max_nodes; + INSERT_PADDING_BYTES(4); + + std::array<EAPoLNodeInfo, UDSMaxNodes> nodes; +}; + +static_assert(sizeof(EAPoLLogoffPacket) == 0x298, "EAPoLLogoffPacket has the wrong size"); + /** * Generates an unencrypted 802.11 data payload. * @returns The generated frame payload. @@ -74,5 +118,47 @@ static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wron std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, u16 src_node, u16 sequence_number); +/* + * Generates an unencrypted 802.11 data frame body with the EAPoL-Start format for UDS + * communication. + * @returns The generated frame body. + */ +std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info); + +/* + * Returns the EtherType of the specified 802.11 frame. + */ +EtherType GetFrameEtherType(const std::vector<u8>& frame); + +/* + * Returns the EAPoL type (Start / Logoff) of the specified 802.11 frame. + * Note: The frame *must* be an EAPoL frame. + */ +u16 GetEAPoLFrameType(const std::vector<u8>& frame); + +/* + * Returns a deserialized NodeInfo structure from the information inside an EAPoL-Start packet + * encapsulated in an 802.11 data frame. + */ +NodeInfo DeserializeNodeInfoFromFrame(const std::vector<u8>& frame); + +/* + * Returns a NodeInfo constructed from the data in the specified EAPoLNodeInfo. + */ +NodeInfo DeserializeNodeInfo(const EAPoLNodeInfo& node); + +/* + * Generates an unencrypted 802.11 data frame body with the EAPoL-Logoff format for UDS + * communication. + * @returns The generated frame body. + */ +std::vector<u8> GenerateEAPoLLogoffFrame(const MacAddress& mac_address, u16 network_node_id, + const NodeList& nodes, u8 max_nodes, u8 total_nodes); + +/* + * Returns a EAPoLLogoffPacket representing the specified 802.11-encapsulated data frame. + */ +EAPoLLogoffPacket ParseEAPoLLogoffFrame(const std::vector<u8>& frame); + } // namespace NWM } // namespace Service diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 5e7fc68f9..854ab9a05 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -36,6 +36,10 @@ ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> ServiceManager::RegisterService std::string name, unsigned int max_sessions) { CASCADE_CODE(ValidateServiceName(name)); + + if (registered_services.find(name) != registered_services.end()) + return ERR_ALREADY_REGISTERED; + Kernel::SharedPtr<Kernel::ServerPort> server_port; Kernel::SharedPtr<Kernel::ClientPort> client_port; std::tie(server_port, client_port) = Kernel::ServerPort::CreatePortPair(max_sessions, name); diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 8f0dbf2db..9f60a7965 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -32,6 +32,9 @@ constexpr ResultCode ERR_ACCESS_DENIED(6, ErrorModule::SRV, ErrorSummary::Invali ErrorLevel::Permanent); // 0xD8E06406 constexpr ResultCode ERR_NAME_CONTAINS_NUL(7, ErrorModule::SRV, ErrorSummary::WrongArgument, ErrorLevel::Permanent); // 0xD9006407 +constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorDescription::AlreadyExists, ErrorModule::OS, + ErrorSummary::WrongArgument, + ErrorLevel::Permanent); // 0xD9001BFC class ServiceManager { public: diff --git a/src/core/hle/service/sm/srv.cpp b/src/core/hle/service/sm/srv.cpp index 352941e69..5c955cf54 100644 --- a/src/core/hle/service/sm/srv.cpp +++ b/src/core/hle/service/sm/srv.cpp @@ -13,6 +13,7 @@ #include "core/hle/kernel/errors.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/semaphore.h" +#include "core/hle/kernel/server_port.h" #include "core/hle/kernel/server_session.h" #include "core/hle/service/sm/sm.h" #include "core/hle/service/sm/srv.h" @@ -184,12 +185,35 @@ void SRV::PublishToSubscriber(Kernel::HLERequestContext& ctx) { flags); } +void SRV::RegisterService(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x3, 4, 0); + + auto name_buf = rp.PopRaw<std::array<char, 8>>(); + size_t name_len = rp.Pop<u32>(); + u32 max_sessions = rp.Pop<u32>(); + + std::string name(name_buf.data(), std::min(name_len, name_buf.size())); + + auto port = service_manager->RegisterService(name, max_sessions); + + if (port.Failed()) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(port.Code()); + LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), port.Code().raw); + return; + } + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushObjects(port.Unwrap()); +} + SRV::SRV(std::shared_ptr<ServiceManager> service_manager) : ServiceFramework("srv:", 4), service_manager(std::move(service_manager)) { static const FunctionInfo functions[] = { {0x00010002, &SRV::RegisterClient, "RegisterClient"}, {0x00020000, &SRV::EnableNotification, "EnableNotification"}, - {0x00030100, nullptr, "RegisterService"}, + {0x00030100, &SRV::RegisterService, "RegisterService"}, {0x000400C0, nullptr, "UnregisterService"}, {0x00050100, &SRV::GetServiceHandle, "GetServiceHandle"}, {0x000600C2, nullptr, "RegisterPort"}, diff --git a/src/core/hle/service/sm/srv.h b/src/core/hle/service/sm/srv.h index 75cca5184..aad839563 100644 --- a/src/core/hle/service/sm/srv.h +++ b/src/core/hle/service/sm/srv.h @@ -28,6 +28,7 @@ private: void Subscribe(Kernel::HLERequestContext& ctx); void Unsubscribe(Kernel::HLERequestContext& ctx); void PublishToSubscriber(Kernel::HLERequestContext& ctx); + void RegisterService(Kernel::HLERequestContext& ctx); std::shared_ptr<ServiceManager> service_manager; Kernel::SharedPtr<Kernel::Semaphore> notification_semaphore; diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index dfc36748c..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) // Create an event to wake the thread up after the specified nanosecond delay has passed thread->WakeAfterDelay(nano_seconds); + thread->wakeup_callback = [](ThreadWakeupReason reason, + Kernel::SharedPtr<Kernel::Thread> thread, + Kernel::SharedPtr<Kernel::WaitObject> object) { + + ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY); + + if (reason == ThreadWakeupReason::Timeout) { + thread->SetWaitSynchronizationResult(Kernel::RESULT_TIMEOUT); + return; + } + + ASSERT(reason == ThreadWakeupReason::Signal); + thread->SetWaitSynchronizationResult(RESULT_SUCCESS); + + // WaitSynchronization1 doesn't have an output index like WaitSynchronizationN, so we + // don't have to do anything else here. + }; + Core::System::GetInstance().PrepareReschedule(); // 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 // Create an event to wake the thread up after the specified nanosecond delay has passed thread->WakeAfterDelay(nano_seconds); + thread->wakeup_callback = [](ThreadWakeupReason reason, + Kernel::SharedPtr<Kernel::Thread> thread, + Kernel::SharedPtr<Kernel::WaitObject> object) { + + ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ALL); + + if (reason == ThreadWakeupReason::Timeout) { + thread->SetWaitSynchronizationResult(Kernel::RESULT_TIMEOUT); + return; + } + + ASSERT(reason == ThreadWakeupReason::Signal); + + thread->SetWaitSynchronizationResult(RESULT_SUCCESS); + // The wait_all case does not update the output index. + }; + Core::System::GetInstance().PrepareReschedule(); // This value gets set to -1 by default in this case, it is not modified after this. @@ -361,7 +396,7 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha // We found a ready object, acquire it and set the result value Kernel::WaitObject* object = itr->get(); object->Acquire(thread); - *out = std::distance(objects.begin(), itr); + *out = static_cast<s32>(std::distance(objects.begin(), itr)); return RESULT_SUCCESS; } @@ -389,12 +424,28 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha // Create an event to wake the thread up after the specified nanosecond delay has passed thread->WakeAfterDelay(nano_seconds); + thread->wakeup_callback = [](ThreadWakeupReason reason, + Kernel::SharedPtr<Kernel::Thread> thread, + Kernel::SharedPtr<Kernel::WaitObject> object) { + + ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY); + + if (reason == ThreadWakeupReason::Timeout) { + thread->SetWaitSynchronizationResult(Kernel::RESULT_TIMEOUT); + return; + } + + ASSERT(reason == ThreadWakeupReason::Signal); + + thread->SetWaitSynchronizationResult(RESULT_SUCCESS); + thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get())); + }; + Core::System::GetInstance().PrepareReschedule(); // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a // signal in one of its wait objects. // Otherwise we retain the default value of timeout, and -1 in the out parameter - thread->wait_set_output = true; *out = -1; return Kernel::RESULT_TIMEOUT; } @@ -469,7 +520,7 @@ static ResultCode ReplyAndReceive(s32* index, Kernel::Handle* handles, s32 handl // We found a ready object, acquire it and set the result value Kernel::WaitObject* object = itr->get(); object->Acquire(thread); - *index = std::distance(objects.begin(), itr); + *index = static_cast<s32>(std::distance(objects.begin(), itr)); if (object->GetHandleType() == Kernel::HandleType::ServerSession) { auto server_session = static_cast<Kernel::ServerSession*>(object); @@ -483,8 +534,6 @@ static ResultCode ReplyAndReceive(s32* index, Kernel::Handle* handles, s32 handl // No objects were ready to be acquired, prepare to suspend the thread. - // TODO(Subv): Perform IPC translation upon wakeup. - // Put the thread to sleep thread->status = THREADSTATUS_WAIT_SYNCH_ANY; @@ -496,12 +545,24 @@ static ResultCode ReplyAndReceive(s32* index, Kernel::Handle* handles, s32 handl thread->wait_objects = std::move(objects); + thread->wakeup_callback = [](ThreadWakeupReason reason, + Kernel::SharedPtr<Kernel::Thread> thread, + Kernel::SharedPtr<Kernel::WaitObject> object) { + + ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY); + ASSERT(reason == ThreadWakeupReason::Signal); + + thread->SetWaitSynchronizationResult(RESULT_SUCCESS); + thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get())); + + // TODO(Subv): Perform IPC translation upon wakeup. + }; + Core::System::GetInstance().PrepareReschedule(); // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a // signal in one of its wait objects, or to 0xC8A01836 if there was a translation error. // By default the index is set to -1. - thread->wait_set_output = true; *index = -1; return RESULT_SUCCESS; } @@ -656,8 +717,9 @@ static ResultCode CreateThread(Kernel::Handle* out_handle, u32 priority, u32 ent "Newly created thread must run in the SysCore (Core1), unimplemented."); } - CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create(name, entry_point, priority, - arg, processor_id, stack_top)); + CASCADE_RESULT(SharedPtr<Thread> thread, + Kernel::Thread::Create(name, entry_point, priority, arg, processor_id, stack_top, + Kernel::g_current_process)); thread->context.fpscr = FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO; // 0x03C00000 @@ -682,7 +744,7 @@ static void ExitThread() { } /// Gets the priority for the specified thread -static ResultCode GetThreadPriority(s32* priority, Kernel::Handle handle) { +static ResultCode GetThreadPriority(u32* priority, Kernel::Handle handle) { const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); if (thread == nullptr) return ERR_INVALID_HANDLE; @@ -692,7 +754,7 @@ static ResultCode GetThreadPriority(s32* priority, Kernel::Handle handle) { } /// Sets the priority for the specified thread -static ResultCode SetThreadPriority(Kernel::Handle handle, s32 priority) { +static ResultCode SetThreadPriority(Kernel::Handle handle, u32 priority) { if (priority > THREADPRIO_LOWEST) { return Kernel::ERR_OUT_OF_RANGE; } @@ -977,7 +1039,7 @@ static void SleepThread(s64 nanoseconds) { static s64 GetSystemTick() { s64 result = CoreTiming::GetTicks(); // Advance time to defeat dumb games (like Cubic Ninja) that busy-wait for the frame to end. - Core::CPU().AddTicks(150); // Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b + CoreTiming::AddTicks(150); // Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b return result; } |