diff options
Diffstat (limited to 'src/core')
31 files changed, 583 insertions, 201 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 5c99c00f5..f5cf5c16a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -636,6 +636,8 @@ add_library(core STATIC memory.h network/network.cpp network/network.h + network/network_interface.cpp + network/network_interface.h network/sockets.h perf_stats.cpp perf_stats.h diff --git a/src/core/core.cpp b/src/core/core.cpp index d3e84c4ef..b0dc594d4 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -84,8 +84,6 @@ FileSys::StorageId GetStorageIdForFrontendSlot( } // Anonymous namespace -/*static*/ System System::s_instance; - FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, const std::string& path) { // To account for split 00+01+etc files. @@ -425,6 +423,13 @@ struct System::Impl { System::System() : impl{std::make_unique<Impl>(*this)} {} System::~System() = default; +void System::InitializeGlobalInstance() { + if (s_instance) { + abort(); + } + s_instance = std::unique_ptr<System>(new System); +} + CpuManager& System::GetCpuManager() { return impl->cpu_manager; } @@ -494,12 +499,6 @@ const ARM_Interface& System::CurrentArmInterface() const { return impl->kernel.CurrentPhysicalCore().ArmInterface(); } -std::size_t System::CurrentCoreIndex() const { - std::size_t core = impl->kernel.GetCurrentHostThreadID(); - ASSERT(core < Core::Hardware::NUM_CPU_CORES); - return core; -} - Kernel::PhysicalCore& System::CurrentPhysicalCore() { return impl->kernel.CurrentPhysicalCore(); } diff --git a/src/core/core.h b/src/core/core.h index ea143043c..65b447a1c 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -121,9 +121,14 @@ public: * @returns Reference to the instance of the System singleton class. */ [[deprecated("Use of the global system instance is deprecated")]] static System& GetInstance() { - return s_instance; + if (!s_instance) { + abort(); + } + return *s_instance; } + static void InitializeGlobalInstance(); + /// Enumeration representing the return values of the System Initialize and Load process. enum class ResultStatus : u32 { Success, ///< Succeeded @@ -205,9 +210,6 @@ public: /// Gets an ARM interface to the CPU core that is currently running [[nodiscard]] const ARM_Interface& CurrentArmInterface() const; - /// Gets the index of the currently running CPU core - [[nodiscard]] std::size_t CurrentCoreIndex() const; - /// Gets the physical core for the CPU core that is currently running [[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore(); @@ -396,7 +398,7 @@ private: struct Impl; std::unique_ptr<Impl> impl; - static System s_instance; + inline static std::unique_ptr<System> s_instance{}; }; } // namespace Core diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 7e195346b..de2e5563e 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -21,34 +21,25 @@ namespace Core { CpuManager::CpuManager(System& system_) : system{system_} {} CpuManager::~CpuManager() = default; -void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) { - cpu_manager.RunThread(core); +void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, + std::size_t core) { + cpu_manager.RunThread(stop_token, core); } void CpuManager::Initialize() { running_mode = true; if (is_multicore) { for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { - core_data[core].host_thread = - std::make_unique<std::thread>(ThreadStart, std::ref(*this), core); + core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core); } } else { - core_data[0].host_thread = std::make_unique<std::thread>(ThreadStart, std::ref(*this), 0); + core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0); } } void CpuManager::Shutdown() { running_mode = false; Pause(false); - if (is_multicore) { - for (auto& data : core_data) { - data.host_thread->join(); - data.host_thread.reset(); - } - } else { - core_data[0].host_thread->join(); - core_data[0].host_thread.reset(); - } } std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { @@ -127,17 +118,18 @@ void CpuManager::MultiCoreRunGuestLoop() { physical_core = &kernel.CurrentPhysicalCore(); } system.ExitDynarmicProfile(); - physical_core->ArmInterface().ClearExclusiveState(); - kernel.CurrentScheduler()->RescheduleCurrentCore(); + { + Kernel::KScopedDisableDispatch dd(kernel); + physical_core->ArmInterface().ClearExclusiveState(); + } } } void CpuManager::MultiCoreRunIdleThread() { auto& kernel = system.Kernel(); while (true) { - auto& physical_core = kernel.CurrentPhysicalCore(); - physical_core.Idle(); - kernel.CurrentScheduler()->RescheduleCurrentCore(); + Kernel::KScopedDisableDispatch dd(kernel); + kernel.CurrentPhysicalCore().Idle(); } } @@ -145,12 +137,12 @@ void CpuManager::MultiCoreRunSuspendThread() { auto& kernel = system.Kernel(); kernel.CurrentScheduler()->OnThreadStart(); while (true) { - auto core = kernel.GetCurrentHostThreadID(); + auto core = kernel.CurrentPhysicalCoreIndex(); auto& scheduler = *kernel.CurrentScheduler(); Kernel::KThread* current_thread = scheduler.GetCurrentThread(); Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context); ASSERT(scheduler.ContextSwitchPending()); - ASSERT(core == kernel.GetCurrentHostThreadID()); + ASSERT(core == kernel.CurrentPhysicalCoreIndex()); scheduler.RescheduleCurrentCore(); } } @@ -317,7 +309,7 @@ void CpuManager::Pause(bool paused) { } } -void CpuManager::RunThread(std::size_t core) { +void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) { /// Initialization system.RegisterCoreThread(core); std::string name; @@ -356,8 +348,8 @@ void CpuManager::RunThread(std::size_t core) { sc_sync_first_use = false; } - // Abort if emulation was killed before the session really starts - if (!system.IsPoweredOn()) { + // Emulation was stopped + if (stop_token.stop_requested()) { return; } diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index 140263b09..9d92d4af0 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -78,9 +78,9 @@ private: void SingleCoreRunSuspendThread(); void SingleCorePause(bool paused); - static void ThreadStart(CpuManager& cpu_manager, std::size_t core); + static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core); - void RunThread(std::size_t core); + void RunThread(std::stop_token stop_token, std::size_t core); struct CoreData { std::shared_ptr<Common::Fiber> host_context; @@ -89,7 +89,7 @@ private: std::atomic<bool> is_running; std::atomic<bool> is_paused; std::atomic<bool> initialized; - std::unique_ptr<std::thread> host_thread; + std::jthread host_thread; }; std::atomic<bool> running_mode{}; diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp index 1b429bc1e..6771ef621 100644 --- a/src/core/hle/kernel/k_address_arbiter.cpp +++ b/src/core/hle/kernel/k_address_arbiter.cpp @@ -28,7 +28,7 @@ bool ReadFromUser(Core::System& system, s32* out, VAddr address) { bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) { auto& monitor = system.Monitor(); - const auto current_core = system.CurrentCoreIndex(); + const auto current_core = system.Kernel().CurrentPhysicalCoreIndex(); // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. // TODO(bunnei): We should call CanAccessAtomic(..) here. @@ -58,7 +58,7 @@ bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 valu bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) { auto& monitor = system.Monitor(); - const auto current_core = system.CurrentCoreIndex(); + const auto current_core = system.Kernel().CurrentPhysicalCoreIndex(); // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. // TODO(bunnei): We should call CanAccessAtomic(..) here. diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h index e4fcdbc67..165b76747 100644 --- a/src/core/hle/kernel/k_auto_object.h +++ b/src/core/hle/kernel/k_auto_object.h @@ -170,6 +170,10 @@ public: } } + const std::string& GetName() const { + return name; + } + private: void RegisterWithKernel(); void UnregisterWithKernel(); diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp index ef14ad1d2..4174f35fd 100644 --- a/src/core/hle/kernel/k_condition_variable.cpp +++ b/src/core/hle/kernel/k_condition_variable.cpp @@ -35,7 +35,7 @@ bool WriteToUser(Core::System& system, VAddr address, const u32* p) { bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero, u32 new_orr_mask) { auto& monitor = system.Monitor(); - const auto current_core = system.CurrentCoreIndex(); + const auto current_core = system.Kernel().CurrentPhysicalCoreIndex(); // Load the value from the address. const auto expected = monitor.ExclusiveRead32(current_core, address); diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp index 6a420d5b0..d720c2dda 100644 --- a/src/core/hle/kernel/k_handle_table.cpp +++ b/src/core/hle/kernel/k_handle_table.cpp @@ -13,6 +13,7 @@ ResultCode KHandleTable::Finalize() { // Get the table and clear our record of it. u16 saved_table_size = 0; { + KScopedDisableDispatch dd(kernel); KScopedSpinLock lk(m_lock); std::swap(m_table_size, saved_table_size); @@ -43,6 +44,7 @@ bool KHandleTable::Remove(Handle handle) { // Find the object and free the entry. KAutoObject* obj = nullptr; { + KScopedDisableDispatch dd(kernel); KScopedSpinLock lk(m_lock); if (this->IsValidHandle(handle)) { @@ -61,6 +63,7 @@ bool KHandleTable::Remove(Handle handle) { } ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { + KScopedDisableDispatch dd(kernel); KScopedSpinLock lk(m_lock); // Never exceed our capacity. @@ -83,6 +86,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { } ResultCode KHandleTable::Reserve(Handle* out_handle) { + KScopedDisableDispatch dd(kernel); KScopedSpinLock lk(m_lock); // Never exceed our capacity. @@ -93,6 +97,7 @@ ResultCode KHandleTable::Reserve(Handle* out_handle) { } void KHandleTable::Unreserve(Handle handle) { + KScopedDisableDispatch dd(kernel); KScopedSpinLock lk(m_lock); // Unpack the handle. @@ -111,6 +116,7 @@ void KHandleTable::Unreserve(Handle handle) { } void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) { + KScopedDisableDispatch dd(kernel); KScopedSpinLock lk(m_lock); // Unpack the handle. diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h index 2ff6aa160..75dcec7df 100644 --- a/src/core/hle/kernel/k_handle_table.h +++ b/src/core/hle/kernel/k_handle_table.h @@ -69,6 +69,7 @@ public: template <typename T = KAutoObject> KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const { // Lock and look up in table. + KScopedDisableDispatch dd(kernel); KScopedSpinLock lk(m_lock); if constexpr (std::is_same_v<T, KAutoObject>) { @@ -123,6 +124,7 @@ public: size_t num_opened; { // Lock the table. + KScopedDisableDispatch dd(kernel); KScopedSpinLock lk(m_lock); for (num_opened = 0; num_opened < num_handles; num_opened++) { // Get the current handle. diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 8ead1a769..3d7e6707e 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -59,6 +59,7 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority thread->GetContext64().cpu_registers[0] = 0; thread->GetContext32().cpu_registers[1] = thread_handle; thread->GetContext64().cpu_registers[1] = thread_handle; + thread->DisableDispatch(); auto& kernel = system.Kernel(); // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index 6a7d80d03..6ddbae52c 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -376,20 +376,18 @@ void KScheduler::ClearSchedulerUpdateNeeded(KernelCore& kernel) { } void KScheduler::DisableScheduling(KernelCore& kernel) { - if (auto* scheduler = kernel.CurrentScheduler(); scheduler) { - ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 0); - scheduler->GetCurrentThread()->DisableDispatch(); - } + ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 0); + GetCurrentThreadPointer(kernel)->DisableDispatch(); } void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) { - if (auto* scheduler = kernel.CurrentScheduler(); scheduler) { - ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1); - if (scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1) { - scheduler->GetCurrentThread()->EnableDispatch(); - } + ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 1); + + if (GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() > 1) { + GetCurrentThreadPointer(kernel)->EnableDispatch(); + } else { + RescheduleCores(kernel, cores_needing_scheduling); } - RescheduleCores(kernel, cores_needing_scheduling); } u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) { @@ -617,13 +615,17 @@ KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, c state.highest_priority_thread = nullptr; } -KScheduler::~KScheduler() { +void KScheduler::Finalize() { if (idle_thread) { idle_thread->Close(); idle_thread = nullptr; } } +KScheduler::~KScheduler() { + ASSERT(!idle_thread); +} + KThread* KScheduler::GetCurrentThread() const { if (auto result = current_thread.load(); result) { return result; @@ -642,10 +644,12 @@ void KScheduler::RescheduleCurrentCore() { if (phys_core.IsInterrupted()) { phys_core.ClearInterrupt(); } + guard.Lock(); if (state.needs_scheduling.load()) { Schedule(); } else { + GetCurrentThread()->EnableDispatch(); guard.Unlock(); } } @@ -655,26 +659,33 @@ void KScheduler::OnThreadStart() { } void KScheduler::Unload(KThread* thread) { + ASSERT(thread); + LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr"); - if (thread) { - if (thread->IsCallingSvc()) { - thread->ClearIsCallingSvc(); - } - if (!thread->IsTerminationRequested()) { - prev_thread = thread; - - Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); - cpu_core.SaveContext(thread->GetContext32()); - cpu_core.SaveContext(thread->GetContext64()); - // Save the TPIDR_EL0 system register in case it was modified. - thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); - cpu_core.ClearExclusiveState(); - } else { - prev_thread = nullptr; - } - thread->context_guard.Unlock(); + if (thread->IsCallingSvc()) { + thread->ClearIsCallingSvc(); } + + auto& physical_core = system.Kernel().PhysicalCore(core_id); + if (!physical_core.IsInitialized()) { + return; + } + + Core::ARM_Interface& cpu_core = physical_core.ArmInterface(); + cpu_core.SaveContext(thread->GetContext32()); + cpu_core.SaveContext(thread->GetContext64()); + // Save the TPIDR_EL0 system register in case it was modified. + thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); + cpu_core.ClearExclusiveState(); + + if (!thread->IsTerminationRequested() && thread->GetActiveCore() == core_id) { + prev_thread = thread; + } else { + prev_thread = nullptr; + } + + thread->context_guard.Unlock(); } void KScheduler::Reload(KThread* thread) { @@ -683,11 +694,6 @@ void KScheduler::Reload(KThread* thread) { if (thread) { ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable."); - auto* const thread_owner_process = thread->GetOwnerProcess(); - if (thread_owner_process != nullptr) { - system.Kernel().MakeCurrentProcess(thread_owner_process); - } - Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); cpu_core.LoadContext(thread->GetContext32()); cpu_core.LoadContext(thread->GetContext64()); @@ -705,7 +711,7 @@ void KScheduler::SwitchContextStep2() { } void KScheduler::ScheduleImpl() { - KThread* previous_thread = current_thread.load(); + KThread* previous_thread = GetCurrentThread(); KThread* next_thread = state.highest_priority_thread; state.needs_scheduling = false; @@ -717,10 +723,15 @@ void KScheduler::ScheduleImpl() { // If we're not actually switching thread, there's nothing to do. if (next_thread == current_thread.load()) { + previous_thread->EnableDispatch(); guard.Unlock(); return; } + if (next_thread->GetCurrentCore() != core_id) { + next_thread->SetCurrentCore(core_id); + } + current_thread.store(next_thread); KProcess* const previous_process = system.Kernel().CurrentProcess(); @@ -731,11 +742,7 @@ void KScheduler::ScheduleImpl() { Unload(previous_thread); std::shared_ptr<Common::Fiber>* old_context; - if (previous_thread != nullptr) { - old_context = &previous_thread->GetHostContext(); - } else { - old_context = &idle_thread->GetHostContext(); - } + old_context = &previous_thread->GetHostContext(); guard.Unlock(); Common::Fiber::YieldTo(*old_context, *switch_fiber); diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h index 12cfae919..516e0cdba 100644 --- a/src/core/hle/kernel/k_scheduler.h +++ b/src/core/hle/kernel/k_scheduler.h @@ -33,6 +33,8 @@ public: explicit KScheduler(Core::System& system_, s32 core_id_); ~KScheduler(); + void Finalize(); + /// Reschedules to the next available thread (call after current thread is suspended) void RescheduleCurrentCore(); diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 9f1d3156b..0f6808ade 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -14,6 +14,7 @@ #include "common/fiber.h" #include "common/logging/log.h" #include "common/scope_exit.h" +#include "common/settings.h" #include "common/thread_queue_list.h" #include "core/core.h" #include "core/cpu_manager.h" @@ -188,7 +189,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s // Setup the stack parameters. StackParameters& sp = GetStackParameters(); sp.cur_thread = this; - sp.disable_count = 1; + sp.disable_count = 0; SetInExceptionHandler(); // Set thread ID. @@ -215,9 +216,10 @@ ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uint // Initialize the thread. R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type)); - // Initialize host context. + // Initialize emulation parameters. thread->host_context = std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter); + thread->is_single_core = !Settings::values.use_multi_core.GetValue(); return ResultSuccess; } @@ -970,6 +972,9 @@ ResultCode KThread::Run() { // Set our state and finish. SetState(ThreadState::Runnable); + + DisableDispatch(); + return ResultSuccess; } } @@ -1054,4 +1059,16 @@ s32 GetCurrentCoreId(KernelCore& kernel) { return GetCurrentThread(kernel).GetCurrentCore(); } +KScopedDisableDispatch::~KScopedDisableDispatch() { + if (GetCurrentThread(kernel).GetDisableDispatchCount() <= 1) { + auto scheduler = kernel.CurrentScheduler(); + + if (scheduler) { + scheduler->RescheduleCurrentCore(); + } + } else { + GetCurrentThread(kernel).EnableDispatch(); + } +} + } // namespace Kernel diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index c77f44ad4..e4c4c877d 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -450,16 +450,39 @@ public: sleeping_queue = q; } + [[nodiscard]] bool IsKernelThread() const { + return GetActiveCore() == 3; + } + + [[nodiscard]] bool IsDispatchTrackingDisabled() const { + return is_single_core || IsKernelThread(); + } + [[nodiscard]] s32 GetDisableDispatchCount() const { + if (IsDispatchTrackingDisabled()) { + // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch. + return 1; + } + return this->GetStackParameters().disable_count; } void DisableDispatch() { + if (IsDispatchTrackingDisabled()) { + // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch. + return; + } + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0); this->GetStackParameters().disable_count++; } void EnableDispatch() { + if (IsDispatchTrackingDisabled()) { + // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch. + return; + } + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0); this->GetStackParameters().disable_count--; } @@ -708,6 +731,7 @@ private: // For emulation std::shared_ptr<Common::Fiber> host_context{}; + bool is_single_core{}; // For debugging std::vector<KSynchronizationObject*> wait_objects_for_debugging; @@ -752,4 +776,16 @@ public: } }; +class KScopedDisableDispatch { +public: + [[nodiscard]] explicit KScopedDisableDispatch(KernelCore& kernel_) : kernel{kernel_} { + GetCurrentThread(kernel).DisableDispatch(); + } + + ~KScopedDisableDispatch(); + +private: + KernelCore& kernel; +}; + } // namespace Kernel diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index bea945301..8fdab44e4 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -85,8 +85,9 @@ struct KernelCore::Impl { } void InitializeCores() { - for (auto& core : cores) { - core.Initialize(current_process->Is64BitProcess()); + for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { + cores[core_id].Initialize(current_process->Is64BitProcess()); + system.Memory().SetCurrentPageTable(*current_process, core_id); } } @@ -131,15 +132,6 @@ struct KernelCore::Impl { next_user_process_id = KProcess::ProcessIDMin; next_thread_id = 1; - for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { - if (suspend_threads[core_id]) { - suspend_threads[core_id]->Close(); - suspend_threads[core_id] = nullptr; - } - - schedulers[core_id].reset(); - } - cores.clear(); global_handle_table->Finalize(); @@ -167,6 +159,16 @@ struct KernelCore::Impl { CleanupObject(time_shared_mem); CleanupObject(system_resource_limit); + for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { + if (suspend_threads[core_id]) { + suspend_threads[core_id]->Close(); + suspend_threads[core_id] = nullptr; + } + + schedulers[core_id]->Finalize(); + schedulers[core_id].reset(); + } + // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others next_host_thread_id = Core::Hardware::NUM_CPU_CORES; @@ -257,14 +259,6 @@ struct KernelCore::Impl { void MakeCurrentProcess(KProcess* process) { current_process = process; - if (process == nullptr) { - return; - } - - const u32 core_id = GetCurrentHostThreadID(); - if (core_id < Core::Hardware::NUM_CPU_CORES) { - system.Memory().SetCurrentPageTable(*process, core_id); - } } static inline thread_local u32 host_thread_id = UINT32_MAX; @@ -827,16 +821,20 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const { return impl->cores[id]; } +size_t KernelCore::CurrentPhysicalCoreIndex() const { + const u32 core_id = impl->GetCurrentHostThreadID(); + if (core_id >= Core::Hardware::NUM_CPU_CORES) { + return Core::Hardware::NUM_CPU_CORES - 1; + } + return core_id; +} + Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() { - u32 core_id = impl->GetCurrentHostThreadID(); - ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); - return impl->cores[core_id]; + return impl->cores[CurrentPhysicalCoreIndex()]; } const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const { - u32 core_id = impl->GetCurrentHostThreadID(); - ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); - return impl->cores[core_id]; + return impl->cores[CurrentPhysicalCoreIndex()]; } Kernel::KScheduler* KernelCore::CurrentScheduler() { @@ -1029,6 +1027,9 @@ void KernelCore::Suspend(bool in_suspention) { impl->suspend_threads[core_id]->SetState(state); impl->suspend_threads[core_id]->SetWaitReasonForDebugging( ThreadWaitReasonForDebugging::Suspended); + if (!should_suspend) { + impl->suspend_threads[core_id]->DisableDispatch(); + } } } } @@ -1043,13 +1044,11 @@ void KernelCore::ExceptionalExit() { } void KernelCore::EnterSVCProfile() { - std::size_t core = impl->GetCurrentHostThreadID(); - impl->svc_ticks[core] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC)); + impl->svc_ticks[CurrentPhysicalCoreIndex()] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC)); } void KernelCore::ExitSVCProfile() { - std::size_t core = impl->GetCurrentHostThreadID(); - MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]); + MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[CurrentPhysicalCoreIndex()]); } std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 3a6db0b1c..57535433b 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -146,6 +146,9 @@ public: /// Gets the an instance of the respective physical CPU core. const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const; + /// Gets the current physical core index for the running host thread. + std::size_t CurrentPhysicalCoreIndex() const; + /// Gets the sole instance of the Scheduler at the current running core. Kernel::KScheduler* CurrentScheduler(); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 2eb532472..890c52198 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -877,7 +877,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle const u64 thread_ticks = current_thread->GetCpuTime(); out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); - } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { + } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) { out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks; } @@ -1078,8 +1078,8 @@ static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Hand for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetCurrentThread()) { current = true; + break; } - break; } // If the thread is current, retry until it isn't. diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp index ef6854d62..36a4aa9cd 100644 --- a/src/core/hle/service/am/applets/applet_error.cpp +++ b/src/core/hle/service/am/applets/applet_error.cpp @@ -16,6 +16,30 @@ namespace Service::AM::Applets { +struct ErrorCode { + u32 error_category{}; + u32 error_number{}; + + static constexpr ErrorCode FromU64(u64 error_code) { + return { + .error_category{static_cast<u32>(error_code >> 32)}, + .error_number{static_cast<u32>(error_code & 0xFFFFFFFF)}, + }; + } + + static constexpr ErrorCode FromResultCode(ResultCode result) { + return { + .error_category{2000 + static_cast<u32>(result.module.Value())}, + .error_number{result.description.Value()}, + }; + } + + constexpr ResultCode ToResultCode() const { + return ResultCode{static_cast<ErrorModule>(error_category - 2000), error_number}; + } +}; +static_assert(sizeof(ErrorCode) == 0x8, "ErrorCode has incorrect size."); + #pragma pack(push, 4) struct ShowError { u8 mode; @@ -76,12 +100,7 @@ void CopyArgumentData(const std::vector<u8>& data, T& variable) { } ResultCode Decode64BitError(u64 error) { - const auto description = (error >> 32) & 0x1FFF; - auto module = error & 0x3FF; - if (module >= 2000) - module -= 2000; - module &= 0x1FF; - return {static_cast<ErrorModule>(module), static_cast<u32>(description)}; + return ErrorCode::FromU64(error).ToResultCode(); } } // Anonymous namespace diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index e742db48f..0a53c0c81 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -11,6 +11,7 @@ #include "core/hle/service/nifm/nifm.h" #include "core/hle/service/service.h" #include "core/network/network.h" +#include "core/network/network_interface.h" namespace Service::NIFM { @@ -179,10 +180,10 @@ private: IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - if (Settings::values.bcat_backend.GetValue() == "none") { - rb.PushEnum(RequestState::NotSubmitted); - } else { + if (Network::GetHostIPv4Address().has_value()) { rb.PushEnum(RequestState::Connected); + } else { + rb.PushEnum(RequestState::NotSubmitted); } } @@ -322,12 +323,15 @@ private: void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); - const auto [ipv4, error] = Network::GetHostIPv4Address(); - UNIMPLEMENTED_IF(error != Network::Errno::SUCCESS); + auto ipv4 = Network::GetHostIPv4Address(); + if (!ipv4) { + LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0"); + ipv4.emplace(Network::IPv4Address{0, 0, 0, 0}); + } IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.PushRaw(ipv4); + rb.PushRaw(*ipv4); } void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NIFM, "called"); @@ -354,10 +358,10 @@ private: static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), "IpConfigInfo has incorrect size."); - const IpConfigInfo ip_config_info{ + IpConfigInfo ip_config_info{ .ip_address_setting{ .is_automatic{true}, - .current_address{192, 168, 1, 100}, + .current_address{0, 0, 0, 0}, .subnet_mask{255, 255, 255, 0}, .gateway{192, 168, 1, 1}, }, @@ -368,6 +372,19 @@ private: }, }; + const auto iface = Network::GetSelectedNetworkInterface(); + if (iface) { + ip_config_info.ip_address_setting = + IpAddressSetting{.is_automatic{true}, + .current_address{Network::TranslateIPv4(iface->ip_address)}, + .subnet_mask{Network::TranslateIPv4(iface->subnet_mask)}, + .gateway{Network::TranslateIPv4(iface->gateway)}}; + + } else { + LOG_ERROR(Service_NIFM, + "Couldn't get host network configuration info, using default values"); + } + IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; rb.Push(ResultSuccess); rb.PushRaw<IpConfigInfo>(ip_config_info); @@ -384,10 +401,10 @@ private: IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - if (Settings::values.bcat_backend.GetValue() == "none") { - rb.Push<u8>(0); - } else { + if (Network::GetHostIPv4Address().has_value()) { rb.Push<u8>(1); + } else { + rb.Push<u8>(0); } } void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { @@ -395,10 +412,10 @@ private: IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - if (Settings::values.bcat_backend.GetValue() == "none") { - rb.Push<u8>(0); - } else { + if (Network::GetHostIPv4Address().has_value()) { rb.Push<u8>(1); + } else { + rb.Push<u8>(0); } } }; diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index 59ddf6298..b4c3a6099 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp @@ -9,17 +9,20 @@ #include "core/core.h" #include "core/hle/kernel/k_writable_event.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/nvflinger/buffer_queue.h" namespace Service::NVFlinger { -BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_) - : id(id_), layer_id(layer_id_), buffer_wait_event{kernel} { - Kernel::KAutoObject::Create(std::addressof(buffer_wait_event)); - buffer_wait_event.Initialize("BufferQueue:WaitEvent"); +BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_, + KernelHelpers::ServiceContext& service_context_) + : id(id_), layer_id(layer_id_), service_context{service_context_} { + buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent"); } -BufferQueue::~BufferQueue() = default; +BufferQueue::~BufferQueue() { + service_context.CloseEvent(buffer_wait_event); +} void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { ASSERT(slot < buffer_slots); @@ -41,7 +44,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) .multi_fence = {}, }; - buffer_wait_event.GetWritableEvent().Signal(); + buffer_wait_event->GetWritableEvent().Signal(); } std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width, @@ -119,7 +122,7 @@ void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& mult } free_buffers_condition.notify_one(); - buffer_wait_event.GetWritableEvent().Signal(); + buffer_wait_event->GetWritableEvent().Signal(); } std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() { @@ -154,7 +157,7 @@ void BufferQueue::ReleaseBuffer(u32 slot) { } free_buffers_condition.notify_one(); - buffer_wait_event.GetWritableEvent().Signal(); + buffer_wait_event->GetWritableEvent().Signal(); } void BufferQueue::Connect() { @@ -169,7 +172,7 @@ void BufferQueue::Disconnect() { std::unique_lock lock{queue_sequence_mutex}; queue_sequence.clear(); } - buffer_wait_event.GetWritableEvent().Signal(); + buffer_wait_event->GetWritableEvent().Signal(); is_connect = false; free_buffers_condition.notify_one(); } @@ -189,11 +192,11 @@ u32 BufferQueue::Query(QueryType type) { } Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() { - return buffer_wait_event.GetWritableEvent(); + return buffer_wait_event->GetWritableEvent(); } Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() { - return buffer_wait_event.GetReadableEvent(); + return buffer_wait_event->GetReadableEvent(); } } // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h index 61e337ac5..759247eb0 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ b/src/core/hle/service/nvflinger/buffer_queue.h @@ -24,6 +24,10 @@ class KReadableEvent; class KWritableEvent; } // namespace Kernel +namespace Service::KernelHelpers { +class ServiceContext; +} // namespace Service::KernelHelpers + namespace Service::NVFlinger { constexpr u32 buffer_slots = 0x40; @@ -54,7 +58,8 @@ public: NativeWindowFormat = 2, }; - explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_); + explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_, + KernelHelpers::ServiceContext& service_context_); ~BufferQueue(); enum class BufferTransformFlags : u32 { @@ -130,12 +135,14 @@ private: std::list<u32> free_buffers; std::array<Buffer, buffer_slots> buffers; std::list<u32> queue_sequence; - Kernel::KEvent buffer_wait_event; + Kernel::KEvent* buffer_wait_event{}; std::mutex free_buffers_mutex; std::condition_variable free_buffers_condition; std::mutex queue_sequence_mutex; + + KernelHelpers::ServiceContext& service_context; }; } // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 941748970..00bff8caf 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -61,12 +61,13 @@ void NVFlinger::SplitVSync() { } } -NVFlinger::NVFlinger(Core::System& system_) : system(system_) { - displays.emplace_back(0, "Default", system); - displays.emplace_back(1, "External", system); - displays.emplace_back(2, "Edid", system); - displays.emplace_back(3, "Internal", system); - displays.emplace_back(4, "Null", system); +NVFlinger::NVFlinger(Core::System& system_) + : system(system_), service_context(system_, "nvflinger") { + displays.emplace_back(0, "Default", service_context, system); + displays.emplace_back(1, "External", service_context, system); + displays.emplace_back(2, "Edid", service_context, system); + displays.emplace_back(3, "Internal", service_context, system); + displays.emplace_back(4, "Null", service_context, system); guard = std::make_shared<std::mutex>(); // Schedule the screen composition events @@ -146,7 +147,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { const u32 buffer_queue_id = next_buffer_queue_id++; buffer_queues.emplace_back( - std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id)); + std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id, service_context)); display.CreateLayer(layer_id, *buffer_queues.back()); } diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index d80fd07ef..6d84cafb4 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -15,6 +15,7 @@ #include <vector> #include "common/common_types.h" +#include "core/hle/service/kernel_helpers.h" namespace Common { class Event; @@ -135,6 +136,8 @@ private: std::unique_ptr<std::thread> vsync_thread; std::unique_ptr<Common::Event> wait_event; std::atomic<bool> is_running{}; + + KernelHelpers::ServiceContext service_context; }; } // namespace Service::NVFlinger diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp index 0dd342dbf..b7705c02a 100644 --- a/src/core/hle/service/vi/display/vi_display.cpp +++ b/src/core/hle/service/vi/display/vi_display.cpp @@ -12,18 +12,21 @@ #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_writable_event.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/vi/display/vi_display.h" #include "core/hle/service/vi/layer/vi_layer.h" namespace Service::VI { -Display::Display(u64 id, std::string name_, Core::System& system) - : display_id{id}, name{std::move(name_)}, vsync_event{system.Kernel()} { - Kernel::KAutoObject::Create(std::addressof(vsync_event)); - vsync_event.Initialize(fmt::format("Display VSync Event {}", id)); +Display::Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_, + Core::System& system_) + : display_id{id}, name{std::move(name_)}, service_context{service_context_} { + vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id)); } -Display::~Display() = default; +Display::~Display() { + service_context.CloseEvent(vsync_event); +} Layer& Display::GetLayer(std::size_t index) { return *layers.at(index); @@ -34,11 +37,11 @@ const Layer& Display::GetLayer(std::size_t index) const { } Kernel::KReadableEvent& Display::GetVSyncEvent() { - return vsync_event.GetReadableEvent(); + return vsync_event->GetReadableEvent(); } void Display::SignalVSyncEvent() { - vsync_event.GetWritableEvent().Signal(); + vsync_event->GetWritableEvent().Signal(); } void Display::CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue) { diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h index 166f2a4cc..0979fc421 100644 --- a/src/core/hle/service/vi/display/vi_display.h +++ b/src/core/hle/service/vi/display/vi_display.h @@ -18,6 +18,9 @@ class KEvent; namespace Service::NVFlinger { class BufferQueue; } +namespace Service::KernelHelpers { +class ServiceContext; +} // namespace Service::KernelHelpers namespace Service::VI { @@ -31,10 +34,13 @@ class Display { public: /// Constructs a display with a given unique ID and name. /// - /// @param id The unique ID for this display. + /// @param id The unique ID for this display. + /// @param service_context_ The ServiceContext for the owning service. /// @param name_ The name for this display. + /// @param system_ The global system instance. /// - Display(u64 id, std::string name_, Core::System& system); + Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_, + Core::System& system_); ~Display(); /// Gets the unique ID assigned to this display. @@ -98,9 +104,10 @@ public: private: u64 display_id; std::string name; + KernelHelpers::ServiceContext& service_context; std::vector<std::shared_ptr<Layer>> layers; - Kernel::KEvent vsync_event; + Kernel::KEvent* vsync_event{}; }; } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 3e5949d52..8e8fc40ca 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -1158,7 +1158,7 @@ private: const auto layer_id = nv_flinger.CreateLayer(display_id); if (!layer_id) { - LOG_ERROR(Service_VI, "Layer not found! layer_id={}", *layer_id); + LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERR_NOT_FOUND); return; diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp index 375bc79ec..4732d4485 100644 --- a/src/core/network/network.cpp +++ b/src/core/network/network.cpp @@ -10,9 +10,10 @@ #include "common/common_funcs.h" #ifdef _WIN32 -#define _WINSOCK_DEPRECATED_NO_WARNINGS // gethostname #include <winsock2.h> +#include <ws2tcpip.h> #elif YUZU_UNIX +#include <arpa/inet.h> #include <errno.h> #include <fcntl.h> #include <netdb.h> @@ -27,7 +28,9 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/network/network.h" +#include "core/network/network_interface.h" #include "core/network/sockets.h" namespace Network { @@ -47,11 +50,6 @@ void Finalize() { WSACleanup(); } -constexpr IPv4Address TranslateIPv4(in_addr addr) { - auto& bytes = addr.S_un.S_un_b; - return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4}; -} - sockaddr TranslateFromSockAddrIn(SockAddrIn input) { sockaddr_in result; @@ -138,12 +136,6 @@ void Initialize() {} void Finalize() {} -constexpr IPv4Address TranslateIPv4(in_addr addr) { - const u32 bytes = addr.s_addr; - return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8), - static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)}; -} - sockaddr TranslateFromSockAddrIn(SockAddrIn input) { sockaddr_in result; @@ -182,7 +174,7 @@ linger MakeLinger(bool enable, u32 linger_value) { } bool EnableNonBlock(int fd, bool enable) { - int flags = fcntl(fd, F_GETFD); + int flags = fcntl(fd, F_GETFL); if (flags == -1) { return false; } @@ -191,7 +183,7 @@ bool EnableNonBlock(int fd, bool enable) { } else { flags &= ~O_NONBLOCK; } - return fcntl(fd, F_SETFD, flags) == 0; + return fcntl(fd, F_SETFL, flags) == 0; } Errno TranslateNativeError(int e) { @@ -227,8 +219,12 @@ Errno GetAndLogLastError() { #else int e = errno; #endif + const Errno err = TranslateNativeError(e); + if (err == Errno::AGAIN) { + return err; + } LOG_ERROR(Network, "Socket operation error: {}", NativeErrorToString(e)); - return TranslateNativeError(e); + return err; } int TranslateDomain(Domain domain) { @@ -353,27 +349,29 @@ NetworkInstance::~NetworkInstance() { Finalize(); } -std::pair<IPv4Address, Errno> GetHostIPv4Address() { - std::array<char, 256> name{}; - if (gethostname(name.data(), static_cast<int>(name.size()) - 1) == SOCKET_ERROR) { - return {IPv4Address{}, GetAndLogLastError()}; +std::optional<IPv4Address> GetHostIPv4Address() { + const std::string& selected_network_interface = Settings::values.network_interface.GetValue(); + const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); + if (network_interfaces.size() == 0) { + LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); + return {}; } - hostent* const ent = gethostbyname(name.data()); - if (!ent) { - return {IPv4Address{}, GetAndLogLastError()}; - } - if (ent->h_addr_list == nullptr) { - UNIMPLEMENTED_MSG("No addr provided in hostent->h_addr_list"); - return {IPv4Address{}, Errno::SUCCESS}; - } - if (ent->h_length != sizeof(in_addr)) { - UNIMPLEMENTED_MSG("Unexpected size={} in hostent->h_length", ent->h_length); - } + const auto res = + std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) { + return iface.name == selected_network_interface; + }); + + if (res != network_interfaces.end()) { + char ip_addr[16] = {}; + ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr); + LOG_INFO(Network, "IP address: {}", ip_addr); - in_addr addr; - std::memcpy(&addr, ent->h_addr_list[0], sizeof(addr)); - return {TranslateIPv4(addr), Errno::SUCCESS}; + return TranslateIPv4(res->ip_address); + } else { + LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); + return {}; + } } std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { diff --git a/src/core/network/network.h b/src/core/network/network.h index bd30f1899..fdd3e4655 100644 --- a/src/core/network/network.h +++ b/src/core/network/network.h @@ -5,11 +5,18 @@ #pragma once #include <array> +#include <optional> #include <utility> #include "common/common_funcs.h" #include "common/common_types.h" +#ifdef _WIN32 +#include <winsock2.h> +#elif YUZU_UNIX +#include <netinet/in.h> +#endif + namespace Network { class Socket; @@ -92,8 +99,21 @@ public: ~NetworkInstance(); }; +#ifdef _WIN32 +constexpr IPv4Address TranslateIPv4(in_addr addr) { + auto& bytes = addr.S_un.S_un_b; + return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4}; +} +#elif YUZU_UNIX +constexpr IPv4Address TranslateIPv4(in_addr addr) { + const u32 bytes = addr.s_addr; + return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8), + static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)}; +} +#endif + /// @brief Returns host's IPv4 address -/// @return Pair of an array of human ordered IPv4 address (e.g. 192.168.0.1) and an error code -std::pair<IPv4Address, Errno> GetHostIPv4Address(); +/// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array +std::optional<IPv4Address> GetHostIPv4Address(); } // namespace Network diff --git a/src/core/network/network_interface.cpp b/src/core/network/network_interface.cpp new file mode 100644 index 000000000..cecc9aa11 --- /dev/null +++ b/src/core/network/network_interface.cpp @@ -0,0 +1,203 @@ +// Copyright 2021 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <fstream> +#include <sstream> +#include <vector> + +#include "common/bit_cast.h" +#include "common/common_types.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "common/string_util.h" +#include "core/network/network_interface.h" + +#ifdef _WIN32 +#include <iphlpapi.h> +#else +#include <cerrno> +#include <ifaddrs.h> +#include <net/if.h> +#endif + +namespace Network { + +#ifdef _WIN32 + +std::vector<NetworkInterface> GetAvailableNetworkInterfaces() { + std::vector<IP_ADAPTER_ADDRESSES> adapter_addresses; + DWORD ret = ERROR_BUFFER_OVERFLOW; + DWORD buf_size = 0; + + // retry up to 5 times + for (int i = 0; i < 5 && ret == ERROR_BUFFER_OVERFLOW; i++) { + ret = GetAdaptersAddresses( + AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_GATEWAYS, + nullptr, adapter_addresses.data(), &buf_size); + + if (ret == ERROR_BUFFER_OVERFLOW) { + adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1); + } else { + break; + } + } + + if (ret == NO_ERROR) { + std::vector<NetworkInterface> result; + + for (auto current_address = adapter_addresses.data(); current_address != nullptr; + current_address = current_address->Next) { + if (current_address->FirstUnicastAddress == nullptr || + current_address->FirstUnicastAddress->Address.lpSockaddr == nullptr) { + continue; + } + + if (current_address->OperStatus != IfOperStatusUp) { + continue; + } + + const auto ip_addr = Common::BitCast<struct sockaddr_in>( + *current_address->FirstUnicastAddress->Address.lpSockaddr) + .sin_addr; + + ULONG mask = 0; + if (ConvertLengthToIpv4Mask(current_address->FirstUnicastAddress->OnLinkPrefixLength, + &mask) != NO_ERROR) { + LOG_ERROR(Network, "Failed to convert IPv4 prefix length to subnet mask"); + continue; + } + + struct in_addr gateway = {.S_un{.S_addr{0}}}; + if (current_address->FirstGatewayAddress != nullptr && + current_address->FirstGatewayAddress->Address.lpSockaddr != nullptr) { + gateway = Common::BitCast<struct sockaddr_in>( + *current_address->FirstGatewayAddress->Address.lpSockaddr) + .sin_addr; + } + + result.push_back(NetworkInterface{ + .name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})}, + .ip_address{ip_addr}, + .subnet_mask = in_addr{.S_un{.S_addr{mask}}}, + .gateway = gateway}); + } + + return result; + } else { + LOG_ERROR(Network, "Failed to get network interfaces with GetAdaptersAddresses"); + return {}; + } +} + +#else + +std::vector<NetworkInterface> GetAvailableNetworkInterfaces() { + std::vector<NetworkInterface> result; + + struct ifaddrs* ifaddr = nullptr; + + if (getifaddrs(&ifaddr) != 0) { + LOG_ERROR(Network, "Failed to get network interfaces with getifaddrs: {}", + std::strerror(errno)); + return result; + } + + for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr) { + continue; + } + + if (ifa->ifa_addr->sa_family != AF_INET) { + continue; + } + + if ((ifa->ifa_flags & IFF_UP) == 0 || (ifa->ifa_flags & IFF_LOOPBACK) != 0) { + continue; + } + + std::uint32_t gateway{0}; + std::ifstream file{"/proc/net/route"}; + if (file.is_open()) { + + // ignore header + file.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); + + bool gateway_found = false; + + for (std::string line; std::getline(file, line);) { + std::istringstream iss{line}; + + std::string iface_name{}; + iss >> iface_name; + if (iface_name != ifa->ifa_name) { + continue; + } + + iss >> std::hex; + + std::uint32_t dest{0}; + iss >> dest; + if (dest != 0) { + // not the default route + continue; + } + + iss >> gateway; + + std::uint16_t flags{0}; + iss >> flags; + + // flag RTF_GATEWAY (defined in <linux/route.h>) + if ((flags & 0x2) == 0) { + continue; + } + + gateway_found = true; + break; + } + + if (!gateway_found) { + gateway = 0; + } + } else { + LOG_ERROR(Network, "Failed to open \"/proc/net/route\""); + } + + result.push_back(NetworkInterface{ + .name{ifa->ifa_name}, + .ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr}, + .subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr}, + .gateway{in_addr{.s_addr = gateway}}}); + } + + freeifaddrs(ifaddr); + + return result; +} + +#endif + +std::optional<NetworkInterface> GetSelectedNetworkInterface() { + const std::string& selected_network_interface = Settings::values.network_interface.GetValue(); + const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); + if (network_interfaces.size() == 0) { + LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); + return {}; + } + + const auto res = + std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) { + return iface.name == selected_network_interface; + }); + + if (res != network_interfaces.end()) { + return *res; + } else { + LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); + return {}; + } +} + +} // namespace Network diff --git a/src/core/network/network_interface.h b/src/core/network/network_interface.h new file mode 100644 index 000000000..980edb2f5 --- /dev/null +++ b/src/core/network/network_interface.h @@ -0,0 +1,29 @@ +// Copyright 2021 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <optional> +#include <string> +#include <vector> + +#ifdef _WIN32 +#include <winsock2.h> +#else +#include <netinet/in.h> +#endif + +namespace Network { + +struct NetworkInterface { + std::string name; + struct in_addr ip_address; + struct in_addr subnet_mask; + struct in_addr gateway; +}; + +std::vector<NetworkInterface> GetAvailableNetworkInterfaces(); +std::optional<NetworkInterface> GetSelectedNetworkInterface(); + +} // namespace Network |