aboutsummaryrefslogtreecommitdiff
path: root/src/core/hle/kernel
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-29 03:39:52 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-29 03:39:52 -0400
commite17fd9cef116a088f080ca502951e500864a58b6 (patch)
treee7ff65f614622515d6a2e4263521909eea89a5d8 /src/core/hle/kernel
parentea6c1193fbfa95e586bb6d129dc684163a12cb3e (diff)
parent80c79333fe6fd28a31b65b997f1cbf2987711c91 (diff)
Merge Switch 2 work from local Eden (nx2 at b0dd3fe)
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp2
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/k_system_control.h4
-rw-r--r--src/core/hle/kernel/global_scheduler_context.cpp5
-rw-r--r--src/core/hle/kernel/k_memory_block.h4
-rw-r--r--src/core/hle/kernel/k_page_table_base.cpp3
-rw-r--r--src/core/hle/kernel/k_process.cpp7
-rw-r--r--src/core/hle/kernel/k_spin_lock.cpp48
-rw-r--r--src/core/hle/kernel/k_spin_lock.h10
-rw-r--r--src/core/hle/kernel/kernel.cpp2
-rw-r--r--src/core/hle/kernel/physical_core.cpp19
-rw-r--r--src/core/hle/kernel/physical_core.h3
-rw-r--r--src/core/hle/kernel/svc.cpp53
-rw-r--r--src/core/hle/kernel/svc/svc_info.cpp47
-rw-r--r--src/core/hle/kernel/svc/svc_physical_memory.cpp39
-rw-r--r--src/core/hle/kernel/svc/svc_thread.cpp17
-rw-r--r--src/core/hle/kernel/svc_types.h12
16 files changed, 258 insertions, 17 deletions
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
index 3343d1d282..ba4737e25f 100644
--- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
+++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
@@ -94,7 +94,7 @@ size_t KSystemControl::Init::GetIntendedMemorySize() {
case Smc::MemorySize_10GB:
return 10_GiB;
case Smc::MemorySize_12GB:
- return 12_GiB;
+ return 12_GiB + KSystemControl::Nx2GraphicsServerMemorySize;
}
}
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h
index 41a25ba1c8..cc5572bde8 100644
--- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h
+++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h
@@ -21,6 +21,10 @@ public:
// This can be overridden as needed.
static constexpr size_t SecureAppletMemorySize = 4 * 1024 * 1024; // 4_MB
+ // Extra DRAM on the 12 GB layout for memory the Switch 2 graphics server keeps itself (nx2:gpu
+ // 214 GPU-only regions). It sits in the non-secure system pool, outside the application pool.
+ static constexpr size_t Nx2GraphicsServerMemorySize = 8ULL * 1024 * 1024 * 1024; // 8_GiB
+
public:
class Init {
public:
diff --git a/src/core/hle/kernel/global_scheduler_context.cpp b/src/core/hle/kernel/global_scheduler_context.cpp
index 6d1cd030e0..98d14c8549 100644
--- a/src/core/hle/kernel/global_scheduler_context.cpp
+++ b/src/core/hle/kernel/global_scheduler_context.cpp
@@ -40,12 +40,15 @@ void GlobalSchedulerContext::RemoveThread(KThread* thread) noexcept {
/// @note This operation happens every 10ms.
void GlobalSchedulerContext::PreemptThreads(KernelCore& kernel) noexcept {
// The priority levels at which the global scheduler preempts threads every 10 ms. They are
- // ordered from Core 0 to Core 3.
+ // ordered from Core 0 to Core 5; core 3 is shared with the system, 4 and 5 are application
+ // cores like 0..2.
static constexpr std::array<u32, Core::Hardware::NUM_CPU_CORES> per_core{
59,
59,
59,
63,
+ 59,
+ 59,
};
ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel));
for (u32 core_id = 0; core_id < per_core.size(); core_id++)
diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h
index f6543bdb3a..2abca31d87 100644
--- a/src/core/hle/kernel/k_memory_block.h
+++ b/src/core/hle/kernel/k_memory_block.h
@@ -195,7 +195,9 @@ enum class KMemoryAttribute : u8 {
Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached),
PermissionLocked = static_cast<u8>(Svc::MemoryAttribute::PermissionLocked),
- SetMask = Uncached | PermissionLocked,
+ GpuAccessible = static_cast<u8>(Svc::MemoryAttribute::GpuAccessible),
+
+ SetMask = Uncached | PermissionLocked | GpuAccessible,
};
DECLARE_ENUM_FLAG_OPERATORS(KMemoryAttribute);
diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp
index 2a0a7d49d5..fd5d2c2dce 100644
--- a/src/core/hle/kernel/k_page_table_base.cpp
+++ b/src/core/hle/kernel/k_page_table_base.cpp
@@ -2102,7 +2102,8 @@ Result KPageTableBase::SetMemoryAttribute(KProcessAddress addr, size_t size, KMe
constexpr KMemoryAttribute AttributeTestMask =
~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared);
const KMemoryState state_test_mask =
- (True(mask & KMemoryAttribute::Uncached) ? KMemoryState::FlagCanChangeAttribute
+ (True(mask & (KMemoryAttribute::Uncached | KMemoryAttribute::GpuAccessible))
+ ? KMemoryState::FlagCanChangeAttribute
: KMemoryState::None) |
(True(mask & KMemoryAttribute::PermissionLocked) ? KMemoryState::FlagCanPermissionLock
: KMemoryState::None);
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 27d1b1bfc4..4aac818b77 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -1261,6 +1261,13 @@ Result KProcess::LoadFromMetadata(KernelCore& kernel, const FileSys::ProgramMeta
// Assign remaining properties.
m_ideal_core_id = metadata.GetMainThreadCore();
+ // Switch 2 applications get more memory than a 64-bit heap region holds (nninitStartup sizes
+ // its heap as total memory minus ~6.3 GiB), so count the alias region as well, bounded by the
+ // pool through the resource limit.
+ if (metadata.IsSwitch2()) {
+ m_max_process_memory = m_page_table.GetHeapRegionSize() + m_page_table.GetAliasRegionSize();
+ }
+
// Set up emulation context.
this->InitializeInterfaces(kernel);
diff --git a/src/core/hle/kernel/k_spin_lock.cpp b/src/core/hle/kernel/k_spin_lock.cpp
index 8525320374..107d6f1aa3 100644
--- a/src/core/hle/kernel/k_spin_lock.cpp
+++ b/src/core/hle/kernel/k_spin_lock.cpp
@@ -3,18 +3,60 @@
#include "core/hle/kernel/k_spin_lock.h"
+#if defined(_M_X64) || defined(_M_IX86) || defined(__x86_64__) || defined(__i386__)
+#include <immintrin.h>
+#endif
+
namespace Kernel {
+namespace {
+
+// Spins of about 30-140 cycles each before a contended Lock sleeps (a few microseconds).
+constexpr int SpinCount = 128;
+
+void Pause() {
+#if defined(_M_X64) || defined(_M_IX86) || defined(__x86_64__) || defined(__i386__)
+ _mm_pause();
+#elif defined(__aarch64__)
+ asm volatile("yield");
+#endif
+}
+
+} // Anonymous namespace
+
void KSpinLock::Lock() {
- m_lock.lock();
+ u32 expected = 0;
+ if (m_state.compare_exchange_strong(expected, 1, std::memory_order_acquire,
+ std::memory_order_relaxed)) {
+ return;
+ }
+ for (int spin = 0; spin < SpinCount; ++spin) {
+ Pause();
+ if (m_state.load(std::memory_order_relaxed) == 0) {
+ expected = 0;
+ if (m_state.compare_exchange_weak(expected, 1, std::memory_order_acquire,
+ std::memory_order_relaxed)) {
+ return;
+ }
+ }
+ }
+ // Mark the lock contended and sleep until it is released; taken this way it stays marked,
+ // so its Unlock wakes the next sleeper.
+ while (m_state.exchange(2, std::memory_order_acquire) != 0) {
+ m_state.wait(2, std::memory_order_relaxed);
+ }
}
void KSpinLock::Unlock() {
- m_lock.unlock();
+ if (m_state.exchange(0, std::memory_order_release) == 2) {
+ m_state.notify_one();
+ }
}
bool KSpinLock::TryLock() {
- return m_lock.try_lock();
+ u32 expected = 0;
+ return m_state.compare_exchange_strong(expected, 1, std::memory_order_acquire,
+ std::memory_order_relaxed);
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_spin_lock.h b/src/core/hle/kernel/k_spin_lock.h
index 094a1e6be0..f7bc8b4d05 100644
--- a/src/core/hle/kernel/k_spin_lock.h
+++ b/src/core/hle/kernel/k_spin_lock.h
@@ -3,9 +3,11 @@
#pragma once
-#include <mutex>
+#include <atomic>
+#include <mutex> // Kernel headers that use std::mutex get it through this one
#include "common/common_funcs.h"
+#include "common/common_types.h"
#include "core/hle/kernel/k_scoped_lock.h"
namespace Kernel {
@@ -22,7 +24,11 @@ public:
bool TryLock();
private:
- std::mutex m_lock;
+ // 0 free, 1 held, 2 held with (possible) sleepers. The scheduler lock is taken by every
+ // core thousands of times a frame and held for microseconds: a contended Lock spins briefly
+ // before sleeping, instead of putting the host thread to sleep at once and paying its
+ // wake-up latency (std::mutex did).
+ std::atomic<u32> m_state{0};
};
// TODO(bunnei): Alias for now, in case we want to implement these accurately in the future.
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 4f1d849209..b437dd4b4e 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -171,7 +171,7 @@ struct KernelCore::Impl {
schedulers[core_id].reset();
}
- // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
+ // Next host thead ID to use, IDs below NUM_CPU_CORES represent core threads, the rest others
next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
// Close kernel objects that were not freed on shutdown
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
index c5236c64e3..c3ae7629aa 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -4,6 +4,9 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include <chrono>
+#include <cstdlib>
+#include <thread>
#include "common/scope_exit.h"
#include "common/settings.h"
#include "core/core.h"
@@ -222,8 +225,22 @@ void PhysicalCore::LogBacktrace(KernelCore& kernel) {
}
void PhysicalCore::Idle() {
+ // Guest job systems hand work between cores many times per frame; blocking at once adds the
+ // host's thread wake-up latency to every handoff. EDEN_NX2_IDLE_SPIN_US=<n>: poll for that
+ // many microseconds before blocking.
+ static const auto spin = std::chrono::microseconds{[] {
+ const char* text = std::getenv("EDEN_NX2_IDLE_SPIN_US");
+ return text ? std::strtoll(text, nullptr, 10) : 0LL;
+ }()};
+ if (spin.count() > 0) {
+ const auto deadline = std::chrono::steady_clock::now() + spin;
+ while (!m_is_interrupted.load(std::memory_order_acquire) &&
+ std::chrono::steady_clock::now() < deadline) {
+ std::this_thread::yield();
+ }
+ }
std::unique_lock lk{m_guard};
- m_on_interrupt.wait(lk, [this] { return m_is_interrupted; });
+ m_on_interrupt.wait(lk, [this] { return m_is_interrupted.load(); });
}
bool PhysicalCore::IsInterrupted() const {
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h
index b92201204a..20371d6717 100644
--- a/src/core/hle/kernel/physical_core.h
+++ b/src/core/hle/kernel/physical_core.h
@@ -6,6 +6,7 @@
#pragma once
+#include <atomic>
#include <condition_variable>
#include <cstddef>
#include <memory>
@@ -71,7 +72,7 @@ private:
std::condition_variable m_on_interrupt;
Core::ArmInterface* m_arm_interface{};
KThread* m_current_thread{};
- bool m_is_interrupted{};
+ std::atomic<bool> m_is_interrupted{};
bool m_is_single_core{};
};
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 3542d5a3b6..53d964ecbc 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -6,6 +6,8 @@
// This file is automatically generated using svc_generator.py.
// DO NOT MODIFY IT MANUALLY
+#include <cstdlib>
+#include <cstring>
#include <type_traits>
#include "core/arm/arm_interface.h"
@@ -2505,11 +2507,60 @@ void Call(Core::System& system, u32 imm) {
LOG_TRACE(Kernel_SVC, "{} [0]={:#x} [1]={:#x} [2]={:#x} [3]={:#x} [4]={:#x} [5]={:#x} [6]={:#x}",
imm, GetArg64(args, 0), GetArg64(args, 1), GetArg64(args, 2),
GetArg64(args, 3), GetArg64(args, 4), GetArg64(args, 5), GetArg64(args, 6));
- if (process.Is64Bit()) {
+ // Set EDEN_NX2_SVC_TRACE=1 to log every SVC with its arguments and results, or
+ // EDEN_NX2_SVC_TRACE=<first>:<last> for presents first..last only (a frame-time profile).
+ static const std::pair<u64, u64> trace_range = [] {
+ const char* text = std::getenv("EDEN_NX2_SVC_TRACE");
+ if (text == nullptr) return std::pair<u64, u64>{1, 0};
+ const char* colon = std::strchr(text, ':');
+ if (colon == nullptr) return std::pair<u64, u64>{0, ~u64{}};
+ return std::pair<u64, u64>{std::strtoull(text, nullptr, 10), std::strtoull(colon + 1, nullptr, 10)};
+ }();
+ const u64 presents = Core::g_nx2_present_count.load(std::memory_order_relaxed);
+ const bool svc_trace = presents >= trace_range.first && presents <= trace_range.second;
+ if (svc_trace) {
+ LOG_INFO(Kernel_SVC, "svc {:#04x} in t{} [0]={:#x} [1]={:#x} [2]={:#x} [3]={:#x}", imm,
+ GetCurrentThread(kernel).GetThreadId(),
+ GetArg64(args, 0), GetArg64(args, 1), GetArg64(args, 2), GetArg64(args, 3));
+ }
+ if (imm == 0x80) {
+ // Switch 2 only: svcSetGpuMemoryAttribute(address, size, mask, attr). nn::gpu::sdk and
+ // nn::gs native memory set the new "GPU accessible" attribute (0x20) before registering
+ // memory with the GPU and clear it on destruction; NVN can see it through QueryMemory.
+ // Same checks as svcSetMemoryAttribute, restricted to that bit (as in changes.patch).
+ const u64 address = GetArg64(args, 0);
+ const u64 size = GetArg64(args, 1);
+ const u32 mask = static_cast<u32>(GetArg64(args, 2));
+ const u32 attr = static_cast<u32>(GetArg64(args, 3));
+ constexpr u32 Supported = static_cast<u32>(Svc::MemoryAttribute::GpuAccessible);
+ auto& page_table = process.GetPageTable();
+ Result result = ResultSuccess;
+ if (!Common::IsAligned(address, PageSize)) {
+ result = ResultInvalidAddress;
+ } else if (size == 0 || !Common::IsAligned(size, PageSize)) {
+ result = ResultInvalidSize;
+ } else if ((attr & ~mask) != 0 || ((mask | attr) & ~Supported) != 0) {
+ result = ResultInvalidCombination;
+ } else if (address + size <= address || !page_table.Contains(address, size)) {
+ result = ResultInvalidCurrentMemory;
+ } else {
+ result = page_table.SetMemoryAttribute(address, size,
+ static_cast<KMemoryAttribute>(mask),
+ static_cast<KMemoryAttribute>(attr));
+ }
+ LOG_DEBUG(Kernel_SVC, "Switch 2 svc 0x80 addr={:#x} size={:#x} mask={:#x} attr={:#x} -> {:#x}",
+ address, size, mask, attr, result.raw);
+ args[0] = result.raw;
+ } else if (process.Is64Bit()) {
Call64(system, imm, args);
} else {
Call32(system, imm, args);
}
+ if (svc_trace) {
+ LOG_INFO(Kernel_SVC, "svc {:#04x} out t{} [0]={:#x} [1]={:#x} [2]={:#x}", imm,
+ GetCurrentThread(kernel).GetThreadId(), GetArg64(args, 0), GetArg64(args, 1),
+ GetArg64(args, 2));
+ }
//kernel.ExitSVCProfile();
kernel.CurrentPhysicalCore().LoadSvcArguments(process, args);
}
diff --git a/src/core/hle/kernel/svc/svc_info.cpp b/src/core/hle/kernel/svc/svc_info.cpp
index 863bb58fa8..bbd7d32445 100644
--- a/src/core/hle/kernel/svc/svc_info.cpp
+++ b/src/core/hle/kernel/svc/svc_info.cpp
@@ -4,6 +4,8 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include <cstdlib>
+
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_memory_manager.h"
@@ -40,6 +42,11 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
case InfoType::UsedNonSystemMemorySize:
case InfoType::IsApplication:
case InfoType::FreeThreadCount:
+ case InfoType::Nx2RegionAddress:
+ case InfoType::Nx2RegionSize:
+ case InfoType::Nx2VammSize:
+ case InfoType::Nx2RemoteMemorySize:
+ case InfoType::Nx2RemoteMemoryUsed:
case InfoType::AliasRegionExtraSize: {
R_UNLESS(info_sub_id == 0, ResultInvalidEnumValue);
@@ -66,6 +73,35 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
case InfoType::HeapRegionSize:
*result = process->GetPageTable().GetHeapRegionSize();
R_SUCCEED();
+ case InfoType::Nx2RegionAddress:
+ case InfoType::Nx2RegionSize: {
+ // Looks like the region Switch 2 VAMM reserves address space in (a title asks for a
+ // ~7.5 GiB reservation). VAMM backs it with MapPhysicalMemory, which only works in
+ // the alias region here, so hand out the upper half of the alias region.
+ auto& page_table = process->GetPageTable();
+ const u64 half = page_table.GetAliasRegionSize() / 2;
+ *result = info_id_type == InfoType::Nx2RegionAddress
+ ? GetInteger(page_table.GetAliasRegionStart()) + half
+ : half;
+ LOG_WARNING(Kernel_SVC, "(STUBBED) Switch 2 InfoType {}, returning {:#x}",
+ static_cast<u32>(info_id_type), *result);
+ R_SUCCEED();
+ }
+ case InfoType::Nx2RemoteMemorySize:
+ // Unknown real value; nothing backs it yet.
+ *result = 0;
+ LOG_WARNING(Kernel_SVC, "(STUBBED) Switch 2 InfoType 31 (remote memory size), returning 0");
+ R_SUCCEED();
+ case InfoType::Nx2RemoteMemoryUsed:
+ *result = 0;
+ LOG_WARNING(Kernel_SVC, "(STUBBED) Switch 2 InfoType 32 (remote memory used), returning 0");
+ R_SUCCEED();
+ case InfoType::Nx2VammSize:
+ // A page granule: the SDK checks module addresses/sizes against (value - 1) and
+ // VAMM maps one granule up front. 4 KiB matches what Switch 1 kernels use.
+ *result = Core::Memory::YUZU_PAGESIZE;
+ LOG_WARNING(Kernel_SVC, "(STUBBED) Switch 2 InfoType 33, returning {:#x}", *result);
+ R_SUCCEED();
case InfoType::AslrRegionAddress:
*result = GetInteger(process->GetPageTable().GetAliasCodeRegionStart());
R_SUCCEED();
@@ -310,7 +346,16 @@ Result GetSystemInfo(Core::System& system, uint64_t* out, SystemInfoType info_ty
Result GetInfo64(Core::System& system, uint64_t* out, InfoType info_type, Handle handle,
uint64_t info_subtype) {
- R_RETURN(GetInfo(system, out, info_type, handle, info_subtype));
+ const Result result = GetInfo(system, out, info_type, handle, info_subtype);
+ // EDEN_NX2_MEM_TRACE: log the memory layout and size queries of a title (diagnostics).
+ static const bool trace = std::getenv("EDEN_NX2_MEM_TRACE") != nullptr;
+ const u32 type = static_cast<u32>(info_type);
+ if (static u32 logs{}; trace && logs < 200 && (type <= 7 || (type >= 12 && type <= 33))) {
+ ++logs;
+ LOG_INFO(Kernel_SVC, "mem trace: GetInfo type {} sub {:#x} -> {:#x} = {:#x}", type,
+ info_subtype, result.raw, *out);
+ }
+ R_RETURN(result);
}
Result GetSystemInfo64(Core::System& system, uint64_t* out, SystemInfoType info_type, Handle handle,
diff --git a/src/core/hle/kernel/svc/svc_physical_memory.cpp b/src/core/hle/kernel/svc/svc_physical_memory.cpp
index 48915b2b1e..4fde85fdaa 100644
--- a/src/core/hle/kernel/svc/svc_physical_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_physical_memory.cpp
@@ -4,6 +4,8 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include <cstdlib>
+
#include "core/core.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/svc.h"
@@ -142,16 +144,47 @@ Result SetUnsafeLimit(Core::System& system, uint64_t limit) {
R_THROW(ResultNotImplemented);
}
+// EDEN_NX2_MEM_TRACE: log the heap and physical memory requests of a title (diagnostics).
+static bool MemTrace() {
+ static const bool trace = std::getenv("EDEN_NX2_MEM_TRACE") != nullptr;
+ return trace;
+}
+
Result SetHeapSize64(Core::System& system, uint64_t* out_address, uint64_t size) {
- R_RETURN(SetHeapSize(system, out_address, size));
+ const Result result = SetHeapSize(system, out_address, size);
+ if (MemTrace()) {
+ LOG_INFO(Kernel_SVC, "mem trace: SetHeapSize {:#x} -> {:#x} at {:#x}", size, result.raw,
+ *out_address);
+ }
+ R_RETURN(result);
}
Result MapPhysicalMemory64(Core::System& system, uint64_t address, uint64_t size) {
- R_RETURN(MapPhysicalMemory(system, address, size));
+ const Result result = MapPhysicalMemory(system, address, size);
+ static u64 total{};
+ if (MemTrace()) {
+ if (result.IsSuccess()) {
+ total += size;
+ }
+ static u32 logs{};
+ if (result.IsError() || logs++ < 400) {
+ LOG_INFO(Kernel_SVC, "mem trace: MapPhysicalMemory {:#x}+{:#x} -> {:#x} (mapped total {:#x})",
+ address, size, result.raw, total);
+ }
+ }
+ R_RETURN(result);
}
Result UnmapPhysicalMemory64(Core::System& system, uint64_t address, uint64_t size) {
- R_RETURN(UnmapPhysicalMemory(system, address, size));
+ const Result result = UnmapPhysicalMemory(system, address, size);
+ if (MemTrace()) {
+ static u32 logs{};
+ if (result.IsError() || logs++ < 400) {
+ LOG_INFO(Kernel_SVC, "mem trace: UnmapPhysicalMemory {:#x}+{:#x} -> {:#x}", address,
+ size, result.raw);
+ }
+ }
+ R_RETURN(result);
}
Result MapPhysicalMemoryUnsafe64(Core::System& system, uint64_t address, uint64_t size) {
diff --git a/src/core/hle/kernel/svc/svc_thread.cpp b/src/core/hle/kernel/svc/svc_thread.cpp
index dbca13dd26..7f6b959256 100644
--- a/src/core/hle/kernel/svc/svc_thread.cpp
+++ b/src/core/hle/kernel/svc/svc_thread.cpp
@@ -20,6 +20,18 @@ constexpr bool IsValidVirtualCoreId(int32_t core_id) {
return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES));
}
+// Switch 2 hardware has eight cores. Six are emulated (the most an application is given), so
+// fold any id past them back onto the low cores, both in core ids and in affinity masks.
+constexpr int32_t FoldExtraCoreId(int32_t core_id) {
+ constexpr auto num_cores = static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES);
+ return core_id >= num_cores && core_id < 2 * num_cores ? core_id - num_cores : core_id;
+}
+
+constexpr u64 FoldExtraCoreMask(u64 mask) {
+ constexpr u64 low = (1ULL << Core::Hardware::NUM_CPU_CORES) - 1;
+ return (mask & low) | ((mask >> Core::Hardware::NUM_CPU_CORES) & low);
+}
+
} // Anonymous namespace
/// Creates a new thread
@@ -36,6 +48,7 @@ Result CreateThread(Core::System& system, Handle* out_handle, u64 entry_point, u
if (core_id == IdealCoreUseProcessValue) {
core_id = process.GetIdealCoreId();
}
+ core_id = FoldExtraCoreId(core_id);
// Validate arguments.
R_UNLESS(IsValidVirtualCoreId(core_id), ResultInvalidCoreId);
@@ -261,6 +274,10 @@ Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id
R_UNLESS((affinity_mask | process_core_mask) == process_core_mask, ResultInvalidCoreId);
R_UNLESS(affinity_mask != 0, ResultInvalidCombination);
+ // Validated against the real mask above; fold to the emulated cores from here on.
+ affinity_mask = FoldExtraCoreMask(affinity_mask);
+ core_id = FoldExtraCoreId(core_id);
+
// Validate the core id.
if (IsValidVirtualCoreId(core_id)) {
R_UNLESS(((1ULL << core_id) & affinity_mask) != 0, ResultInvalidCombination);
diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h
index b0f4796da4..0bf2aaaa2d 100644
--- a/src/core/hle/kernel/svc_types.h
+++ b/src/core/hle/kernel/svc_types.h
@@ -50,6 +50,7 @@ enum class MemoryAttribute : u32 {
DeviceShared = (1 << 2),
Uncached = (1 << 3),
PermissionLocked = (1 << 4),
+ GpuAccessible = (1 << 5), // Switch 2 (svc 0x80)
};
DECLARE_ENUM_FLAG_OPERATORS(MemoryAttribute);
@@ -157,6 +158,17 @@ enum class InfoType : u32 {
IsSvcPermitted = 26,
IoRegionHint = 27,
AliasRegionExtraSize = 28,
+ // Switch 2 kernels: address/size of a region the SDK queries between the alias and
+ // ASLR regions during nnosInitialize; likely the VAMM address-space region.
+ Nx2RegionAddress = 29,
+ Nx2RegionSize = 30,
+ // Switch 2 kernels: nn::os::GetRemoteMemoryUsage returns {id 31, id 32}; used by
+ // nn::gsv/nn::nvsrv BootWithRemoteMemory (memory set aside for the graphics stack).
+ Nx2RemoteMemorySize = 31,
+ Nx2RemoteMemoryUsed = 32,
+ // Switch 2 kernels: a page granule. The SDK aligns module ranges and VAMM's first
+ // MapPhysicalMemory to it.
+ Nx2VammSize = 33,
TransferMemoryHint = 34,