summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt3
-rw-r--r--src/core/arm/arm_interface.h7
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp9
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h3
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp18
-rw-r--r--src/core/arm/unicorn/arm_unicorn.h3
-rw-r--r--src/core/core.cpp12
-rw-r--r--src/core/core.h14
-rw-r--r--src/core/core_cpu.cpp19
-rw-r--r--src/core/file_sys/program_metadata.cpp4
-rw-r--r--src/core/file_sys/program_metadata.h4
-rw-r--r--src/core/hardware_interrupt_manager.cpp30
-rw-r--r--src/core/hardware_interrupt_manager.h31
-rw-r--r--src/core/hle/kernel/code_set.h3
-rw-r--r--src/core/hle/kernel/physical_memory.h19
-rw-r--r--src/core/hle/kernel/process.cpp49
-rw-r--r--src/core/hle/kernel/process.h45
-rw-r--r--src/core/hle/kernel/shared_memory.cpp6
-rw-r--r--src/core/hle/kernel/shared_memory.h13
-rw-r--r--src/core/hle/kernel/svc.cpp138
-rw-r--r--src/core/hle/kernel/svc_wrap.h5
-rw-r--r--src/core/hle/kernel/transfer_memory.cpp2
-rw-r--r--src/core/hle/kernel/transfer_memory.h3
-rw-r--r--src/core/hle/kernel/vm_manager.cpp319
-rw-r--r--src/core/hle/kernel/vm_manager.h57
-rw-r--r--src/core/hle/service/am/am.cpp32
-rw-r--r--src/core/hle/service/am/am.h3
-rw-r--r--src/core/hle/service/audio/audio.cpp6
-rw-r--r--src/core/hle/service/audio/audio.h6
-rw-r--r--src/core/hle/service/audio/audout_u.cpp36
-rw-r--r--src/core/hle/service/audio/audout_u.h12
-rw-r--r--src/core/hle/service/audio/audren_u.cpp205
-rw-r--r--src/core/hle/service/audio/audren_u.h24
-rw-r--r--src/core/hle/service/friend/friend.cpp35
-rw-r--r--src/core/hle/service/ldr/ldr.cpp32
-rw-r--r--src/core/hle/service/mii/mii.cpp16
-rw-r--r--src/core/hle/service/ns/pl_u.cpp12
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdevice.h13
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp11
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp15
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp152
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h15
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp7
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp44
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h41
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h5
-rw-r--r--src/core/hle/service/nvdrv/interface.cpp48
-rw-r--r--src/core/hle/service/nvdrv/interface.h4
-rw-r--r--src/core/hle/service/nvdrv/nvdata.h48
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp59
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h88
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp23
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h11
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp23
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h4
-rw-r--r--src/core/hle/service/pm/pm.cpp124
-rw-r--r--src/core/hle/service/pm/pm.h6
-rw-r--r--src/core/hle/service/service.cpp6
-rw-r--r--src/core/hle/service/vi/vi.cpp48
-rw-r--r--src/core/loader/elf.cpp2
-rw-r--r--src/core/loader/kip.cpp2
-rw-r--r--src/core/loader/nro.cpp2
-rw-r--r--src/core/loader/nso.cpp2
-rw-r--r--src/core/settings.cpp1
-rw-r--r--src/core/settings.h1
-rw-r--r--src/core/telemetry_session.cpp1
76 files changed, 1619 insertions, 472 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index f4325f0f8..5462decee 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -111,6 +111,8 @@ add_library(core STATIC
frontend/scope_acquire_window_context.h
gdbstub/gdbstub.cpp
gdbstub/gdbstub.h
+ hardware_interrupt_manager.cpp
+ hardware_interrupt_manager.h
hle/ipc.h
hle/ipc_helpers.h
hle/kernel/address_arbiter.cpp
@@ -372,6 +374,7 @@ add_library(core STATIC
hle/service/nvdrv/devices/nvmap.h
hle/service/nvdrv/interface.cpp
hle/service/nvdrv/interface.h
+ hle/service/nvdrv/nvdata.h
hle/service/nvdrv/nvdrv.cpp
hle/service/nvdrv/nvdrv.h
hle/service/nvdrv/nvmemp.cpp
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index c6691a8e1..45e94e625 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -44,13 +44,6 @@ public:
/// Step CPU by one instruction
virtual void Step() = 0;
- /// Maps a backing memory region for the CPU
- virtual void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
- Kernel::VMAPermission perms) = 0;
-
- /// Unmaps a region of memory that was previously mapped using MapBackingMemory
- virtual void UnmapMemory(VAddr address, std::size_t size) = 0;
-
/// Clear all instruction cache
virtual void ClearInstructionCache() = 0;
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 44307fa19..f1506b372 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -177,15 +177,6 @@ ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor,
ARM_Dynarmic::~ARM_Dynarmic() = default;
-void ARM_Dynarmic::MapBackingMemory(u64 address, std::size_t size, u8* memory,
- Kernel::VMAPermission perms) {
- inner_unicorn.MapBackingMemory(address, size, memory, perms);
-}
-
-void ARM_Dynarmic::UnmapMemory(u64 address, std::size_t size) {
- inner_unicorn.UnmapMemory(address, size);
-}
-
void ARM_Dynarmic::SetPC(u64 pc) {
jit->SetPC(pc);
}
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index b701e97a3..504d46c68 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -23,9 +23,6 @@ public:
ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
~ARM_Dynarmic() override;
- void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
- Kernel::VMAPermission perms) override;
- void UnmapMemory(u64 address, std::size_t size) override;
void SetPC(u64 pc) override;
u64 GetPC() const override;
u64 GetReg(int index) const override;
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
index 4e07fe8b5..97d5c2a8a 100644
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ b/src/core/arm/unicorn/arm_unicorn.cpp
@@ -50,11 +50,14 @@ static void CodeHook(uc_engine* uc, uint64_t address, uint32_t size, void* user_
static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value,
void* user_data) {
+ auto* const system = static_cast<System*>(user_data);
+
ARM_Interface::ThreadContext ctx{};
- Core::CurrentArmInterface().SaveContext(ctx);
+ system->CurrentArmInterface().SaveContext(ctx);
ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x{:X}, pc=0x{:X}, lr=0x{:X}", addr,
ctx.pc, ctx.cpu_registers[30]);
- return {};
+
+ return false;
}
ARM_Unicorn::ARM_Unicorn(System& system) : system{system} {
@@ -65,7 +68,7 @@ ARM_Unicorn::ARM_Unicorn(System& system) : system{system} {
uc_hook hook{};
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_INTR, (void*)InterruptHook, this, 0, -1));
- CHECKED(uc_hook_add(uc, &hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, this, 0, -1));
+ CHECKED(uc_hook_add(uc, &hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, &system, 0, -1));
if (GDBStub::IsServerEnabled()) {
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_CODE, (void*)CodeHook, this, 0, -1));
last_bkpt_hit = false;
@@ -76,15 +79,6 @@ ARM_Unicorn::~ARM_Unicorn() {
CHECKED(uc_close(uc));
}
-void ARM_Unicorn::MapBackingMemory(VAddr address, std::size_t size, u8* memory,
- Kernel::VMAPermission perms) {
- CHECKED(uc_mem_map_ptr(uc, address, size, static_cast<u32>(perms), memory));
-}
-
-void ARM_Unicorn::UnmapMemory(VAddr address, std::size_t size) {
- CHECKED(uc_mem_unmap(uc, address, size));
-}
-
void ARM_Unicorn::SetPC(u64 pc) {
CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &pc));
}
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h
index 34e974b4d..fe2ffd70c 100644
--- a/src/core/arm/unicorn/arm_unicorn.h
+++ b/src/core/arm/unicorn/arm_unicorn.h
@@ -18,9 +18,6 @@ public:
explicit ARM_Unicorn(System& system);
~ARM_Unicorn() override;
- void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
- Kernel::VMAPermission perms) override;
- void UnmapMemory(VAddr address, std::size_t size) override;
void SetPC(u64 pc) override;
u64 GetPC() const override;
u64 GetReg(int index) const override;
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 4aceee785..20d64f3b0 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -19,6 +19,7 @@
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h"
#include "core/gdbstub/gdbstub.h"
+#include "core/hardware_interrupt_manager.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
@@ -151,7 +152,7 @@ struct System::Impl {
if (!renderer->Init()) {
return ResultStatus::ErrorVideoCore;
}
-
+ interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
gpu_core = VideoCore::CreateGPU(system);
is_powered_on = true;
@@ -298,6 +299,7 @@ struct System::Impl {
std::unique_ptr<VideoCore::RendererBase> renderer;
std::unique_ptr<Tegra::GPU> gpu_core;
std::shared_ptr<Tegra::DebugContext> debug_context;
+ std::unique_ptr<Core::Hardware::InterruptManager> interrupt_manager;
CpuCoreManager cpu_core_manager;
bool is_powered_on = false;
@@ -444,6 +446,14 @@ const Tegra::GPU& System::GPU() const {
return *impl->gpu_core;
}
+Core::Hardware::InterruptManager& System::InterruptManager() {
+ return *impl->interrupt_manager;
+}
+
+const Core::Hardware::InterruptManager& System::InterruptManager() const {
+ return *impl->interrupt_manager;
+}
+
VideoCore::RendererBase& System::Renderer() {
return *impl->renderer;
}
diff --git a/src/core/core.h b/src/core/core.h
index 11e73278e..0138d93b0 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -70,6 +70,10 @@ namespace Core::Timing {
class CoreTiming;
}
+namespace Core::Hardware {
+class InterruptManager;
+}
+
namespace Core {
class ARM_Interface;
@@ -234,6 +238,12 @@ public:
/// Provides a constant reference to the core timing instance.
const Timing::CoreTiming& CoreTiming() const;
+ /// Provides a reference to the interrupt manager instance.
+ Core::Hardware::InterruptManager& InterruptManager();
+
+ /// Provides a constant reference to the interrupt manager instance.
+ const Core::Hardware::InterruptManager& InterruptManager() const;
+
/// Provides a reference to the kernel instance.
Kernel::KernelCore& Kernel();
@@ -327,10 +337,6 @@ private:
static System s_instance;
};
-inline ARM_Interface& CurrentArmInterface() {
- return System::GetInstance().CurrentArmInterface();
-}
-
inline Kernel::Process* CurrentProcess() {
return System::GetInstance().CurrentProcess();
}
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
index 99b7d387d..21c410e34 100644
--- a/src/core/core_cpu.cpp
+++ b/src/core/core_cpu.cpp
@@ -53,16 +53,12 @@ bool CpuBarrier::Rendezvous() {
Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier,
std::size_t core_index)
: cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} {
- if (Settings::values.cpu_jit_enabled) {
#ifdef ARCHITECTURE_x86_64
- arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index);
+ arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index);
#else
- arm_interface = std::make_unique<ARM_Unicorn>(system);
- LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
+ arm_interface = std::make_unique<ARM_Unicorn>(system);
+ LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
#endif
- } else {
- arm_interface = std::make_unique<ARM_Unicorn>(system);
- }
scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface);
}
@@ -70,15 +66,12 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba
Cpu::~Cpu() = default;
std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) {
- if (Settings::values.cpu_jit_enabled) {
#ifdef ARCHITECTURE_x86_64
- return std::make_unique<DynarmicExclusiveMonitor>(num_cores);
+ return std::make_unique<DynarmicExclusiveMonitor>(num_cores);
#else
- return nullptr; // TODO(merry): Passthrough exclusive monitor
+ // TODO(merry): Passthrough exclusive monitor
+ return nullptr;
#endif
- } else {
- return nullptr; // TODO(merry): Passthrough exclusive monitor
- }
}
void Cpu::RunLoop(bool tight_loop) {
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index eb76174c5..7310b3602 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -94,6 +94,10 @@ u64 ProgramMetadata::GetFilesystemPermissions() const {
return aci_file_access.permissions;
}
+u32 ProgramMetadata::GetSystemResourceSize() const {
+ return npdm_header.system_resource_size;
+}
+
const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const {
return aci_kernel_capabilities;
}
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
index 43bf2820a..88ec97d85 100644
--- a/src/core/file_sys/program_metadata.h
+++ b/src/core/file_sys/program_metadata.h
@@ -58,6 +58,7 @@ public:
u32 GetMainThreadStackSize() const;
u64 GetTitleID() const;
u64 GetFilesystemPermissions() const;
+ u32 GetSystemResourceSize() const;
const KernelCapabilityDescriptors& GetKernelCapabilities() const;
void Print() const;
@@ -76,7 +77,8 @@ private:
u8 reserved_3;
u8 main_thread_priority;
u8 main_thread_cpu;
- std::array<u8, 8> reserved_4;
+ std::array<u8, 4> reserved_4;
+ u32_le system_resource_size;
u32_le process_category;
u32_le main_stack_size;
std::array<u8, 0x10> application_name;
diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp
new file mode 100644
index 000000000..c2115db2d
--- /dev/null
+++ b/src/core/hardware_interrupt_manager.cpp
@@ -0,0 +1,30 @@
+// Copyright 2019 Yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/hardware_interrupt_manager.h"
+#include "core/hle/service/nvdrv/interface.h"
+#include "core/hle/service/sm/sm.h"
+
+namespace Core::Hardware {
+
+InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) {
+ gpu_interrupt_event =
+ system.CoreTiming().RegisterEvent("GPUInterrupt", [this](u64 message, s64) {
+ auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv");
+ const u32 syncpt = static_cast<u32>(message >> 32);
+ const u32 value = static_cast<u32>(message);
+ nvdrv->SignalGPUInterruptSyncpt(syncpt, value);
+ });
+}
+
+InterruptManager::~InterruptManager() = default;
+
+void InterruptManager::GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
+ const u64 msg = (static_cast<u64>(syncpoint_id) << 32ULL) | value;
+ system.CoreTiming().ScheduleEvent(10, gpu_interrupt_event, msg);
+}
+
+} // namespace Core::Hardware
diff --git a/src/core/hardware_interrupt_manager.h b/src/core/hardware_interrupt_manager.h
new file mode 100644
index 000000000..494db883a
--- /dev/null
+++ b/src/core/hardware_interrupt_manager.h
@@ -0,0 +1,31 @@
+// Copyright 2019 Yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace Core {
+class System;
+}
+
+namespace Core::Timing {
+struct EventType;
+}
+
+namespace Core::Hardware {
+
+class InterruptManager {
+public:
+ explicit InterruptManager(Core::System& system);
+ ~InterruptManager();
+
+ void GPUInterruptSyncpt(u32 syncpoint_id, u32 value);
+
+private:
+ Core::System& system;
+ Core::Timing::EventType* gpu_interrupt_event{};
+};
+
+} // namespace Core::Hardware
diff --git a/src/core/hle/kernel/code_set.h b/src/core/hle/kernel/code_set.h
index 879957dcb..d8ad54030 100644
--- a/src/core/hle/kernel/code_set.h
+++ b/src/core/hle/kernel/code_set.h
@@ -8,6 +8,7 @@
#include <vector>
#include "common/common_types.h"
+#include "core/hle/kernel/physical_memory.h"
namespace Kernel {
@@ -77,7 +78,7 @@ struct CodeSet final {
}
/// The overall data that backs this code set.
- std::vector<u8> memory;
+ Kernel::PhysicalMemory memory;
/// The segments that comprise this code set.
std::array<Segment, 3> segments;
diff --git a/src/core/hle/kernel/physical_memory.h b/src/core/hle/kernel/physical_memory.h
new file mode 100644
index 000000000..090565310
--- /dev/null
+++ b/src/core/hle/kernel/physical_memory.h
@@ -0,0 +1,19 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/alignment.h"
+
+namespace Kernel {
+
+// This encapsulation serves 2 purposes:
+// - First, to encapsulate host physical memory under a single type and set an
+// standard for managing it.
+// - Second to ensure all host backing memory used is aligned to 256 bytes due
+// to strict alignment restrictions on GPU memory.
+
+using PhysicalMemory = std::vector<u8, Common::AlignmentAllocator<u8, 256>>;
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index f45ef05f6..e80a12ac3 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -129,20 +129,17 @@ u64 Process::GetTotalPhysicalMemoryAvailable() const {
return vm_manager.GetTotalPhysicalMemoryAvailable();
}
-u64 Process::GetTotalPhysicalMemoryAvailableWithoutMmHeap() const {
- // TODO: Subtract the personal heap size from this when the
- // personal heap is implemented.
- return GetTotalPhysicalMemoryAvailable();
+u64 Process::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const {
+ return GetTotalPhysicalMemoryAvailable() - GetSystemResourceSize();
}
u64 Process::GetTotalPhysicalMemoryUsed() const {
- return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size;
+ return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size +
+ GetSystemResourceUsage();
}
-u64 Process::GetTotalPhysicalMemoryUsedWithoutMmHeap() const {
- // TODO: Subtract the personal heap size from this when the
- // personal heap is implemented.
- return GetTotalPhysicalMemoryUsed();
+u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const {
+ return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage();
}
void Process::RegisterThread(const Thread* thread) {
@@ -172,6 +169,7 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
program_id = metadata.GetTitleID();
ideal_core = metadata.GetMainThreadCore();
is_64bit_process = metadata.Is64BitProgram();
+ system_resource_size = metadata.GetSystemResourceSize();
vm_manager.Reset(metadata.GetAddressSpaceType());
@@ -186,19 +184,11 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
}
void Process::Run(s32 main_thread_priority, u64 stack_size) {
- // The kernel always ensures that the given stack size is page aligned.
- main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE);
-
- // Allocate and map the main thread stack
- // TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part
- // of the user address space.
- const VAddr mapping_address = vm_manager.GetTLSIORegionEndAddress() - main_thread_stack_size;
- vm_manager
- .MapMemoryBlock(mapping_address, std::make_shared<std::vector<u8>>(main_thread_stack_size),
- 0, main_thread_stack_size, MemoryState::Stack)
- .Unwrap();
+ AllocateMainThreadStack(stack_size);
+ tls_region_address = CreateTLSRegion();
vm_manager.LogLayout();
+
ChangeStatus(ProcessStatus::Running);
SetupMainThread(*this, kernel, main_thread_priority);
@@ -228,6 +218,9 @@ void Process::PrepareForTermination() {
stop_threads(system.Scheduler(2).GetThreadList());
stop_threads(system.Scheduler(3).GetThreadList());
+ FreeTLSRegion(tls_region_address);
+ tls_region_address = 0;
+
ChangeStatus(ProcessStatus::Exited);
}
@@ -254,7 +247,7 @@ VAddr Process::CreateTLSRegion() {
ASSERT(region_address.Succeeded());
const auto map_result = vm_manager.MapMemoryBlock(
- *region_address, std::make_shared<std::vector<u8>>(Memory::PAGE_SIZE), 0,
+ *region_address, std::make_shared<PhysicalMemory>(Memory::PAGE_SIZE), 0,
Memory::PAGE_SIZE, MemoryState::ThreadLocal);
ASSERT(map_result.Succeeded());
@@ -284,7 +277,7 @@ void Process::FreeTLSRegion(VAddr tls_address) {
}
void Process::LoadModule(CodeSet module_, VAddr base_addr) {
- const auto memory = std::make_shared<std::vector<u8>>(std::move(module_.memory));
+ const auto memory = std::make_shared<PhysicalMemory>(std::move(module_.memory));
const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions,
MemoryState memory_state) {
@@ -327,4 +320,16 @@ void Process::ChangeStatus(ProcessStatus new_status) {
WakeupAllWaitingThreads();
}
+void Process::AllocateMainThreadStack(u64 stack_size) {
+ // The kernel always ensures that the given stack size is page aligned.
+ main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE);
+
+ // Allocate and map the main thread stack
+ const VAddr mapping_address = vm_manager.GetTLSIORegionEndAddress() - main_thread_stack_size;
+ vm_manager
+ .MapMemoryBlock(mapping_address, std::make_shared<PhysicalMemory>(main_thread_stack_size),
+ 0, main_thread_stack_size, MemoryState::Stack)
+ .Unwrap();
+}
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 83ea02bee..c2df451f3 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -135,6 +135,11 @@ public:
return mutex;
}
+ /// Gets the address to the process' dedicated TLS region.
+ VAddr GetTLSRegionAddress() const {
+ return tls_region_address;
+ }
+
/// Gets the current status of the process
ProcessStatus GetStatus() const {
return status;
@@ -168,8 +173,24 @@ public:
return capabilities.GetPriorityMask();
}
- u32 IsVirtualMemoryEnabled() const {
- return is_virtual_address_memory_enabled;
+ /// Gets the amount of secure memory to allocate for memory management.
+ u32 GetSystemResourceSize() const {
+ return system_resource_size;
+ }
+
+ /// Gets the amount of secure memory currently in use for memory management.
+ u32 GetSystemResourceUsage() const {
+ // On hardware, this returns the amount of system resource memory that has
+ // been used by the kernel. This is problematic for Yuzu to emulate, because
+ // system resource memory is used for page tables -- and yuzu doesn't really
+ // have a way to calculate how much memory is required for page tables for
+ // the current process at any given time.
+ // TODO: Is this even worth implementing? Games may retrieve this value via
+ // an SDK function that gets used + available system resource size for debug
+ // or diagnostic purposes. However, it seems unlikely that a game would make
+ // decisions based on how much system memory is dedicated to its page tables.
+ // Is returning a value other than zero wise?
+ return 0;
}
/// Whether this process is an AArch64 or AArch32 process.
@@ -196,15 +217,15 @@ public:
u64 GetTotalPhysicalMemoryAvailable() const;
/// Retrieves the total physical memory available to this process in bytes,
- /// without the size of the personal heap added to it.
- u64 GetTotalPhysicalMemoryAvailableWithoutMmHeap() const;
+ /// without the size of the personal system resource heap added to it.
+ u64 GetTotalPhysicalMemoryAvailableWithoutSystemResource() const;
/// Retrieves the total physical memory used by this process in bytes.
u64 GetTotalPhysicalMemoryUsed() const;
/// Retrieves the total physical memory used by this process in bytes,
- /// without the size of the personal heap added to it.
- u64 GetTotalPhysicalMemoryUsedWithoutMmHeap() const;
+ /// without the size of the personal system resource heap added to it.
+ u64 GetTotalPhysicalMemoryUsedWithoutSystemResource() const;
/// Gets the list of all threads created with this process as their owner.
const std::list<const Thread*>& GetThreadList() const {
@@ -280,6 +301,9 @@ private:
/// a process signal.
void ChangeStatus(ProcessStatus new_status);
+ /// Allocates the main thread stack for the process, given the stack size in bytes.
+ void AllocateMainThreadStack(u64 stack_size);
+
/// Memory manager for this process.
Kernel::VMManager vm_manager;
@@ -298,12 +322,16 @@ private:
/// Title ID corresponding to the process
u64 program_id = 0;
+ /// Specifies additional memory to be reserved for the process's memory management by the
+ /// system. When this is non-zero, secure memory is allocated and used for page table allocation
+ /// instead of using the normal global page tables/memory block management.
+ u32 system_resource_size = 0;
+
/// Resource limit descriptor for this process
SharedPtr<ResourceLimit> resource_limit;
/// The ideal CPU core for this process, threads are scheduled on this core by default.
u8 ideal_core = 0;
- u32 is_virtual_address_memory_enabled = 0;
/// The Thread Local Storage area is allocated as processes create threads,
/// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
@@ -338,6 +366,9 @@ private:
/// variable related facilities.
Mutex mutex;
+ /// Address indicating the location of the process' dedicated TLS region.
+ VAddr tls_region_address = 0;
+
/// Random values for svcGetInfo RandomEntropy
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{};
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index f15c5ee36..a815c4eea 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -28,7 +28,7 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process* owner_
shared_memory->other_permissions = other_permissions;
if (address == 0) {
- shared_memory->backing_block = std::make_shared<std::vector<u8>>(size);
+ shared_memory->backing_block = std::make_shared<Kernel::PhysicalMemory>(size);
shared_memory->backing_block_offset = 0;
// Refresh the address mappings for the current process.
@@ -59,8 +59,8 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process* owner_
}
SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
- KernelCore& kernel, std::shared_ptr<std::vector<u8>> heap_block, std::size_t offset, u64 size,
- MemoryPermission permissions, MemoryPermission other_permissions, std::string name) {
+ KernelCore& kernel, std::shared_ptr<Kernel::PhysicalMemory> heap_block, std::size_t offset,
+ u64 size, MemoryPermission permissions, MemoryPermission other_permissions, std::string name) {
SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel));
shared_memory->owner_process = nullptr;
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index c2b6155e1..01ca6dcd2 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -10,6 +10,7 @@
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/physical_memory.h"
#include "core/hle/kernel/process.h"
#include "core/hle/result.h"
@@ -62,12 +63,10 @@ public:
* block.
* @param name Optional object name, used for debugging purposes.
*/
- static SharedPtr<SharedMemory> CreateForApplet(KernelCore& kernel,
- std::shared_ptr<std::vector<u8>> heap_block,
- std::size_t offset, u64 size,
- MemoryPermission permissions,
- MemoryPermission other_permissions,
- std::string name = "Unknown Applet");
+ static SharedPtr<SharedMemory> CreateForApplet(
+ KernelCore& kernel, std::shared_ptr<Kernel::PhysicalMemory> heap_block, std::size_t offset,
+ u64 size, MemoryPermission permissions, MemoryPermission other_permissions,
+ std::string name = "Unknown Applet");
std::string GetTypeName() const override {
return "SharedMemory";
@@ -135,7 +134,7 @@ private:
~SharedMemory() override;
/// Backing memory for this shared memory block.
- std::shared_ptr<std::vector<u8>> backing_block;
+ std::shared_ptr<PhysicalMemory> backing_block;
/// Offset into the backing block for this shared memory.
std::size_t backing_block_offset = 0;
/// Size of the memory block. Page-aligned.
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 332573a95..1fd1a732a 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -318,7 +318,14 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad
return result;
}
- return vm_manager.UnmapRange(dst_addr, size);
+ const auto unmap_res = vm_manager.UnmapRange(dst_addr, size);
+
+ // Reprotect the source mapping on success
+ if (unmap_res.IsSuccess()) {
+ ASSERT(vm_manager.ReprotectRange(src_addr, size, VMAPermission::ReadWrite).IsSuccess());
+ }
+
+ return unmap_res;
}
/// Connect to an OS service given the port name, returns the handle to the port to out
@@ -729,16 +736,16 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
StackRegionBaseAddr = 14,
StackRegionSize = 15,
// 3.0.0+
- IsVirtualAddressMemoryEnabled = 16,
- PersonalMmHeapUsage = 17,
+ SystemResourceSize = 16,
+ SystemResourceUsage = 17,
TitleId = 18,
// 4.0.0+
PrivilegedProcessId = 19,
// 5.0.0+
UserExceptionContextAddr = 20,
// 6.0.0+
- TotalPhysicalMemoryAvailableWithoutMmHeap = 21,
- TotalPhysicalMemoryUsedWithoutMmHeap = 22,
+ TotalPhysicalMemoryAvailableWithoutSystemResource = 21,
+ TotalPhysicalMemoryUsedWithoutSystemResource = 22,
};
const auto info_id_type = static_cast<GetInfoType>(info_id);
@@ -756,12 +763,12 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
case GetInfoType::StackRegionSize:
case GetInfoType::TotalPhysicalMemoryAvailable:
case GetInfoType::TotalPhysicalMemoryUsed:
- case GetInfoType::IsVirtualAddressMemoryEnabled:
- case GetInfoType::PersonalMmHeapUsage:
+ case GetInfoType::SystemResourceSize:
+ case GetInfoType::SystemResourceUsage:
case GetInfoType::TitleId:
case GetInfoType::UserExceptionContextAddr:
- case GetInfoType::TotalPhysicalMemoryAvailableWithoutMmHeap:
- case GetInfoType::TotalPhysicalMemoryUsedWithoutMmHeap: {
+ case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource:
+ case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource: {
if (info_sub_id != 0) {
return ERR_INVALID_ENUM_VALUE;
}
@@ -822,8 +829,13 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
*result = process->GetTotalPhysicalMemoryUsed();
return RESULT_SUCCESS;
- case GetInfoType::IsVirtualAddressMemoryEnabled:
- *result = process->IsVirtualMemoryEnabled();
+ case GetInfoType::SystemResourceSize:
+ *result = process->GetSystemResourceSize();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::SystemResourceUsage:
+ LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage");
+ *result = process->GetSystemResourceUsage();
return RESULT_SUCCESS;
case GetInfoType::TitleId:
@@ -831,17 +843,15 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
return RESULT_SUCCESS;
case GetInfoType::UserExceptionContextAddr:
- LOG_WARNING(Kernel_SVC,
- "(STUBBED) Attempted to query user exception context address, returned 0");
- *result = 0;
+ *result = process->GetTLSRegionAddress();
return RESULT_SUCCESS;
- case GetInfoType::TotalPhysicalMemoryAvailableWithoutMmHeap:
- *result = process->GetTotalPhysicalMemoryAvailable();
+ case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource:
+ *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource();
return RESULT_SUCCESS;
- case GetInfoType::TotalPhysicalMemoryUsedWithoutMmHeap:
- *result = process->GetTotalPhysicalMemoryUsedWithoutMmHeap();
+ case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource:
+ *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource();
return RESULT_SUCCESS;
default:
@@ -946,6 +956,86 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
}
}
+/// Maps memory at a desired address
+static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
+ LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
+
+ if (!Common::Is4KBAligned(addr)) {
+ LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (!Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
+ return ERR_INVALID_SIZE;
+ }
+
+ if (size == 0) {
+ LOG_ERROR(Kernel_SVC, "Size is zero");
+ return ERR_INVALID_SIZE;
+ }
+
+ if (!(addr < addr + size)) {
+ LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ Process* const current_process = system.Kernel().CurrentProcess();
+ auto& vm_manager = current_process->VMManager();
+
+ if (current_process->GetSystemResourceSize() == 0) {
+ LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
+ return ERR_INVALID_STATE;
+ }
+
+ if (!vm_manager.IsWithinMapRegion(addr, size)) {
+ LOG_ERROR(Kernel_SVC, "Range not within map region");
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ return vm_manager.MapPhysicalMemory(addr, size);
+}
+
+/// Unmaps memory previously mapped via MapPhysicalMemory
+static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
+ LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
+
+ if (!Common::Is4KBAligned(addr)) {
+ LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (!Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
+ return ERR_INVALID_SIZE;
+ }
+
+ if (size == 0) {
+ LOG_ERROR(Kernel_SVC, "Size is zero");
+ return ERR_INVALID_SIZE;
+ }
+
+ if (!(addr < addr + size)) {
+ LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ Process* const current_process = system.Kernel().CurrentProcess();
+ auto& vm_manager = current_process->VMManager();
+
+ if (current_process->GetSystemResourceSize() == 0) {
+ LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
+ return ERR_INVALID_STATE;
+ }
+
+ if (!vm_manager.IsWithinMapRegion(addr, size)) {
+ LOG_ERROR(Kernel_SVC, "Range not within map region");
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ return vm_manager.UnmapPhysicalMemory(addr, size);
+}
+
/// Sets the thread activity
static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity);
@@ -1647,8 +1737,8 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var
// Wait for an address (via Address Arbiter)
static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value,
s64 timeout) {
- LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}",
- address, type, value, timeout);
+ LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", address,
+ type, value, timeout);
// If the passed address is a kernel virtual address, return invalid memory state.
if (Memory::IsKernelVirtualAddress(address)) {
@@ -1670,8 +1760,8 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type,
// Signals to an address (via Address Arbiter)
static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value,
s32 num_to_wake) {
- LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}",
- address, type, value, num_to_wake);
+ LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}",
+ address, type, value, num_to_wake);
// If the passed address is a kernel virtual address, return invalid memory state.
if (Memory::IsKernelVirtualAddress(address)) {
@@ -2303,8 +2393,8 @@ static const FunctionDef SVC_Table[] = {
{0x29, SvcWrap<GetInfo>, "GetInfo"},
{0x2A, nullptr, "FlushEntireDataCache"},
{0x2B, nullptr, "FlushDataCache"},
- {0x2C, nullptr, "MapPhysicalMemory"},
- {0x2D, nullptr, "UnmapPhysicalMemory"},
+ {0x2C, SvcWrap<MapPhysicalMemory>, "MapPhysicalMemory"},
+ {0x2D, SvcWrap<UnmapPhysicalMemory>, "UnmapPhysicalMemory"},
{0x2E, nullptr, "GetFutureThreadInfo"},
{0x2F, nullptr, "GetLastThreadInfo"},
{0x30, SvcWrap<GetResourceLimitLimitValue>, "GetResourceLimitLimitValue"},
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 865473c6f..c2d8d0dc3 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -32,6 +32,11 @@ void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0)).raw);
}
+template <ResultCode func(Core::System&, u64, u64)>
+void SvcWrap(Core::System& system) {
+ FuncReturn(system, func(system, Param(system, 0), Param(system, 1)).raw);
+}
+
template <ResultCode func(Core::System&, u32)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
diff --git a/src/core/hle/kernel/transfer_memory.cpp b/src/core/hle/kernel/transfer_memory.cpp
index 26c4e5e67..1113c815e 100644
--- a/src/core/hle/kernel/transfer_memory.cpp
+++ b/src/core/hle/kernel/transfer_memory.cpp
@@ -47,7 +47,7 @@ ResultCode TransferMemory::MapMemory(VAddr address, u64 size, MemoryPermission p
return ERR_INVALID_STATE;
}
- backing_block = std::make_shared<std::vector<u8>>(size);
+ backing_block = std::make_shared<PhysicalMemory>(size);
const auto map_state = owner_permissions == MemoryPermission::None
? MemoryState::TransferMemoryIsolated
diff --git a/src/core/hle/kernel/transfer_memory.h b/src/core/hle/kernel/transfer_memory.h
index a140b1e2b..6be9dc094 100644
--- a/src/core/hle/kernel/transfer_memory.h
+++ b/src/core/hle/kernel/transfer_memory.h
@@ -8,6 +8,7 @@
#include <vector>
#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/physical_memory.h"
union ResultCode;
@@ -82,7 +83,7 @@ private:
~TransferMemory() override;
/// Memory block backing this instance.
- std::shared_ptr<std::vector<u8>> backing_block;
+ std::shared_ptr<PhysicalMemory> backing_block;
/// The base address for the memory managed by this instance.
VAddr base_address = 0;
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 501544090..40cea1e7c 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -5,13 +5,15 @@
#include <algorithm>
#include <iterator>
#include <utility>
+#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/memory_hook.h"
-#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/errors.h"
+#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
#include "core/memory_setup.h"
@@ -49,10 +51,14 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
type != next.type) {
return false;
}
- if (type == VMAType::AllocatedMemoryBlock &&
- (backing_block != next.backing_block || offset + size != next.offset)) {
+ if ((attribute & MemoryAttribute::DeviceMapped) == MemoryAttribute::DeviceMapped) {
+ // TODO: Can device mapped memory be merged sanely?
+ // Not merging it may cause inaccuracies versus hardware when memory layout is queried.
return false;
}
+ if (type == VMAType::AllocatedMemoryBlock) {
+ return true;
+ }
if (type == VMAType::BackingMemory && backing_memory + size != next.backing_memory) {
return false;
}
@@ -98,9 +104,9 @@ bool VMManager::IsValidHandle(VMAHandle handle) const {
}
ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
- std::shared_ptr<std::vector<u8>> block,
+ std::shared_ptr<PhysicalMemory> block,
std::size_t offset, u64 size,
- MemoryState state) {
+ MemoryState state, VMAPermission perm) {
ASSERT(block != nullptr);
ASSERT(offset + size <= block->size());
@@ -109,17 +115,8 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
VirtualMemoryArea& final_vma = vma_handle->second;
ASSERT(final_vma.size == size);
- system.ArmInterface(0).MapBackingMemory(target, size, block->data() + offset,
- VMAPermission::ReadWriteExecute);
- system.ArmInterface(1).MapBackingMemory(target, size, block->data() + offset,
- VMAPermission::ReadWriteExecute);
- system.ArmInterface(2).MapBackingMemory(target, size, block->data() + offset,
- VMAPermission::ReadWriteExecute);
- system.ArmInterface(3).MapBackingMemory(target, size, block->data() + offset,
- VMAPermission::ReadWriteExecute);
-
final_vma.type = VMAType::AllocatedMemoryBlock;
- final_vma.permissions = VMAPermission::ReadWrite;
+ final_vma.permissions = perm;
final_vma.state = state;
final_vma.backing_block = std::move(block);
final_vma.offset = offset;
@@ -137,11 +134,6 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
VirtualMemoryArea& final_vma = vma_handle->second;
ASSERT(final_vma.size == size);
- system.ArmInterface(0).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
- system.ArmInterface(1).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
- system.ArmInterface(2).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
- system.ArmInterface(3).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
-
final_vma.type = VMAType::BackingMemory;
final_vma.permissions = VMAPermission::ReadWrite;
final_vma.state = state;
@@ -230,11 +222,6 @@ ResultCode VMManager::UnmapRange(VAddr target, u64 size) {
ASSERT(FindVMA(target)->second.size >= size);
- system.ArmInterface(0).UnmapMemory(target, size);
- system.ArmInterface(1).UnmapMemory(target, size);
- system.ArmInterface(2).UnmapMemory(target, size);
- system.ArmInterface(3).UnmapMemory(target, size);
-
return RESULT_SUCCESS;
}
@@ -274,7 +261,7 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) {
if (heap_memory == nullptr) {
// Initialize heap
- heap_memory = std::make_shared<std::vector<u8>>(size);
+ heap_memory = std::make_shared<PhysicalMemory>(size);
heap_end = heap_region_base + size;
} else {
UnmapRange(heap_region_base, GetCurrentHeapSize());
@@ -308,6 +295,166 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) {
return MakeResult<VAddr>(heap_region_base);
}
+ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) {
+ const auto end_addr = target + size;
+ const auto last_addr = end_addr - 1;
+ VAddr cur_addr = target;
+
+ ResultCode result = RESULT_SUCCESS;
+
+ // Check how much memory we've already mapped.
+ const auto mapped_size_result = SizeOfAllocatedVMAsInRange(target, size);
+ if (mapped_size_result.Failed()) {
+ return mapped_size_result.Code();
+ }
+
+ // If we've already mapped the desired amount, return early.
+ const std::size_t mapped_size = *mapped_size_result;
+ if (mapped_size == size) {
+ return RESULT_SUCCESS;
+ }
+
+ // Check that we can map the memory we want.
+ const auto res_limit = system.CurrentProcess()->GetResourceLimit();
+ const u64 physmem_remaining = res_limit->GetMaxResourceValue(ResourceType::PhysicalMemory) -
+ res_limit->GetCurrentResourceValue(ResourceType::PhysicalMemory);
+ if (physmem_remaining < (size - mapped_size)) {
+ return ERR_RESOURCE_LIMIT_EXCEEDED;
+ }
+
+ // Keep track of the memory regions we unmap.
+ std::vector<std::pair<u64, u64>> mapped_regions;
+
+ // Iterate, trying to map memory.
+ {
+ cur_addr = target;
+
+ auto iter = FindVMA(target);
+ ASSERT_MSG(iter != vma_map.end(), "MapPhysicalMemory iter != end");
+
+ while (true) {
+ const auto& vma = iter->second;
+ const auto vma_start = vma.base;
+ const auto vma_end = vma_start + vma.size;
+ const auto vma_last = vma_end - 1;
+
+ // Map the memory block
+ const auto map_size = std::min(end_addr - cur_addr, vma_end - cur_addr);
+ if (vma.state == MemoryState::Unmapped) {
+ const auto map_res =
+ MapMemoryBlock(cur_addr, std::make_shared<PhysicalMemory>(map_size, 0), 0,
+ map_size, MemoryState::Heap, VMAPermission::ReadWrite);
+ result = map_res.Code();
+ if (result.IsError()) {
+ break;
+ }
+
+ mapped_regions.emplace_back(cur_addr, map_size);
+ }
+
+ // Break once we hit the end of the range.
+ if (last_addr <= vma_last) {
+ break;
+ }
+
+ // Advance to the next block.
+ cur_addr = vma_end;
+ iter = FindVMA(cur_addr);
+ ASSERT_MSG(iter != vma_map.end(), "MapPhysicalMemory iter != end");
+ }
+ }
+
+ // If we failed, unmap memory.
+ if (result.IsError()) {
+ for (const auto [unmap_address, unmap_size] : mapped_regions) {
+ ASSERT_MSG(UnmapRange(unmap_address, unmap_size).IsSuccess(),
+ "MapPhysicalMemory un-map on error");
+ }
+
+ return result;
+ }
+
+ // Update amount of mapped physical memory.
+ physical_memory_mapped += size - mapped_size;
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) {
+ const auto end_addr = target + size;
+ const auto last_addr = end_addr - 1;
+ VAddr cur_addr = target;
+
+ ResultCode result = RESULT_SUCCESS;
+
+ // Check how much memory is currently mapped.
+ const auto mapped_size_result = SizeOfUnmappablePhysicalMemoryInRange(target, size);
+ if (mapped_size_result.Failed()) {
+ return mapped_size_result.Code();
+ }
+
+ // If we've already unmapped all the memory, return early.
+ const std::size_t mapped_size = *mapped_size_result;
+ if (mapped_size == 0) {
+ return RESULT_SUCCESS;
+ }
+
+ // Keep track of the memory regions we unmap.
+ std::vector<std::pair<u64, u64>> unmapped_regions;
+
+ // Try to unmap regions.
+ {
+ cur_addr = target;
+
+ auto iter = FindVMA(target);
+ ASSERT_MSG(iter != vma_map.end(), "UnmapPhysicalMemory iter != end");
+
+ while (true) {
+ const auto& vma = iter->second;
+ const auto vma_start = vma.base;
+ const auto vma_end = vma_start + vma.size;
+ const auto vma_last = vma_end - 1;
+
+ // Unmap the memory block
+ const auto unmap_size = std::min(end_addr - cur_addr, vma_end - cur_addr);
+ if (vma.state == MemoryState::Heap) {
+ result = UnmapRange(cur_addr, unmap_size);
+ if (result.IsError()) {
+ break;
+ }
+
+ unmapped_regions.emplace_back(cur_addr, unmap_size);
+ }
+
+ // Break once we hit the end of the range.
+ if (last_addr <= vma_last) {
+ break;
+ }
+
+ // Advance to the next block.
+ cur_addr = vma_end;
+ iter = FindVMA(cur_addr);
+ ASSERT_MSG(iter != vma_map.end(), "UnmapPhysicalMemory iter != end");
+ }
+ }
+
+ // If we failed, re-map regions.
+ // TODO: Preserve memory contents?
+ if (result.IsError()) {
+ for (const auto [map_address, map_size] : unmapped_regions) {
+ const auto remap_res =
+ MapMemoryBlock(map_address, std::make_shared<PhysicalMemory>(map_size, 0), 0,
+ map_size, MemoryState::Heap, VMAPermission::None);
+ ASSERT_MSG(remap_res.Succeeded(), "UnmapPhysicalMemory re-map on error");
+ }
+ }
+
+ // Update mapped amount
+ physical_memory_mapped -= mapped_size;
+
+ return RESULT_SUCCESS;
+}
+
ResultCode VMManager::MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) {
constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped;
const auto src_check_result = CheckRangeState(
@@ -447,7 +594,7 @@ ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, Mem
ASSERT_MSG(vma_offset + size <= vma->second.size,
"Shared memory exceeds bounds of mapped block");
- const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
+ const std::shared_ptr<PhysicalMemory>& backing_block = vma->second.backing_block;
const std::size_t backing_block_offset = vma->second.offset + vma_offset;
CASCADE_RESULT(auto new_vma,
@@ -455,12 +602,12 @@ ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, Mem
// Protect mirror with permissions from old region
Reprotect(new_vma, vma->second.permissions);
// Remove permissions from old region
- Reprotect(vma, VMAPermission::None);
+ ReprotectRange(src_addr, size, VMAPermission::None);
return RESULT_SUCCESS;
}
-void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) {
+void VMManager::RefreshMemoryBlockMappings(const PhysicalMemory* block) {
// If this ever proves to have a noticeable performance impact, allow users of the function to
// specify a specific range of addresses to limit the scan to.
for (const auto& p : vma_map) {
@@ -588,14 +735,14 @@ VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u64 offset_in_vma) {
VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) {
const VMAIter next_vma = std::next(iter);
if (next_vma != vma_map.end() && iter->second.CanBeMergedWith(next_vma->second)) {
- iter->second.size += next_vma->second.size;
+ MergeAdjacentVMA(iter->second, next_vma->second);
vma_map.erase(next_vma);
}
if (iter != vma_map.begin()) {
VMAIter prev_vma = std::prev(iter);
if (prev_vma->second.CanBeMergedWith(iter->second)) {
- prev_vma->second.size += iter->second.size;
+ MergeAdjacentVMA(prev_vma->second, iter->second);
vma_map.erase(iter);
iter = prev_vma;
}
@@ -604,6 +751,38 @@ VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) {
return iter;
}
+void VMManager::MergeAdjacentVMA(VirtualMemoryArea& left, const VirtualMemoryArea& right) {
+ ASSERT(left.CanBeMergedWith(right));
+
+ // Always merge allocated memory blocks, even when they don't share the same backing block.
+ if (left.type == VMAType::AllocatedMemoryBlock &&
+ (left.backing_block != right.backing_block || left.offset + left.size != right.offset)) {
+ // Check if we can save work.
+ if (left.offset == 0 && left.size == left.backing_block->size()) {
+ // Fast case: left is an entire backing block.
+ left.backing_block->insert(left.backing_block->end(),
+ right.backing_block->begin() + right.offset,
+ right.backing_block->begin() + right.offset + right.size);
+ } else {
+ // Slow case: make a new memory block for left and right.
+ auto new_memory = std::make_shared<PhysicalMemory>();
+ new_memory->insert(new_memory->end(), left.backing_block->begin() + left.offset,
+ left.backing_block->begin() + left.offset + left.size);
+ new_memory->insert(new_memory->end(), right.backing_block->begin() + right.offset,
+ right.backing_block->begin() + right.offset + right.size);
+ left.backing_block = new_memory;
+ left.offset = 0;
+ }
+
+ // Page table update is needed, because backing memory changed.
+ left.size += right.size;
+ UpdatePageTableForVMA(left);
+ } else {
+ // Just update the size.
+ left.size += right.size;
+ }
+}
+
void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
switch (vma.type) {
case VMAType::Free:
@@ -778,6 +957,84 @@ VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, Memo
std::make_tuple(initial_state, initial_permissions, initial_attributes & ~ignore_mask));
}
+ResultVal<std::size_t> VMManager::SizeOfAllocatedVMAsInRange(VAddr address,
+ std::size_t size) const {
+ const VAddr end_addr = address + size;
+ const VAddr last_addr = end_addr - 1;
+ std::size_t mapped_size = 0;
+
+ VAddr cur_addr = address;
+ auto iter = FindVMA(cur_addr);
+ ASSERT_MSG(iter != vma_map.end(), "SizeOfAllocatedVMAsInRange iter != end");
+
+ while (true) {
+ const auto& vma = iter->second;
+ const VAddr vma_start = vma.base;
+ const VAddr vma_end = vma_start + vma.size;
+ const VAddr vma_last = vma_end - 1;
+
+ // Add size if relevant.
+ if (vma.state != MemoryState::Unmapped) {
+ mapped_size += std::min(end_addr - cur_addr, vma_end - cur_addr);
+ }
+
+ // Break once we hit the end of the range.
+ if (last_addr <= vma_last) {
+ break;
+ }
+
+ // Advance to the next block.
+ cur_addr = vma_end;
+ iter = std::next(iter);
+ ASSERT_MSG(iter != vma_map.end(), "SizeOfAllocatedVMAsInRange iter != end");
+ }
+
+ return MakeResult(mapped_size);
+}
+
+ResultVal<std::size_t> VMManager::SizeOfUnmappablePhysicalMemoryInRange(VAddr address,
+ std::size_t size) const {
+ const VAddr end_addr = address + size;
+ const VAddr last_addr = end_addr - 1;
+ std::size_t mapped_size = 0;
+
+ VAddr cur_addr = address;
+ auto iter = FindVMA(cur_addr);
+ ASSERT_MSG(iter != vma_map.end(), "SizeOfUnmappablePhysicalMemoryInRange iter != end");
+
+ while (true) {
+ const auto& vma = iter->second;
+ const auto vma_start = vma.base;
+ const auto vma_end = vma_start + vma.size;
+ const auto vma_last = vma_end - 1;
+ const auto state = vma.state;
+ const auto attr = vma.attribute;
+
+ // Memory within region must be free or mapped heap.
+ if (!((state == MemoryState::Heap && attr == MemoryAttribute::None) ||
+ (state == MemoryState::Unmapped))) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ // Add size if relevant.
+ if (state != MemoryState::Unmapped) {
+ mapped_size += std::min(end_addr - cur_addr, vma_end - cur_addr);
+ }
+
+ // Break once we hit the end of the range.
+ if (last_addr <= vma_last) {
+ break;
+ }
+
+ // Advance to the next block.
+ cur_addr = vma_end;
+ iter = std::next(iter);
+ ASSERT_MSG(iter != vma_map.end(), "SizeOfUnmappablePhysicalMemoryInRange iter != end");
+ }
+
+ return MakeResult(mapped_size);
+}
+
u64 VMManager::GetTotalPhysicalMemoryAvailable() const {
LOG_WARNING(Kernel, "(STUBBED) called");
return 0xF8000000;
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 9fe6ac3f4..b18cde619 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -11,6 +11,7 @@
#include "common/common_types.h"
#include "common/memory_hook.h"
#include "common/page_table.h"
+#include "core/hle/kernel/physical_memory.h"
#include "core/hle/result.h"
#include "core/memory.h"
@@ -290,7 +291,7 @@ struct VirtualMemoryArea {
// Settings for type = AllocatedMemoryBlock
/// Memory block backing this VMA.
- std::shared_ptr<std::vector<u8>> backing_block = nullptr;
+ std::shared_ptr<PhysicalMemory> backing_block = nullptr;
/// Offset into the backing_memory the mapping starts from.
std::size_t offset = 0;
@@ -348,8 +349,9 @@ public:
* @param size Size of the mapping.
* @param state MemoryState tag to attach to the VMA.
*/
- ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block,
- std::size_t offset, u64 size, MemoryState state);
+ ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<PhysicalMemory> block,
+ std::size_t offset, u64 size, MemoryState state,
+ VMAPermission perm = VMAPermission::ReadWrite);
/**
* Maps an unmanaged host memory pointer at a given address.
@@ -450,6 +452,34 @@ public:
///
ResultVal<VAddr> SetHeapSize(u64 size);
+ /// Maps memory at a given address.
+ ///
+ /// @param addr The virtual address to map memory at.
+ /// @param size The amount of memory to map.
+ ///
+ /// @note The destination address must lie within the Map region.
+ ///
+ /// @note This function requires that SystemResourceSize be non-zero,
+ /// however, this is just because if it were not then the
+ /// resulting page tables could be exploited on hardware by
+ /// a malicious program. SystemResource usage does not need
+ /// to be explicitly checked or updated here.
+ ResultCode MapPhysicalMemory(VAddr target, u64 size);
+
+ /// Unmaps memory at a given address.
+ ///
+ /// @param addr The virtual address to unmap memory at.
+ /// @param size The amount of memory to unmap.
+ ///
+ /// @note The destination address must lie within the Map region.
+ ///
+ /// @note This function requires that SystemResourceSize be non-zero,
+ /// however, this is just because if it were not then the
+ /// resulting page tables could be exploited on hardware by
+ /// a malicious program. SystemResource usage does not need
+ /// to be explicitly checked or updated here.
+ ResultCode UnmapPhysicalMemory(VAddr target, u64 size);
+
/// Maps a region of memory as code memory.
///
/// @param dst_address The base address of the region to create the aliasing memory region.
@@ -518,7 +548,7 @@ public:
* Scans all VMAs and updates the page table range of any that use the given vector as backing
* memory. This should be called after any operation that causes reallocation of the vector.
*/
- void RefreshMemoryBlockMappings(const std::vector<u8>* block);
+ void RefreshMemoryBlockMappings(const PhysicalMemory* block);
/// Dumps the address space layout to the log, for debugging
void LogLayout() const;
@@ -657,6 +687,11 @@ private:
*/
VMAIter MergeAdjacent(VMAIter vma);
+ /**
+ * Merges two adjacent VMAs.
+ */
+ void MergeAdjacentVMA(VirtualMemoryArea& left, const VirtualMemoryArea& right);
+
/// Updates the pages corresponding to this VMA so they match the VMA's attributes.
void UpdatePageTableForVMA(const VirtualMemoryArea& vma);
@@ -701,6 +736,13 @@ private:
MemoryAttribute attribute_mask, MemoryAttribute attribute,
MemoryAttribute ignore_mask) const;
+ /// Gets the amount of memory currently mapped (state != Unmapped) in a range.
+ ResultVal<std::size_t> SizeOfAllocatedVMAsInRange(VAddr address, std::size_t size) const;
+
+ /// Gets the amount of memory unmappable by UnmapPhysicalMemory in a range.
+ ResultVal<std::size_t> SizeOfUnmappablePhysicalMemoryInRange(VAddr address,
+ std::size_t size) const;
+
/**
* A map covering the entirety of the managed address space, keyed by the `base` field of each
* VMA. It must always be modified by splitting or merging VMAs, so that the invariant
@@ -736,12 +778,17 @@ private:
// the entire virtual address space extents that bound the allocations, including any holes.
// This makes deallocation and reallocation of holes fast and keeps process memory contiguous
// in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
- std::shared_ptr<std::vector<u8>> heap_memory;
+ std::shared_ptr<PhysicalMemory> heap_memory;
// The end of the currently allocated heap. This is not an inclusive
// end of the range. This is essentially 'base_address + current_size'.
VAddr heap_end = 0;
+ // The current amount of memory mapped via MapPhysicalMemory.
+ // This is used here (and in Nintendo's kernel) only for debugging, and does not impact
+ // any behavior.
+ u64 physical_memory_mapped = 0;
+
Core::System& system;
};
} // namespace Kernel
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 9fdcf2965..a192a1f5f 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -266,8 +266,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
{65, nullptr, "ReportUserIsActive"},
{66, nullptr, "GetCurrentIlluminance"},
{67, nullptr, "IsIlluminanceAvailable"},
- {68, nullptr, "SetAutoSleepDisabled"},
- {69, nullptr, "IsAutoSleepDisabled"},
+ {68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"},
+ {69, &ISelfController::IsAutoSleepDisabled, "IsAutoSleepDisabled"},
{70, nullptr, "ReportMultimediaError"},
{71, nullptr, "GetCurrentIlluminanceEx"},
{80, nullptr, "SetWirelessPriorityMode"},
@@ -454,6 +454,34 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
rb.Push<u32>(idle_time_detection_extension);
}
+void ISelfController::SetAutoSleepDisabled(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ is_auto_sleep_disabled = rp.Pop<bool>();
+
+ // On the system itself, if the previous state of is_auto_sleep_disabled
+ // differed from the current value passed in, it'd signify the internal
+ // window manager to update (and also increment some statistics like update counts)
+ //
+ // It'd also indicate this change to an idle handling context.
+ //
+ // However, given we're emulating this behavior, most of this can be ignored
+ // and it's sufficient to simply set the member variable for querying via
+ // IsAutoSleepDisabled().
+
+ LOG_DEBUG(Service_AM, "called. is_auto_sleep_disabled={}", is_auto_sleep_disabled);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void ISelfController::IsAutoSleepDisabled(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called.");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(is_auto_sleep_disabled);
+}
+
void ISelfController::GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called.");
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 14b010164..6cb582483 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -133,6 +133,8 @@ private:
void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
+ void SetAutoSleepDisabled(Kernel::HLERequestContext& ctx);
+ void IsAutoSleepDisabled(Kernel::HLERequestContext& ctx);
void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
@@ -142,6 +144,7 @@ private:
u32 idle_time_detection_extension = 0;
u64 num_fatal_sections_entered = 0;
+ bool is_auto_sleep_disabled = false;
};
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp
index 128df7db5..1781bec83 100644
--- a/src/core/hle/service/audio/audio.cpp
+++ b/src/core/hle/service/audio/audio.cpp
@@ -19,16 +19,16 @@
namespace Service::Audio {
-void InstallInterfaces(SM::ServiceManager& service_manager) {
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
std::make_shared<AudCtl>()->InstallAsService(service_manager);
std::make_shared<AudOutA>()->InstallAsService(service_manager);
- std::make_shared<AudOutU>()->InstallAsService(service_manager);
+ std::make_shared<AudOutU>(system)->InstallAsService(service_manager);
std::make_shared<AudInA>()->InstallAsService(service_manager);
std::make_shared<AudInU>()->InstallAsService(service_manager);
std::make_shared<AudRecA>()->InstallAsService(service_manager);
std::make_shared<AudRecU>()->InstallAsService(service_manager);
std::make_shared<AudRenA>()->InstallAsService(service_manager);
- std::make_shared<AudRenU>()->InstallAsService(service_manager);
+ std::make_shared<AudRenU>(system)->InstallAsService(service_manager);
std::make_shared<CodecCtl>()->InstallAsService(service_manager);
std::make_shared<HwOpus>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/audio/audio.h b/src/core/hle/service/audio/audio.h
index f5bd3bf5f..b6d13912e 100644
--- a/src/core/hle/service/audio/audio.h
+++ b/src/core/hle/service/audio/audio.h
@@ -4,6 +4,10 @@
#pragma once
+namespace Core {
+class System;
+}
+
namespace Service::SM {
class ServiceManager;
}
@@ -11,6 +15,6 @@ class ServiceManager;
namespace Service::Audio {
/// Registers all Audio services with the specified service manager.
-void InstallInterfaces(SM::ServiceManager& service_manager);
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 7db6eb08d..fb84a8f13 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -40,8 +40,8 @@ enum class AudioState : u32 {
class IAudioOut final : public ServiceFramework<IAudioOut> {
public:
- IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core, std::string&& device_name,
- std::string&& unique_name)
+ IAudioOut(Core::System& system, AudoutParams audio_params, AudioCore::AudioOut& audio_core,
+ std::string&& device_name, std::string&& unique_name)
: ServiceFramework("IAudioOut"), audio_core(audio_core),
device_name(std::move(device_name)), audio_params(audio_params) {
// clang-format off
@@ -65,7 +65,6 @@ public:
RegisterHandlers(functions);
// This is the event handle used to check if the audio buffer was released
- auto& system = Core::System::GetInstance();
buffer_event = Kernel::WritableEvent::CreateEventPair(
system.Kernel(), Kernel::ResetType::Manual, "IAudioOutBufferReleased");
@@ -212,6 +211,22 @@ private:
Kernel::EventPair buffer_event;
};
+AudOutU::AudOutU(Core::System& system_) : ServiceFramework("audout:u"), system{system_} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"},
+ {1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"},
+ {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"},
+ {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ audio_core = std::make_unique<AudioCore::AudioOut>();
+}
+
+AudOutU::~AudOutU() = default;
+
void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
@@ -248,7 +263,7 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())};
auto audio_out_interface = std::make_shared<IAudioOut>(
- params, *audio_core, std::move(device_name), std::move(unique_name));
+ system, params, *audio_core, std::move(device_name), std::move(unique_name));
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
rb.Push(RESULT_SUCCESS);
@@ -256,20 +271,9 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
rb.Push<u32>(params.channel_count);
rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
- rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
+ rb.PushIpcInterface<IAudioOut>(audio_out_interface);
audio_out_interfaces.push_back(std::move(audio_out_interface));
}
-AudOutU::AudOutU() : ServiceFramework("audout:u") {
- static const FunctionInfo functions[] = {{0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"},
- {1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"},
- {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"},
- {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"}};
- RegisterHandlers(functions);
- audio_core = std::make_unique<AudioCore::AudioOut>();
-}
-
-AudOutU::~AudOutU() = default;
-
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h
index aed4c43b2..c9f532ccd 100644
--- a/src/core/hle/service/audio/audout_u.h
+++ b/src/core/hle/service/audio/audout_u.h
@@ -11,6 +11,10 @@ namespace AudioCore {
class AudioOut;
}
+namespace Core {
+class System;
+}
+
namespace Kernel {
class HLERequestContext;
}
@@ -21,15 +25,17 @@ class IAudioOut;
class AudOutU final : public ServiceFramework<AudOutU> {
public:
- AudOutU();
+ explicit AudOutU(Core::System& system_);
~AudOutU() override;
private:
+ void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
+ void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
+
std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces;
std::unique_ptr<AudioCore::AudioOut> audio_core;
- void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
- void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
+ Core::System& system;
};
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 3711e1ea1..5b0b7f17e 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -5,6 +5,7 @@
#include <algorithm>
#include <array>
#include <memory>
+#include <string_view>
#include "audio_core/audio_renderer.h"
#include "common/alignment.h"
@@ -25,7 +26,8 @@ namespace Service::Audio {
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
public:
- explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params)
+ explicit IAudioRenderer(Core::System& system, AudioCore::AudioRendererParameter audren_params,
+ const std::size_t instance_number)
: ServiceFramework("IAudioRenderer") {
// clang-format off
static const FunctionInfo functions[] = {
@@ -45,11 +47,10 @@ public:
// clang-format on
RegisterHandlers(functions);
- auto& system = Core::System::GetInstance();
system_event = Kernel::WritableEvent::CreateEventPair(
system.Kernel(), Kernel::ResetType::Manual, "IAudioRenderer:SystemEvent");
- renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), audren_params,
- system_event.writable);
+ renderer = std::make_unique<AudioCore::AudioRenderer>(
+ system.CoreTiming(), audren_params, system_event.writable, instance_number);
}
private:
@@ -159,7 +160,8 @@ private:
class IAudioDevice final : public ServiceFramework<IAudioDevice> {
public:
- IAudioDevice() : ServiceFramework("IAudioDevice") {
+ explicit IAudioDevice(Core::System& system, u32_le revision_num)
+ : ServiceFramework("IAudioDevice"), revision{revision_num} {
static const FunctionInfo functions[] = {
{0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
{1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"},
@@ -177,7 +179,7 @@ public:
};
RegisterHandlers(functions);
- auto& kernel = Core::System::GetInstance().Kernel();
+ auto& kernel = system.Kernel();
buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
"IAudioOutBufferReleasedEvent");
@@ -188,15 +190,47 @@ public:
}
private:
+ using AudioDeviceName = std::array<char, 256>;
+ static constexpr std::array<std::string_view, 4> audio_device_names{{
+ "AudioStereoJackOutput",
+ "AudioBuiltInSpeakerOutput",
+ "AudioTvOutput",
+ "AudioUsbDeviceOutput",
+ }};
+ enum class DeviceType {
+ AHUBHeadphones,
+ AHUBSpeakers,
+ HDA,
+ USBOutput,
+ };
+
void ListAudioDeviceName(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "called");
- constexpr std::array<char, 15> audio_interface{{"AudioInterface"}};
- ctx.WriteBuffer(audio_interface);
+ const bool usb_output_supported =
+ IsFeatureSupported(AudioFeatures::AudioUSBDeviceOutput, revision);
+ const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioDeviceName);
+
+ std::vector<AudioDeviceName> name_buffer;
+ name_buffer.reserve(audio_device_names.size());
+
+ for (std::size_t i = 0; i < count && i < audio_device_names.size(); i++) {
+ const auto type = static_cast<DeviceType>(i);
+
+ if (!usb_output_supported && type == DeviceType::USBOutput) {
+ continue;
+ }
+
+ const auto& device_name = audio_device_names[i];
+ auto& entry = name_buffer.emplace_back();
+ device_name.copy(entry.data(), device_name.size());
+ }
+
+ ctx.WriteBuffer(name_buffer);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(1);
+ rb.Push(static_cast<u32>(name_buffer.size()));
}
void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) {
@@ -215,12 +249,16 @@ private:
void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
- constexpr std::array<char, 12> audio_interface{{"AudioDevice"}};
- ctx.WriteBuffer(audio_interface);
+ // Currently set to always be TV audio output.
+ const auto& device_name = audio_device_names[2];
- IPC::ResponseBuilder rb{ctx, 3};
+ AudioDeviceName out_device_name{};
+ device_name.copy(out_device_name.data(), device_name.size());
+
+ ctx.WriteBuffer(out_device_name);
+
+ IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(1);
}
void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) {
@@ -249,12 +287,13 @@ private:
rb.PushCopyObjects(audio_output_device_switch_event.readable);
}
+ u32_le revision = 0;
Kernel::EventPair buffer_event;
Kernel::EventPair audio_output_device_switch_event;
}; // namespace Audio
-AudRenU::AudRenU() : ServiceFramework("audren:u") {
+AudRenU::AudRenU(Core::System& system_) : ServiceFramework("audren:u"), system{system_} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"},
@@ -327,7 +366,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
};
// Calculates the portion of the size related to the mix data (and the sorting thereof).
- const auto calculate_mix_info_size = [this](const AudioCore::AudioRendererParameter& params) {
+ const auto calculate_mix_info_size = [](const AudioCore::AudioRendererParameter& params) {
// The size of the mixing info data structure.
constexpr u64 mix_info_size = 0x940;
@@ -399,7 +438,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
// Calculates the part of the size related to the splitter context.
const auto calculate_splitter_context_size =
- [this](const AudioCore::AudioRendererParameter& params) -> u64 {
+ [](const AudioCore::AudioRendererParameter& params) -> u64 {
if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
return 0;
}
@@ -446,7 +485,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
};
// Calculates the part of the size related to performance statistics.
- const auto calculate_perf_size = [this](const AudioCore::AudioRendererParameter& params) {
+ const auto calculate_perf_size = [](const AudioCore::AudioRendererParameter& params) {
// Extra size value appended to the end of the calculation.
constexpr u64 appended = 128;
@@ -473,78 +512,76 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
};
// Calculates the part of the size that relates to the audio command buffer.
- const auto calculate_command_buffer_size =
- [this](const AudioCore::AudioRendererParameter& params) {
- constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
+ const auto calculate_command_buffer_size = [](const AudioCore::AudioRendererParameter& params) {
+ constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
- if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
- constexpr u64 command_buffer_size = 0x18000;
+ if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
+ constexpr u64 command_buffer_size = 0x18000;
- return command_buffer_size + alignment;
- }
+ return command_buffer_size + alignment;
+ }
- // When the variadic command buffer is supported, this means
- // the command generator for the audio renderer can issue commands
- // that are (as one would expect), variable in size. So what we need to do
- // is determine the maximum possible size for a few command data structures
- // then multiply them by the amount of present commands indicated by the given
- // respective audio parameters.
+ // When the variadic command buffer is supported, this means
+ // the command generator for the audio renderer can issue commands
+ // that are (as one would expect), variable in size. So what we need to do
+ // is determine the maximum possible size for a few command data structures
+ // then multiply them by the amount of present commands indicated by the given
+ // respective audio parameters.
- constexpr u64 max_biquad_filters = 2;
- constexpr u64 max_mix_buffers = 24;
+ constexpr u64 max_biquad_filters = 2;
+ constexpr u64 max_mix_buffers = 24;
- constexpr u64 biquad_filter_command_size = 0x2C;
+ constexpr u64 biquad_filter_command_size = 0x2C;
- constexpr u64 depop_mix_command_size = 0x24;
- constexpr u64 depop_setup_command_size = 0x50;
+ constexpr u64 depop_mix_command_size = 0x24;
+ constexpr u64 depop_setup_command_size = 0x50;
- constexpr u64 effect_command_max_size = 0x540;
+ constexpr u64 effect_command_max_size = 0x540;
- constexpr u64 mix_command_size = 0x1C;
- constexpr u64 mix_ramp_command_size = 0x24;
- constexpr u64 mix_ramp_grouped_command_size = 0x13C;
+ constexpr u64 mix_command_size = 0x1C;
+ constexpr u64 mix_ramp_command_size = 0x24;
+ constexpr u64 mix_ramp_grouped_command_size = 0x13C;
- constexpr u64 perf_command_size = 0x28;
+ constexpr u64 perf_command_size = 0x28;
- constexpr u64 sink_command_size = 0x130;
+ constexpr u64 sink_command_size = 0x130;
- constexpr u64 submix_command_max_size =
- depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
+ constexpr u64 submix_command_max_size =
+ depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
- constexpr u64 volume_command_size = 0x1C;
- constexpr u64 volume_ramp_command_size = 0x20;
+ constexpr u64 volume_command_size = 0x1C;
+ constexpr u64 volume_ramp_command_size = 0x20;
- constexpr u64 voice_biquad_filter_command_size =
- biquad_filter_command_size * max_biquad_filters;
- constexpr u64 voice_data_command_size = 0x9C;
- const u64 voice_command_max_size =
- (params.splitter_count * depop_setup_command_size) +
- (voice_data_command_size + voice_biquad_filter_command_size +
- volume_ramp_command_size + mix_ramp_grouped_command_size);
+ constexpr u64 voice_biquad_filter_command_size =
+ biquad_filter_command_size * max_biquad_filters;
+ constexpr u64 voice_data_command_size = 0x9C;
+ const u64 voice_command_max_size =
+ (params.splitter_count * depop_setup_command_size) +
+ (voice_data_command_size + voice_biquad_filter_command_size + volume_ramp_command_size +
+ mix_ramp_grouped_command_size);
- // Now calculate the individual elements that comprise the size and add them together.
- const u64 effect_commands_size = params.effect_count * effect_command_max_size;
+ // Now calculate the individual elements that comprise the size and add them together.
+ const u64 effect_commands_size = params.effect_count * effect_command_max_size;
- const u64 final_mix_commands_size =
- depop_mix_command_size + volume_command_size * max_mix_buffers;
+ const u64 final_mix_commands_size =
+ depop_mix_command_size + volume_command_size * max_mix_buffers;
- const u64 perf_commands_size =
- perf_command_size *
- (CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
+ const u64 perf_commands_size =
+ perf_command_size * (CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
- const u64 sink_commands_size = params.sink_count * sink_command_size;
+ const u64 sink_commands_size = params.sink_count * sink_command_size;
- const u64 splitter_commands_size =
- params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size;
+ const u64 splitter_commands_size =
+ params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size;
- const u64 submix_commands_size = params.submix_count * submix_command_max_size;
+ const u64 submix_commands_size = params.submix_count * submix_command_max_size;
- const u64 voice_commands_size = params.voice_count * voice_command_max_size;
+ const u64 voice_commands_size = params.voice_count * voice_command_max_size;
- return effect_commands_size + final_mix_commands_size + perf_commands_size +
- sink_commands_size + splitter_commands_size + submix_commands_size +
- voice_commands_size + alignment;
- };
+ return effect_commands_size + final_mix_commands_size + perf_commands_size +
+ sink_commands_size + splitter_commands_size + submix_commands_size +
+ voice_commands_size + alignment;
+ };
IPC::RequestParser rp{ctx};
const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
@@ -577,12 +614,16 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
}
void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ IPC::RequestParser rp{ctx};
+ const u64 aruid = rp.Pop<u64>();
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ LOG_DEBUG(Service_Audio, "called. aruid={:016X}", aruid);
+ // Revisionless variant of GetAudioDeviceServiceWithRevisionInfo that
+ // always assumes the initial release revision (REV1).
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<Audio::IAudioDevice>();
+ rb.PushIpcInterface<IAudioDevice>(system, Common::MakeMagic('R', 'E', 'V', '1'));
}
void AudRenU::OpenAudioRendererAuto(Kernel::HLERequestContext& ctx) {
@@ -592,13 +633,19 @@ void AudRenU::OpenAudioRendererAuto(Kernel::HLERequestContext& ctx) {
}
void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ struct Parameters {
+ u32 revision;
+ u64 aruid;
+ };
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ IPC::RequestParser rp{ctx};
+ const auto [revision, aruid] = rp.PopRaw<Parameters>();
+
+ LOG_DEBUG(Service_Audio, "called. revision={:08X}, aruid={:016X}", revision, aruid);
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<Audio::IAudioDevice>(); // TODO(ogniK): Figure out what is different
- // based on the current revision
+ rb.PushIpcInterface<IAudioDevice>(system, revision);
}
void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
@@ -607,14 +654,16 @@ void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IAudioRenderer>(params);
+ rb.PushIpcInterface<IAudioRenderer>(system, params, audren_instance_count++);
}
-bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const {
+bool IsFeatureSupported(AudioFeatures feature, u32_le revision) {
// Byte swap
const u32_be version_num = revision - Common::MakeMagic('R', 'E', 'V', '0');
switch (feature) {
+ case AudioFeatures::AudioUSBDeviceOutput:
+ return version_num >= 4U;
case AudioFeatures::Splitter:
return version_num >= 2U;
case AudioFeatures::PerformanceMetricsVersion2:
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h
index 1d3c8df61..4e0ccc792 100644
--- a/src/core/hle/service/audio/audren_u.h
+++ b/src/core/hle/service/audio/audren_u.h
@@ -6,6 +6,10 @@
#include "core/hle/service/service.h"
+namespace Core {
+class System;
+}
+
namespace Kernel {
class HLERequestContext;
}
@@ -14,7 +18,7 @@ namespace Service::Audio {
class AudRenU final : public ServiceFramework<AudRenU> {
public:
- explicit AudRenU();
+ explicit AudRenU(Core::System& system_);
~AudRenU() override;
private:
@@ -26,13 +30,19 @@ private:
void OpenAudioRendererImpl(Kernel::HLERequestContext& ctx);
- enum class AudioFeatures : u32 {
- Splitter,
- PerformanceMetricsVersion2,
- VariadicCommandBuffer,
- };
+ std::size_t audren_instance_count = 0;
+ Core::System& system;
+};
- bool IsFeatureSupported(AudioFeatures feature, u32_le revision) const;
+// Describes a particular audio feature that may be supported in a particular revision.
+enum class AudioFeatures : u32 {
+ AudioUSBDeviceOutput,
+ Splitter,
+ PerformanceMetricsVersion2,
+ VariadicCommandBuffer,
};
+// Tests if a particular audio feature is supported with a given audio revision.
+bool IsFeatureSupported(AudioFeatures feature, u32_le revision);
+
} // namespace Service::Audio
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index dec541f2e..d1ec12ef9 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -22,7 +22,7 @@ public:
{0, nullptr, "GetCompletionEvent"},
{1, nullptr, "Cancel"},
{10100, nullptr, "GetFriendListIds"},
- {10101, nullptr, "GetFriendList"},
+ {10101, &IFriendService::GetFriendList, "GetFriendList"},
{10102, nullptr, "UpdateFriendInfo"},
{10110, nullptr, "GetFriendProfileImage"},
{10200, nullptr, "SendFriendRequestForApplication"},
@@ -99,6 +99,23 @@ public:
}
private:
+ enum class PresenceFilter : u32 {
+ None = 0,
+ Online = 1,
+ OnlinePlay = 2,
+ OnlineOrOnlinePlay = 3,
+ };
+
+ struct SizedFriendFilter {
+ PresenceFilter presence;
+ u8 is_favorite;
+ u8 same_app;
+ u8 same_app_played;
+ u8 arbitary_app_played;
+ u64 group_id;
+ };
+ static_assert(sizeof(SizedFriendFilter) == 0x10, "SizedFriendFilter is an invalid size");
+
void DeclareCloseOnlinePlaySession(Kernel::HLERequestContext& ctx) {
// Stub used by Splatoon 2
LOG_WARNING(Service_ACC, "(STUBBED) called");
@@ -112,6 +129,22 @@ private:
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
+
+ void GetFriendList(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto friend_offset = rp.Pop<u32>();
+ const auto uuid = rp.PopRaw<Common::UUID>();
+ [[maybe_unused]] const auto filter = rp.PopRaw<SizedFriendFilter>();
+ const auto pid = rp.Pop<u64>();
+ LOG_WARNING(Service_ACC, "(STUBBED) called, offset={}, uuid={}, pid={}", friend_offset,
+ uuid.Format(), pid);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+
+ rb.Push<u32>(0); // Friend count
+ // TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId"
+ }
};
class INotificationService final : public ServiceFramework<INotificationService> {
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index b839303ac..8ddad8682 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -345,14 +345,16 @@ public:
vm_manager
.MirrorMemory(*map_address, nro_address, nro_size, Kernel::MemoryState::ModuleCode)
.IsSuccess());
- ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess());
+ ASSERT(vm_manager.ReprotectRange(nro_address, nro_size, Kernel::VMAPermission::None)
+ .IsSuccess());
if (bss_size > 0) {
ASSERT(vm_manager
.MirrorMemory(*map_address + nro_size, bss_address, bss_size,
Kernel::MemoryState::ModuleCode)
.IsSuccess());
- ASSERT(vm_manager.UnmapRange(bss_address, bss_size).IsSuccess());
+ ASSERT(vm_manager.ReprotectRange(bss_address, bss_size, Kernel::VMAPermission::None)
+ .IsSuccess());
}
vm_manager.ReprotectRange(*map_address, header.text_size,
@@ -364,7 +366,8 @@ public:
Core::System::GetInstance().InvalidateCpuInstructionCaches();
- nro.insert_or_assign(*map_address, NROInfo{hash, nro_size + bss_size});
+ nro.insert_or_assign(*map_address,
+ NROInfo{hash, nro_address, nro_size, bss_address, bss_size});
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
@@ -409,9 +412,23 @@ public:
}
auto& vm_manager = Core::CurrentProcess()->VMManager();
- const auto& nro_size = iter->second.size;
+ const auto& nro_info = iter->second;
- ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess());
+ // Unmap the mirrored memory
+ ASSERT(
+ vm_manager.UnmapRange(nro_address, nro_info.nro_size + nro_info.bss_size).IsSuccess());
+
+ // Reprotect the source memory
+ ASSERT(vm_manager
+ .ReprotectRange(nro_info.nro_address, nro_info.nro_size,
+ Kernel::VMAPermission::ReadWrite)
+ .IsSuccess());
+ if (nro_info.bss_size > 0) {
+ ASSERT(vm_manager
+ .ReprotectRange(nro_info.bss_address, nro_info.bss_size,
+ Kernel::VMAPermission::ReadWrite)
+ .IsSuccess());
+ }
Core::System::GetInstance().InvalidateCpuInstructionCaches();
@@ -473,7 +490,10 @@ private:
struct NROInfo {
SHA256Hash hash;
- u64 size;
+ VAddr nro_address;
+ u64 nro_size;
+ VAddr bss_address;
+ u64 bss_size;
};
bool initialized = false;
diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp
index ce84e25ed..0b3923ad9 100644
--- a/src/core/hle/service/mii/mii.cpp
+++ b/src/core/hle/service/mii/mii.cpp
@@ -48,7 +48,7 @@ public:
{19, nullptr, "Export"},
{20, nullptr, "IsBrokenDatabaseWithClearFlag"},
{21, &IDatabaseService::GetIndex, "GetIndex"},
- {22, nullptr, "SetInterfaceVersion"},
+ {22, &IDatabaseService::SetInterfaceVersion, "SetInterfaceVersion"},
{23, nullptr, "Convert"},
};
// clang-format on
@@ -350,8 +350,22 @@ private:
rb.Push(index);
}
+ void SetInterfaceVersion(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ current_interface_version = rp.PopRaw<u32>();
+
+ LOG_DEBUG(Service_Mii, "called, interface_version={:08X}", current_interface_version);
+
+ UNIMPLEMENTED_IF(current_interface_version != 1);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
MiiManager db;
+ u32 current_interface_version = 0;
+
// Last read offsets of Get functions
std::array<u32, 4> offsets{};
};
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index ad176f89d..2a522136d 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -77,7 +77,7 @@ enum class LoadState : u32 {
Done = 1,
};
-static void DecryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output,
+static void DecryptSharedFont(const std::vector<u32>& input, Kernel::PhysicalMemory& output,
std::size_t& offset) {
ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE,
"Shared fonts exceeds 17mb!");
@@ -94,7 +94,7 @@ static void DecryptSharedFont(const std::vector<u32>& input, std::vector<u8>& ou
offset += transformed_font.size() * sizeof(u32);
}
-static void EncryptSharedFont(const std::vector<u8>& input, std::vector<u8>& output,
+static void EncryptSharedFont(const std::vector<u8>& input, Kernel::PhysicalMemory& output,
std::size_t& offset) {
ASSERT_MSG(offset + input.size() + 8 < SHARED_FONT_MEM_SIZE, "Shared fonts exceeds 17mb!");
const u32 KEY = EXPECTED_MAGIC ^ EXPECTED_RESULT;
@@ -121,7 +121,7 @@ struct PL_U::Impl {
return shared_font_regions.at(index);
}
- void BuildSharedFontsRawRegions(const std::vector<u8>& input) {
+ void BuildSharedFontsRawRegions(const Kernel::PhysicalMemory& input) {
// As we can derive the xor key we can just populate the offsets
// based on the shared memory dump
unsigned cur_offset = 0;
@@ -144,7 +144,7 @@ struct PL_U::Impl {
Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem;
/// Backing memory for the shared font data
- std::shared_ptr<std::vector<u8>> shared_font;
+ std::shared_ptr<Kernel::PhysicalMemory> shared_font;
// Automatically populated based on shared_fonts dump or system archives.
std::vector<FontRegion> shared_font_regions;
@@ -166,7 +166,7 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
// Rebuild shared fonts from data ncas
if (nand->HasEntry(static_cast<u64>(FontArchives::Standard),
FileSys::ContentRecordType::Data)) {
- impl->shared_font = std::make_shared<std::vector<u8>>(SHARED_FONT_MEM_SIZE);
+ impl->shared_font = std::make_shared<Kernel::PhysicalMemory>(SHARED_FONT_MEM_SIZE);
for (auto font : SHARED_FONTS) {
const auto nca =
nand->GetEntry(static_cast<u64>(font.first), FileSys::ContentRecordType::Data);
@@ -207,7 +207,7 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
}
} else {
- impl->shared_font = std::make_shared<std::vector<u8>>(
+ impl->shared_font = std::make_shared<Kernel::PhysicalMemory>(
SHARED_FONT_MEM_SIZE); // Shared memory needs to always be allocated and a fixed size
const std::string user_path = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir);
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index 4f6042b00..5b8248433 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -8,6 +8,11 @@
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/swap.h"
+#include "core/hle/service/nvdrv/nvdata.h"
+
+namespace Core {
+class System;
+}
namespace Service::Nvidia::Devices {
@@ -15,7 +20,7 @@ namespace Service::Nvidia::Devices {
/// implement the ioctl interface.
class nvdevice {
public:
- nvdevice() = default;
+ explicit nvdevice(Core::System& system) : system{system} {};
virtual ~nvdevice() = default;
union Ioctl {
u32_le raw;
@@ -33,7 +38,11 @@ public:
* @param output A buffer where the output data will be written to.
* @returns The result code of the ioctl.
*/
- virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) = 0;
+ virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ IoctlCtrl& ctrl) = 0;
+
+protected:
+ Core::System& system;
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 20c7c39aa..926a1285d 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -13,10 +13,12 @@
namespace Service::Nvidia::Devices {
-nvdisp_disp0::nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
+nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
+ : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
nvdisp_disp0 ::~nvdisp_disp0() = default;
-u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ IoctlCtrl& ctrl) {
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
}
@@ -34,9 +36,8 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
addr, offset, width, height, stride, static_cast<PixelFormat>(format),
transform, crop_rect};
- auto& instance = Core::System::GetInstance();
- instance.GetPerfStats().EndGameFrame();
- instance.GPU().SwapBuffers(framebuffer);
+ system.GetPerfStats().EndGameFrame();
+ system.GPU().SwapBuffers(&framebuffer);
}
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index 12f3ef825..e79e490ff 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -17,10 +17,11 @@ class nvmap;
class nvdisp_disp0 final : public nvdevice {
public:
- explicit nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev);
+ explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
~nvdisp_disp0() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ IoctlCtrl& ctrl) override;
/// Performs a screen flip, drawing the buffer pointed to by the handle.
void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index af62d33d2..24ab3f2e9 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -22,10 +22,12 @@ enum {
};
}
-nvhost_as_gpu::nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
+nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
+ : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
nvhost_as_gpu::~nvhost_as_gpu() = default;
-u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ IoctlCtrl& ctrl) {
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
command.raw, input.size(), output.size());
@@ -65,7 +67,7 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>&
LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages,
params.page_size, params.flags);
- auto& gpu = Core::System::GetInstance().GPU();
+ auto& gpu = system.GPU();
const u64 size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)};
if (params.flags & 1) {
params.offset = gpu.MemoryManager().AllocateSpace(params.offset, size, 1);
@@ -85,7 +87,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
std::vector<IoctlRemapEntry> entries(num_entries);
std::memcpy(entries.data(), input.data(), input.size());
- auto& gpu = Core::System::GetInstance().GPU();
+ auto& gpu = system.GPU();
for (const auto& entry : entries) {
LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
entry.offset, entry.nvmap_handle, entry.pages);
@@ -136,7 +138,7 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
// case to prevent unexpected behavior.
ASSERT(object->id == params.nvmap_handle);
- auto& gpu = Core::System::GetInstance().GPU();
+ auto& gpu = system.GPU();
if (params.flags & 1) {
params.offset = gpu.MemoryManager().MapBufferEx(object->addr, params.offset, object->size);
@@ -173,8 +175,7 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
return 0;
}
- params.offset = Core::System::GetInstance().GPU().MemoryManager().UnmapBuffer(params.offset,
- itr->second.size);
+ params.offset = system.GPU().MemoryManager().UnmapBuffer(params.offset, itr->second.size);
buffer_mappings.erase(itr->second.offset);
std::memcpy(output.data(), &params, output.size());
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index eb14b1da8..30ca5f4c3 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -17,10 +17,11 @@ class nvmap;
class nvhost_as_gpu final : public nvdevice {
public:
- explicit nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev);
+ explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
~nvhost_as_gpu() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ IoctlCtrl& ctrl) override;
private:
enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index b39fb9ef9..9a66a5f88 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -7,14 +7,20 @@
#include "common/assert.h"
#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h"
+#include "video_core/gpu.h"
namespace Service::Nvidia::Devices {
-nvhost_ctrl::nvhost_ctrl() = default;
+nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface)
+ : nvdevice(system), events_interface{events_interface} {}
nvhost_ctrl::~nvhost_ctrl() = default;
-u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ IoctlCtrl& ctrl) {
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
command.raw, input.size(), output.size());
@@ -22,11 +28,15 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<
case IoctlCommand::IocGetConfigCommand:
return NvOsGetConfigU32(input, output);
case IoctlCommand::IocCtrlEventWaitCommand:
- return IocCtrlEventWait(input, output, false);
+ return IocCtrlEventWait(input, output, false, ctrl);
case IoctlCommand::IocCtrlEventWaitAsyncCommand:
- return IocCtrlEventWait(input, output, true);
+ return IocCtrlEventWait(input, output, true, ctrl);
case IoctlCommand::IocCtrlEventRegisterCommand:
return IocCtrlEventRegister(input, output);
+ case IoctlCommand::IocCtrlEventUnregisterCommand:
+ return IocCtrlEventUnregister(input, output);
+ case IoctlCommand::IocCtrlEventSignalCommand:
+ return IocCtrlEventSignal(input, output);
}
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
@@ -41,23 +51,137 @@ u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>&
}
u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
- bool is_async) {
+ bool is_async, IoctlCtrl& ctrl) {
IocCtrlEventWaitParams params{};
std::memcpy(&params, input.data(), sizeof(params));
- LOG_WARNING(Service_NVDRV,
- "(STUBBED) called, syncpt_id={}, threshold={}, timeout={}, is_async={}",
- params.syncpt_id, params.threshold, params.timeout, is_async);
+ LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}",
+ params.syncpt_id, params.threshold, params.timeout, is_async);
- // TODO(Subv): Implement actual syncpt waiting.
- params.value = 0;
+ if (params.syncpt_id >= MaxSyncPoints) {
+ return NvResult::BadParameter;
+ }
+
+ auto& gpu = system.GPU();
+ // This is mostly to take into account unimplemented features. As synced
+ // gpu is always synced.
+ if (!gpu.IsAsync()) {
+ return NvResult::Success;
+ }
+ auto lock = gpu.LockSync();
+ const u32 current_syncpoint_value = gpu.GetSyncpointValue(params.syncpt_id);
+ const s32 diff = current_syncpoint_value - params.threshold;
+ if (diff >= 0) {
+ params.value = current_syncpoint_value;
+ std::memcpy(output.data(), &params, sizeof(params));
+ return NvResult::Success;
+ }
+ const u32 target_value = current_syncpoint_value - diff;
+
+ if (!is_async) {
+ params.value = 0;
+ }
+
+ if (params.timeout == 0) {
+ std::memcpy(output.data(), &params, sizeof(params));
+ return NvResult::Timeout;
+ }
+
+ u32 event_id;
+ if (is_async) {
+ event_id = params.value & 0x00FF;
+ if (event_id >= MaxNvEvents) {
+ std::memcpy(output.data(), &params, sizeof(params));
+ return NvResult::BadParameter;
+ }
+ } else {
+ if (ctrl.fresh_call) {
+ const auto result = events_interface.GetFreeEvent();
+ if (result) {
+ event_id = *result;
+ } else {
+ LOG_CRITICAL(Service_NVDRV, "No Free Events available!");
+ event_id = params.value & 0x00FF;
+ }
+ } else {
+ event_id = ctrl.event_id;
+ }
+ }
+
+ EventState status = events_interface.status[event_id];
+ if (event_id < MaxNvEvents || status == EventState::Free || status == EventState::Registered) {
+ events_interface.SetEventStatus(event_id, EventState::Waiting);
+ events_interface.assigned_syncpt[event_id] = params.syncpt_id;
+ events_interface.assigned_value[event_id] = target_value;
+ if (is_async) {
+ params.value = params.syncpt_id << 4;
+ } else {
+ params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000;
+ }
+ params.value |= event_id;
+ events_interface.events[event_id].writable->Clear();
+ gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
+ if (!is_async && ctrl.fresh_call) {
+ ctrl.must_delay = true;
+ ctrl.timeout = params.timeout;
+ ctrl.event_id = event_id;
+ return NvResult::Timeout;
+ }
+ std::memcpy(output.data(), &params, sizeof(params));
+ return NvResult::Timeout;
+ }
std::memcpy(output.data(), &params, sizeof(params));
- return 0;
+ return NvResult::BadParameter;
}
u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
- LOG_WARNING(Service_NVDRV, "(STUBBED) called");
- // TODO(bunnei): Implement this.
- return 0;
+ IocCtrlEventRegisterParams params{};
+ std::memcpy(&params, input.data(), sizeof(params));
+ const u32 event_id = params.user_event_id & 0x00FF;
+ LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
+ if (event_id >= MaxNvEvents) {
+ return NvResult::BadParameter;
+ }
+ if (events_interface.registered[event_id]) {
+ return NvResult::BadParameter;
+ }
+ events_interface.RegisterEvent(event_id);
+ return NvResult::Success;
+}
+
+u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output) {
+ IocCtrlEventUnregisterParams params{};
+ std::memcpy(&params, input.data(), sizeof(params));
+ const u32 event_id = params.user_event_id & 0x00FF;
+ LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
+ if (event_id >= MaxNvEvents) {
+ return NvResult::BadParameter;
+ }
+ if (!events_interface.registered[event_id]) {
+ return NvResult::BadParameter;
+ }
+ events_interface.UnregisterEvent(event_id);
+ return NvResult::Success;
+}
+
+u32 nvhost_ctrl::IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output) {
+ IocCtrlEventSignalParams params{};
+ std::memcpy(&params, input.data(), sizeof(params));
+ // TODO(Blinkhawk): This is normally called when an NvEvents timeout on WaitSynchronization
+ // It is believed from RE to cancel the GPU Event. However, better research is required
+ u32 event_id = params.user_event_id & 0x00FF;
+ LOG_WARNING(Service_NVDRV, "(STUBBED) called, user_event_id: {:X}", event_id);
+ if (event_id >= MaxNvEvents) {
+ return NvResult::BadParameter;
+ }
+ if (events_interface.status[event_id] == EventState::Waiting) {
+ auto& gpu = system.GPU();
+ if (gpu.CancelSyncptInterrupt(events_interface.assigned_syncpt[event_id],
+ events_interface.assigned_value[event_id])) {
+ events_interface.LiberateEvent(event_id);
+ events_interface.events[event_id].writable->Signal();
+ }
+ }
+ return NvResult::Success;
}
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 6d0de2212..14e6e7e57 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -8,15 +8,17 @@
#include <vector>
#include "common/common_types.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
+#include "core/hle/service/nvdrv/nvdrv.h"
namespace Service::Nvidia::Devices {
class nvhost_ctrl final : public nvdevice {
public:
- nvhost_ctrl();
+ explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface);
~nvhost_ctrl() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ IoctlCtrl& ctrl) override;
private:
enum class IoctlCommand : u32_le {
@@ -132,9 +134,16 @@ private:
u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
- u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async);
+ u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async,
+ IoctlCtrl& ctrl);
u32 IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
+
+ u32 IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
+
+ u32 IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output);
+
+ EventInterface& events_interface;
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 0e28755bd..988effd90 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -12,10 +12,11 @@
namespace Service::Nvidia::Devices {
-nvhost_ctrl_gpu::nvhost_ctrl_gpu() = default;
+nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {}
nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;
-u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ IoctlCtrl& ctrl) {
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
command.raw, input.size(), output.size());
@@ -185,7 +186,7 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o
IoctlGetGpuTime params{};
std::memcpy(&params, input.data(), input.size());
- const auto ns = Core::Timing::CyclesToNs(Core::System::GetInstance().CoreTiming().GetTicks());
+ const auto ns = Core::Timing::CyclesToNs(system.CoreTiming().GetTicks());
params.gpu_time = static_cast<u64_le>(ns.count());
std::memcpy(output.data(), &params, output.size());
return 0;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index 240435eea..2b035ae3f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -13,10 +13,11 @@ namespace Service::Nvidia::Devices {
class nvhost_ctrl_gpu final : public nvdevice {
public:
- nvhost_ctrl_gpu();
+ explicit nvhost_ctrl_gpu(Core::System& system);
~nvhost_ctrl_gpu() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ IoctlCtrl& ctrl) override;
private:
enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 8ce7bc7a5..241dac881 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -13,10 +13,12 @@
namespace Service::Nvidia::Devices {
-nvhost_gpu::nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
+nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
+ : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
nvhost_gpu::~nvhost_gpu() = default;
-u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ IoctlCtrl& ctrl) {
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
command.raw, input.size(), output.size());
@@ -119,8 +121,10 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou
params.num_entries, params.flags, params.unk0, params.unk1, params.unk2,
params.unk3);
- params.fence_out.id = 0;
- params.fence_out.value = 0;
+ auto& gpu = system.GPU();
+ params.fence_out.id = assigned_syncpoints;
+ params.fence_out.value = gpu.GetSyncpointValue(assigned_syncpoints);
+ assigned_syncpoints++;
std::memcpy(output.data(), &params, output.size());
return 0;
}
@@ -143,7 +147,7 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
- params.address, params.num_entries, params.flags);
+ params.address, params.num_entries, params.flags.raw);
ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) +
params.num_entries * sizeof(Tegra::CommandListHeader),
@@ -153,10 +157,18 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)],
params.num_entries * sizeof(Tegra::CommandListHeader));
- Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries));
+ UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
+ UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
+
+ auto& gpu = system.GPU();
+ u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id);
+ if (params.flags.increment.Value()) {
+ params.fence_out.value += current_syncpoint_value;
+ } else {
+ params.fence_out.value = current_syncpoint_value;
+ }
+ gpu.PushGPUEntries(std::move(entries));
- params.fence_out.id = 0;
- params.fence_out.value = 0;
std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
return 0;
}
@@ -168,16 +180,24 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output)
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
- params.address, params.num_entries, params.flags);
+ params.address, params.num_entries, params.flags.raw);
Tegra::CommandList entries(params.num_entries);
Memory::ReadBlock(params.address, entries.data(),
params.num_entries * sizeof(Tegra::CommandListHeader));
- Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries));
+ UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
+ UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
+
+ auto& gpu = system.GPU();
+ u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id);
+ if (params.flags.increment.Value()) {
+ params.fence_out.value += current_syncpoint_value;
+ } else {
+ params.fence_out.value = current_syncpoint_value;
+ }
+ gpu.PushGPUEntries(std::move(entries));
- params.fence_out.id = 0;
- params.fence_out.value = 0;
std::memcpy(output.data(), &params, output.size());
return 0;
}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index 62beb5c0c..d2e8fbae9 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -10,6 +10,7 @@
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
+#include "core/hle/service/nvdrv/nvdata.h"
namespace Service::Nvidia::Devices {
@@ -20,10 +21,11 @@ constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b);
class nvhost_gpu final : public nvdevice {
public:
- explicit nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev);
+ explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
~nvhost_gpu() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ IoctlCtrl& ctrl) override;
private:
enum class IoctlCommand : u32_le {
@@ -113,11 +115,7 @@ private:
static_assert(sizeof(IoctlGetErrorNotification) == 16,
"IoctlGetErrorNotification is incorrect size");
- struct IoctlFence {
- u32_le id;
- u32_le value;
- };
- static_assert(sizeof(IoctlFence) == 8, "IoctlFence is incorrect size");
+ static_assert(sizeof(Fence) == 8, "Fence is incorrect size");
struct IoctlAllocGpfifoEx {
u32_le num_entries;
@@ -132,13 +130,13 @@ private:
static_assert(sizeof(IoctlAllocGpfifoEx) == 32, "IoctlAllocGpfifoEx is incorrect size");
struct IoctlAllocGpfifoEx2 {
- u32_le num_entries; // in
- u32_le flags; // in
- u32_le unk0; // in (1 works)
- IoctlFence fence_out; // out
- u32_le unk1; // in
- u32_le unk2; // in
- u32_le unk3; // in
+ u32_le num_entries; // in
+ u32_le flags; // in
+ u32_le unk0; // in (1 works)
+ Fence fence_out; // out
+ u32_le unk1; // in
+ u32_le unk2; // in
+ u32_le unk3; // in
};
static_assert(sizeof(IoctlAllocGpfifoEx2) == 32, "IoctlAllocGpfifoEx2 is incorrect size");
@@ -153,10 +151,16 @@ private:
struct IoctlSubmitGpfifo {
u64_le address; // pointer to gpfifo entry structs
u32_le num_entries; // number of fence objects being submitted
- u32_le flags;
- IoctlFence fence_out; // returned new fence object for others to wait on
- };
- static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(IoctlFence),
+ union {
+ u32_le raw;
+ BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list
+ BitField<1, 1, u32_le> add_increment; // append an increment to the list
+ BitField<2, 1, u32_le> new_hw_format; // Mostly ignored
+ BitField<8, 1, u32_le> increment; // increment the returned fence
+ } flags;
+ Fence fence_out; // returned new fence object for others to wait on
+ };
+ static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence),
"IoctlSubmitGpfifo is incorrect size");
struct IoctlGetWaitbase {
@@ -184,6 +188,7 @@ private:
u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
std::shared_ptr<nvmap> nvmap_dev;
+ u32 assigned_syncpoints{};
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index f5e8ea7c3..f572ad30f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -10,10 +10,11 @@
namespace Service::Nvidia::Devices {
-nvhost_nvdec::nvhost_nvdec() = default;
+nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system) {}
nvhost_nvdec::~nvhost_nvdec() = default;
-u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ IoctlCtrl& ctrl) {
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
command.raw, input.size(), output.size());
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 0e7b284f8..2710f0511 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -13,10 +13,11 @@ namespace Service::Nvidia::Devices {
class nvhost_nvdec final : public nvdevice {
public:
- nvhost_nvdec();
+ explicit nvhost_nvdec(Core::System& system);
~nvhost_nvdec() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ IoctlCtrl& ctrl) override;
private:
enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
index 3e0951ab0..38282956f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
@@ -10,10 +10,11 @@
namespace Service::Nvidia::Devices {
-nvhost_nvjpg::nvhost_nvjpg() = default;
+nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {}
nvhost_nvjpg::~nvhost_nvjpg() = default;
-u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ IoctlCtrl& ctrl) {
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
command.raw, input.size(), output.size());
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
index 89fd5e95e..379766693 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
@@ -13,10 +13,11 @@ namespace Service::Nvidia::Devices {
class nvhost_nvjpg final : public nvdevice {
public:
- nvhost_nvjpg();
+ explicit nvhost_nvjpg(Core::System& system);
~nvhost_nvjpg() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ IoctlCtrl& ctrl) override;
private:
enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index d544f0f31..70e8091db 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -10,10 +10,11 @@
namespace Service::Nvidia::Devices {
-nvhost_vic::nvhost_vic() = default;
+nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system) {}
nvhost_vic::~nvhost_vic() = default;
-u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ IoctlCtrl& ctrl) {
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
command.raw, input.size(), output.size());
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index fc24c3f9c..7d111977e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -13,10 +13,11 @@ namespace Service::Nvidia::Devices {
class nvhost_vic final : public nvdevice {
public:
- nvhost_vic();
+ explicit nvhost_vic(Core::System& system);
~nvhost_vic() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ IoctlCtrl& ctrl) override;
private:
enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 1ec796fc6..223b496b7 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -18,7 +18,7 @@ enum {
};
}
-nvmap::nvmap() = default;
+nvmap::nvmap(Core::System& system) : nvdevice(system) {}
nvmap::~nvmap() = default;
VAddr nvmap::GetObjectAddress(u32 handle) const {
@@ -28,7 +28,8 @@ VAddr nvmap::GetObjectAddress(u32 handle) const {
return object->addr;
}
-u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ IoctlCtrl& ctrl) {
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::Create:
return IocCreate(input, output);
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index 396230c19..bf4a101c2 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -16,13 +16,14 @@ namespace Service::Nvidia::Devices {
class nvmap final : public nvdevice {
public:
- nvmap();
+ explicit nvmap(Core::System& system);
~nvmap() override;
/// Returns the allocated address of an nvmap object given its handle.
VAddr GetObjectAddress(u32 handle) const;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ IoctlCtrl& ctrl) override;
/// Represents an nvmap object.
struct Object {
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index b60fc748b..d5be64ed2 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -8,12 +8,18 @@
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nvdrv/interface.h"
+#include "core/hle/service/nvdrv/nvdata.h"
#include "core/hle/service/nvdrv/nvdrv.h"
namespace Service::Nvidia {
+void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
+ nvdrv->SignalSyncpt(syncpoint_id, value);
+}
+
void NVDRV::Open(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NVDRV, "called");
@@ -36,11 +42,31 @@ void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
std::vector<u8> output(ctx.GetWriteBufferSize());
+ IoctlCtrl ctrl{};
+
+ u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output, ctrl);
+
+ if (ctrl.must_delay) {
+ ctrl.fresh_call = false;
+ ctx.SleepClientThread(
+ "NVServices::DelayedResponse", ctrl.timeout,
+ [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
+ Kernel::ThreadWakeupReason reason) {
+ IoctlCtrl ctrl2{ctrl};
+ std::vector<u8> output2 = output;
+ u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output2, ctrl2);
+ ctx.WriteBuffer(output2);
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(result);
+ },
+ nvdrv->GetEventWriteable(ctrl.event_id));
+ } else {
+ ctx.WriteBuffer(output);
+ }
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push(nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output));
-
- ctx.WriteBuffer(output);
+ rb.Push(result);
}
void NVDRV::Close(Kernel::HLERequestContext& ctx) {
@@ -66,13 +92,19 @@ void NVDRV::Initialize(Kernel::HLERequestContext& ctx) {
void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u32 fd = rp.Pop<u32>();
- u32 event_id = rp.Pop<u32>();
+ // TODO(Blinkhawk): Figure the meaning of the flag at bit 16
+ u32 event_id = rp.Pop<u32>() & 0x000000FF;
LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id);
IPC::ResponseBuilder rb{ctx, 3, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(query_event.readable);
- rb.Push<u32>(0);
+ if (event_id < MaxNvEvents) {
+ rb.PushCopyObjects(nvdrv->GetEvent(event_id));
+ rb.Push<u32>(NvResult::Success);
+ } else {
+ rb.Push<u32>(0);
+ rb.Push<u32>(NvResult::BadParameter);
+ }
}
void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) {
@@ -127,10 +159,6 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
{13, &NVDRV::FinishInitialize, "FinishInitialize"},
};
RegisterHandlers(functions);
-
- auto& kernel = Core::System::GetInstance().Kernel();
- query_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
- "NVDRV::query_event");
}
NVDRV::~NVDRV() = default;
diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h
index 5b4889910..10a0ecd52 100644
--- a/src/core/hle/service/nvdrv/interface.h
+++ b/src/core/hle/service/nvdrv/interface.h
@@ -19,6 +19,8 @@ public:
NVDRV(std::shared_ptr<Module> nvdrv, const char* name);
~NVDRV() override;
+ void SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value);
+
private:
void Open(Kernel::HLERequestContext& ctx);
void Ioctl(Kernel::HLERequestContext& ctx);
@@ -33,8 +35,6 @@ private:
std::shared_ptr<Module> nvdrv;
u64 pid{};
-
- Kernel::EventPair query_event;
};
} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
new file mode 100644
index 000000000..ac03cbc23
--- /dev/null
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <array>
+#include "common/common_types.h"
+
+namespace Service::Nvidia {
+
+constexpr u32 MaxSyncPoints = 192;
+constexpr u32 MaxNvEvents = 64;
+
+struct Fence {
+ s32 id;
+ u32 value;
+};
+
+static_assert(sizeof(Fence) == 8, "Fence has wrong size");
+
+struct MultiFence {
+ u32 num_fences;
+ std::array<Fence, 4> fences;
+};
+
+enum NvResult : u32 {
+ Success = 0,
+ BadParameter = 4,
+ Timeout = 5,
+ ResourceError = 15,
+};
+
+enum class EventState {
+ Free = 0,
+ Registered = 1,
+ Waiting = 2,
+ Busy = 3,
+};
+
+struct IoctlCtrl {
+ // First call done to the servioce for services that call itself again after a call.
+ bool fresh_call{true};
+ // Tells the Ioctl Wrapper that it must delay the IPC response and send the thread to sleep
+ bool must_delay{};
+ // Timeout for the delay
+ s64 timeout{};
+ // NV Event Id
+ s32 event_id{-1};
+};
+
+} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 6e4b8f2c6..2011a226a 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -4,7 +4,10 @@
#include <utility>
+#include <fmt/format.h>
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
@@ -22,8 +25,9 @@
namespace Service::Nvidia {
-void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger) {
- auto module_ = std::make_shared<Module>();
+void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
+ Core::System& system) {
+ auto module_ = std::make_shared<Module>(system);
std::make_shared<NVDRV>(module_, "nvdrv")->InstallAsService(service_manager);
std::make_shared<NVDRV>(module_, "nvdrv:a")->InstallAsService(service_manager);
std::make_shared<NVDRV>(module_, "nvdrv:s")->InstallAsService(service_manager);
@@ -32,17 +36,25 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
nvflinger.SetNVDrvInstance(module_);
}
-Module::Module() {
- auto nvmap_dev = std::make_shared<Devices::nvmap>();
- devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(nvmap_dev);
- devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(nvmap_dev);
- devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>();
+Module::Module(Core::System& system) {
+ auto& kernel = system.Kernel();
+ for (u32 i = 0; i < MaxNvEvents; i++) {
+ std::string event_label = fmt::format("NVDRV::NvEvent_{}", i);
+ events_interface.events[i] = Kernel::WritableEvent::CreateEventPair(
+ kernel, Kernel::ResetType::Automatic, event_label);
+ events_interface.status[i] = EventState::Free;
+ events_interface.registered[i] = false;
+ }
+ auto nvmap_dev = std::make_shared<Devices::nvmap>(system);
+ devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev);
+ devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev);
+ devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(system);
devices["/dev/nvmap"] = nvmap_dev;
- devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(nvmap_dev);
- devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>();
- devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>();
- devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>();
- devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>();
+ devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev);
+ devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(system, events_interface);
+ devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system);
+ devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system);
+ devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system);
}
Module::~Module() = default;
@@ -59,12 +71,13 @@ u32 Module::Open(const std::string& device_name) {
return fd;
}
-u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output) {
+u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output,
+ IoctlCtrl& ctrl) {
auto itr = open_files.find(fd);
ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device");
auto& device = itr->second;
- return device->ioctl({command}, input, output);
+ return device->ioctl({command}, input, output, ctrl);
}
ResultCode Module::Close(u32 fd) {
@@ -77,4 +90,22 @@ ResultCode Module::Close(u32 fd) {
return RESULT_SUCCESS;
}
+void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) {
+ for (u32 i = 0; i < MaxNvEvents; i++) {
+ if (events_interface.assigned_syncpt[i] == syncpoint_id &&
+ events_interface.assigned_value[i] == value) {
+ events_interface.LiberateEvent(i);
+ events_interface.events[i].writable->Signal();
+ }
+ }
+}
+
+Kernel::SharedPtr<Kernel::ReadableEvent> Module::GetEvent(const u32 event_id) const {
+ return events_interface.events[event_id].readable;
+}
+
+Kernel::SharedPtr<Kernel::WritableEvent> Module::GetEventWriteable(const u32 event_id) const {
+ return events_interface.events[event_id].writable;
+}
+
} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index 53564f696..a339ab672 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -8,8 +8,14 @@
#include <unordered_map>
#include <vector>
#include "common/common_types.h"
+#include "core/hle/kernel/writable_event.h"
+#include "core/hle/service/nvdrv/nvdata.h"
#include "core/hle/service/service.h"
+namespace Core {
+class System;
+}
+
namespace Service::NVFlinger {
class NVFlinger;
}
@@ -20,16 +26,72 @@ namespace Devices {
class nvdevice;
}
-struct IoctlFence {
- u32 id;
- u32 value;
+struct EventInterface {
+ // Mask representing currently busy events
+ u64 events_mask{};
+ // Each kernel event associated to an NV event
+ std::array<Kernel::EventPair, MaxNvEvents> events;
+ // The status of the current NVEvent
+ std::array<EventState, MaxNvEvents> status{};
+ // Tells if an NVEvent is registered or not
+ std::array<bool, MaxNvEvents> registered{};
+ // When an NVEvent is waiting on GPU interrupt, this is the sync_point
+ // associated with it.
+ std::array<u32, MaxNvEvents> assigned_syncpt{};
+ // This is the value of the GPU interrupt for which the NVEvent is waiting
+ // for.
+ std::array<u32, MaxNvEvents> assigned_value{};
+ // Constant to denote an unasigned syncpoint.
+ static constexpr u32 unassigned_syncpt = 0xFFFFFFFF;
+ std::optional<u32> GetFreeEvent() const {
+ u64 mask = events_mask;
+ for (u32 i = 0; i < MaxNvEvents; i++) {
+ const bool is_free = (mask & 0x1) == 0;
+ if (is_free) {
+ if (status[i] == EventState::Registered || status[i] == EventState::Free) {
+ return {i};
+ }
+ }
+ mask = mask >> 1;
+ }
+ return {};
+ }
+ void SetEventStatus(const u32 event_id, EventState new_status) {
+ EventState old_status = status[event_id];
+ if (old_status == new_status) {
+ return;
+ }
+ status[event_id] = new_status;
+ if (new_status == EventState::Registered) {
+ registered[event_id] = true;
+ }
+ if (new_status == EventState::Waiting || new_status == EventState::Busy) {
+ events_mask |= (1ULL << event_id);
+ }
+ }
+ void RegisterEvent(const u32 event_id) {
+ registered[event_id] = true;
+ if (status[event_id] == EventState::Free) {
+ status[event_id] = EventState::Registered;
+ }
+ }
+ void UnregisterEvent(const u32 event_id) {
+ registered[event_id] = false;
+ if (status[event_id] == EventState::Registered) {
+ status[event_id] = EventState::Free;
+ }
+ }
+ void LiberateEvent(const u32 event_id) {
+ status[event_id] = registered[event_id] ? EventState::Registered : EventState::Free;
+ events_mask &= ~(1ULL << event_id);
+ assigned_syncpt[event_id] = unassigned_syncpt;
+ assigned_value[event_id] = 0;
+ }
};
-static_assert(sizeof(IoctlFence) == 8, "IoctlFence has wrong size");
-
class Module final {
public:
- Module();
+ Module(Core::System& system);
~Module();
/// Returns a pointer to one of the available devices, identified by its name.
@@ -44,10 +106,17 @@ public:
/// Opens a device node and returns a file descriptor to it.
u32 Open(const std::string& device_name);
/// Sends an ioctl command to the specified file descriptor.
- u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output);
+ u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output,
+ IoctlCtrl& ctrl);
/// Closes a device file descriptor and returns operation success.
ResultCode Close(u32 fd);
+ void SignalSyncpt(const u32 syncpoint_id, const u32 value);
+
+ Kernel::SharedPtr<Kernel::ReadableEvent> GetEvent(u32 event_id) const;
+
+ Kernel::SharedPtr<Kernel::WritableEvent> GetEventWriteable(u32 event_id) const;
+
private:
/// Id to use for the next open file descriptor.
u32 next_fd = 1;
@@ -57,9 +126,12 @@ private:
/// Mapping of device node names to their implementation.
std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices;
+
+ EventInterface events_interface;
};
/// Registers all NVDRV services with the specified service manager.
-void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger);
+void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
+ Core::System& system);
} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index 5731e815f..e1a07d3ee 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -34,7 +34,8 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
buffer_wait_event.writable->Signal();
}
-std::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
+std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
+ u32 height) {
auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) {
// Only consider free buffers. Buffers become free once again after they've been Acquired
// and Released by the compositor, see the NVFlinger::Compose method.
@@ -51,7 +52,7 @@ std::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
}
itr->status = Buffer::Status::Dequeued;
- return itr->slot;
+ return {{itr->slot, &itr->multi_fence}};
}
const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
@@ -63,7 +64,8 @@ const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
}
void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
- const Common::Rectangle<int>& crop_rect) {
+ const Common::Rectangle<int>& crop_rect, u32 swap_interval,
+ Service::Nvidia::MultiFence& multi_fence) {
auto itr = std::find_if(queue.begin(), queue.end(),
[&](const Buffer& buffer) { return buffer.slot == slot; });
ASSERT(itr != queue.end());
@@ -71,12 +73,21 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
itr->status = Buffer::Status::Queued;
itr->transform = transform;
itr->crop_rect = crop_rect;
+ itr->swap_interval = swap_interval;
+ itr->multi_fence = multi_fence;
+ queue_sequence.push_back(slot);
}
std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
- auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) {
- return buffer.status == Buffer::Status::Queued;
- });
+ auto itr = queue.end();
+ // Iterate to find a queued buffer matching the requested slot.
+ while (itr == queue.end() && !queue_sequence.empty()) {
+ u32 slot = queue_sequence.front();
+ itr = std::find_if(queue.begin(), queue.end(), [&slot](const Buffer& buffer) {
+ return buffer.status == Buffer::Status::Queued && buffer.slot == slot;
+ });
+ queue_sequence.pop_front();
+ }
if (itr == queue.end())
return {};
itr->status = Buffer::Status::Acquired;
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index e1ccb6171..356bedb81 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -4,6 +4,7 @@
#pragma once
+#include <list>
#include <optional>
#include <vector>
@@ -12,6 +13,7 @@
#include "common/swap.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/writable_event.h"
+#include "core/hle/service/nvdrv/nvdata.h"
namespace Service::NVFlinger {
@@ -68,13 +70,17 @@ public:
IGBPBuffer igbp_buffer;
BufferTransformFlags transform;
Common::Rectangle<int> crop_rect;
+ u32 swap_interval;
+ Service::Nvidia::MultiFence multi_fence;
};
void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer);
- std::optional<u32> DequeueBuffer(u32 width, u32 height);
+ std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> DequeueBuffer(u32 width,
+ u32 height);
const IGBPBuffer& RequestBuffer(u32 slot) const;
void QueueBuffer(u32 slot, BufferTransformFlags transform,
- const Common::Rectangle<int>& crop_rect);
+ const Common::Rectangle<int>& crop_rect, u32 swap_interval,
+ Service::Nvidia::MultiFence& multi_fence);
std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
void ReleaseBuffer(u32 slot);
u32 Query(QueryType type);
@@ -92,6 +98,7 @@ private:
u64 layer_id;
std::vector<Buffer> queue;
+ std::list<u32> queue_sequence;
Kernel::EventPair buffer_wait_event;
};
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 3c5c53e24..f9db79370 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -37,15 +37,14 @@ NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_t
displays.emplace_back(4, "Null");
// Schedule the screen composition events
- const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : frame_ticks;
-
- composition_event = core_timing.RegisterEvent(
- "ScreenComposition", [this, ticks](u64 userdata, s64 cycles_late) {
- Compose();
- this->core_timing.ScheduleEvent(ticks - cycles_late, composition_event);
- });
-
- core_timing.ScheduleEvent(ticks, composition_event);
+ composition_event = core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata,
+ s64 cycles_late) {
+ Compose();
+ const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks();
+ this->core_timing.ScheduleEvent(std::max<s64>(0LL, ticks - cycles_late), composition_event);
+ });
+
+ core_timing.ScheduleEvent(frame_ticks, composition_event);
}
NVFlinger::~NVFlinger() {
@@ -206,8 +205,14 @@ void NVFlinger::Compose() {
igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride,
buffer->get().transform, buffer->get().crop_rect);
+ swap_interval = buffer->get().swap_interval;
buffer_queue.ReleaseBuffer(buffer->get().slot);
}
}
+s64 NVFlinger::GetNextTicks() const {
+ constexpr s64 max_hertz = 120LL;
+ return (Core::Timing::BASE_CLOCK_RATE * (1LL << swap_interval)) / max_hertz;
+}
+
} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index c0a83fffb..988be8726 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -74,6 +74,8 @@ public:
/// finished.
void Compose();
+ s64 GetNextTicks() const;
+
private:
/// Finds the display identified by the specified ID.
VI::Display* FindDisplay(u64 display_id);
@@ -98,6 +100,8 @@ private:
/// layers.
u32 next_buffer_queue_id = 1;
+ u32 swap_interval = 1;
+
/// Event that handles screen composition.
Core::Timing::EventType* composition_event;
diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp
index ebcc41a43..fe6b5f798 100644
--- a/src/core/hle/service/pm/pm.cpp
+++ b/src/core/hle/service/pm/pm.cpp
@@ -3,11 +3,44 @@
// Refer to the license.txt file included.
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/process.h"
#include "core/hle/service/pm/pm.h"
#include "core/hle/service/service.h"
namespace Service::PM {
+namespace {
+
+constexpr ResultCode ERROR_PROCESS_NOT_FOUND{ErrorModule::PM, 1};
+
+constexpr u64 NO_PROCESS_FOUND_PID{0};
+
+std::optional<Kernel::SharedPtr<Kernel::Process>> SearchProcessList(
+ const std::vector<Kernel::SharedPtr<Kernel::Process>>& process_list,
+ std::function<bool(const Kernel::SharedPtr<Kernel::Process>&)> predicate) {
+ const auto iter = std::find_if(process_list.begin(), process_list.end(), predicate);
+
+ if (iter == process_list.end()) {
+ return std::nullopt;
+ }
+
+ return *iter;
+}
+
+void GetApplicationPidGeneric(Kernel::HLERequestContext& ctx,
+ const std::vector<Kernel::SharedPtr<Kernel::Process>>& process_list) {
+ const auto process = SearchProcessList(process_list, [](const auto& process) {
+ return process->GetProcessID() == Kernel::Process::ProcessIDMin;
+ });
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(process.has_value() ? (*process)->GetProcessID() : NO_PROCESS_FOUND_PID);
+}
+
+} // Anonymous namespace
+
class BootMode final : public ServiceFramework<BootMode> {
public:
explicit BootMode() : ServiceFramework{"pm:bm"} {
@@ -41,14 +74,15 @@ private:
class DebugMonitor final : public ServiceFramework<DebugMonitor> {
public:
- explicit DebugMonitor() : ServiceFramework{"pm:dmnt"} {
+ explicit DebugMonitor(const Kernel::KernelCore& kernel)
+ : ServiceFramework{"pm:dmnt"}, kernel(kernel) {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetDebugProcesses"},
{1, nullptr, "StartDebugProcess"},
- {2, nullptr, "GetTitlePid"},
+ {2, &DebugMonitor::GetTitlePid, "GetTitlePid"},
{3, nullptr, "EnableDebugForTitleId"},
- {4, nullptr, "GetApplicationPid"},
+ {4, &DebugMonitor::GetApplicationPid, "GetApplicationPid"},
{5, nullptr, "EnableDebugForApplication"},
{6, nullptr, "DisableDebug"},
};
@@ -56,21 +90,77 @@ public:
RegisterHandlers(functions);
}
+
+private:
+ void GetTitlePid(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto title_id = rp.PopRaw<u64>();
+
+ LOG_DEBUG(Service_PM, "called, title_id={:016X}", title_id);
+
+ const auto process =
+ SearchProcessList(kernel.GetProcessList(), [title_id](const auto& process) {
+ return process->GetTitleID() == title_id;
+ });
+
+ if (!process.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_PROCESS_NOT_FOUND);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push((*process)->GetProcessID());
+ }
+
+ void GetApplicationPid(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PM, "called");
+ GetApplicationPidGeneric(ctx, kernel.GetProcessList());
+ }
+
+ const Kernel::KernelCore& kernel;
};
class Info final : public ServiceFramework<Info> {
public:
- explicit Info() : ServiceFramework{"pm:info"} {
+ explicit Info(const std::vector<Kernel::SharedPtr<Kernel::Process>>& process_list)
+ : ServiceFramework{"pm:info"}, process_list(process_list) {
static const FunctionInfo functions[] = {
- {0, nullptr, "GetTitleId"},
+ {0, &Info::GetTitleId, "GetTitleId"},
};
RegisterHandlers(functions);
}
+
+private:
+ void GetTitleId(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto process_id = rp.PopRaw<u64>();
+
+ LOG_DEBUG(Service_PM, "called, process_id={:016X}", process_id);
+
+ const auto process = SearchProcessList(process_list, [process_id](const auto& process) {
+ return process->GetProcessID() == process_id;
+ });
+
+ if (!process.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_PROCESS_NOT_FOUND);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push((*process)->GetTitleID());
+ }
+
+ const std::vector<Kernel::SharedPtr<Kernel::Process>>& process_list;
};
class Shell final : public ServiceFramework<Shell> {
public:
- explicit Shell() : ServiceFramework{"pm:shell"} {
+ explicit Shell(const Kernel::KernelCore& kernel)
+ : ServiceFramework{"pm:shell"}, kernel(kernel) {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "LaunchProcess"},
@@ -79,21 +169,31 @@ public:
{3, nullptr, "GetProcessEventWaiter"},
{4, nullptr, "GetProcessEventType"},
{5, nullptr, "NotifyBootFinished"},
- {6, nullptr, "GetApplicationPid"},
+ {6, &Shell::GetApplicationPid, "GetApplicationPid"},
{7, nullptr, "BoostSystemMemoryResourceLimit"},
{8, nullptr, "EnableAdditionalSystemThreads"},
+ {9, nullptr, "GetUnimplementedEventHandle"},
};
// clang-format on
RegisterHandlers(functions);
}
+
+private:
+ void GetApplicationPid(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PM, "called");
+ GetApplicationPidGeneric(ctx, kernel.GetProcessList());
+ }
+
+ const Kernel::KernelCore& kernel;
};
-void InstallInterfaces(SM::ServiceManager& sm) {
- std::make_shared<BootMode>()->InstallAsService(sm);
- std::make_shared<DebugMonitor>()->InstallAsService(sm);
- std::make_shared<Info>()->InstallAsService(sm);
- std::make_shared<Shell>()->InstallAsService(sm);
+void InstallInterfaces(Core::System& system) {
+ std::make_shared<BootMode>()->InstallAsService(system.ServiceManager());
+ std::make_shared<DebugMonitor>(system.Kernel())->InstallAsService(system.ServiceManager());
+ std::make_shared<Info>(system.Kernel().GetProcessList())
+ ->InstallAsService(system.ServiceManager());
+ std::make_shared<Shell>(system.Kernel())->InstallAsService(system.ServiceManager());
}
} // namespace Service::PM
diff --git a/src/core/hle/service/pm/pm.h b/src/core/hle/service/pm/pm.h
index cc8d3f215..852e7050c 100644
--- a/src/core/hle/service/pm/pm.h
+++ b/src/core/hle/service/pm/pm.h
@@ -4,8 +4,8 @@
#pragma once
-namespace Service::SM {
-class ServiceManager;
+namespace Core {
+class System;
}
namespace Service::PM {
@@ -16,6 +16,6 @@ enum class SystemBootMode {
};
/// Registers all PM services with the specified service manager.
-void InstallInterfaces(SM::ServiceManager& service_manager);
+void InstallInterfaces(Core::System& system);
} // namespace Service::PM
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 952c03e27..3a0f8c3f6 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -206,7 +206,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
AM::InstallInterfaces(*sm, nv_flinger, system);
AOC::InstallInterfaces(*sm);
APM::InstallInterfaces(system);
- Audio::InstallInterfaces(*sm);
+ Audio::InstallInterfaces(*sm, system);
BCAT::InstallInterfaces(*sm);
BPC::InstallInterfaces(*sm);
BtDrv::InstallInterfaces(*sm);
@@ -236,12 +236,12 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
NIM::InstallInterfaces(*sm);
NPNS::InstallInterfaces(*sm);
NS::InstallInterfaces(*sm);
- Nvidia::InstallInterfaces(*sm, *nv_flinger);
+ Nvidia::InstallInterfaces(*sm, *nv_flinger, system);
PCIe::InstallInterfaces(*sm);
PCTL::InstallInterfaces(*sm);
PCV::InstallInterfaces(*sm);
PlayReport::InstallInterfaces(*sm);
- PM::InstallInterfaces(*sm);
+ PM::InstallInterfaces(system);
PSC::InstallInterfaces(*sm);
PSM::InstallInterfaces(*sm);
Set::InstallInterfaces(*sm);
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index f1fa6ccd1..199b30635 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -21,6 +21,7 @@
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/writable_event.h"
+#include "core/hle/service/nvdrv/nvdata.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
#include "core/hle/service/nvflinger/nvflinger.h"
@@ -328,32 +329,22 @@ public:
Data data;
};
-struct BufferProducerFence {
- u32 is_valid;
- std::array<Nvidia::IoctlFence, 4> fences;
-};
-static_assert(sizeof(BufferProducerFence) == 36, "BufferProducerFence has wrong size");
-
class IGBPDequeueBufferResponseParcel : public Parcel {
public:
- explicit IGBPDequeueBufferResponseParcel(u32 slot) : slot(slot) {}
+ explicit IGBPDequeueBufferResponseParcel(u32 slot, Service::Nvidia::MultiFence& multi_fence)
+ : slot(slot), multi_fence(multi_fence) {}
~IGBPDequeueBufferResponseParcel() override = default;
protected:
void SerializeData() override {
- // TODO(Subv): Find out how this Fence is used.
- BufferProducerFence fence = {};
- fence.is_valid = 1;
- for (auto& fence_ : fence.fences)
- fence_.id = -1;
-
Write(slot);
Write<u32_le>(1);
- WriteObject(fence);
+ WriteObject(multi_fence);
Write<u32_le>(0);
}
u32_le slot;
+ Service::Nvidia::MultiFence multi_fence;
};
class IGBPRequestBufferRequestParcel : public Parcel {
@@ -400,12 +391,6 @@ public:
data = Read<Data>();
}
- struct Fence {
- u32_le id;
- u32_le value;
- };
- static_assert(sizeof(Fence) == 8, "Fence has wrong size");
-
struct Data {
u32_le slot;
INSERT_PADDING_WORDS(3);
@@ -418,15 +403,15 @@ public:
s32_le scaling_mode;
NVFlinger::BufferQueue::BufferTransformFlags transform;
u32_le sticky_transform;
- INSERT_PADDING_WORDS(2);
- u32_le fence_is_valid;
- std::array<Fence, 2> fences;
+ INSERT_PADDING_WORDS(1);
+ u32_le swap_interval;
+ Service::Nvidia::MultiFence multi_fence;
Common::Rectangle<int> GetCropRect() const {
return {crop_left, crop_top, crop_right, crop_bottom};
}
};
- static_assert(sizeof(Data) == 80, "ParcelData has wrong size");
+ static_assert(sizeof(Data) == 96, "ParcelData has wrong size");
Data data;
};
@@ -547,11 +532,11 @@ private:
IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
const u32 width{request.data.width};
const u32 height{request.data.height};
- std::optional<u32> slot = buffer_queue.DequeueBuffer(width, height);
+ auto result = buffer_queue.DequeueBuffer(width, height);
- if (slot) {
+ if (result) {
// Buffer is available
- IGBPDequeueBufferResponseParcel response{*slot};
+ IGBPDequeueBufferResponseParcel response{result->first, *result->second};
ctx.WriteBuffer(response.Serialize());
} else {
// Wait the current thread until a buffer becomes available
@@ -561,10 +546,10 @@ private:
Kernel::ThreadWakeupReason reason) {
// Repeat TransactParcel DequeueBuffer when a buffer is available
auto& buffer_queue = nv_flinger->FindBufferQueue(id);
- std::optional<u32> slot = buffer_queue.DequeueBuffer(width, height);
- ASSERT_MSG(slot != std::nullopt, "Could not dequeue buffer.");
+ auto result = buffer_queue.DequeueBuffer(width, height);
+ ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer.");
- IGBPDequeueBufferResponseParcel response{*slot};
+ IGBPDequeueBufferResponseParcel response{result->first, *result->second};
ctx.WriteBuffer(response.Serialize());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -582,7 +567,8 @@ private:
IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};
buffer_queue.QueueBuffer(request.data.slot, request.data.transform,
- request.data.GetCropRect());
+ request.data.GetCropRect(), request.data.swap_interval,
+ request.data.multi_fence);
IGBPQueueBufferResponseParcel response{1280, 720};
ctx.WriteBuffer(response.Serialize());
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 6d4b02375..f1795fdd6 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -295,7 +295,7 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
}
}
- std::vector<u8> program_image(total_image_size);
+ Kernel::PhysicalMemory program_image(total_image_size);
std::size_t current_image_position = 0;
Kernel::CodeSet codeset;
diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp
index 70051c13a..474b55cb1 100644
--- a/src/core/loader/kip.cpp
+++ b/src/core/loader/kip.cpp
@@ -69,7 +69,7 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process) {
const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
Kernel::CodeSet codeset;
- std::vector<u8> program_image;
+ Kernel::PhysicalMemory program_image;
const auto load_segment = [&program_image](Kernel::CodeSet::Segment& segment,
const std::vector<u8>& data, u32 offset) {
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 6a0ca389b..e92e2e06e 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -143,7 +143,7 @@ static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data,
}
// Build program image
- std::vector<u8> program_image(PageAlignSize(nro_header.file_size));
+ Kernel::PhysicalMemory program_image(PageAlignSize(nro_header.file_size));
std::memcpy(program_image.data(), data.data(), program_image.size());
if (program_image.size() != PageAlignSize(nro_header.file_size)) {
return {};
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 29311404a..70c90109f 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -89,7 +89,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
// Build program image
Kernel::CodeSet codeset;
- std::vector<u8> program_image;
+ Kernel::PhysicalMemory program_image;
for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
std::vector<u8> data =
file.ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 63aa59690..0dd1632ac 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -85,7 +85,6 @@ void LogSettings() {
LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0));
LogSetting("System_CurrentUser", Settings::values.current_user);
LogSetting("System_LanguageIndex", Settings::values.language_index);
- LogSetting("Core_CpuJitEnabled", Settings::values.cpu_jit_enabled);
LogSetting("Core_UseMultiCore", Settings::values.use_multi_core);
LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor);
LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit);
diff --git a/src/core/settings.h b/src/core/settings.h
index acf18d653..6638ce8f9 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -378,7 +378,6 @@ struct Values {
std::atomic_bool is_device_reload_pending{true};
// Core
- bool cpu_jit_enabled;
bool use_multi_core;
// Data Storage
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 98f49042a..793d102d3 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -168,7 +168,6 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
AddField(Telemetry::FieldType::UserConfig, "Audio_SinkId", Settings::values.sink_id);
AddField(Telemetry::FieldType::UserConfig, "Audio_EnableAudioStretching",
Settings::values.enable_audio_stretching);
- AddField(Telemetry::FieldType::UserConfig, "Core_UseCpuJit", Settings::values.cpu_jit_enabled);
AddField(Telemetry::FieldType::UserConfig, "Core_UseMultiCore",
Settings::values.use_multi_core);
AddField(Telemetry::FieldType::UserConfig, "Renderer_ResolutionFactor",