summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/core.cpp15
-rw-r--r--src/core/core.h12
-rw-r--r--src/core/cpu_manager.cpp40
-rw-r--r--src/core/cpu_manager.h6
-rw-r--r--src/core/hle/api_version.h14
-rw-r--r--src/core/hle/kernel/k_address_arbiter.cpp4
-rw-r--r--src/core/hle/kernel/k_auto_object.h4
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp2
-rw-r--r--src/core/hle/kernel/k_handle_table.cpp6
-rw-r--r--src/core/hle/kernel/k_handle_table.h2
-rw-r--r--src/core/hle/kernel/k_process.cpp1
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp85
-rw-r--r--src/core/hle/kernel/k_scheduler.h2
-rw-r--r--src/core/hle/kernel/k_thread.cpp21
-rw-r--r--src/core/hle/kernel/k_thread.h36
-rw-r--r--src/core/hle/kernel/kernel.cpp57
-rw-r--r--src/core/hle/kernel/kernel.h3
-rw-r--r--src/core/hle/kernel/svc.cpp4
-rw-r--r--src/core/hle/service/acc/acc.cpp22
-rw-r--r--src/core/hle/service/am/applets/applet_error.cpp31
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard.cpp6
-rw-r--r--src/core/hle/service/friend/friend.cpp6
-rw-r--r--src/core/hle/service/nifm/nifm.cpp45
-rw-r--r--src/core/hle/service/ns/language.cpp3
-rw-r--r--src/core/hle/service/ns/ns_language.h42
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp58
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h45
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp25
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h11
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp15
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h3
-rw-r--r--src/core/hle/service/set/set.cpp23
-rw-r--r--src/core/hle/service/set/set.h1
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp17
-rw-r--r--src/core/hle/service/vi/display/vi_display.h13
-rw-r--r--src/core/hle/service/vi/vi.cpp2
-rw-r--r--src/core/memory.cpp552
-rw-r--r--src/core/memory.h102
-rw-r--r--src/core/network/network.cpp66
-rw-r--r--src/core/network/network.h24
-rw-r--r--src/core/network/network_interface.cpp203
-rw-r--r--src/core/network/network_interface.h29
43 files changed, 793 insertions, 867 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/api_version.h b/src/core/hle/api_version.h
index 5e10a7ad9..43d5670a9 100644
--- a/src/core/hle/api_version.h
+++ b/src/core/hle/api_version.h
@@ -12,9 +12,9 @@ namespace HLE::ApiVersion {
// Horizon OS version constants.
-constexpr u8 HOS_VERSION_MAJOR = 11;
-constexpr u8 HOS_VERSION_MINOR = 0;
-constexpr u8 HOS_VERSION_MICRO = 1;
+constexpr u8 HOS_VERSION_MAJOR = 12;
+constexpr u8 HOS_VERSION_MINOR = 1;
+constexpr u8 HOS_VERSION_MICRO = 0;
// NintendoSDK version constants.
@@ -22,15 +22,15 @@ constexpr u8 SDK_REVISION_MAJOR = 1;
constexpr u8 SDK_REVISION_MINOR = 0;
constexpr char PLATFORM_STRING[] = "NX";
-constexpr char VERSION_HASH[] = "69103fcb2004dace877094c2f8c29e6113be5dbf";
-constexpr char DISPLAY_VERSION[] = "11.0.1";
-constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 11.0.1-1.0";
+constexpr char VERSION_HASH[] = "76b10c2dab7d3aa73fc162f8dff1655e6a21caf4";
+constexpr char DISPLAY_VERSION[] = "12.1.0";
+constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 12.1.0-1.0";
// Atmosphere version constants.
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MAJOR = 0;
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MINOR = 19;
-constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 4;
+constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 5;
constexpr u32 GetTargetFirmware() {
return u32{HOS_VERSION_MAJOR} << 24 | u32{HOS_VERSION_MINOR} << 16 |
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 92fbc5532..8673384ee 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);
- }
}
/// Creates a new host thread ID, should only be called by GetHostThreadId
@@ -824,16 +818,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() {
@@ -1026,6 +1024,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();
+ }
}
}
}
@@ -1040,13 +1041,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/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 2e969f2a8..882fc1492 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -292,7 +292,7 @@ public:
protected:
void Get(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
+ LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
ProfileBase profile_base{};
ProfileData data{};
if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
@@ -301,7 +301,7 @@ protected:
rb.Push(ResultSuccess);
rb.PushRaw(profile_base);
} else {
- LOG_ERROR(Service_ACC, "Failed to get profile base and data for user={}",
+ LOG_ERROR(Service_ACC, "Failed to get profile base and data for user=0x{}",
user_id.Format());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code
@@ -309,14 +309,14 @@ protected:
}
void GetBase(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
+ LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
ProfileBase profile_base{};
if (profile_manager.GetProfileBase(user_id, profile_base)) {
IPC::ResponseBuilder rb{ctx, 16};
rb.Push(ResultSuccess);
rb.PushRaw(profile_base);
} else {
- LOG_ERROR(Service_ACC, "Failed to get profile base for user={}", user_id.Format());
+ LOG_ERROR(Service_ACC, "Failed to get profile base for user=0x{}", user_id.Format());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code
}
@@ -372,7 +372,7 @@ protected:
const auto user_data = ctx.ReadBuffer();
- LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid={}",
+ LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
Common::StringFromFixedZeroTerminatedBuffer(
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
base.timestamp, base.user_uuid.Format());
@@ -405,7 +405,7 @@ protected:
const auto user_data = ctx.ReadBuffer();
const auto image_data = ctx.ReadBuffer(1);
- LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid={}",
+ LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
Common::StringFromFixedZeroTerminatedBuffer(
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
base.timestamp, base.user_uuid.Format());
@@ -662,7 +662,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
Common::UUID user_id = rp.PopRaw<Common::UUID>();
- LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
+ LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
@@ -693,7 +693,7 @@ void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
Common::UUID user_id = rp.PopRaw<Common::UUID>();
- LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
+ LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
@@ -802,7 +802,7 @@ void Module::Interface::GetProfileEditor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
Common::UUID user_id = rp.PopRaw<Common::UUID>();
- LOG_DEBUG(Service_ACC, "called, user_id={}", user_id.Format());
+ LOG_DEBUG(Service_ACC, "called, user_id=0x{}", user_id.Format());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
@@ -844,7 +844,7 @@ void Module::Interface::StoreSaveDataThumbnailApplication(Kernel::HLERequestCont
IPC::RequestParser rp{ctx};
const auto uuid = rp.PopRaw<Common::UUID>();
- LOG_WARNING(Service_ACC, "(STUBBED) called, uuid={}", uuid.Format());
+ LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}", uuid.Format());
// TODO(ogniK): Check if application ID is zero on acc initialize. As we don't have a reliable
// way of confirming things like the TID, we're going to assume a non zero value for the time
@@ -858,7 +858,7 @@ void Module::Interface::StoreSaveDataThumbnailSystem(Kernel::HLERequestContext&
const auto uuid = rp.PopRaw<Common::UUID>();
const auto tid = rp.Pop<u64_le>();
- LOG_WARNING(Service_ACC, "(STUBBED) called, uuid={}, tid={:016X}", uuid.Format(), tid);
+ LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}, tid={:016X}", uuid.Format(), tid);
StoreSaveDataThumbnail(ctx, uuid, tid);
}
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/am/applets/applet_software_keyboard.cpp b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
index 7cae90609..c89aa1bbf 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
@@ -377,7 +377,8 @@ void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) {
if (swkbd_config_common.use_utf8) {
std::string utf8_submitted_text = Common::UTF16ToUTF8(current_text);
- const u64 buffer_size = sizeof(u64) + utf8_submitted_text.size();
+ // Include the null terminator in the buffer size.
+ const u64 buffer_size = utf8_submitted_text.size() + 1;
LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-8 Submitted Text: {}", buffer_size,
utf8_submitted_text);
@@ -386,7 +387,8 @@ void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) {
std::memcpy(out_data.data() + sizeof(u64), utf8_submitted_text.data(),
utf8_submitted_text.size());
} else {
- const u64 buffer_size = sizeof(u64) + current_text.size() * sizeof(char16_t);
+ // Include the null terminator in the buffer size.
+ const u64 buffer_size = (current_text.size() + 1) * sizeof(char16_t);
LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-16 Submitted Text: {}", buffer_size,
Common::UTF16ToUTF8(current_text));
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index a3c939c0c..b58c152ce 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -158,7 +158,7 @@ private:
const auto local_play = rp.Pop<bool>();
const auto uuid = rp.PopRaw<Common::UUID>();
- LOG_WARNING(Service_Friend, "(STUBBED) called local_play={} uuid={}", local_play,
+ LOG_WARNING(Service_Friend, "(STUBBED) called, local_play={}, uuid=0x{}", local_play,
uuid.Format());
IPC::ResponseBuilder rb{ctx, 2};
@@ -171,7 +171,7 @@ private:
const auto uuid = rp.PopRaw<Common::UUID>();
[[maybe_unused]] const auto filter = rp.PopRaw<SizedFriendFilter>();
const auto pid = rp.Pop<u64>();
- LOG_WARNING(Service_Friend, "(STUBBED) called, offset={}, uuid={}, pid={}", friend_offset,
+ LOG_WARNING(Service_Friend, "(STUBBED) called, offset={}, uuid=0x{}, pid={}", friend_offset,
uuid.Format(), pid);
IPC::ResponseBuilder rb{ctx, 3};
@@ -289,7 +289,7 @@ void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx
IPC::RequestParser rp{ctx};
auto uuid = rp.PopRaw<Common::UUID>();
- LOG_DEBUG(Service_Friend, "called, uuid={}", uuid.Format());
+ LOG_DEBUG(Service_Friend, "called, uuid=0x{}", uuid.Format());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
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/ns/language.cpp b/src/core/hle/service/ns/language.cpp
index 29c4a820c..7d9e4a20b 100644
--- a/src/core/hle/service/ns/language.cpp
+++ b/src/core/hle/service/ns/language.cpp
@@ -339,13 +339,16 @@ std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
case Set::LanguageCode::FR_CA:
return ApplicationLanguage::CanadianFrench;
case Set::LanguageCode::PT:
+ case Set::LanguageCode::PT_BR:
return ApplicationLanguage::Portuguese;
case Set::LanguageCode::RU:
return ApplicationLanguage::Russian;
case Set::LanguageCode::KO:
return ApplicationLanguage::Korean;
+ case Set::LanguageCode::ZH_TW:
case Set::LanguageCode::ZH_HANT:
return ApplicationLanguage::TraditionalChinese;
+ case Set::LanguageCode::ZH_CN:
case Set::LanguageCode::ZH_HANS:
return ApplicationLanguage::SimplifiedChinese;
default:
diff --git a/src/core/hle/service/ns/ns_language.h b/src/core/hle/service/ns/ns_language.h
deleted file mode 100644
index 59ac85a19..000000000
--- a/src/core/hle/service/ns/ns_language.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-#include <optional>
-#include <string>
-#include "common/common_types.h"
-#include "core/hle/service/set/set.h"
-
-namespace Service::NS {
-/// This is nn::ns::detail::ApplicationLanguage
-enum class ApplicationLanguage : u8 {
- AmericanEnglish = 0,
- BritishEnglish,
- Japanese,
- French,
- German,
- LatinAmericanSpanish,
- Spanish,
- Italian,
- Dutch,
- CanadianFrench,
- Portuguese,
- Russian,
- Korean,
- TraditionalChinese,
- SimplifiedChinese,
- Count
-};
-using ApplicationLanguagePriorityList =
- const std::array<ApplicationLanguage, static_cast<std::size_t>(ApplicationLanguage::Count)>;
-
-constexpr u32 GetSupportedLanguageFlag(const ApplicationLanguage lang) {
- return 1U << static_cast<u32>(lang);
-}
-
-const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(ApplicationLanguage lang);
-std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
- Service::Set::LanguageCode language_code);
-std::optional<Service::Set::LanguageCode> ConvertToLanguageCode(ApplicationLanguage lang);
-} // namespace Service::NS \ No newline at end of file
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
index 1403a39d0..845de724d 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -166,8 +166,6 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto
LOG_ERROR(Service_NVDRV, "failed to map size={}", object->size);
} else {
cmd_buffer.map_address = object->dma_map_addr;
- AddBufferMap(object->dma_map_addr, object->size, object->addr,
- object->status == nvmap::Object::Status::Allocated);
}
}
std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer));
@@ -178,30 +176,11 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto
}
NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
- IoctlMapBuffer params{};
- std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
- std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
- SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
-
- auto& gpu = system.GPU();
-
- for (auto& cmd_buffer : cmd_buffer_handles) {
- const auto object{nvmap_dev->GetObject(cmd_buffer.map_handle)};
- if (!object) {
- LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmd_buffer.map_handle);
- std::memcpy(output.data(), &params, output.size());
- return NvResult::InvalidState;
- }
- if (const auto size{RemoveBufferMap(object->dma_map_addr)}; size) {
- gpu.MemoryManager().Unmap(object->dma_map_addr, *size);
- } else {
- // This occurs quite frequently, however does not seem to impact functionality
- LOG_DEBUG(Service_NVDRV, "invalid offset=0x{:X} dma=0x{:X}", object->addr,
- object->dma_map_addr);
- }
- object->dma_map_addr = 0;
- }
+ // This is intntionally stubbed.
+ // Skip unmapping buffers here, as to not break the continuity of the VP9 reference frame
+ // addresses, and risk invalidating data before the async GPU thread is done with it
std::memset(output.data(), 0, output.size());
+ LOG_DEBUG(Service_NVDRV, "(STUBBED) called");
return NvResult::Success;
}
@@ -212,33 +191,4 @@ NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input,
return NvResult::Success;
}
-std::optional<nvhost_nvdec_common::BufferMap> nvhost_nvdec_common::FindBufferMap(
- GPUVAddr gpu_addr) const {
- const auto it = std::find_if(
- buffer_mappings.begin(), buffer_mappings.upper_bound(gpu_addr), [&](const auto& entry) {
- return (gpu_addr >= entry.second.StartAddr() && gpu_addr < entry.second.EndAddr());
- });
-
- ASSERT(it != buffer_mappings.end());
- return it->second;
-}
-
-void nvhost_nvdec_common::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr,
- bool is_allocated) {
- buffer_mappings.insert_or_assign(gpu_addr, BufferMap{gpu_addr, size, cpu_addr, is_allocated});
-}
-
-std::optional<std::size_t> nvhost_nvdec_common::RemoveBufferMap(GPUVAddr gpu_addr) {
- const auto iter{buffer_mappings.find(gpu_addr)};
- if (iter == buffer_mappings.end()) {
- return std::nullopt;
- }
- std::size_t size = 0;
- if (iter->second.IsAllocated()) {
- size = iter->second.Size();
- }
- buffer_mappings.erase(iter);
- return size;
-}
-
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
index da10f5f41..af59f00d2 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
@@ -23,45 +23,6 @@ public:
~nvhost_nvdec_common() override;
protected:
- class BufferMap final {
- public:
- constexpr BufferMap() = default;
-
- constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_)
- : start_addr{start_addr_}, end_addr{start_addr_ + size_} {}
-
- constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_, VAddr cpu_addr_,
- bool is_allocated_)
- : start_addr{start_addr_}, end_addr{start_addr_ + size_}, cpu_addr{cpu_addr_},
- is_allocated{is_allocated_} {}
-
- constexpr VAddr StartAddr() const {
- return start_addr;
- }
-
- constexpr VAddr EndAddr() const {
- return end_addr;
- }
-
- constexpr std::size_t Size() const {
- return end_addr - start_addr;
- }
-
- constexpr VAddr CpuAddr() const {
- return cpu_addr;
- }
-
- constexpr bool IsAllocated() const {
- return is_allocated;
- }
-
- private:
- GPUVAddr start_addr{};
- GPUVAddr end_addr{};
- VAddr cpu_addr{};
- bool is_allocated{};
- };
-
struct IoctlSetNvmapFD {
s32_le nvmap_fd{};
};
@@ -154,17 +115,11 @@ protected:
NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
- std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const;
- void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated);
- std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr);
-
s32_le nvmap_fd{};
u32_le submit_timeout{};
std::shared_ptr<nvmap> nvmap_dev;
SyncpointManager& syncpoint_manager;
std::array<u32, MaxSyncPoints> device_syncpoints{};
- // This is expected to be ordered, therefore we must use a map, not unordered_map
- std::map<GPUVAddr, BufferMap> buffer_mappings;
};
}; // namespace Devices
} // namespace Service::Nvidia
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/set/set.cpp b/src/core/hle/service/set/set.cpp
index 522a604a5..8795eb6b7 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -12,7 +12,7 @@
namespace Service::Set {
namespace {
-constexpr std::array<LanguageCode, 17> available_language_codes = {{
+constexpr std::array<LanguageCode, 18> available_language_codes = {{
LanguageCode::JA,
LanguageCode::EN_US,
LanguageCode::FR,
@@ -30,6 +30,7 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{
LanguageCode::ES_419,
LanguageCode::ZH_HANS,
LanguageCode::ZH_HANT,
+ LanguageCode::PT_BR,
}};
enum class KeyboardLayout : u64 {
@@ -50,7 +51,7 @@ enum class KeyboardLayout : u64 {
ChineseTraditional = 14,
};
-constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 17> language_to_layout{{
+constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 18> language_to_layout{{
{LanguageCode::JA, KeyboardLayout::Japanese},
{LanguageCode::EN_US, KeyboardLayout::EnglishUs},
{LanguageCode::FR, KeyboardLayout::French},
@@ -68,10 +69,11 @@ constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 17> language_to_la
{LanguageCode::ES_419, KeyboardLayout::SpanishLatin},
{LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified},
{LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional},
+ {LanguageCode::PT_BR, KeyboardLayout::Portuguese},
}};
-constexpr std::size_t pre4_0_0_max_entries = 15;
-constexpr std::size_t post4_0_0_max_entries = 17;
+constexpr std::size_t PRE_4_0_0_MAX_ENTRIES = 0xF;
+constexpr std::size_t POST_4_0_0_MAX_ENTRIES = 0x40;
constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625};
@@ -81,9 +83,10 @@ void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t num_la
rb.Push(static_cast<u32>(num_language_codes));
}
-void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_size) {
+void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_entries) {
const std::size_t requested_amount = ctx.GetWriteBufferSize() / sizeof(LanguageCode);
- const std::size_t copy_amount = std::min(requested_amount, max_size);
+ const std::size_t max_amount = std::min(requested_amount, max_entries);
+ const std::size_t copy_amount = std::min(available_language_codes.size(), max_amount);
const std::size_t copy_size = copy_amount * sizeof(LanguageCode);
ctx.WriteBuffer(available_language_codes.data(), copy_size);
@@ -118,7 +121,7 @@ LanguageCode GetLanguageCodeFromIndex(std::size_t index) {
void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
- GetAvailableLanguageCodesImpl(ctx, pre4_0_0_max_entries);
+ GetAvailableLanguageCodesImpl(ctx, PRE_4_0_0_MAX_ENTRIES);
}
void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
@@ -140,19 +143,19 @@ void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
void SET::GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
- GetAvailableLanguageCodesImpl(ctx, post4_0_0_max_entries);
+ GetAvailableLanguageCodesImpl(ctx, POST_4_0_0_MAX_ENTRIES);
}
void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
- PushResponseLanguageCode(ctx, pre4_0_0_max_entries);
+ PushResponseLanguageCode(ctx, PRE_4_0_0_MAX_ENTRIES);
}
void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
- PushResponseLanguageCode(ctx, post4_0_0_max_entries);
+ PushResponseLanguageCode(ctx, POST_4_0_0_MAX_ENTRIES);
}
void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h
index d5bd7828d..acabebeaa 100644
--- a/src/core/hle/service/set/set.h
+++ b/src/core/hle/service/set/set.h
@@ -31,6 +31,7 @@ enum class LanguageCode : u64 {
ES_419 = 0x00003931342D7365,
ZH_HANS = 0x00736E61482D687A,
ZH_HANT = 0x00746E61482D687A,
+ PT_BR = 0x00000052422D7470,
};
LanguageCode GetLanguageCodeFromIndex(std::size_t idx);
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/memory.cpp b/src/core/memory.cpp
index f285c6f63..51c4dea26 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -4,8 +4,6 @@
#include <algorithm>
#include <cstring>
-#include <optional>
-#include <utility>
#include "common/assert.h"
#include "common/atomic_ops.h"
@@ -14,12 +12,10 @@
#include "common/page_table.h"
#include "common/settings.h"
#include "common/swap.h"
-#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
-#include "core/hle/kernel/physical_memory.h"
#include "core/memory.h"
#include "video_core/gpu.h"
@@ -62,17 +58,7 @@ struct Memory::Impl {
}
}
- bool IsValidVirtualAddress(const Kernel::KProcess& process, const VAddr vaddr) const {
- const auto& page_table = process.PageTable().PageTableImpl();
- const auto [pointer, type] = page_table.pointers[vaddr >> PAGE_BITS].PointerType();
- return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory;
- }
-
- bool IsValidVirtualAddress(VAddr vaddr) const {
- return IsValidVirtualAddress(*system.CurrentProcess(), vaddr);
- }
-
- u8* GetPointerFromRasterizerCachedMemory(VAddr vaddr) const {
+ [[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(VAddr vaddr) const {
const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]};
if (!paddr) {
@@ -82,18 +68,6 @@ struct Memory::Impl {
return system.DeviceMemory().GetPointer(paddr) + vaddr;
}
- u8* GetPointer(const VAddr vaddr) const {
- const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
- if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
- return pointer + vaddr;
- }
- const auto type = Common::PageTable::PageInfo::ExtractType(raw_pointer);
- if (type == Common::PageType::RasterizerCachedMemory) {
- return GetPointerFromRasterizerCachedMemory(vaddr);
- }
- return nullptr;
- }
-
u8 Read8(const VAddr addr) {
return Read<u8>(addr);
}
@@ -179,7 +153,7 @@ struct Memory::Impl {
std::string string;
string.reserve(max_length);
for (std::size_t i = 0; i < max_length; ++i) {
- const char c = Read8(vaddr);
+ const char c = Read<s8>(vaddr);
if (c == '\0') {
break;
}
@@ -190,15 +164,14 @@ struct Memory::Impl {
return string;
}
- void ReadBlock(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer,
- const std::size_t size) {
+ void WalkBlock(const Kernel::KProcess& process, const VAddr addr, const std::size_t size,
+ auto on_unmapped, auto on_memory, auto on_rasterizer, auto increment) {
const auto& page_table = process.PageTable().PageTableImpl();
-
std::size_t remaining_size = size;
- std::size_t page_index = src_addr >> PAGE_BITS;
- std::size_t page_offset = src_addr & PAGE_MASK;
+ std::size_t page_index = addr >> PAGE_BITS;
+ std::size_t page_offset = addr & PAGE_MASK;
- while (remaining_size > 0) {
+ while (remaining_size) {
const std::size_t copy_amount =
std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
@@ -206,22 +179,18 @@ struct Memory::Impl {
const auto [pointer, type] = page_table.pointers[page_index].PointerType();
switch (type) {
case Common::PageType::Unmapped: {
- LOG_ERROR(HW_Memory,
- "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
- current_vaddr, src_addr, size);
- std::memset(dest_buffer, 0, copy_amount);
+ on_unmapped(copy_amount, current_vaddr);
break;
}
case Common::PageType::Memory: {
DEBUG_ASSERT(pointer);
- const u8* const src_ptr = pointer + page_offset + (page_index << PAGE_BITS);
- std::memcpy(dest_buffer, src_ptr, copy_amount);
+ u8* mem_ptr = pointer + page_offset + (page_index << PAGE_BITS);
+ on_memory(copy_amount, mem_ptr);
break;
}
case Common::PageType::RasterizerCachedMemory: {
- const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};
- system.GPU().FlushRegion(current_vaddr, copy_amount);
- std::memcpy(dest_buffer, host_ptr, copy_amount);
+ u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};
+ on_rasterizer(current_vaddr, copy_amount, host_ptr);
break;
}
default:
@@ -230,248 +199,122 @@ struct Memory::Impl {
page_index++;
page_offset = 0;
- dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
+ increment(copy_amount);
remaining_size -= copy_amount;
}
}
- void ReadBlockUnsafe(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer,
- const std::size_t size) {
- const auto& page_table = process.PageTable().PageTableImpl();
-
- std::size_t remaining_size = size;
- std::size_t page_index = src_addr >> PAGE_BITS;
- std::size_t page_offset = src_addr & PAGE_MASK;
-
- while (remaining_size > 0) {
- const std::size_t copy_amount =
- std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
- const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
-
- const auto [pointer, type] = page_table.pointers[page_index].PointerType();
- switch (type) {
- case Common::PageType::Unmapped: {
+ template <bool UNSAFE>
+ void ReadBlockImpl(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer,
+ const std::size_t size) {
+ WalkBlock(
+ process, src_addr, size,
+ [src_addr, size, &dest_buffer](const std::size_t copy_amount,
+ const VAddr current_vaddr) {
LOG_ERROR(HW_Memory,
"Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, src_addr, size);
std::memset(dest_buffer, 0, copy_amount);
- break;
- }
- case Common::PageType::Memory: {
- DEBUG_ASSERT(pointer);
- const u8* const src_ptr = pointer + page_offset + (page_index << PAGE_BITS);
+ },
+ [&dest_buffer](const std::size_t copy_amount, const u8* const src_ptr) {
std::memcpy(dest_buffer, src_ptr, copy_amount);
- break;
- }
- case Common::PageType::RasterizerCachedMemory: {
- const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};
+ },
+ [&system = system, &dest_buffer](const VAddr current_vaddr,
+ const std::size_t copy_amount,
+ const u8* const host_ptr) {
+ if constexpr (!UNSAFE) {
+ system.GPU().FlushRegion(current_vaddr, copy_amount);
+ }
std::memcpy(dest_buffer, host_ptr, copy_amount);
- break;
- }
- default:
- UNREACHABLE();
- }
-
- page_index++;
- page_offset = 0;
- dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
- remaining_size -= copy_amount;
- }
+ },
+ [&dest_buffer](const std::size_t copy_amount) {
+ dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
+ });
}
void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
- ReadBlock(*system.CurrentProcess(), src_addr, dest_buffer, size);
+ ReadBlockImpl<false>(*system.CurrentProcess(), src_addr, dest_buffer, size);
}
void ReadBlockUnsafe(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
- ReadBlockUnsafe(*system.CurrentProcess(), src_addr, dest_buffer, size);
+ ReadBlockImpl<true>(*system.CurrentProcess(), src_addr, dest_buffer, size);
}
- void WriteBlock(const Kernel::KProcess& process, const VAddr dest_addr, const void* src_buffer,
- const std::size_t size) {
- const auto& page_table = process.PageTable().PageTableImpl();
- std::size_t remaining_size = size;
- std::size_t page_index = dest_addr >> PAGE_BITS;
- std::size_t page_offset = dest_addr & PAGE_MASK;
-
- while (remaining_size > 0) {
- const std::size_t copy_amount =
- std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
- const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
-
- const auto [pointer, type] = page_table.pointers[page_index].PointerType();
- switch (type) {
- case Common::PageType::Unmapped: {
+ template <bool UNSAFE>
+ void WriteBlockImpl(const Kernel::KProcess& process, const VAddr dest_addr,
+ const void* src_buffer, const std::size_t size) {
+ WalkBlock(
+ process, dest_addr, size,
+ [dest_addr, size](const std::size_t copy_amount, const VAddr current_vaddr) {
LOG_ERROR(HW_Memory,
"Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, dest_addr, size);
- break;
- }
- case Common::PageType::Memory: {
- DEBUG_ASSERT(pointer);
- u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS);
+ },
+ [&src_buffer](const std::size_t copy_amount, u8* const dest_ptr) {
std::memcpy(dest_ptr, src_buffer, copy_amount);
- break;
- }
- case Common::PageType::RasterizerCachedMemory: {
- u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};
- system.GPU().InvalidateRegion(current_vaddr, copy_amount);
- std::memcpy(host_ptr, src_buffer, copy_amount);
- break;
- }
- default:
- UNREACHABLE();
- }
-
- page_index++;
- page_offset = 0;
- src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
- remaining_size -= copy_amount;
- }
- }
-
- void WriteBlockUnsafe(const Kernel::KProcess& process, const VAddr dest_addr,
- const void* src_buffer, const std::size_t size) {
- const auto& page_table = process.PageTable().PageTableImpl();
- std::size_t remaining_size = size;
- std::size_t page_index = dest_addr >> PAGE_BITS;
- std::size_t page_offset = dest_addr & PAGE_MASK;
-
- while (remaining_size > 0) {
- const std::size_t copy_amount =
- std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
- const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
-
- const auto [pointer, type] = page_table.pointers[page_index].PointerType();
- switch (type) {
- case Common::PageType::Unmapped: {
- LOG_ERROR(HW_Memory,
- "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
- current_vaddr, dest_addr, size);
- break;
- }
- case Common::PageType::Memory: {
- DEBUG_ASSERT(pointer);
- u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS);
- std::memcpy(dest_ptr, src_buffer, copy_amount);
- break;
- }
- case Common::PageType::RasterizerCachedMemory: {
- u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};
+ },
+ [&system = system, &src_buffer](const VAddr current_vaddr,
+ const std::size_t copy_amount, u8* const host_ptr) {
+ if constexpr (!UNSAFE) {
+ system.GPU().InvalidateRegion(current_vaddr, copy_amount);
+ }
std::memcpy(host_ptr, src_buffer, copy_amount);
- break;
- }
- default:
- UNREACHABLE();
- }
-
- page_index++;
- page_offset = 0;
- src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
- remaining_size -= copy_amount;
- }
+ },
+ [&src_buffer](const std::size_t copy_amount) {
+ src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
+ });
}
void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) {
- WriteBlock(*system.CurrentProcess(), dest_addr, src_buffer, size);
+ WriteBlockImpl<false>(*system.CurrentProcess(), dest_addr, src_buffer, size);
}
void WriteBlockUnsafe(const VAddr dest_addr, const void* src_buffer, const std::size_t size) {
- WriteBlockUnsafe(*system.CurrentProcess(), dest_addr, src_buffer, size);
+ WriteBlockImpl<true>(*system.CurrentProcess(), dest_addr, src_buffer, size);
}
void ZeroBlock(const Kernel::KProcess& process, const VAddr dest_addr, const std::size_t size) {
- const auto& page_table = process.PageTable().PageTableImpl();
- std::size_t remaining_size = size;
- std::size_t page_index = dest_addr >> PAGE_BITS;
- std::size_t page_offset = dest_addr & PAGE_MASK;
-
- while (remaining_size > 0) {
- const std::size_t copy_amount =
- std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
- const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
-
- const auto [pointer, type] = page_table.pointers[page_index].PointerType();
- switch (type) {
- case Common::PageType::Unmapped: {
+ WalkBlock(
+ process, dest_addr, size,
+ [dest_addr, size](const std::size_t copy_amount, const VAddr current_vaddr) {
LOG_ERROR(HW_Memory,
"Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, dest_addr, size);
- break;
- }
- case Common::PageType::Memory: {
- DEBUG_ASSERT(pointer);
- u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS);
+ },
+ [](const std::size_t copy_amount, u8* const dest_ptr) {
std::memset(dest_ptr, 0, copy_amount);
- break;
- }
- case Common::PageType::RasterizerCachedMemory: {
- u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};
+ },
+ [&system = system](const VAddr current_vaddr, const std::size_t copy_amount,
+ u8* const host_ptr) {
system.GPU().InvalidateRegion(current_vaddr, copy_amount);
std::memset(host_ptr, 0, copy_amount);
- break;
- }
- default:
- UNREACHABLE();
- }
-
- page_index++;
- page_offset = 0;
- remaining_size -= copy_amount;
- }
- }
-
- void ZeroBlock(const VAddr dest_addr, const std::size_t size) {
- ZeroBlock(*system.CurrentProcess(), dest_addr, size);
+ },
+ [](const std::size_t copy_amount) {});
}
void CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr src_addr,
const std::size_t size) {
- const auto& page_table = process.PageTable().PageTableImpl();
- std::size_t remaining_size = size;
- std::size_t page_index = src_addr >> PAGE_BITS;
- std::size_t page_offset = src_addr & PAGE_MASK;
-
- while (remaining_size > 0) {
- const std::size_t copy_amount =
- std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
- const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
-
- const auto [pointer, type] = page_table.pointers[page_index].PointerType();
- switch (type) {
- case Common::PageType::Unmapped: {
+ WalkBlock(
+ process, dest_addr, size,
+ [this, &process, &dest_addr, &src_addr, size](const std::size_t copy_amount,
+ const VAddr current_vaddr) {
LOG_ERROR(HW_Memory,
"Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, src_addr, size);
ZeroBlock(process, dest_addr, copy_amount);
- break;
- }
- case Common::PageType::Memory: {
- DEBUG_ASSERT(pointer);
- const u8* src_ptr = pointer + page_offset + (page_index << PAGE_BITS);
- WriteBlock(process, dest_addr, src_ptr, copy_amount);
- break;
- }
- case Common::PageType::RasterizerCachedMemory: {
- const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};
+ },
+ [this, &process, &dest_addr](const std::size_t copy_amount, const u8* const src_ptr) {
+ WriteBlockImpl<false>(process, dest_addr, src_ptr, copy_amount);
+ },
+ [this, &system = system, &process, &dest_addr](
+ const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) {
system.GPU().FlushRegion(current_vaddr, copy_amount);
- WriteBlock(process, dest_addr, host_ptr, copy_amount);
- break;
- }
- default:
- UNREACHABLE();
- }
-
- page_index++;
- page_offset = 0;
- dest_addr += static_cast<VAddr>(copy_amount);
- src_addr += static_cast<VAddr>(copy_amount);
- remaining_size -= copy_amount;
- }
- }
-
- void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) {
- return CopyBlock(*system.CurrentProcess(), dest_addr, src_addr, size);
+ WriteBlockImpl<false>(process, dest_addr, host_ptr, copy_amount);
+ },
+ [&dest_addr, &src_addr](const std::size_t copy_amount) {
+ dest_addr += static_cast<VAddr>(copy_amount);
+ src_addr += static_cast<VAddr>(copy_amount);
+ });
}
void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
@@ -514,7 +357,7 @@ struct Memory::Impl {
} else {
// Switch page type to uncached if now uncached
switch (page_type) {
- case Common::PageType::Unmapped:
+ case Common::PageType::Unmapped: // NOLINT(bugprone-branch-clone)
// It is not necessary for a process to have this region mapped into its address
// space, for example, a system module need not have a VRAM mapping.
break;
@@ -597,52 +440,68 @@ struct Memory::Impl {
}
}
- /**
- * Reads a particular data type out of memory at the given virtual address.
- *
- * @param vaddr The virtual address to read the data type from.
- *
- * @tparam T The data type to read out of memory. This type *must* be
- * trivially copyable, otherwise the behavior of this function
- * is undefined.
- *
- * @returns The instance of T read from the specified virtual address.
- */
- template <typename T>
- T Read(VAddr vaddr) {
+ [[nodiscard]] u8* GetPointerImpl(VAddr vaddr, auto on_unmapped, auto on_rasterizer) const {
// AARCH64 masks the upper 16 bit of all memory accesses
vaddr &= 0xffffffffffffLL;
if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) {
- LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
- return 0;
+ on_unmapped();
+ return nullptr;
}
// Avoid adding any extra logic to this fast-path block
const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
- if (const u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
- T value;
- std::memcpy(&value, &pointer[vaddr], sizeof(T));
- return value;
+ if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
+ return &pointer[vaddr];
}
switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
case Common::PageType::Unmapped:
- LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
- return 0;
+ on_unmapped();
+ return nullptr;
case Common::PageType::Memory:
- ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
- break;
+ ASSERT_MSG(false, "Mapped memory page without a pointer @ 0x{:016X}", vaddr);
+ return nullptr;
case Common::PageType::RasterizerCachedMemory: {
- const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
- system.GPU().FlushRegion(vaddr, sizeof(T));
- T value;
- std::memcpy(&value, host_ptr, sizeof(T));
- return value;
+ u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
+ on_rasterizer();
+ return host_ptr;
}
default:
UNREACHABLE();
}
- return {};
+ return nullptr;
+ }
+
+ [[nodiscard]] u8* GetPointer(const VAddr vaddr) const {
+ return GetPointerImpl(
+ vaddr, [vaddr]() { LOG_ERROR(HW_Memory, "Unmapped GetPointer @ 0x{:016X}", vaddr); },
+ []() {});
+ }
+
+ /**
+ * Reads a particular data type out of memory at the given virtual address.
+ *
+ * @param vaddr The virtual address to read the data type from.
+ *
+ * @tparam T The data type to read out of memory. This type *must* be
+ * trivially copyable, otherwise the behavior of this function
+ * is undefined.
+ *
+ * @returns The instance of T read from the specified virtual address.
+ */
+ template <typename T>
+ T Read(VAddr vaddr) {
+ T result = 0;
+ const u8* const ptr = GetPointerImpl(
+ vaddr,
+ [vaddr]() {
+ LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, vaddr);
+ },
+ [&system = system, vaddr]() { system.GPU().FlushRegion(vaddr, sizeof(T)); });
+ if (ptr) {
+ std::memcpy(&result, ptr, sizeof(T));
+ }
+ return result;
}
/**
@@ -656,110 +515,46 @@ struct Memory::Impl {
*/
template <typename T>
void Write(VAddr vaddr, const T data) {
- // AARCH64 masks the upper 16 bit of all memory accesses
- vaddr &= 0xffffffffffffLL;
-
- if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) {
- LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
- static_cast<u32>(data), vaddr);
- return;
- }
-
- // Avoid adding any extra logic to this fast-path block
- const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
- if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
- std::memcpy(&pointer[vaddr], &data, sizeof(T));
- return;
- }
- switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
- case Common::PageType::Unmapped:
- LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
- static_cast<u32>(data), vaddr);
- return;
- case Common::PageType::Memory:
- ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
- break;
- case Common::PageType::RasterizerCachedMemory: {
- u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
- system.GPU().InvalidateRegion(vaddr, sizeof(T));
- std::memcpy(host_ptr, &data, sizeof(T));
- break;
- }
- default:
- UNREACHABLE();
+ u8* const ptr = GetPointerImpl(
+ vaddr,
+ [vaddr, data]() {
+ LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8,
+ vaddr, static_cast<u64>(data));
+ },
+ [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); });
+ if (ptr) {
+ std::memcpy(ptr, &data, sizeof(T));
}
}
template <typename T>
bool WriteExclusive(VAddr vaddr, const T data, const T expected) {
- // AARCH64 masks the upper 16 bit of all memory accesses
- vaddr &= 0xffffffffffffLL;
-
- if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) {
- LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
- static_cast<u32>(data), vaddr);
- return true;
- }
-
- const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
- if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
- // NOTE: Avoid adding any extra logic to this fast-path block
- const auto volatile_pointer = reinterpret_cast<volatile T*>(&pointer[vaddr]);
+ u8* const ptr = GetPointerImpl(
+ vaddr,
+ [vaddr, data]() {
+ LOG_ERROR(HW_Memory, "Unmapped WriteExclusive{} @ 0x{:016X} = 0x{:016X}",
+ sizeof(T) * 8, vaddr, static_cast<u64>(data));
+ },
+ [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); });
+ if (ptr) {
+ const auto volatile_pointer = reinterpret_cast<volatile T*>(ptr);
return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
}
- switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
- case Common::PageType::Unmapped:
- LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
- static_cast<u32>(data), vaddr);
- return true;
- case Common::PageType::Memory:
- ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
- break;
- case Common::PageType::RasterizerCachedMemory: {
- u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
- system.GPU().InvalidateRegion(vaddr, sizeof(T));
- auto* pointer = reinterpret_cast<volatile T*>(&host_ptr);
- return Common::AtomicCompareAndSwap(pointer, data, expected);
- }
- default:
- UNREACHABLE();
- }
return true;
}
bool WriteExclusive128(VAddr vaddr, const u128 data, const u128 expected) {
- // AARCH64 masks the upper 16 bit of all memory accesses
- vaddr &= 0xffffffffffffLL;
-
- if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) {
- LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
- static_cast<u32>(data[0]), vaddr);
- return true;
- }
-
- const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
- if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
- // NOTE: Avoid adding any extra logic to this fast-path block
- const auto volatile_pointer = reinterpret_cast<volatile u64*>(&pointer[vaddr]);
+ u8* const ptr = GetPointerImpl(
+ vaddr,
+ [vaddr, data]() {
+ LOG_ERROR(HW_Memory, "Unmapped WriteExclusive128 @ 0x{:016X} = 0x{:016X}{:016X}",
+ vaddr, static_cast<u64>(data[1]), static_cast<u64>(data[0]));
+ },
+ [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(u128)); });
+ if (ptr) {
+ const auto volatile_pointer = reinterpret_cast<volatile u64*>(ptr);
return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
}
- switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
- case Common::PageType::Unmapped:
- LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}{:016X}", sizeof(data) * 8,
- static_cast<u64>(data[1]), static_cast<u64>(data[0]), vaddr);
- return true;
- case Common::PageType::Memory:
- ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
- break;
- case Common::PageType::RasterizerCachedMemory: {
- u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
- system.GPU().InvalidateRegion(vaddr, sizeof(u128));
- auto* pointer = reinterpret_cast<volatile u64*>(&host_ptr);
- return Common::AtomicCompareAndSwap(pointer, data, expected);
- }
- default:
- UNREACHABLE();
- }
return true;
}
@@ -789,12 +584,11 @@ void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
impl->UnmapRegion(page_table, base, size);
}
-bool Memory::IsValidVirtualAddress(const Kernel::KProcess& process, const VAddr vaddr) const {
- return impl->IsValidVirtualAddress(process, vaddr);
-}
-
bool Memory::IsValidVirtualAddress(const VAddr vaddr) const {
- return impl->IsValidVirtualAddress(vaddr);
+ const Kernel::KProcess& process = *system.CurrentProcess();
+ const auto& page_table = process.PageTable().PageTableImpl();
+ const auto [pointer, type] = page_table.pointers[vaddr >> PAGE_BITS].PointerType();
+ return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory;
}
u8* Memory::GetPointer(VAddr vaddr) {
@@ -863,64 +657,38 @@ std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) {
void Memory::ReadBlock(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer,
const std::size_t size) {
- impl->ReadBlock(process, src_addr, dest_buffer, size);
+ impl->ReadBlockImpl<false>(process, src_addr, dest_buffer, size);
}
void Memory::ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
impl->ReadBlock(src_addr, dest_buffer, size);
}
-void Memory::ReadBlockUnsafe(const Kernel::KProcess& process, const VAddr src_addr,
- void* dest_buffer, const std::size_t size) {
- impl->ReadBlockUnsafe(process, src_addr, dest_buffer, size);
-}
-
void Memory::ReadBlockUnsafe(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
impl->ReadBlockUnsafe(src_addr, dest_buffer, size);
}
void Memory::WriteBlock(const Kernel::KProcess& process, VAddr dest_addr, const void* src_buffer,
std::size_t size) {
- impl->WriteBlock(process, dest_addr, src_buffer, size);
+ impl->WriteBlockImpl<false>(process, dest_addr, src_buffer, size);
}
void Memory::WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) {
impl->WriteBlock(dest_addr, src_buffer, size);
}
-void Memory::WriteBlockUnsafe(const Kernel::KProcess& process, VAddr dest_addr,
- const void* src_buffer, std::size_t size) {
- impl->WriteBlockUnsafe(process, dest_addr, src_buffer, size);
-}
-
void Memory::WriteBlockUnsafe(const VAddr dest_addr, const void* src_buffer,
const std::size_t size) {
impl->WriteBlockUnsafe(dest_addr, src_buffer, size);
}
-void Memory::ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) {
- impl->ZeroBlock(process, dest_addr, size);
-}
-
-void Memory::ZeroBlock(VAddr dest_addr, std::size_t size) {
- impl->ZeroBlock(dest_addr, size);
-}
-
void Memory::CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr src_addr,
const std::size_t size) {
impl->CopyBlock(process, dest_addr, src_addr, size);
}
-void Memory::CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) {
- impl->CopyBlock(dest_addr, src_addr, size);
-}
-
void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
impl->RasterizerMarkRegionCached(vaddr, size, cached);
}
-bool IsKernelVirtualAddress(const VAddr vaddr) {
- return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END;
-}
-
} // namespace Core::Memory
diff --git a/src/core/memory.h b/src/core/memory.h
index c91eeced9..b5721b740 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -39,11 +39,6 @@ enum : VAddr {
/// Application stack
DEFAULT_STACK_SIZE = 0x100000,
-
- /// Kernel Virtual Address Range
- KERNEL_REGION_VADDR = 0xFFFFFF8000000000,
- KERNEL_REGION_SIZE = 0x7FFFE00000,
- KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE,
};
/// Central class that handles all memory operations and state.
@@ -56,7 +51,7 @@ public:
Memory& operator=(const Memory&) = delete;
Memory(Memory&&) = default;
- Memory& operator=(Memory&&) = default;
+ Memory& operator=(Memory&&) = delete;
/**
* Resets the state of the Memory system.
@@ -92,24 +87,13 @@ public:
/**
* Checks whether or not the supplied address is a valid virtual
- * address for the given process.
- *
- * @param process The emulated process to check the address against.
- * @param vaddr The virtual address to check the validity of.
- *
- * @returns True if the given virtual address is valid, false otherwise.
- */
- bool IsValidVirtualAddress(const Kernel::KProcess& process, VAddr vaddr) const;
-
- /**
- * Checks whether or not the supplied address is a valid virtual
* address for the current process.
*
* @param vaddr The virtual address to check the validity of.
*
* @returns True if the given virtual address is valid, false otherwise.
*/
- bool IsValidVirtualAddress(VAddr vaddr) const;
+ [[nodiscard]] bool IsValidVirtualAddress(VAddr vaddr) const;
/**
* Gets a pointer to the given address.
@@ -134,7 +118,7 @@ public:
* @returns The pointer to the given address, if the address is valid.
* If the address is not valid, nullptr will be returned.
*/
- const u8* GetPointer(VAddr vaddr) const;
+ [[nodiscard]] const u8* GetPointer(VAddr vaddr) const;
template <typename T>
const T* GetPointer(VAddr vaddr) const {
@@ -328,27 +312,6 @@ public:
std::size_t size);
/**
- * Reads a contiguous block of bytes from a specified process' address space.
- * This unsafe version does not trigger GPU flushing.
- *
- * @param process The process to read the data from.
- * @param src_addr The virtual address to begin reading from.
- * @param dest_buffer The buffer to place the read bytes into.
- * @param size The amount of data to read, in bytes.
- *
- * @note If a size of 0 is specified, then this function reads nothing and
- * no attempts to access memory are made at all.
- *
- * @pre dest_buffer must be at least size bytes in length, otherwise a
- * buffer overrun will occur.
- *
- * @post The range [dest_buffer, size) contains the read bytes from the
- * process' address space.
- */
- void ReadBlockUnsafe(const Kernel::KProcess& process, VAddr src_addr, void* dest_buffer,
- std::size_t size);
-
- /**
* Reads a contiguous block of bytes from the current process' address space.
*
* @param src_addr The virtual address to begin reading from.
@@ -409,26 +372,6 @@ public:
std::size_t size);
/**
- * Writes a range of bytes into a given process' address space at the specified
- * virtual address.
- * This unsafe version does not invalidate GPU Memory.
- *
- * @param process The process to write data into the address space of.
- * @param dest_addr The destination virtual address to begin writing the data at.
- * @param src_buffer The data to write into the process' address space.
- * @param size The size of the data to write, in bytes.
- *
- * @post The address range [dest_addr, size) in the process' address space
- * contains the data that was within src_buffer.
- *
- * @post If an attempt is made to write into an unmapped region of memory, the writes
- * will be ignored and an error will be logged.
- *
- */
- void WriteBlockUnsafe(const Kernel::KProcess& process, VAddr dest_addr, const void* src_buffer,
- std::size_t size);
-
- /**
* Writes a range of bytes into the current process' address space at the specified
* virtual address.
*
@@ -468,29 +411,6 @@ public:
void WriteBlockUnsafe(VAddr dest_addr, const void* src_buffer, std::size_t size);
/**
- * Fills the specified address range within a process' address space with zeroes.
- *
- * @param process The process that will have a portion of its memory zeroed out.
- * @param dest_addr The starting virtual address of the range to zero out.
- * @param size The size of the address range to zero out, in bytes.
- *
- * @post The range [dest_addr, size) within the process' address space is
- * filled with zeroes.
- */
- void ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
-
- /**
- * Fills the specified address range within the current process' address space with zeroes.
- *
- * @param dest_addr The starting virtual address of the range to zero out.
- * @param size The size of the address range to zero out, in bytes.
- *
- * @post The range [dest_addr, size) within the current process' address space is
- * filled with zeroes.
- */
- void ZeroBlock(VAddr dest_addr, std::size_t size);
-
- /**
* Copies data within a process' address space to another location within the
* same address space.
*
@@ -506,19 +426,6 @@ public:
std::size_t size);
/**
- * Copies data within the current process' address space to another location within the
- * same address space.
- *
- * @param dest_addr The destination virtual address to begin copying the data into.
- * @param src_addr The source virtual address to begin copying the data from.
- * @param size The size of the data to copy, in bytes.
- *
- * @post The range [dest_addr, size) within the current process' address space
- * contains the same data within the range [src_addr, size).
- */
- void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size);
-
- /**
* Marks each page within the specified address range as cached or uncached.
*
* @param vaddr The virtual address indicating the start of the address range.
@@ -535,7 +442,4 @@ private:
std::unique_ptr<Impl> impl;
};
-/// Determines if the given VAddr is a kernel address
-bool IsKernelVirtualAddress(VAddr vaddr);
-
} // namespace Core::Memory
diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp
index 526bfa110..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) {
@@ -570,7 +568,7 @@ std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message,
ASSERT(flags == 0);
const sockaddr* to = nullptr;
- const int tolen = addr ? 0 : sizeof(sockaddr);
+ const int tolen = addr ? sizeof(sockaddr) : 0;
sockaddr host_addr_in;
if (addr) {
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