diff options
| author | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-10-29 03:39:52 -0400 |
|---|---|---|
| committer | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-10-29 03:39:52 -0400 |
| commit | e17fd9cef116a088f080ca502951e500864a58b6 (patch) | |
| tree | e7ff65f614622515d6a2e4263521909eea89a5d8 /src/core/hle/kernel | |
| parent | ea6c1193fbfa95e586bb6d129dc684163a12cb3e (diff) | |
| parent | 80c79333fe6fd28a31b65b997f1cbf2987711c91 (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.cpp | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/board/nintendo/nx/k_system_control.h | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/global_scheduler_context.cpp | 5 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_memory_block.h | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_page_table_base.cpp | 3 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_process.cpp | 7 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_spin_lock.cpp | 48 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_spin_lock.h | 10 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.cpp | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/physical_core.cpp | 19 | ||||
| -rw-r--r-- | src/core/hle/kernel/physical_core.h | 3 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 53 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc/svc_info.cpp | 47 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc/svc_physical_memory.cpp | 39 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc/svc_thread.cpp | 17 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc_types.h | 12 |
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, |
