summaryrefslogtreecommitdiff
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
111 frontend/scope_acquire_window_context.h 111 frontend/scope_acquire_window_context.h
112 gdbstub/gdbstub.cpp 112 gdbstub/gdbstub.cpp
113 gdbstub/gdbstub.h 113 gdbstub/gdbstub.h
114 hardware_interrupt_manager.cpp
115 hardware_interrupt_manager.h
114 hle/ipc.h 116 hle/ipc.h
115 hle/ipc_helpers.h 117 hle/ipc_helpers.h
116 hle/kernel/address_arbiter.cpp 118 hle/kernel/address_arbiter.cpp
@@ -372,6 +374,7 @@ add_library(core STATIC
372 hle/service/nvdrv/devices/nvmap.h 374 hle/service/nvdrv/devices/nvmap.h
373 hle/service/nvdrv/interface.cpp 375 hle/service/nvdrv/interface.cpp
374 hle/service/nvdrv/interface.h 376 hle/service/nvdrv/interface.h
377 hle/service/nvdrv/nvdata.h
375 hle/service/nvdrv/nvdrv.cpp 378 hle/service/nvdrv/nvdrv.cpp
376 hle/service/nvdrv/nvdrv.h 379 hle/service/nvdrv/nvdrv.h
377 hle/service/nvdrv/nvmemp.cpp 380 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:
44 /// Step CPU by one instruction 44 /// Step CPU by one instruction
45 virtual void Step() = 0; 45 virtual void Step() = 0;
46 46
47 /// Maps a backing memory region for the CPU
48 virtual void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
49 Kernel::VMAPermission perms) = 0;
50
51 /// Unmaps a region of memory that was previously mapped using MapBackingMemory
52 virtual void UnmapMemory(VAddr address, std::size_t size) = 0;
53
54 /// Clear all instruction cache 47 /// Clear all instruction cache
55 virtual void ClearInstructionCache() = 0; 48 virtual void ClearInstructionCache() = 0;
56 49
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,
177 177
178ARM_Dynarmic::~ARM_Dynarmic() = default; 178ARM_Dynarmic::~ARM_Dynarmic() = default;
179 179
180void ARM_Dynarmic::MapBackingMemory(u64 address, std::size_t size, u8* memory,
181 Kernel::VMAPermission perms) {
182 inner_unicorn.MapBackingMemory(address, size, memory, perms);
183}
184
185void ARM_Dynarmic::UnmapMemory(u64 address, std::size_t size) {
186 inner_unicorn.UnmapMemory(address, size);
187}
188
189void ARM_Dynarmic::SetPC(u64 pc) { 180void ARM_Dynarmic::SetPC(u64 pc) {
190 jit->SetPC(pc); 181 jit->SetPC(pc);
191} 182}
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:
23 ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); 23 ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
24 ~ARM_Dynarmic() override; 24 ~ARM_Dynarmic() override;
25 25
26 void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
27 Kernel::VMAPermission perms) override;
28 void UnmapMemory(u64 address, std::size_t size) override;
29 void SetPC(u64 pc) override; 26 void SetPC(u64 pc) override;
30 u64 GetPC() const override; 27 u64 GetPC() const override;
31 u64 GetReg(int index) const override; 28 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_
50 50
51static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value, 51static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value,
52 void* user_data) { 52 void* user_data) {
53 auto* const system = static_cast<System*>(user_data);
54
53 ARM_Interface::ThreadContext ctx{}; 55 ARM_Interface::ThreadContext ctx{};
54 Core::CurrentArmInterface().SaveContext(ctx); 56 system->CurrentArmInterface().SaveContext(ctx);
55 ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x{:X}, pc=0x{:X}, lr=0x{:X}", addr, 57 ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x{:X}, pc=0x{:X}, lr=0x{:X}", addr,
56 ctx.pc, ctx.cpu_registers[30]); 58 ctx.pc, ctx.cpu_registers[30]);
57 return {}; 59
60 return false;
58} 61}
59 62
60ARM_Unicorn::ARM_Unicorn(System& system) : system{system} { 63ARM_Unicorn::ARM_Unicorn(System& system) : system{system} {
@@ -65,7 +68,7 @@ ARM_Unicorn::ARM_Unicorn(System& system) : system{system} {
65 68
66 uc_hook hook{}; 69 uc_hook hook{};
67 CHECKED(uc_hook_add(uc, &hook, UC_HOOK_INTR, (void*)InterruptHook, this, 0, -1)); 70 CHECKED(uc_hook_add(uc, &hook, UC_HOOK_INTR, (void*)InterruptHook, this, 0, -1));
68 CHECKED(uc_hook_add(uc, &hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, this, 0, -1)); 71 CHECKED(uc_hook_add(uc, &hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, &system, 0, -1));
69 if (GDBStub::IsServerEnabled()) { 72 if (GDBStub::IsServerEnabled()) {
70 CHECKED(uc_hook_add(uc, &hook, UC_HOOK_CODE, (void*)CodeHook, this, 0, -1)); 73 CHECKED(uc_hook_add(uc, &hook, UC_HOOK_CODE, (void*)CodeHook, this, 0, -1));
71 last_bkpt_hit = false; 74 last_bkpt_hit = false;
@@ -76,15 +79,6 @@ ARM_Unicorn::~ARM_Unicorn() {
76 CHECKED(uc_close(uc)); 79 CHECKED(uc_close(uc));
77} 80}
78 81
79void ARM_Unicorn::MapBackingMemory(VAddr address, std::size_t size, u8* memory,
80 Kernel::VMAPermission perms) {
81 CHECKED(uc_mem_map_ptr(uc, address, size, static_cast<u32>(perms), memory));
82}
83
84void ARM_Unicorn::UnmapMemory(VAddr address, std::size_t size) {
85 CHECKED(uc_mem_unmap(uc, address, size));
86}
87
88void ARM_Unicorn::SetPC(u64 pc) { 82void ARM_Unicorn::SetPC(u64 pc) {
89 CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &pc)); 83 CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &pc));
90} 84}
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:
18 explicit ARM_Unicorn(System& system); 18 explicit ARM_Unicorn(System& system);
19 ~ARM_Unicorn() override; 19 ~ARM_Unicorn() override;
20 20
21 void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
22 Kernel::VMAPermission perms) override;
23 void UnmapMemory(VAddr address, std::size_t size) override;
24 void SetPC(u64 pc) override; 21 void SetPC(u64 pc) override;
25 u64 GetPC() const override; 22 u64 GetPC() const override;
26 u64 GetReg(int index) const override; 23 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 @@
19#include "core/file_sys/vfs_concat.h" 19#include "core/file_sys/vfs_concat.h"
20#include "core/file_sys/vfs_real.h" 20#include "core/file_sys/vfs_real.h"
21#include "core/gdbstub/gdbstub.h" 21#include "core/gdbstub/gdbstub.h"
22#include "core/hardware_interrupt_manager.h"
22#include "core/hle/kernel/client_port.h" 23#include "core/hle/kernel/client_port.h"
23#include "core/hle/kernel/kernel.h" 24#include "core/hle/kernel/kernel.h"
24#include "core/hle/kernel/process.h" 25#include "core/hle/kernel/process.h"
@@ -151,7 +152,7 @@ struct System::Impl {
151 if (!renderer->Init()) { 152 if (!renderer->Init()) {
152 return ResultStatus::ErrorVideoCore; 153 return ResultStatus::ErrorVideoCore;
153 } 154 }
154 155 interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
155 gpu_core = VideoCore::CreateGPU(system); 156 gpu_core = VideoCore::CreateGPU(system);
156 157
157 is_powered_on = true; 158 is_powered_on = true;
@@ -298,6 +299,7 @@ struct System::Impl {
298 std::unique_ptr<VideoCore::RendererBase> renderer; 299 std::unique_ptr<VideoCore::RendererBase> renderer;
299 std::unique_ptr<Tegra::GPU> gpu_core; 300 std::unique_ptr<Tegra::GPU> gpu_core;
300 std::shared_ptr<Tegra::DebugContext> debug_context; 301 std::shared_ptr<Tegra::DebugContext> debug_context;
302 std::unique_ptr<Core::Hardware::InterruptManager> interrupt_manager;
301 CpuCoreManager cpu_core_manager; 303 CpuCoreManager cpu_core_manager;
302 bool is_powered_on = false; 304 bool is_powered_on = false;
303 305
@@ -444,6 +446,14 @@ const Tegra::GPU& System::GPU() const {
444 return *impl->gpu_core; 446 return *impl->gpu_core;
445} 447}
446 448
449Core::Hardware::InterruptManager& System::InterruptManager() {
450 return *impl->interrupt_manager;
451}
452
453const Core::Hardware::InterruptManager& System::InterruptManager() const {
454 return *impl->interrupt_manager;
455}
456
447VideoCore::RendererBase& System::Renderer() { 457VideoCore::RendererBase& System::Renderer() {
448 return *impl->renderer; 458 return *impl->renderer;
449} 459}
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 {
70class CoreTiming; 70class CoreTiming;
71} 71}
72 72
73namespace Core::Hardware {
74class InterruptManager;
75}
76
73namespace Core { 77namespace Core {
74 78
75class ARM_Interface; 79class ARM_Interface;
@@ -234,6 +238,12 @@ public:
234 /// Provides a constant reference to the core timing instance. 238 /// Provides a constant reference to the core timing instance.
235 const Timing::CoreTiming& CoreTiming() const; 239 const Timing::CoreTiming& CoreTiming() const;
236 240
241 /// Provides a reference to the interrupt manager instance.
242 Core::Hardware::InterruptManager& InterruptManager();
243
244 /// Provides a constant reference to the interrupt manager instance.
245 const Core::Hardware::InterruptManager& InterruptManager() const;
246
237 /// Provides a reference to the kernel instance. 247 /// Provides a reference to the kernel instance.
238 Kernel::KernelCore& Kernel(); 248 Kernel::KernelCore& Kernel();
239 249
@@ -327,10 +337,6 @@ private:
327 static System s_instance; 337 static System s_instance;
328}; 338};
329 339
330inline ARM_Interface& CurrentArmInterface() {
331 return System::GetInstance().CurrentArmInterface();
332}
333
334inline Kernel::Process* CurrentProcess() { 340inline Kernel::Process* CurrentProcess() {
335 return System::GetInstance().CurrentProcess(); 341 return System::GetInstance().CurrentProcess();
336} 342}
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() {
53Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, 53Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier,
54 std::size_t core_index) 54 std::size_t core_index)
55 : cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} { 55 : cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} {
56 if (Settings::values.cpu_jit_enabled) {
57#ifdef ARCHITECTURE_x86_64 56#ifdef ARCHITECTURE_x86_64
58 arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index); 57 arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index);
59#else 58#else
60 arm_interface = std::make_unique<ARM_Unicorn>(system); 59 arm_interface = std::make_unique<ARM_Unicorn>(system);
61 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); 60 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
62#endif 61#endif
63 } else {
64 arm_interface = std::make_unique<ARM_Unicorn>(system);
65 }
66 62
67 scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface); 63 scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface);
68} 64}
@@ -70,15 +66,12 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba
70Cpu::~Cpu() = default; 66Cpu::~Cpu() = default;
71 67
72std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) { 68std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) {
73 if (Settings::values.cpu_jit_enabled) {
74#ifdef ARCHITECTURE_x86_64 69#ifdef ARCHITECTURE_x86_64
75 return std::make_unique<DynarmicExclusiveMonitor>(num_cores); 70 return std::make_unique<DynarmicExclusiveMonitor>(num_cores);
76#else 71#else
77 return nullptr; // TODO(merry): Passthrough exclusive monitor 72 // TODO(merry): Passthrough exclusive monitor
73 return nullptr;
78#endif 74#endif
79 } else {
80 return nullptr; // TODO(merry): Passthrough exclusive monitor
81 }
82} 75}
83 76
84void Cpu::RunLoop(bool tight_loop) { 77void 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 {
94 return aci_file_access.permissions; 94 return aci_file_access.permissions;
95} 95}
96 96
97u32 ProgramMetadata::GetSystemResourceSize() const {
98 return npdm_header.system_resource_size;
99}
100
97const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const { 101const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const {
98 return aci_kernel_capabilities; 102 return aci_kernel_capabilities;
99} 103}
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:
58 u32 GetMainThreadStackSize() const; 58 u32 GetMainThreadStackSize() const;
59 u64 GetTitleID() const; 59 u64 GetTitleID() const;
60 u64 GetFilesystemPermissions() const; 60 u64 GetFilesystemPermissions() const;
61 u32 GetSystemResourceSize() const;
61 const KernelCapabilityDescriptors& GetKernelCapabilities() const; 62 const KernelCapabilityDescriptors& GetKernelCapabilities() const;
62 63
63 void Print() const; 64 void Print() const;
@@ -76,7 +77,8 @@ private:
76 u8 reserved_3; 77 u8 reserved_3;
77 u8 main_thread_priority; 78 u8 main_thread_priority;
78 u8 main_thread_cpu; 79 u8 main_thread_cpu;
79 std::array<u8, 8> reserved_4; 80 std::array<u8, 4> reserved_4;
81 u32_le system_resource_size;
80 u32_le process_category; 82 u32_le process_category;
81 u32_le main_stack_size; 83 u32_le main_stack_size;
82 std::array<u8, 0x10> application_name; 84 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 @@
1// Copyright 2019 Yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/core.h"
6#include "core/core_timing.h"
7#include "core/hardware_interrupt_manager.h"
8#include "core/hle/service/nvdrv/interface.h"
9#include "core/hle/service/sm/sm.h"
10
11namespace Core::Hardware {
12
13InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) {
14 gpu_interrupt_event =
15 system.CoreTiming().RegisterEvent("GPUInterrupt", [this](u64 message, s64) {
16 auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv");
17 const u32 syncpt = static_cast<u32>(message >> 32);
18 const u32 value = static_cast<u32>(message);
19 nvdrv->SignalGPUInterruptSyncpt(syncpt, value);
20 });
21}
22
23InterruptManager::~InterruptManager() = default;
24
25void InterruptManager::GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
26 const u64 msg = (static_cast<u64>(syncpoint_id) << 32ULL) | value;
27 system.CoreTiming().ScheduleEvent(10, gpu_interrupt_event, msg);
28}
29
30} // 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 @@
1// Copyright 2019 Yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9namespace Core {
10class System;
11}
12
13namespace Core::Timing {
14struct EventType;
15}
16
17namespace Core::Hardware {
18
19class InterruptManager {
20public:
21 explicit InterruptManager(Core::System& system);
22 ~InterruptManager();
23
24 void GPUInterruptSyncpt(u32 syncpoint_id, u32 value);
25
26private:
27 Core::System& system;
28 Core::Timing::EventType* gpu_interrupt_event{};
29};
30
31} // 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 @@
8#include <vector> 8#include <vector>
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/kernel/physical_memory.h"
11 12
12namespace Kernel { 13namespace Kernel {
13 14
@@ -77,7 +78,7 @@ struct CodeSet final {
77 } 78 }
78 79
79 /// The overall data that backs this code set. 80 /// The overall data that backs this code set.
80 std::vector<u8> memory; 81 Kernel::PhysicalMemory memory;
81 82
82 /// The segments that comprise this code set. 83 /// The segments that comprise this code set.
83 std::array<Segment, 3> segments; 84 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 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/alignment.h"
8
9namespace Kernel {
10
11// This encapsulation serves 2 purposes:
12// - First, to encapsulate host physical memory under a single type and set an
13// standard for managing it.
14// - Second to ensure all host backing memory used is aligned to 256 bytes due
15// to strict alignment restrictions on GPU memory.
16
17using PhysicalMemory = std::vector<u8, Common::AlignmentAllocator<u8, 256>>;
18
19} // 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 {
129 return vm_manager.GetTotalPhysicalMemoryAvailable(); 129 return vm_manager.GetTotalPhysicalMemoryAvailable();
130} 130}
131 131
132u64 Process::GetTotalPhysicalMemoryAvailableWithoutMmHeap() const { 132u64 Process::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const {
133 // TODO: Subtract the personal heap size from this when the 133 return GetTotalPhysicalMemoryAvailable() - GetSystemResourceSize();
134 // personal heap is implemented.
135 return GetTotalPhysicalMemoryAvailable();
136} 134}
137 135
138u64 Process::GetTotalPhysicalMemoryUsed() const { 136u64 Process::GetTotalPhysicalMemoryUsed() const {
139 return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size; 137 return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size +
138 GetSystemResourceUsage();
140} 139}
141 140
142u64 Process::GetTotalPhysicalMemoryUsedWithoutMmHeap() const { 141u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const {
143 // TODO: Subtract the personal heap size from this when the 142 return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage();
144 // personal heap is implemented.
145 return GetTotalPhysicalMemoryUsed();
146} 143}
147 144
148void Process::RegisterThread(const Thread* thread) { 145void Process::RegisterThread(const Thread* thread) {
@@ -172,6 +169,7 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
172 program_id = metadata.GetTitleID(); 169 program_id = metadata.GetTitleID();
173 ideal_core = metadata.GetMainThreadCore(); 170 ideal_core = metadata.GetMainThreadCore();
174 is_64bit_process = metadata.Is64BitProgram(); 171 is_64bit_process = metadata.Is64BitProgram();
172 system_resource_size = metadata.GetSystemResourceSize();
175 173
176 vm_manager.Reset(metadata.GetAddressSpaceType()); 174 vm_manager.Reset(metadata.GetAddressSpaceType());
177 175
@@ -186,19 +184,11 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
186} 184}
187 185
188void Process::Run(s32 main_thread_priority, u64 stack_size) { 186void Process::Run(s32 main_thread_priority, u64 stack_size) {
189 // The kernel always ensures that the given stack size is page aligned. 187 AllocateMainThreadStack(stack_size);
190 main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE); 188 tls_region_address = CreateTLSRegion();
191
192 // Allocate and map the main thread stack
193 // TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part
194 // of the user address space.
195 const VAddr mapping_address = vm_manager.GetTLSIORegionEndAddress() - main_thread_stack_size;
196 vm_manager
197 .MapMemoryBlock(mapping_address, std::make_shared<std::vector<u8>>(main_thread_stack_size),
198 0, main_thread_stack_size, MemoryState::Stack)
199 .Unwrap();
200 189
201 vm_manager.LogLayout(); 190 vm_manager.LogLayout();
191
202 ChangeStatus(ProcessStatus::Running); 192 ChangeStatus(ProcessStatus::Running);
203 193
204 SetupMainThread(*this, kernel, main_thread_priority); 194 SetupMainThread(*this, kernel, main_thread_priority);
@@ -228,6 +218,9 @@ void Process::PrepareForTermination() {
228 stop_threads(system.Scheduler(2).GetThreadList()); 218 stop_threads(system.Scheduler(2).GetThreadList());
229 stop_threads(system.Scheduler(3).GetThreadList()); 219 stop_threads(system.Scheduler(3).GetThreadList());
230 220
221 FreeTLSRegion(tls_region_address);
222 tls_region_address = 0;
223
231 ChangeStatus(ProcessStatus::Exited); 224 ChangeStatus(ProcessStatus::Exited);
232} 225}
233 226
@@ -254,7 +247,7 @@ VAddr Process::CreateTLSRegion() {
254 ASSERT(region_address.Succeeded()); 247 ASSERT(region_address.Succeeded());
255 248
256 const auto map_result = vm_manager.MapMemoryBlock( 249 const auto map_result = vm_manager.MapMemoryBlock(
257 *region_address, std::make_shared<std::vector<u8>>(Memory::PAGE_SIZE), 0, 250 *region_address, std::make_shared<PhysicalMemory>(Memory::PAGE_SIZE), 0,
258 Memory::PAGE_SIZE, MemoryState::ThreadLocal); 251 Memory::PAGE_SIZE, MemoryState::ThreadLocal);
259 ASSERT(map_result.Succeeded()); 252 ASSERT(map_result.Succeeded());
260 253
@@ -284,7 +277,7 @@ void Process::FreeTLSRegion(VAddr tls_address) {
284} 277}
285 278
286void Process::LoadModule(CodeSet module_, VAddr base_addr) { 279void Process::LoadModule(CodeSet module_, VAddr base_addr) {
287 const auto memory = std::make_shared<std::vector<u8>>(std::move(module_.memory)); 280 const auto memory = std::make_shared<PhysicalMemory>(std::move(module_.memory));
288 281
289 const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions, 282 const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions,
290 MemoryState memory_state) { 283 MemoryState memory_state) {
@@ -327,4 +320,16 @@ void Process::ChangeStatus(ProcessStatus new_status) {
327 WakeupAllWaitingThreads(); 320 WakeupAllWaitingThreads();
328} 321}
329 322
323void Process::AllocateMainThreadStack(u64 stack_size) {
324 // The kernel always ensures that the given stack size is page aligned.
325 main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE);
326
327 // Allocate and map the main thread stack
328 const VAddr mapping_address = vm_manager.GetTLSIORegionEndAddress() - main_thread_stack_size;
329 vm_manager
330 .MapMemoryBlock(mapping_address, std::make_shared<PhysicalMemory>(main_thread_stack_size),
331 0, main_thread_stack_size, MemoryState::Stack)
332 .Unwrap();
333}
334
330} // namespace Kernel 335} // 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:
135 return mutex; 135 return mutex;
136 } 136 }
137 137
138 /// Gets the address to the process' dedicated TLS region.
139 VAddr GetTLSRegionAddress() const {
140 return tls_region_address;
141 }
142
138 /// Gets the current status of the process 143 /// Gets the current status of the process
139 ProcessStatus GetStatus() const { 144 ProcessStatus GetStatus() const {
140 return status; 145 return status;
@@ -168,8 +173,24 @@ public:
168 return capabilities.GetPriorityMask(); 173 return capabilities.GetPriorityMask();
169 } 174 }
170 175
171 u32 IsVirtualMemoryEnabled() const { 176 /// Gets the amount of secure memory to allocate for memory management.
172 return is_virtual_address_memory_enabled; 177 u32 GetSystemResourceSize() const {
178 return system_resource_size;
179 }
180
181 /// Gets the amount of secure memory currently in use for memory management.
182 u32 GetSystemResourceUsage() const {
183 // On hardware, this returns the amount of system resource memory that has
184 // been used by the kernel. This is problematic for Yuzu to emulate, because
185 // system resource memory is used for page tables -- and yuzu doesn't really
186 // have a way to calculate how much memory is required for page tables for
187 // the current process at any given time.
188 // TODO: Is this even worth implementing? Games may retrieve this value via
189 // an SDK function that gets used + available system resource size for debug
190 // or diagnostic purposes. However, it seems unlikely that a game would make
191 // decisions based on how much system memory is dedicated to its page tables.
192 // Is returning a value other than zero wise?
193 return 0;
173 } 194 }
174 195
175 /// Whether this process is an AArch64 or AArch32 process. 196 /// Whether this process is an AArch64 or AArch32 process.
@@ -196,15 +217,15 @@ public:
196 u64 GetTotalPhysicalMemoryAvailable() const; 217 u64 GetTotalPhysicalMemoryAvailable() const;
197 218
198 /// Retrieves the total physical memory available to this process in bytes, 219 /// Retrieves the total physical memory available to this process in bytes,
199 /// without the size of the personal heap added to it. 220 /// without the size of the personal system resource heap added to it.
200 u64 GetTotalPhysicalMemoryAvailableWithoutMmHeap() const; 221 u64 GetTotalPhysicalMemoryAvailableWithoutSystemResource() const;
201 222
202 /// Retrieves the total physical memory used by this process in bytes. 223 /// Retrieves the total physical memory used by this process in bytes.
203 u64 GetTotalPhysicalMemoryUsed() const; 224 u64 GetTotalPhysicalMemoryUsed() const;
204 225
205 /// Retrieves the total physical memory used by this process in bytes, 226 /// Retrieves the total physical memory used by this process in bytes,
206 /// without the size of the personal heap added to it. 227 /// without the size of the personal system resource heap added to it.
207 u64 GetTotalPhysicalMemoryUsedWithoutMmHeap() const; 228 u64 GetTotalPhysicalMemoryUsedWithoutSystemResource() const;
208 229
209 /// Gets the list of all threads created with this process as their owner. 230 /// Gets the list of all threads created with this process as their owner.
210 const std::list<const Thread*>& GetThreadList() const { 231 const std::list<const Thread*>& GetThreadList() const {
@@ -280,6 +301,9 @@ private:
280 /// a process signal. 301 /// a process signal.
281 void ChangeStatus(ProcessStatus new_status); 302 void ChangeStatus(ProcessStatus new_status);
282 303
304 /// Allocates the main thread stack for the process, given the stack size in bytes.
305 void AllocateMainThreadStack(u64 stack_size);
306
283 /// Memory manager for this process. 307 /// Memory manager for this process.
284 Kernel::VMManager vm_manager; 308 Kernel::VMManager vm_manager;
285 309
@@ -298,12 +322,16 @@ private:
298 /// Title ID corresponding to the process 322 /// Title ID corresponding to the process
299 u64 program_id = 0; 323 u64 program_id = 0;
300 324
325 /// Specifies additional memory to be reserved for the process's memory management by the
326 /// system. When this is non-zero, secure memory is allocated and used for page table allocation
327 /// instead of using the normal global page tables/memory block management.
328 u32 system_resource_size = 0;
329
301 /// Resource limit descriptor for this process 330 /// Resource limit descriptor for this process
302 SharedPtr<ResourceLimit> resource_limit; 331 SharedPtr<ResourceLimit> resource_limit;
303 332
304 /// The ideal CPU core for this process, threads are scheduled on this core by default. 333 /// The ideal CPU core for this process, threads are scheduled on this core by default.
305 u8 ideal_core = 0; 334 u8 ideal_core = 0;
306 u32 is_virtual_address_memory_enabled = 0;
307 335
308 /// The Thread Local Storage area is allocated as processes create threads, 336 /// The Thread Local Storage area is allocated as processes create threads,
309 /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part 337 /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
@@ -338,6 +366,9 @@ private:
338 /// variable related facilities. 366 /// variable related facilities.
339 Mutex mutex; 367 Mutex mutex;
340 368
369 /// Address indicating the location of the process' dedicated TLS region.
370 VAddr tls_region_address = 0;
371
341 /// Random values for svcGetInfo RandomEntropy 372 /// Random values for svcGetInfo RandomEntropy
342 std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{}; 373 std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{};
343 374
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_
28 shared_memory->other_permissions = other_permissions; 28 shared_memory->other_permissions = other_permissions;
29 29
30 if (address == 0) { 30 if (address == 0) {
31 shared_memory->backing_block = std::make_shared<std::vector<u8>>(size); 31 shared_memory->backing_block = std::make_shared<Kernel::PhysicalMemory>(size);
32 shared_memory->backing_block_offset = 0; 32 shared_memory->backing_block_offset = 0;
33 33
34 // Refresh the address mappings for the current process. 34 // Refresh the address mappings for the current process.
@@ -59,8 +59,8 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process* owner_
59} 59}
60 60
61SharedPtr<SharedMemory> SharedMemory::CreateForApplet( 61SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
62 KernelCore& kernel, std::shared_ptr<std::vector<u8>> heap_block, std::size_t offset, u64 size, 62 KernelCore& kernel, std::shared_ptr<Kernel::PhysicalMemory> heap_block, std::size_t offset,
63 MemoryPermission permissions, MemoryPermission other_permissions, std::string name) { 63 u64 size, MemoryPermission permissions, MemoryPermission other_permissions, std::string name) {
64 SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel)); 64 SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel));
65 65
66 shared_memory->owner_process = nullptr; 66 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 @@
10 10
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "core/hle/kernel/object.h" 12#include "core/hle/kernel/object.h"
13#include "core/hle/kernel/physical_memory.h"
13#include "core/hle/kernel/process.h" 14#include "core/hle/kernel/process.h"
14#include "core/hle/result.h" 15#include "core/hle/result.h"
15 16
@@ -62,12 +63,10 @@ public:
62 * block. 63 * block.
63 * @param name Optional object name, used for debugging purposes. 64 * @param name Optional object name, used for debugging purposes.
64 */ 65 */
65 static SharedPtr<SharedMemory> CreateForApplet(KernelCore& kernel, 66 static SharedPtr<SharedMemory> CreateForApplet(
66 std::shared_ptr<std::vector<u8>> heap_block, 67 KernelCore& kernel, std::shared_ptr<Kernel::PhysicalMemory> heap_block, std::size_t offset,
67 std::size_t offset, u64 size, 68 u64 size, MemoryPermission permissions, MemoryPermission other_permissions,
68 MemoryPermission permissions, 69 std::string name = "Unknown Applet");
69 MemoryPermission other_permissions,
70 std::string name = "Unknown Applet");
71 70
72 std::string GetTypeName() const override { 71 std::string GetTypeName() const override {
73 return "SharedMemory"; 72 return "SharedMemory";
@@ -135,7 +134,7 @@ private:
135 ~SharedMemory() override; 134 ~SharedMemory() override;
136 135
137 /// Backing memory for this shared memory block. 136 /// Backing memory for this shared memory block.
138 std::shared_ptr<std::vector<u8>> backing_block; 137 std::shared_ptr<PhysicalMemory> backing_block;
139 /// Offset into the backing block for this shared memory. 138 /// Offset into the backing block for this shared memory.
140 std::size_t backing_block_offset = 0; 139 std::size_t backing_block_offset = 0;
141 /// Size of the memory block. Page-aligned. 140 /// 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
318 return result; 318 return result;
319 } 319 }
320 320
321 return vm_manager.UnmapRange(dst_addr, size); 321 const auto unmap_res = vm_manager.UnmapRange(dst_addr, size);
322
323 // Reprotect the source mapping on success
324 if (unmap_res.IsSuccess()) {
325 ASSERT(vm_manager.ReprotectRange(src_addr, size, VMAPermission::ReadWrite).IsSuccess());
326 }
327
328 return unmap_res;
322} 329}
323 330
324/// Connect to an OS service given the port name, returns the handle to the port to out 331/// 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
729 StackRegionBaseAddr = 14, 736 StackRegionBaseAddr = 14,
730 StackRegionSize = 15, 737 StackRegionSize = 15,
731 // 3.0.0+ 738 // 3.0.0+
732 IsVirtualAddressMemoryEnabled = 16, 739 SystemResourceSize = 16,
733 PersonalMmHeapUsage = 17, 740 SystemResourceUsage = 17,
734 TitleId = 18, 741 TitleId = 18,
735 // 4.0.0+ 742 // 4.0.0+
736 PrivilegedProcessId = 19, 743 PrivilegedProcessId = 19,
737 // 5.0.0+ 744 // 5.0.0+
738 UserExceptionContextAddr = 20, 745 UserExceptionContextAddr = 20,
739 // 6.0.0+ 746 // 6.0.0+
740 TotalPhysicalMemoryAvailableWithoutMmHeap = 21, 747 TotalPhysicalMemoryAvailableWithoutSystemResource = 21,
741 TotalPhysicalMemoryUsedWithoutMmHeap = 22, 748 TotalPhysicalMemoryUsedWithoutSystemResource = 22,
742 }; 749 };
743 750
744 const auto info_id_type = static_cast<GetInfoType>(info_id); 751 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
756 case GetInfoType::StackRegionSize: 763 case GetInfoType::StackRegionSize:
757 case GetInfoType::TotalPhysicalMemoryAvailable: 764 case GetInfoType::TotalPhysicalMemoryAvailable:
758 case GetInfoType::TotalPhysicalMemoryUsed: 765 case GetInfoType::TotalPhysicalMemoryUsed:
759 case GetInfoType::IsVirtualAddressMemoryEnabled: 766 case GetInfoType::SystemResourceSize:
760 case GetInfoType::PersonalMmHeapUsage: 767 case GetInfoType::SystemResourceUsage:
761 case GetInfoType::TitleId: 768 case GetInfoType::TitleId:
762 case GetInfoType::UserExceptionContextAddr: 769 case GetInfoType::UserExceptionContextAddr:
763 case GetInfoType::TotalPhysicalMemoryAvailableWithoutMmHeap: 770 case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource:
764 case GetInfoType::TotalPhysicalMemoryUsedWithoutMmHeap: { 771 case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource: {
765 if (info_sub_id != 0) { 772 if (info_sub_id != 0) {
766 return ERR_INVALID_ENUM_VALUE; 773 return ERR_INVALID_ENUM_VALUE;
767 } 774 }
@@ -822,8 +829,13 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
822 *result = process->GetTotalPhysicalMemoryUsed(); 829 *result = process->GetTotalPhysicalMemoryUsed();
823 return RESULT_SUCCESS; 830 return RESULT_SUCCESS;
824 831
825 case GetInfoType::IsVirtualAddressMemoryEnabled: 832 case GetInfoType::SystemResourceSize:
826 *result = process->IsVirtualMemoryEnabled(); 833 *result = process->GetSystemResourceSize();
834 return RESULT_SUCCESS;
835
836 case GetInfoType::SystemResourceUsage:
837 LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage");
838 *result = process->GetSystemResourceUsage();
827 return RESULT_SUCCESS; 839 return RESULT_SUCCESS;
828 840
829 case GetInfoType::TitleId: 841 case GetInfoType::TitleId:
@@ -831,17 +843,15 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
831 return RESULT_SUCCESS; 843 return RESULT_SUCCESS;
832 844
833 case GetInfoType::UserExceptionContextAddr: 845 case GetInfoType::UserExceptionContextAddr:
834 LOG_WARNING(Kernel_SVC, 846 *result = process->GetTLSRegionAddress();
835 "(STUBBED) Attempted to query user exception context address, returned 0");
836 *result = 0;
837 return RESULT_SUCCESS; 847 return RESULT_SUCCESS;
838 848
839 case GetInfoType::TotalPhysicalMemoryAvailableWithoutMmHeap: 849 case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource:
840 *result = process->GetTotalPhysicalMemoryAvailable(); 850 *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource();
841 return RESULT_SUCCESS; 851 return RESULT_SUCCESS;
842 852
843 case GetInfoType::TotalPhysicalMemoryUsedWithoutMmHeap: 853 case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource:
844 *result = process->GetTotalPhysicalMemoryUsedWithoutMmHeap(); 854 *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource();
845 return RESULT_SUCCESS; 855 return RESULT_SUCCESS;
846 856
847 default: 857 default:
@@ -946,6 +956,86 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
946 } 956 }
947} 957}
948 958
959/// Maps memory at a desired address
960static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
961 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
962
963 if (!Common::Is4KBAligned(addr)) {
964 LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
965 return ERR_INVALID_ADDRESS;
966 }
967
968 if (!Common::Is4KBAligned(size)) {
969 LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
970 return ERR_INVALID_SIZE;
971 }
972
973 if (size == 0) {
974 LOG_ERROR(Kernel_SVC, "Size is zero");
975 return ERR_INVALID_SIZE;
976 }
977
978 if (!(addr < addr + size)) {
979 LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
980 return ERR_INVALID_MEMORY_RANGE;
981 }
982
983 Process* const current_process = system.Kernel().CurrentProcess();
984 auto& vm_manager = current_process->VMManager();
985
986 if (current_process->GetSystemResourceSize() == 0) {
987 LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
988 return ERR_INVALID_STATE;
989 }
990
991 if (!vm_manager.IsWithinMapRegion(addr, size)) {
992 LOG_ERROR(Kernel_SVC, "Range not within map region");
993 return ERR_INVALID_MEMORY_RANGE;
994 }
995
996 return vm_manager.MapPhysicalMemory(addr, size);
997}
998
999/// Unmaps memory previously mapped via MapPhysicalMemory
1000static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
1001 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
1002
1003 if (!Common::Is4KBAligned(addr)) {
1004 LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
1005 return ERR_INVALID_ADDRESS;
1006 }
1007
1008 if (!Common::Is4KBAligned(size)) {
1009 LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
1010 return ERR_INVALID_SIZE;
1011 }
1012
1013 if (size == 0) {
1014 LOG_ERROR(Kernel_SVC, "Size is zero");
1015 return ERR_INVALID_SIZE;
1016 }
1017
1018 if (!(addr < addr + size)) {
1019 LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
1020 return ERR_INVALID_MEMORY_RANGE;
1021 }
1022
1023 Process* const current_process = system.Kernel().CurrentProcess();
1024 auto& vm_manager = current_process->VMManager();
1025
1026 if (current_process->GetSystemResourceSize() == 0) {
1027 LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
1028 return ERR_INVALID_STATE;
1029 }
1030
1031 if (!vm_manager.IsWithinMapRegion(addr, size)) {
1032 LOG_ERROR(Kernel_SVC, "Range not within map region");
1033 return ERR_INVALID_MEMORY_RANGE;
1034 }
1035
1036 return vm_manager.UnmapPhysicalMemory(addr, size);
1037}
1038
949/// Sets the thread activity 1039/// Sets the thread activity
950static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) { 1040static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) {
951 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); 1041 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
1647// Wait for an address (via Address Arbiter) 1737// Wait for an address (via Address Arbiter)
1648static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value, 1738static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value,
1649 s64 timeout) { 1739 s64 timeout) {
1650 LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", 1740 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", address,
1651 address, type, value, timeout); 1741 type, value, timeout);
1652 1742
1653 // If the passed address is a kernel virtual address, return invalid memory state. 1743 // If the passed address is a kernel virtual address, return invalid memory state.
1654 if (Memory::IsKernelVirtualAddress(address)) { 1744 if (Memory::IsKernelVirtualAddress(address)) {
@@ -1670,8 +1760,8 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type,
1670// Signals to an address (via Address Arbiter) 1760// Signals to an address (via Address Arbiter)
1671static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value, 1761static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value,
1672 s32 num_to_wake) { 1762 s32 num_to_wake) {
1673 LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", 1763 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}",
1674 address, type, value, num_to_wake); 1764 address, type, value, num_to_wake);
1675 1765
1676 // If the passed address is a kernel virtual address, return invalid memory state. 1766 // If the passed address is a kernel virtual address, return invalid memory state.
1677 if (Memory::IsKernelVirtualAddress(address)) { 1767 if (Memory::IsKernelVirtualAddress(address)) {
@@ -2303,8 +2393,8 @@ static const FunctionDef SVC_Table[] = {
2303 {0x29, SvcWrap<GetInfo>, "GetInfo"}, 2393 {0x29, SvcWrap<GetInfo>, "GetInfo"},
2304 {0x2A, nullptr, "FlushEntireDataCache"}, 2394 {0x2A, nullptr, "FlushEntireDataCache"},
2305 {0x2B, nullptr, "FlushDataCache"}, 2395 {0x2B, nullptr, "FlushDataCache"},
2306 {0x2C, nullptr, "MapPhysicalMemory"}, 2396 {0x2C, SvcWrap<MapPhysicalMemory>, "MapPhysicalMemory"},
2307 {0x2D, nullptr, "UnmapPhysicalMemory"}, 2397 {0x2D, SvcWrap<UnmapPhysicalMemory>, "UnmapPhysicalMemory"},
2308 {0x2E, nullptr, "GetFutureThreadInfo"}, 2398 {0x2E, nullptr, "GetFutureThreadInfo"},
2309 {0x2F, nullptr, "GetLastThreadInfo"}, 2399 {0x2F, nullptr, "GetLastThreadInfo"},
2310 {0x30, SvcWrap<GetResourceLimitLimitValue>, "GetResourceLimitLimitValue"}, 2400 {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) {
32 FuncReturn(system, func(system, Param(system, 0)).raw); 32 FuncReturn(system, func(system, Param(system, 0)).raw);
33} 33}
34 34
35template <ResultCode func(Core::System&, u64, u64)>
36void SvcWrap(Core::System& system) {
37 FuncReturn(system, func(system, Param(system, 0), Param(system, 1)).raw);
38}
39
35template <ResultCode func(Core::System&, u32)> 40template <ResultCode func(Core::System&, u32)>
36void SvcWrap(Core::System& system) { 41void SvcWrap(Core::System& system) {
37 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw); 42 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
47 return ERR_INVALID_STATE; 47 return ERR_INVALID_STATE;
48 } 48 }
49 49
50 backing_block = std::make_shared<std::vector<u8>>(size); 50 backing_block = std::make_shared<PhysicalMemory>(size);
51 51
52 const auto map_state = owner_permissions == MemoryPermission::None 52 const auto map_state = owner_permissions == MemoryPermission::None
53 ? MemoryState::TransferMemoryIsolated 53 ? 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 @@
8#include <vector> 8#include <vector>
9 9
10#include "core/hle/kernel/object.h" 10#include "core/hle/kernel/object.h"
11#include "core/hle/kernel/physical_memory.h"
11 12
12union ResultCode; 13union ResultCode;
13 14
@@ -82,7 +83,7 @@ private:
82 ~TransferMemory() override; 83 ~TransferMemory() override;
83 84
84 /// Memory block backing this instance. 85 /// Memory block backing this instance.
85 std::shared_ptr<std::vector<u8>> backing_block; 86 std::shared_ptr<PhysicalMemory> backing_block;
86 87
87 /// The base address for the memory managed by this instance. 88 /// The base address for the memory managed by this instance.
88 VAddr base_address = 0; 89 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 @@
5#include <algorithm> 5#include <algorithm>
6#include <iterator> 6#include <iterator>
7#include <utility> 7#include <utility>
8#include "common/alignment.h"
8#include "common/assert.h" 9#include "common/assert.h"
9#include "common/logging/log.h" 10#include "common/logging/log.h"
10#include "common/memory_hook.h" 11#include "common/memory_hook.h"
11#include "core/arm/arm_interface.h"
12#include "core/core.h" 12#include "core/core.h"
13#include "core/file_sys/program_metadata.h" 13#include "core/file_sys/program_metadata.h"
14#include "core/hle/kernel/errors.h" 14#include "core/hle/kernel/errors.h"
15#include "core/hle/kernel/process.h"
16#include "core/hle/kernel/resource_limit.h"
15#include "core/hle/kernel/vm_manager.h" 17#include "core/hle/kernel/vm_manager.h"
16#include "core/memory.h" 18#include "core/memory.h"
17#include "core/memory_setup.h" 19#include "core/memory_setup.h"
@@ -49,10 +51,14 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
49 type != next.type) { 51 type != next.type) {
50 return false; 52 return false;
51 } 53 }
52 if (type == VMAType::AllocatedMemoryBlock && 54 if ((attribute & MemoryAttribute::DeviceMapped) == MemoryAttribute::DeviceMapped) {
53 (backing_block != next.backing_block || offset + size != next.offset)) { 55 // TODO: Can device mapped memory be merged sanely?
56 // Not merging it may cause inaccuracies versus hardware when memory layout is queried.
54 return false; 57 return false;
55 } 58 }
59 if (type == VMAType::AllocatedMemoryBlock) {
60 return true;
61 }
56 if (type == VMAType::BackingMemory && backing_memory + size != next.backing_memory) { 62 if (type == VMAType::BackingMemory && backing_memory + size != next.backing_memory) {
57 return false; 63 return false;
58 } 64 }
@@ -98,9 +104,9 @@ bool VMManager::IsValidHandle(VMAHandle handle) const {
98} 104}
99 105
100ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, 106ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
101 std::shared_ptr<std::vector<u8>> block, 107 std::shared_ptr<PhysicalMemory> block,
102 std::size_t offset, u64 size, 108 std::size_t offset, u64 size,
103 MemoryState state) { 109 MemoryState state, VMAPermission perm) {
104 ASSERT(block != nullptr); 110 ASSERT(block != nullptr);
105 ASSERT(offset + size <= block->size()); 111 ASSERT(offset + size <= block->size());
106 112
@@ -109,17 +115,8 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
109 VirtualMemoryArea& final_vma = vma_handle->second; 115 VirtualMemoryArea& final_vma = vma_handle->second;
110 ASSERT(final_vma.size == size); 116 ASSERT(final_vma.size == size);
111 117
112 system.ArmInterface(0).MapBackingMemory(target, size, block->data() + offset,
113 VMAPermission::ReadWriteExecute);
114 system.ArmInterface(1).MapBackingMemory(target, size, block->data() + offset,
115 VMAPermission::ReadWriteExecute);
116 system.ArmInterface(2).MapBackingMemory(target, size, block->data() + offset,
117 VMAPermission::ReadWriteExecute);
118 system.ArmInterface(3).MapBackingMemory(target, size, block->data() + offset,
119 VMAPermission::ReadWriteExecute);
120
121 final_vma.type = VMAType::AllocatedMemoryBlock; 118 final_vma.type = VMAType::AllocatedMemoryBlock;
122 final_vma.permissions = VMAPermission::ReadWrite; 119 final_vma.permissions = perm;
123 final_vma.state = state; 120 final_vma.state = state;
124 final_vma.backing_block = std::move(block); 121 final_vma.backing_block = std::move(block);
125 final_vma.offset = offset; 122 final_vma.offset = offset;
@@ -137,11 +134,6 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
137 VirtualMemoryArea& final_vma = vma_handle->second; 134 VirtualMemoryArea& final_vma = vma_handle->second;
138 ASSERT(final_vma.size == size); 135 ASSERT(final_vma.size == size);
139 136
140 system.ArmInterface(0).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
141 system.ArmInterface(1).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
142 system.ArmInterface(2).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
143 system.ArmInterface(3).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
144
145 final_vma.type = VMAType::BackingMemory; 137 final_vma.type = VMAType::BackingMemory;
146 final_vma.permissions = VMAPermission::ReadWrite; 138 final_vma.permissions = VMAPermission::ReadWrite;
147 final_vma.state = state; 139 final_vma.state = state;
@@ -230,11 +222,6 @@ ResultCode VMManager::UnmapRange(VAddr target, u64 size) {
230 222
231 ASSERT(FindVMA(target)->second.size >= size); 223 ASSERT(FindVMA(target)->second.size >= size);
232 224
233 system.ArmInterface(0).UnmapMemory(target, size);
234 system.ArmInterface(1).UnmapMemory(target, size);
235 system.ArmInterface(2).UnmapMemory(target, size);
236 system.ArmInterface(3).UnmapMemory(target, size);
237
238 return RESULT_SUCCESS; 225 return RESULT_SUCCESS;
239} 226}
240 227
@@ -274,7 +261,7 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) {
274 261
275 if (heap_memory == nullptr) { 262 if (heap_memory == nullptr) {
276 // Initialize heap 263 // Initialize heap
277 heap_memory = std::make_shared<std::vector<u8>>(size); 264 heap_memory = std::make_shared<PhysicalMemory>(size);
278 heap_end = heap_region_base + size; 265 heap_end = heap_region_base + size;
279 } else { 266 } else {
280 UnmapRange(heap_region_base, GetCurrentHeapSize()); 267 UnmapRange(heap_region_base, GetCurrentHeapSize());
@@ -308,6 +295,166 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) {
308 return MakeResult<VAddr>(heap_region_base); 295 return MakeResult<VAddr>(heap_region_base);
309} 296}
310 297
298ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) {
299 const auto end_addr = target + size;
300 const auto last_addr = end_addr - 1;
301 VAddr cur_addr = target;
302
303 ResultCode result = RESULT_SUCCESS;
304
305 // Check how much memory we've already mapped.
306 const auto mapped_size_result = SizeOfAllocatedVMAsInRange(target, size);
307 if (mapped_size_result.Failed()) {
308 return mapped_size_result.Code();
309 }
310
311 // If we've already mapped the desired amount, return early.
312 const std::size_t mapped_size = *mapped_size_result;
313 if (mapped_size == size) {
314 return RESULT_SUCCESS;
315 }
316
317 // Check that we can map the memory we want.
318 const auto res_limit = system.CurrentProcess()->GetResourceLimit();
319 const u64 physmem_remaining = res_limit->GetMaxResourceValue(ResourceType::PhysicalMemory) -
320 res_limit->GetCurrentResourceValue(ResourceType::PhysicalMemory);
321 if (physmem_remaining < (size - mapped_size)) {
322 return ERR_RESOURCE_LIMIT_EXCEEDED;
323 }
324
325 // Keep track of the memory regions we unmap.
326 std::vector<std::pair<u64, u64>> mapped_regions;
327
328 // Iterate, trying to map memory.
329 {
330 cur_addr = target;
331
332 auto iter = FindVMA(target);
333 ASSERT_MSG(iter != vma_map.end(), "MapPhysicalMemory iter != end");
334
335 while (true) {
336 const auto& vma = iter->second;
337 const auto vma_start = vma.base;
338 const auto vma_end = vma_start + vma.size;
339 const auto vma_last = vma_end - 1;
340
341 // Map the memory block
342 const auto map_size = std::min(end_addr - cur_addr, vma_end - cur_addr);
343 if (vma.state == MemoryState::Unmapped) {
344 const auto map_res =
345 MapMemoryBlock(cur_addr, std::make_shared<PhysicalMemory>(map_size, 0), 0,
346 map_size, MemoryState::Heap, VMAPermission::ReadWrite);
347 result = map_res.Code();
348 if (result.IsError()) {
349 break;
350 }
351
352 mapped_regions.emplace_back(cur_addr, map_size);
353 }
354
355 // Break once we hit the end of the range.
356 if (last_addr <= vma_last) {
357 break;
358 }
359
360 // Advance to the next block.
361 cur_addr = vma_end;
362 iter = FindVMA(cur_addr);
363 ASSERT_MSG(iter != vma_map.end(), "MapPhysicalMemory iter != end");
364 }
365 }
366
367 // If we failed, unmap memory.
368 if (result.IsError()) {
369 for (const auto [unmap_address, unmap_size] : mapped_regions) {
370 ASSERT_MSG(UnmapRange(unmap_address, unmap_size).IsSuccess(),
371 "MapPhysicalMemory un-map on error");
372 }
373
374 return result;
375 }
376
377 // Update amount of mapped physical memory.
378 physical_memory_mapped += size - mapped_size;
379
380 return RESULT_SUCCESS;
381}
382
383ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) {
384 const auto end_addr = target + size;
385 const auto last_addr = end_addr - 1;
386 VAddr cur_addr = target;
387
388 ResultCode result = RESULT_SUCCESS;
389
390 // Check how much memory is currently mapped.
391 const auto mapped_size_result = SizeOfUnmappablePhysicalMemoryInRange(target, size);
392 if (mapped_size_result.Failed()) {
393 return mapped_size_result.Code();
394 }
395
396 // If we've already unmapped all the memory, return early.
397 const std::size_t mapped_size = *mapped_size_result;
398 if (mapped_size == 0) {
399 return RESULT_SUCCESS;
400 }
401
402 // Keep track of the memory regions we unmap.
403 std::vector<std::pair<u64, u64>> unmapped_regions;
404
405 // Try to unmap regions.
406 {
407 cur_addr = target;
408
409 auto iter = FindVMA(target);
410 ASSERT_MSG(iter != vma_map.end(), "UnmapPhysicalMemory iter != end");
411
412 while (true) {
413 const auto& vma = iter->second;
414 const auto vma_start = vma.base;
415 const auto vma_end = vma_start + vma.size;
416 const auto vma_last = vma_end - 1;
417
418 // Unmap the memory block
419 const auto unmap_size = std::min(end_addr - cur_addr, vma_end - cur_addr);
420 if (vma.state == MemoryState::Heap) {
421 result = UnmapRange(cur_addr, unmap_size);
422 if (result.IsError()) {
423 break;
424 }
425
426 unmapped_regions.emplace_back(cur_addr, unmap_size);
427 }
428
429 // Break once we hit the end of the range.
430 if (last_addr <= vma_last) {
431 break;
432 }
433
434 // Advance to the next block.
435 cur_addr = vma_end;
436 iter = FindVMA(cur_addr);
437 ASSERT_MSG(iter != vma_map.end(), "UnmapPhysicalMemory iter != end");
438 }
439 }
440
441 // If we failed, re-map regions.
442 // TODO: Preserve memory contents?
443 if (result.IsError()) {
444 for (const auto [map_address, map_size] : unmapped_regions) {
445 const auto remap_res =
446 MapMemoryBlock(map_address, std::make_shared<PhysicalMemory>(map_size, 0), 0,
447 map_size, MemoryState::Heap, VMAPermission::None);
448 ASSERT_MSG(remap_res.Succeeded(), "UnmapPhysicalMemory re-map on error");
449 }
450 }
451
452 // Update mapped amount
453 physical_memory_mapped -= mapped_size;
454
455 return RESULT_SUCCESS;
456}
457
311ResultCode VMManager::MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) { 458ResultCode VMManager::MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) {
312 constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped; 459 constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped;
313 const auto src_check_result = CheckRangeState( 460 const auto src_check_result = CheckRangeState(
@@ -447,7 +594,7 @@ ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, Mem
447 ASSERT_MSG(vma_offset + size <= vma->second.size, 594 ASSERT_MSG(vma_offset + size <= vma->second.size,
448 "Shared memory exceeds bounds of mapped block"); 595 "Shared memory exceeds bounds of mapped block");
449 596
450 const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block; 597 const std::shared_ptr<PhysicalMemory>& backing_block = vma->second.backing_block;
451 const std::size_t backing_block_offset = vma->second.offset + vma_offset; 598 const std::size_t backing_block_offset = vma->second.offset + vma_offset;
452 599
453 CASCADE_RESULT(auto new_vma, 600 CASCADE_RESULT(auto new_vma,
@@ -455,12 +602,12 @@ ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, Mem
455 // Protect mirror with permissions from old region 602 // Protect mirror with permissions from old region
456 Reprotect(new_vma, vma->second.permissions); 603 Reprotect(new_vma, vma->second.permissions);
457 // Remove permissions from old region 604 // Remove permissions from old region
458 Reprotect(vma, VMAPermission::None); 605 ReprotectRange(src_addr, size, VMAPermission::None);
459 606
460 return RESULT_SUCCESS; 607 return RESULT_SUCCESS;
461} 608}
462 609
463void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) { 610void VMManager::RefreshMemoryBlockMappings(const PhysicalMemory* block) {
464 // If this ever proves to have a noticeable performance impact, allow users of the function to 611 // If this ever proves to have a noticeable performance impact, allow users of the function to
465 // specify a specific range of addresses to limit the scan to. 612 // specify a specific range of addresses to limit the scan to.
466 for (const auto& p : vma_map) { 613 for (const auto& p : vma_map) {
@@ -588,14 +735,14 @@ VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u64 offset_in_vma) {
588VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) { 735VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) {
589 const VMAIter next_vma = std::next(iter); 736 const VMAIter next_vma = std::next(iter);
590 if (next_vma != vma_map.end() && iter->second.CanBeMergedWith(next_vma->second)) { 737 if (next_vma != vma_map.end() && iter->second.CanBeMergedWith(next_vma->second)) {
591 iter->second.size += next_vma->second.size; 738 MergeAdjacentVMA(iter->second, next_vma->second);
592 vma_map.erase(next_vma); 739 vma_map.erase(next_vma);
593 } 740 }
594 741
595 if (iter != vma_map.begin()) { 742 if (iter != vma_map.begin()) {
596 VMAIter prev_vma = std::prev(iter); 743 VMAIter prev_vma = std::prev(iter);
597 if (prev_vma->second.CanBeMergedWith(iter->second)) { 744 if (prev_vma->second.CanBeMergedWith(iter->second)) {
598 prev_vma->second.size += iter->second.size; 745 MergeAdjacentVMA(prev_vma->second, iter->second);
599 vma_map.erase(iter); 746 vma_map.erase(iter);
600 iter = prev_vma; 747 iter = prev_vma;
601 } 748 }
@@ -604,6 +751,38 @@ VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) {
604 return iter; 751 return iter;
605} 752}
606 753
754void VMManager::MergeAdjacentVMA(VirtualMemoryArea& left, const VirtualMemoryArea& right) {
755 ASSERT(left.CanBeMergedWith(right));
756
757 // Always merge allocated memory blocks, even when they don't share the same backing block.
758 if (left.type == VMAType::AllocatedMemoryBlock &&
759 (left.backing_block != right.backing_block || left.offset + left.size != right.offset)) {
760 // Check if we can save work.
761 if (left.offset == 0 && left.size == left.backing_block->size()) {
762 // Fast case: left is an entire backing block.
763 left.backing_block->insert(left.backing_block->end(),
764 right.backing_block->begin() + right.offset,
765 right.backing_block->begin() + right.offset + right.size);
766 } else {
767 // Slow case: make a new memory block for left and right.
768 auto new_memory = std::make_shared<PhysicalMemory>();
769 new_memory->insert(new_memory->end(), left.backing_block->begin() + left.offset,
770 left.backing_block->begin() + left.offset + left.size);
771 new_memory->insert(new_memory->end(), right.backing_block->begin() + right.offset,
772 right.backing_block->begin() + right.offset + right.size);
773 left.backing_block = new_memory;
774 left.offset = 0;
775 }
776
777 // Page table update is needed, because backing memory changed.
778 left.size += right.size;
779 UpdatePageTableForVMA(left);
780 } else {
781 // Just update the size.
782 left.size += right.size;
783 }
784}
785
607void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { 786void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
608 switch (vma.type) { 787 switch (vma.type) {
609 case VMAType::Free: 788 case VMAType::Free:
@@ -778,6 +957,84 @@ VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, Memo
778 std::make_tuple(initial_state, initial_permissions, initial_attributes & ~ignore_mask)); 957 std::make_tuple(initial_state, initial_permissions, initial_attributes & ~ignore_mask));
779} 958}
780 959
960ResultVal<std::size_t> VMManager::SizeOfAllocatedVMAsInRange(VAddr address,
961 std::size_t size) const {
962 const VAddr end_addr = address + size;
963 const VAddr last_addr = end_addr - 1;
964 std::size_t mapped_size = 0;
965
966 VAddr cur_addr = address;
967 auto iter = FindVMA(cur_addr);
968 ASSERT_MSG(iter != vma_map.end(), "SizeOfAllocatedVMAsInRange iter != end");
969
970 while (true) {
971 const auto& vma = iter->second;
972 const VAddr vma_start = vma.base;
973 const VAddr vma_end = vma_start + vma.size;
974 const VAddr vma_last = vma_end - 1;
975
976 // Add size if relevant.
977 if (vma.state != MemoryState::Unmapped) {
978 mapped_size += std::min(end_addr - cur_addr, vma_end - cur_addr);
979 }
980
981 // Break once we hit the end of the range.
982 if (last_addr <= vma_last) {
983 break;
984 }
985
986 // Advance to the next block.
987 cur_addr = vma_end;
988 iter = std::next(iter);
989 ASSERT_MSG(iter != vma_map.end(), "SizeOfAllocatedVMAsInRange iter != end");
990 }
991
992 return MakeResult(mapped_size);
993}
994
995ResultVal<std::size_t> VMManager::SizeOfUnmappablePhysicalMemoryInRange(VAddr address,
996 std::size_t size) const {
997 const VAddr end_addr = address + size;
998 const VAddr last_addr = end_addr - 1;
999 std::size_t mapped_size = 0;
1000
1001 VAddr cur_addr = address;
1002 auto iter = FindVMA(cur_addr);
1003 ASSERT_MSG(iter != vma_map.end(), "SizeOfUnmappablePhysicalMemoryInRange iter != end");
1004
1005 while (true) {
1006 const auto& vma = iter->second;
1007 const auto vma_start = vma.base;
1008 const auto vma_end = vma_start + vma.size;
1009 const auto vma_last = vma_end - 1;
1010 const auto state = vma.state;
1011 const auto attr = vma.attribute;
1012
1013 // Memory within region must be free or mapped heap.
1014 if (!((state == MemoryState::Heap && attr == MemoryAttribute::None) ||
1015 (state == MemoryState::Unmapped))) {
1016 return ERR_INVALID_ADDRESS_STATE;
1017 }
1018
1019 // Add size if relevant.
1020 if (state != MemoryState::Unmapped) {
1021 mapped_size += std::min(end_addr - cur_addr, vma_end - cur_addr);
1022 }
1023
1024 // Break once we hit the end of the range.
1025 if (last_addr <= vma_last) {
1026 break;
1027 }
1028
1029 // Advance to the next block.
1030 cur_addr = vma_end;
1031 iter = std::next(iter);
1032 ASSERT_MSG(iter != vma_map.end(), "SizeOfUnmappablePhysicalMemoryInRange iter != end");
1033 }
1034
1035 return MakeResult(mapped_size);
1036}
1037
781u64 VMManager::GetTotalPhysicalMemoryAvailable() const { 1038u64 VMManager::GetTotalPhysicalMemoryAvailable() const {
782 LOG_WARNING(Kernel, "(STUBBED) called"); 1039 LOG_WARNING(Kernel, "(STUBBED) called");
783 return 0xF8000000; 1040 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 @@
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/memory_hook.h" 12#include "common/memory_hook.h"
13#include "common/page_table.h" 13#include "common/page_table.h"
14#include "core/hle/kernel/physical_memory.h"
14#include "core/hle/result.h" 15#include "core/hle/result.h"
15#include "core/memory.h" 16#include "core/memory.h"
16 17
@@ -290,7 +291,7 @@ struct VirtualMemoryArea {
290 291
291 // Settings for type = AllocatedMemoryBlock 292 // Settings for type = AllocatedMemoryBlock
292 /// Memory block backing this VMA. 293 /// Memory block backing this VMA.
293 std::shared_ptr<std::vector<u8>> backing_block = nullptr; 294 std::shared_ptr<PhysicalMemory> backing_block = nullptr;
294 /// Offset into the backing_memory the mapping starts from. 295 /// Offset into the backing_memory the mapping starts from.
295 std::size_t offset = 0; 296 std::size_t offset = 0;
296 297
@@ -348,8 +349,9 @@ public:
348 * @param size Size of the mapping. 349 * @param size Size of the mapping.
349 * @param state MemoryState tag to attach to the VMA. 350 * @param state MemoryState tag to attach to the VMA.
350 */ 351 */
351 ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block, 352 ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<PhysicalMemory> block,
352 std::size_t offset, u64 size, MemoryState state); 353 std::size_t offset, u64 size, MemoryState state,
354 VMAPermission perm = VMAPermission::ReadWrite);
353 355
354 /** 356 /**
355 * Maps an unmanaged host memory pointer at a given address. 357 * Maps an unmanaged host memory pointer at a given address.
@@ -450,6 +452,34 @@ public:
450 /// 452 ///
451 ResultVal<VAddr> SetHeapSize(u64 size); 453 ResultVal<VAddr> SetHeapSize(u64 size);
452 454
455 /// Maps memory at a given address.
456 ///
457 /// @param addr The virtual address to map memory at.
458 /// @param size The amount of memory to map.
459 ///
460 /// @note The destination address must lie within the Map region.
461 ///
462 /// @note This function requires that SystemResourceSize be non-zero,
463 /// however, this is just because if it were not then the
464 /// resulting page tables could be exploited on hardware by
465 /// a malicious program. SystemResource usage does not need
466 /// to be explicitly checked or updated here.
467 ResultCode MapPhysicalMemory(VAddr target, u64 size);
468
469 /// Unmaps memory at a given address.
470 ///
471 /// @param addr The virtual address to unmap memory at.
472 /// @param size The amount of memory to unmap.
473 ///
474 /// @note The destination address must lie within the Map region.
475 ///
476 /// @note This function requires that SystemResourceSize be non-zero,
477 /// however, this is just because if it were not then the
478 /// resulting page tables could be exploited on hardware by
479 /// a malicious program. SystemResource usage does not need
480 /// to be explicitly checked or updated here.
481 ResultCode UnmapPhysicalMemory(VAddr target, u64 size);
482
453 /// Maps a region of memory as code memory. 483 /// Maps a region of memory as code memory.
454 /// 484 ///
455 /// @param dst_address The base address of the region to create the aliasing memory region. 485 /// @param dst_address The base address of the region to create the aliasing memory region.
@@ -518,7 +548,7 @@ public:
518 * Scans all VMAs and updates the page table range of any that use the given vector as backing 548 * Scans all VMAs and updates the page table range of any that use the given vector as backing
519 * memory. This should be called after any operation that causes reallocation of the vector. 549 * memory. This should be called after any operation that causes reallocation of the vector.
520 */ 550 */
521 void RefreshMemoryBlockMappings(const std::vector<u8>* block); 551 void RefreshMemoryBlockMappings(const PhysicalMemory* block);
522 552
523 /// Dumps the address space layout to the log, for debugging 553 /// Dumps the address space layout to the log, for debugging
524 void LogLayout() const; 554 void LogLayout() const;
@@ -657,6 +687,11 @@ private:
657 */ 687 */
658 VMAIter MergeAdjacent(VMAIter vma); 688 VMAIter MergeAdjacent(VMAIter vma);
659 689
690 /**
691 * Merges two adjacent VMAs.
692 */
693 void MergeAdjacentVMA(VirtualMemoryArea& left, const VirtualMemoryArea& right);
694
660 /// Updates the pages corresponding to this VMA so they match the VMA's attributes. 695 /// Updates the pages corresponding to this VMA so they match the VMA's attributes.
661 void UpdatePageTableForVMA(const VirtualMemoryArea& vma); 696 void UpdatePageTableForVMA(const VirtualMemoryArea& vma);
662 697
@@ -701,6 +736,13 @@ private:
701 MemoryAttribute attribute_mask, MemoryAttribute attribute, 736 MemoryAttribute attribute_mask, MemoryAttribute attribute,
702 MemoryAttribute ignore_mask) const; 737 MemoryAttribute ignore_mask) const;
703 738
739 /// Gets the amount of memory currently mapped (state != Unmapped) in a range.
740 ResultVal<std::size_t> SizeOfAllocatedVMAsInRange(VAddr address, std::size_t size) const;
741
742 /// Gets the amount of memory unmappable by UnmapPhysicalMemory in a range.
743 ResultVal<std::size_t> SizeOfUnmappablePhysicalMemoryInRange(VAddr address,
744 std::size_t size) const;
745
704 /** 746 /**
705 * A map covering the entirety of the managed address space, keyed by the `base` field of each 747 * A map covering the entirety of the managed address space, keyed by the `base` field of each
706 * VMA. It must always be modified by splitting or merging VMAs, so that the invariant 748 * VMA. It must always be modified by splitting or merging VMAs, so that the invariant
@@ -736,12 +778,17 @@ private:
736 // the entire virtual address space extents that bound the allocations, including any holes. 778 // the entire virtual address space extents that bound the allocations, including any holes.
737 // This makes deallocation and reallocation of holes fast and keeps process memory contiguous 779 // This makes deallocation and reallocation of holes fast and keeps process memory contiguous
738 // in the emulator address space, allowing Memory::GetPointer to be reasonably safe. 780 // in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
739 std::shared_ptr<std::vector<u8>> heap_memory; 781 std::shared_ptr<PhysicalMemory> heap_memory;
740 782
741 // The end of the currently allocated heap. This is not an inclusive 783 // The end of the currently allocated heap. This is not an inclusive
742 // end of the range. This is essentially 'base_address + current_size'. 784 // end of the range. This is essentially 'base_address + current_size'.
743 VAddr heap_end = 0; 785 VAddr heap_end = 0;
744 786
787 // The current amount of memory mapped via MapPhysicalMemory.
788 // This is used here (and in Nintendo's kernel) only for debugging, and does not impact
789 // any behavior.
790 u64 physical_memory_mapped = 0;
791
745 Core::System& system; 792 Core::System& system;
746}; 793};
747} // namespace Kernel 794} // 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
266 {65, nullptr, "ReportUserIsActive"}, 266 {65, nullptr, "ReportUserIsActive"},
267 {66, nullptr, "GetCurrentIlluminance"}, 267 {66, nullptr, "GetCurrentIlluminance"},
268 {67, nullptr, "IsIlluminanceAvailable"}, 268 {67, nullptr, "IsIlluminanceAvailable"},
269 {68, nullptr, "SetAutoSleepDisabled"}, 269 {68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"},
270 {69, nullptr, "IsAutoSleepDisabled"}, 270 {69, &ISelfController::IsAutoSleepDisabled, "IsAutoSleepDisabled"},
271 {70, nullptr, "ReportMultimediaError"}, 271 {70, nullptr, "ReportMultimediaError"},
272 {71, nullptr, "GetCurrentIlluminanceEx"}, 272 {71, nullptr, "GetCurrentIlluminanceEx"},
273 {80, nullptr, "SetWirelessPriorityMode"}, 273 {80, nullptr, "SetWirelessPriorityMode"},
@@ -454,6 +454,34 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
454 rb.Push<u32>(idle_time_detection_extension); 454 rb.Push<u32>(idle_time_detection_extension);
455} 455}
456 456
457void ISelfController::SetAutoSleepDisabled(Kernel::HLERequestContext& ctx) {
458 IPC::RequestParser rp{ctx};
459 is_auto_sleep_disabled = rp.Pop<bool>();
460
461 // On the system itself, if the previous state of is_auto_sleep_disabled
462 // differed from the current value passed in, it'd signify the internal
463 // window manager to update (and also increment some statistics like update counts)
464 //
465 // It'd also indicate this change to an idle handling context.
466 //
467 // However, given we're emulating this behavior, most of this can be ignored
468 // and it's sufficient to simply set the member variable for querying via
469 // IsAutoSleepDisabled().
470
471 LOG_DEBUG(Service_AM, "called. is_auto_sleep_disabled={}", is_auto_sleep_disabled);
472
473 IPC::ResponseBuilder rb{ctx, 2};
474 rb.Push(RESULT_SUCCESS);
475}
476
477void ISelfController::IsAutoSleepDisabled(Kernel::HLERequestContext& ctx) {
478 LOG_DEBUG(Service_AM, "called.");
479
480 IPC::ResponseBuilder rb{ctx, 3};
481 rb.Push(RESULT_SUCCESS);
482 rb.Push(is_auto_sleep_disabled);
483}
484
457void ISelfController::GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx) { 485void ISelfController::GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx) {
458 LOG_DEBUG(Service_AM, "called."); 486 LOG_DEBUG(Service_AM, "called.");
459 487
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:
133 void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx); 133 void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
134 void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); 134 void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
135 void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); 135 void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
136 void SetAutoSleepDisabled(Kernel::HLERequestContext& ctx);
137 void IsAutoSleepDisabled(Kernel::HLERequestContext& ctx);
136 void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); 138 void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
137 void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); 139 void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
138 140
@@ -142,6 +144,7 @@ private:
142 144
143 u32 idle_time_detection_extension = 0; 145 u32 idle_time_detection_extension = 0;
144 u64 num_fatal_sections_entered = 0; 146 u64 num_fatal_sections_entered = 0;
147 bool is_auto_sleep_disabled = false;
145}; 148};
146 149
147class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { 150class 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 @@
19 19
20namespace Service::Audio { 20namespace Service::Audio {
21 21
22void InstallInterfaces(SM::ServiceManager& service_manager) { 22void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
23 std::make_shared<AudCtl>()->InstallAsService(service_manager); 23 std::make_shared<AudCtl>()->InstallAsService(service_manager);
24 std::make_shared<AudOutA>()->InstallAsService(service_manager); 24 std::make_shared<AudOutA>()->InstallAsService(service_manager);
25 std::make_shared<AudOutU>()->InstallAsService(service_manager); 25 std::make_shared<AudOutU>(system)->InstallAsService(service_manager);
26 std::make_shared<AudInA>()->InstallAsService(service_manager); 26 std::make_shared<AudInA>()->InstallAsService(service_manager);
27 std::make_shared<AudInU>()->InstallAsService(service_manager); 27 std::make_shared<AudInU>()->InstallAsService(service_manager);
28 std::make_shared<AudRecA>()->InstallAsService(service_manager); 28 std::make_shared<AudRecA>()->InstallAsService(service_manager);
29 std::make_shared<AudRecU>()->InstallAsService(service_manager); 29 std::make_shared<AudRecU>()->InstallAsService(service_manager);
30 std::make_shared<AudRenA>()->InstallAsService(service_manager); 30 std::make_shared<AudRenA>()->InstallAsService(service_manager);
31 std::make_shared<AudRenU>()->InstallAsService(service_manager); 31 std::make_shared<AudRenU>(system)->InstallAsService(service_manager);
32 std::make_shared<CodecCtl>()->InstallAsService(service_manager); 32 std::make_shared<CodecCtl>()->InstallAsService(service_manager);
33 std::make_shared<HwOpus>()->InstallAsService(service_manager); 33 std::make_shared<HwOpus>()->InstallAsService(service_manager);
34 34
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 @@
4 4
5#pragma once 5#pragma once
6 6
7namespace Core {
8class System;
9}
10
7namespace Service::SM { 11namespace Service::SM {
8class ServiceManager; 12class ServiceManager;
9} 13}
@@ -11,6 +15,6 @@ class ServiceManager;
11namespace Service::Audio { 15namespace Service::Audio {
12 16
13/// Registers all Audio services with the specified service manager. 17/// Registers all Audio services with the specified service manager.
14void InstallInterfaces(SM::ServiceManager& service_manager); 18void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
15 19
16} // namespace Service::Audio 20} // 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 {
40 40
41class IAudioOut final : public ServiceFramework<IAudioOut> { 41class IAudioOut final : public ServiceFramework<IAudioOut> {
42public: 42public:
43 IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core, std::string&& device_name, 43 IAudioOut(Core::System& system, AudoutParams audio_params, AudioCore::AudioOut& audio_core,
44 std::string&& unique_name) 44 std::string&& device_name, std::string&& unique_name)
45 : ServiceFramework("IAudioOut"), audio_core(audio_core), 45 : ServiceFramework("IAudioOut"), audio_core(audio_core),
46 device_name(std::move(device_name)), audio_params(audio_params) { 46 device_name(std::move(device_name)), audio_params(audio_params) {
47 // clang-format off 47 // clang-format off
@@ -65,7 +65,6 @@ public:
65 RegisterHandlers(functions); 65 RegisterHandlers(functions);
66 66
67 // This is the event handle used to check if the audio buffer was released 67 // This is the event handle used to check if the audio buffer was released
68 auto& system = Core::System::GetInstance();
69 buffer_event = Kernel::WritableEvent::CreateEventPair( 68 buffer_event = Kernel::WritableEvent::CreateEventPair(
70 system.Kernel(), Kernel::ResetType::Manual, "IAudioOutBufferReleased"); 69 system.Kernel(), Kernel::ResetType::Manual, "IAudioOutBufferReleased");
71 70
@@ -212,6 +211,22 @@ private:
212 Kernel::EventPair buffer_event; 211 Kernel::EventPair buffer_event;
213}; 212};
214 213
214AudOutU::AudOutU(Core::System& system_) : ServiceFramework("audout:u"), system{system_} {
215 // clang-format off
216 static const FunctionInfo functions[] = {
217 {0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"},
218 {1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"},
219 {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"},
220 {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"},
221 };
222 // clang-format on
223
224 RegisterHandlers(functions);
225 audio_core = std::make_unique<AudioCore::AudioOut>();
226}
227
228AudOutU::~AudOutU() = default;
229
215void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { 230void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
216 LOG_DEBUG(Service_Audio, "called"); 231 LOG_DEBUG(Service_Audio, "called");
217 232
@@ -248,7 +263,7 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
248 263
249 std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())}; 264 std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())};
250 auto audio_out_interface = std::make_shared<IAudioOut>( 265 auto audio_out_interface = std::make_shared<IAudioOut>(
251 params, *audio_core, std::move(device_name), std::move(unique_name)); 266 system, params, *audio_core, std::move(device_name), std::move(unique_name));
252 267
253 IPC::ResponseBuilder rb{ctx, 6, 0, 1}; 268 IPC::ResponseBuilder rb{ctx, 6, 0, 1};
254 rb.Push(RESULT_SUCCESS); 269 rb.Push(RESULT_SUCCESS);
@@ -256,20 +271,9 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
256 rb.Push<u32>(params.channel_count); 271 rb.Push<u32>(params.channel_count);
257 rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16)); 272 rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
258 rb.Push<u32>(static_cast<u32>(AudioState::Stopped)); 273 rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
259 rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface); 274 rb.PushIpcInterface<IAudioOut>(audio_out_interface);
260 275
261 audio_out_interfaces.push_back(std::move(audio_out_interface)); 276 audio_out_interfaces.push_back(std::move(audio_out_interface));
262} 277}
263 278
264AudOutU::AudOutU() : ServiceFramework("audout:u") {
265 static const FunctionInfo functions[] = {{0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"},
266 {1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"},
267 {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"},
268 {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"}};
269 RegisterHandlers(functions);
270 audio_core = std::make_unique<AudioCore::AudioOut>();
271}
272
273AudOutU::~AudOutU() = default;
274
275} // namespace Service::Audio 279} // 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 {
11class AudioOut; 11class AudioOut;
12} 12}
13 13
14namespace Core {
15class System;
16}
17
14namespace Kernel { 18namespace Kernel {
15class HLERequestContext; 19class HLERequestContext;
16} 20}
@@ -21,15 +25,17 @@ class IAudioOut;
21 25
22class AudOutU final : public ServiceFramework<AudOutU> { 26class AudOutU final : public ServiceFramework<AudOutU> {
23public: 27public:
24 AudOutU(); 28 explicit AudOutU(Core::System& system_);
25 ~AudOutU() override; 29 ~AudOutU() override;
26 30
27private: 31private:
32 void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
33 void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
34
28 std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces; 35 std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces;
29 std::unique_ptr<AudioCore::AudioOut> audio_core; 36 std::unique_ptr<AudioCore::AudioOut> audio_core;
30 37
31 void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); 38 Core::System& system;
32 void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
33}; 39};
34 40
35} // namespace Service::Audio 41} // 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 @@
5#include <algorithm> 5#include <algorithm>
6#include <array> 6#include <array>
7#include <memory> 7#include <memory>
8#include <string_view>
8 9
9#include "audio_core/audio_renderer.h" 10#include "audio_core/audio_renderer.h"
10#include "common/alignment.h" 11#include "common/alignment.h"
@@ -25,7 +26,8 @@ namespace Service::Audio {
25 26
26class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { 27class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
27public: 28public:
28 explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params) 29 explicit IAudioRenderer(Core::System& system, AudioCore::AudioRendererParameter audren_params,
30 const std::size_t instance_number)
29 : ServiceFramework("IAudioRenderer") { 31 : ServiceFramework("IAudioRenderer") {
30 // clang-format off 32 // clang-format off
31 static const FunctionInfo functions[] = { 33 static const FunctionInfo functions[] = {
@@ -45,11 +47,10 @@ public:
45 // clang-format on 47 // clang-format on
46 RegisterHandlers(functions); 48 RegisterHandlers(functions);
47 49
48 auto& system = Core::System::GetInstance();
49 system_event = Kernel::WritableEvent::CreateEventPair( 50 system_event = Kernel::WritableEvent::CreateEventPair(
50 system.Kernel(), Kernel::ResetType::Manual, "IAudioRenderer:SystemEvent"); 51 system.Kernel(), Kernel::ResetType::Manual, "IAudioRenderer:SystemEvent");
51 renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), audren_params, 52 renderer = std::make_unique<AudioCore::AudioRenderer>(
52 system_event.writable); 53 system.CoreTiming(), audren_params, system_event.writable, instance_number);
53 } 54 }
54 55
55private: 56private:
@@ -159,7 +160,8 @@ private:
159 160
160class IAudioDevice final : public ServiceFramework<IAudioDevice> { 161class IAudioDevice final : public ServiceFramework<IAudioDevice> {
161public: 162public:
162 IAudioDevice() : ServiceFramework("IAudioDevice") { 163 explicit IAudioDevice(Core::System& system, u32_le revision_num)
164 : ServiceFramework("IAudioDevice"), revision{revision_num} {
163 static const FunctionInfo functions[] = { 165 static const FunctionInfo functions[] = {
164 {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"}, 166 {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
165 {1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"}, 167 {1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"},
@@ -177,7 +179,7 @@ public:
177 }; 179 };
178 RegisterHandlers(functions); 180 RegisterHandlers(functions);
179 181
180 auto& kernel = Core::System::GetInstance().Kernel(); 182 auto& kernel = system.Kernel();
181 buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, 183 buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
182 "IAudioOutBufferReleasedEvent"); 184 "IAudioOutBufferReleasedEvent");
183 185
@@ -188,15 +190,47 @@ public:
188 } 190 }
189 191
190private: 192private:
193 using AudioDeviceName = std::array<char, 256>;
194 static constexpr std::array<std::string_view, 4> audio_device_names{{
195 "AudioStereoJackOutput",
196 "AudioBuiltInSpeakerOutput",
197 "AudioTvOutput",
198 "AudioUsbDeviceOutput",
199 }};
200 enum class DeviceType {
201 AHUBHeadphones,
202 AHUBSpeakers,
203 HDA,
204 USBOutput,
205 };
206
191 void ListAudioDeviceName(Kernel::HLERequestContext& ctx) { 207 void ListAudioDeviceName(Kernel::HLERequestContext& ctx) {
192 LOG_WARNING(Service_Audio, "(STUBBED) called"); 208 LOG_DEBUG(Service_Audio, "called");
193 209
194 constexpr std::array<char, 15> audio_interface{{"AudioInterface"}}; 210 const bool usb_output_supported =
195 ctx.WriteBuffer(audio_interface); 211 IsFeatureSupported(AudioFeatures::AudioUSBDeviceOutput, revision);
212 const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioDeviceName);
213
214 std::vector<AudioDeviceName> name_buffer;
215 name_buffer.reserve(audio_device_names.size());
216
217 for (std::size_t i = 0; i < count && i < audio_device_names.size(); i++) {
218 const auto type = static_cast<DeviceType>(i);
219
220 if (!usb_output_supported && type == DeviceType::USBOutput) {
221 continue;
222 }
223
224 const auto& device_name = audio_device_names[i];
225 auto& entry = name_buffer.emplace_back();
226 device_name.copy(entry.data(), device_name.size());
227 }
228
229 ctx.WriteBuffer(name_buffer);
196 230
197 IPC::ResponseBuilder rb{ctx, 3}; 231 IPC::ResponseBuilder rb{ctx, 3};
198 rb.Push(RESULT_SUCCESS); 232 rb.Push(RESULT_SUCCESS);
199 rb.Push<u32>(1); 233 rb.Push(static_cast<u32>(name_buffer.size()));
200 } 234 }
201 235
202 void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) { 236 void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) {
@@ -215,12 +249,16 @@ private:
215 void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) { 249 void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) {
216 LOG_WARNING(Service_Audio, "(STUBBED) called"); 250 LOG_WARNING(Service_Audio, "(STUBBED) called");
217 251
218 constexpr std::array<char, 12> audio_interface{{"AudioDevice"}}; 252 // Currently set to always be TV audio output.
219 ctx.WriteBuffer(audio_interface); 253 const auto& device_name = audio_device_names[2];
220 254
221 IPC::ResponseBuilder rb{ctx, 3}; 255 AudioDeviceName out_device_name{};
256 device_name.copy(out_device_name.data(), device_name.size());
257
258 ctx.WriteBuffer(out_device_name);
259
260 IPC::ResponseBuilder rb{ctx, 2};
222 rb.Push(RESULT_SUCCESS); 261 rb.Push(RESULT_SUCCESS);
223 rb.Push<u32>(1);
224 } 262 }
225 263
226 void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) { 264 void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) {
@@ -249,12 +287,13 @@ private:
249 rb.PushCopyObjects(audio_output_device_switch_event.readable); 287 rb.PushCopyObjects(audio_output_device_switch_event.readable);
250 } 288 }
251 289
290 u32_le revision = 0;
252 Kernel::EventPair buffer_event; 291 Kernel::EventPair buffer_event;
253 Kernel::EventPair audio_output_device_switch_event; 292 Kernel::EventPair audio_output_device_switch_event;
254 293
255}; // namespace Audio 294}; // namespace Audio
256 295
257AudRenU::AudRenU() : ServiceFramework("audren:u") { 296AudRenU::AudRenU(Core::System& system_) : ServiceFramework("audren:u"), system{system_} {
258 // clang-format off 297 // clang-format off
259 static const FunctionInfo functions[] = { 298 static const FunctionInfo functions[] = {
260 {0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"}, 299 {0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"},
@@ -327,7 +366,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
327 }; 366 };
328 367
329 // Calculates the portion of the size related to the mix data (and the sorting thereof). 368 // Calculates the portion of the size related to the mix data (and the sorting thereof).
330 const auto calculate_mix_info_size = [this](const AudioCore::AudioRendererParameter& params) { 369 const auto calculate_mix_info_size = [](const AudioCore::AudioRendererParameter& params) {
331 // The size of the mixing info data structure. 370 // The size of the mixing info data structure.
332 constexpr u64 mix_info_size = 0x940; 371 constexpr u64 mix_info_size = 0x940;
333 372
@@ -399,7 +438,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
399 438
400 // Calculates the part of the size related to the splitter context. 439 // Calculates the part of the size related to the splitter context.
401 const auto calculate_splitter_context_size = 440 const auto calculate_splitter_context_size =
402 [this](const AudioCore::AudioRendererParameter& params) -> u64 { 441 [](const AudioCore::AudioRendererParameter& params) -> u64 {
403 if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { 442 if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
404 return 0; 443 return 0;
405 } 444 }
@@ -446,7 +485,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
446 }; 485 };
447 486
448 // Calculates the part of the size related to performance statistics. 487 // Calculates the part of the size related to performance statistics.
449 const auto calculate_perf_size = [this](const AudioCore::AudioRendererParameter& params) { 488 const auto calculate_perf_size = [](const AudioCore::AudioRendererParameter& params) {
450 // Extra size value appended to the end of the calculation. 489 // Extra size value appended to the end of the calculation.
451 constexpr u64 appended = 128; 490 constexpr u64 appended = 128;
452 491
@@ -473,78 +512,76 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
473 }; 512 };
474 513
475 // Calculates the part of the size that relates to the audio command buffer. 514 // Calculates the part of the size that relates to the audio command buffer.
476 const auto calculate_command_buffer_size = 515 const auto calculate_command_buffer_size = [](const AudioCore::AudioRendererParameter& params) {
477 [this](const AudioCore::AudioRendererParameter& params) { 516 constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
478 constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
479 517
480 if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) { 518 if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
481 constexpr u64 command_buffer_size = 0x18000; 519 constexpr u64 command_buffer_size = 0x18000;
482 520
483 return command_buffer_size + alignment; 521 return command_buffer_size + alignment;
484 } 522 }
485 523
486 // When the variadic command buffer is supported, this means 524 // When the variadic command buffer is supported, this means
487 // the command generator for the audio renderer can issue commands 525 // the command generator for the audio renderer can issue commands
488 // that are (as one would expect), variable in size. So what we need to do 526 // that are (as one would expect), variable in size. So what we need to do
489 // is determine the maximum possible size for a few command data structures 527 // is determine the maximum possible size for a few command data structures
490 // then multiply them by the amount of present commands indicated by the given 528 // then multiply them by the amount of present commands indicated by the given
491 // respective audio parameters. 529 // respective audio parameters.
492 530
493 constexpr u64 max_biquad_filters = 2; 531 constexpr u64 max_biquad_filters = 2;
494 constexpr u64 max_mix_buffers = 24; 532 constexpr u64 max_mix_buffers = 24;
495 533
496 constexpr u64 biquad_filter_command_size = 0x2C; 534 constexpr u64 biquad_filter_command_size = 0x2C;
497 535
498 constexpr u64 depop_mix_command_size = 0x24; 536 constexpr u64 depop_mix_command_size = 0x24;
499 constexpr u64 depop_setup_command_size = 0x50; 537 constexpr u64 depop_setup_command_size = 0x50;
500 538
501 constexpr u64 effect_command_max_size = 0x540; 539 constexpr u64 effect_command_max_size = 0x540;
502 540
503 constexpr u64 mix_command_size = 0x1C; 541 constexpr u64 mix_command_size = 0x1C;
504 constexpr u64 mix_ramp_command_size = 0x24; 542 constexpr u64 mix_ramp_command_size = 0x24;
505 constexpr u64 mix_ramp_grouped_command_size = 0x13C; 543 constexpr u64 mix_ramp_grouped_command_size = 0x13C;
506 544
507 constexpr u64 perf_command_size = 0x28; 545 constexpr u64 perf_command_size = 0x28;
508 546
509 constexpr u64 sink_command_size = 0x130; 547 constexpr u64 sink_command_size = 0x130;
510 548
511 constexpr u64 submix_command_max_size = 549 constexpr u64 submix_command_max_size =
512 depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers; 550 depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
513 551
514 constexpr u64 volume_command_size = 0x1C; 552 constexpr u64 volume_command_size = 0x1C;
515 constexpr u64 volume_ramp_command_size = 0x20; 553 constexpr u64 volume_ramp_command_size = 0x20;
516 554
517 constexpr u64 voice_biquad_filter_command_size = 555 constexpr u64 voice_biquad_filter_command_size =
518 biquad_filter_command_size * max_biquad_filters; 556 biquad_filter_command_size * max_biquad_filters;
519 constexpr u64 voice_data_command_size = 0x9C; 557 constexpr u64 voice_data_command_size = 0x9C;
520 const u64 voice_command_max_size = 558 const u64 voice_command_max_size =
521 (params.splitter_count * depop_setup_command_size) + 559 (params.splitter_count * depop_setup_command_size) +
522 (voice_data_command_size + voice_biquad_filter_command_size + 560 (voice_data_command_size + voice_biquad_filter_command_size + volume_ramp_command_size +
523 volume_ramp_command_size + mix_ramp_grouped_command_size); 561 mix_ramp_grouped_command_size);
524 562
525 // Now calculate the individual elements that comprise the size and add them together. 563 // Now calculate the individual elements that comprise the size and add them together.
526 const u64 effect_commands_size = params.effect_count * effect_command_max_size; 564 const u64 effect_commands_size = params.effect_count * effect_command_max_size;
527 565
528 const u64 final_mix_commands_size = 566 const u64 final_mix_commands_size =
529 depop_mix_command_size + volume_command_size * max_mix_buffers; 567 depop_mix_command_size + volume_command_size * max_mix_buffers;
530 568
531 const u64 perf_commands_size = 569 const u64 perf_commands_size =
532 perf_command_size * 570 perf_command_size * (CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
533 (CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
534 571
535 const u64 sink_commands_size = params.sink_count * sink_command_size; 572 const u64 sink_commands_size = params.sink_count * sink_command_size;
536 573
537 const u64 splitter_commands_size = 574 const u64 splitter_commands_size =
538 params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size; 575 params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size;
539 576
540 const u64 submix_commands_size = params.submix_count * submix_command_max_size; 577 const u64 submix_commands_size = params.submix_count * submix_command_max_size;
541 578
542 const u64 voice_commands_size = params.voice_count * voice_command_max_size; 579 const u64 voice_commands_size = params.voice_count * voice_command_max_size;
543 580
544 return effect_commands_size + final_mix_commands_size + perf_commands_size + 581 return effect_commands_size + final_mix_commands_size + perf_commands_size +
545 sink_commands_size + splitter_commands_size + submix_commands_size + 582 sink_commands_size + splitter_commands_size + submix_commands_size +
546 voice_commands_size + alignment; 583 voice_commands_size + alignment;
547 }; 584 };
548 585
549 IPC::RequestParser rp{ctx}; 586 IPC::RequestParser rp{ctx};
550 const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); 587 const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
@@ -577,12 +614,16 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
577} 614}
578 615
579void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) { 616void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) {
580 LOG_DEBUG(Service_Audio, "called"); 617 IPC::RequestParser rp{ctx};
618 const u64 aruid = rp.Pop<u64>();
581 619
582 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 620 LOG_DEBUG(Service_Audio, "called. aruid={:016X}", aruid);
583 621
622 // Revisionless variant of GetAudioDeviceServiceWithRevisionInfo that
623 // always assumes the initial release revision (REV1).
624 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
584 rb.Push(RESULT_SUCCESS); 625 rb.Push(RESULT_SUCCESS);
585 rb.PushIpcInterface<Audio::IAudioDevice>(); 626 rb.PushIpcInterface<IAudioDevice>(system, Common::MakeMagic('R', 'E', 'V', '1'));
586} 627}
587 628
588void AudRenU::OpenAudioRendererAuto(Kernel::HLERequestContext& ctx) { 629void AudRenU::OpenAudioRendererAuto(Kernel::HLERequestContext& ctx) {
@@ -592,13 +633,19 @@ void AudRenU::OpenAudioRendererAuto(Kernel::HLERequestContext& ctx) {
592} 633}
593 634
594void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) { 635void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) {
595 LOG_WARNING(Service_Audio, "(STUBBED) called"); 636 struct Parameters {
637 u32 revision;
638 u64 aruid;
639 };
596 640
597 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 641 IPC::RequestParser rp{ctx};
642 const auto [revision, aruid] = rp.PopRaw<Parameters>();
643
644 LOG_DEBUG(Service_Audio, "called. revision={:08X}, aruid={:016X}", revision, aruid);
598 645
646 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
599 rb.Push(RESULT_SUCCESS); 647 rb.Push(RESULT_SUCCESS);
600 rb.PushIpcInterface<Audio::IAudioDevice>(); // TODO(ogniK): Figure out what is different 648 rb.PushIpcInterface<IAudioDevice>(system, revision);
601 // based on the current revision
602} 649}
603 650
604void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) { 651void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
@@ -607,14 +654,16 @@ void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
607 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 654 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
608 655
609 rb.Push(RESULT_SUCCESS); 656 rb.Push(RESULT_SUCCESS);
610 rb.PushIpcInterface<IAudioRenderer>(params); 657 rb.PushIpcInterface<IAudioRenderer>(system, params, audren_instance_count++);
611} 658}
612 659
613bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const { 660bool IsFeatureSupported(AudioFeatures feature, u32_le revision) {
614 // Byte swap 661 // Byte swap
615 const u32_be version_num = revision - Common::MakeMagic('R', 'E', 'V', '0'); 662 const u32_be version_num = revision - Common::MakeMagic('R', 'E', 'V', '0');
616 663
617 switch (feature) { 664 switch (feature) {
665 case AudioFeatures::AudioUSBDeviceOutput:
666 return version_num >= 4U;
618 case AudioFeatures::Splitter: 667 case AudioFeatures::Splitter:
619 return version_num >= 2U; 668 return version_num >= 2U;
620 case AudioFeatures::PerformanceMetricsVersion2: 669 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 @@
6 6
7#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
8 8
9namespace Core {
10class System;
11}
12
9namespace Kernel { 13namespace Kernel {
10class HLERequestContext; 14class HLERequestContext;
11} 15}
@@ -14,7 +18,7 @@ namespace Service::Audio {
14 18
15class AudRenU final : public ServiceFramework<AudRenU> { 19class AudRenU final : public ServiceFramework<AudRenU> {
16public: 20public:
17 explicit AudRenU(); 21 explicit AudRenU(Core::System& system_);
18 ~AudRenU() override; 22 ~AudRenU() override;
19 23
20private: 24private:
@@ -26,13 +30,19 @@ private:
26 30
27 void OpenAudioRendererImpl(Kernel::HLERequestContext& ctx); 31 void OpenAudioRendererImpl(Kernel::HLERequestContext& ctx);
28 32
29 enum class AudioFeatures : u32 { 33 std::size_t audren_instance_count = 0;
30 Splitter, 34 Core::System& system;
31 PerformanceMetricsVersion2, 35};
32 VariadicCommandBuffer,
33 };
34 36
35 bool IsFeatureSupported(AudioFeatures feature, u32_le revision) const; 37// Describes a particular audio feature that may be supported in a particular revision.
38enum class AudioFeatures : u32 {
39 AudioUSBDeviceOutput,
40 Splitter,
41 PerformanceMetricsVersion2,
42 VariadicCommandBuffer,
36}; 43};
37 44
45// Tests if a particular audio feature is supported with a given audio revision.
46bool IsFeatureSupported(AudioFeatures feature, u32_le revision);
47
38} // namespace Service::Audio 48} // 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:
22 {0, nullptr, "GetCompletionEvent"}, 22 {0, nullptr, "GetCompletionEvent"},
23 {1, nullptr, "Cancel"}, 23 {1, nullptr, "Cancel"},
24 {10100, nullptr, "GetFriendListIds"}, 24 {10100, nullptr, "GetFriendListIds"},
25 {10101, nullptr, "GetFriendList"}, 25 {10101, &IFriendService::GetFriendList, "GetFriendList"},
26 {10102, nullptr, "UpdateFriendInfo"}, 26 {10102, nullptr, "UpdateFriendInfo"},
27 {10110, nullptr, "GetFriendProfileImage"}, 27 {10110, nullptr, "GetFriendProfileImage"},
28 {10200, nullptr, "SendFriendRequestForApplication"}, 28 {10200, nullptr, "SendFriendRequestForApplication"},
@@ -99,6 +99,23 @@ public:
99 } 99 }
100 100
101private: 101private:
102 enum class PresenceFilter : u32 {
103 None = 0,
104 Online = 1,
105 OnlinePlay = 2,
106 OnlineOrOnlinePlay = 3,
107 };
108
109 struct SizedFriendFilter {
110 PresenceFilter presence;
111 u8 is_favorite;
112 u8 same_app;
113 u8 same_app_played;
114 u8 arbitary_app_played;
115 u64 group_id;
116 };
117 static_assert(sizeof(SizedFriendFilter) == 0x10, "SizedFriendFilter is an invalid size");
118
102 void DeclareCloseOnlinePlaySession(Kernel::HLERequestContext& ctx) { 119 void DeclareCloseOnlinePlaySession(Kernel::HLERequestContext& ctx) {
103 // Stub used by Splatoon 2 120 // Stub used by Splatoon 2
104 LOG_WARNING(Service_ACC, "(STUBBED) called"); 121 LOG_WARNING(Service_ACC, "(STUBBED) called");
@@ -112,6 +129,22 @@ private:
112 IPC::ResponseBuilder rb{ctx, 2}; 129 IPC::ResponseBuilder rb{ctx, 2};
113 rb.Push(RESULT_SUCCESS); 130 rb.Push(RESULT_SUCCESS);
114 } 131 }
132
133 void GetFriendList(Kernel::HLERequestContext& ctx) {
134 IPC::RequestParser rp{ctx};
135 const auto friend_offset = rp.Pop<u32>();
136 const auto uuid = rp.PopRaw<Common::UUID>();
137 [[maybe_unused]] const auto filter = rp.PopRaw<SizedFriendFilter>();
138 const auto pid = rp.Pop<u64>();
139 LOG_WARNING(Service_ACC, "(STUBBED) called, offset={}, uuid={}, pid={}", friend_offset,
140 uuid.Format(), pid);
141
142 IPC::ResponseBuilder rb{ctx, 3};
143 rb.Push(RESULT_SUCCESS);
144
145 rb.Push<u32>(0); // Friend count
146 // TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId"
147 }
115}; 148};
116 149
117class INotificationService final : public ServiceFramework<INotificationService> { 150class 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:
345 vm_manager 345 vm_manager
346 .MirrorMemory(*map_address, nro_address, nro_size, Kernel::MemoryState::ModuleCode) 346 .MirrorMemory(*map_address, nro_address, nro_size, Kernel::MemoryState::ModuleCode)
347 .IsSuccess()); 347 .IsSuccess());
348 ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess()); 348 ASSERT(vm_manager.ReprotectRange(nro_address, nro_size, Kernel::VMAPermission::None)
349 .IsSuccess());
349 350
350 if (bss_size > 0) { 351 if (bss_size > 0) {
351 ASSERT(vm_manager 352 ASSERT(vm_manager
352 .MirrorMemory(*map_address + nro_size, bss_address, bss_size, 353 .MirrorMemory(*map_address + nro_size, bss_address, bss_size,
353 Kernel::MemoryState::ModuleCode) 354 Kernel::MemoryState::ModuleCode)
354 .IsSuccess()); 355 .IsSuccess());
355 ASSERT(vm_manager.UnmapRange(bss_address, bss_size).IsSuccess()); 356 ASSERT(vm_manager.ReprotectRange(bss_address, bss_size, Kernel::VMAPermission::None)
357 .IsSuccess());
356 } 358 }
357 359
358 vm_manager.ReprotectRange(*map_address, header.text_size, 360 vm_manager.ReprotectRange(*map_address, header.text_size,
@@ -364,7 +366,8 @@ public:
364 366
365 Core::System::GetInstance().InvalidateCpuInstructionCaches(); 367 Core::System::GetInstance().InvalidateCpuInstructionCaches();
366 368
367 nro.insert_or_assign(*map_address, NROInfo{hash, nro_size + bss_size}); 369 nro.insert_or_assign(*map_address,
370 NROInfo{hash, nro_address, nro_size, bss_address, bss_size});
368 371
369 IPC::ResponseBuilder rb{ctx, 4}; 372 IPC::ResponseBuilder rb{ctx, 4};
370 rb.Push(RESULT_SUCCESS); 373 rb.Push(RESULT_SUCCESS);
@@ -409,9 +412,23 @@ public:
409 } 412 }
410 413
411 auto& vm_manager = Core::CurrentProcess()->VMManager(); 414 auto& vm_manager = Core::CurrentProcess()->VMManager();
412 const auto& nro_size = iter->second.size; 415 const auto& nro_info = iter->second;
413 416
414 ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess()); 417 // Unmap the mirrored memory
418 ASSERT(
419 vm_manager.UnmapRange(nro_address, nro_info.nro_size + nro_info.bss_size).IsSuccess());
420
421 // Reprotect the source memory
422 ASSERT(vm_manager
423 .ReprotectRange(nro_info.nro_address, nro_info.nro_size,
424 Kernel::VMAPermission::ReadWrite)
425 .IsSuccess());
426 if (nro_info.bss_size > 0) {
427 ASSERT(vm_manager
428 .ReprotectRange(nro_info.bss_address, nro_info.bss_size,
429 Kernel::VMAPermission::ReadWrite)
430 .IsSuccess());
431 }
415 432
416 Core::System::GetInstance().InvalidateCpuInstructionCaches(); 433 Core::System::GetInstance().InvalidateCpuInstructionCaches();
417 434
@@ -473,7 +490,10 @@ private:
473 490
474 struct NROInfo { 491 struct NROInfo {
475 SHA256Hash hash; 492 SHA256Hash hash;
476 u64 size; 493 VAddr nro_address;
494 u64 nro_size;
495 VAddr bss_address;
496 u64 bss_size;
477 }; 497 };
478 498
479 bool initialized = false; 499 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:
48 {19, nullptr, "Export"}, 48 {19, nullptr, "Export"},
49 {20, nullptr, "IsBrokenDatabaseWithClearFlag"}, 49 {20, nullptr, "IsBrokenDatabaseWithClearFlag"},
50 {21, &IDatabaseService::GetIndex, "GetIndex"}, 50 {21, &IDatabaseService::GetIndex, "GetIndex"},
51 {22, nullptr, "SetInterfaceVersion"}, 51 {22, &IDatabaseService::SetInterfaceVersion, "SetInterfaceVersion"},
52 {23, nullptr, "Convert"}, 52 {23, nullptr, "Convert"},
53 }; 53 };
54 // clang-format on 54 // clang-format on
@@ -350,8 +350,22 @@ private:
350 rb.Push(index); 350 rb.Push(index);
351 } 351 }
352 352
353 void SetInterfaceVersion(Kernel::HLERequestContext& ctx) {
354 IPC::RequestParser rp{ctx};
355 current_interface_version = rp.PopRaw<u32>();
356
357 LOG_DEBUG(Service_Mii, "called, interface_version={:08X}", current_interface_version);
358
359 UNIMPLEMENTED_IF(current_interface_version != 1);
360
361 IPC::ResponseBuilder rb{ctx, 2};
362 rb.Push(RESULT_SUCCESS);
363 }
364
353 MiiManager db; 365 MiiManager db;
354 366
367 u32 current_interface_version = 0;
368
355 // Last read offsets of Get functions 369 // Last read offsets of Get functions
356 std::array<u32, 4> offsets{}; 370 std::array<u32, 4> offsets{};
357}; 371};
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 {
77 Done = 1, 77 Done = 1,
78}; 78};
79 79
80static void DecryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, 80static void DecryptSharedFont(const std::vector<u32>& input, Kernel::PhysicalMemory& output,
81 std::size_t& offset) { 81 std::size_t& offset) {
82 ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE, 82 ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE,
83 "Shared fonts exceeds 17mb!"); 83 "Shared fonts exceeds 17mb!");
@@ -94,7 +94,7 @@ static void DecryptSharedFont(const std::vector<u32>& input, std::vector<u8>& ou
94 offset += transformed_font.size() * sizeof(u32); 94 offset += transformed_font.size() * sizeof(u32);
95} 95}
96 96
97static void EncryptSharedFont(const std::vector<u8>& input, std::vector<u8>& output, 97static void EncryptSharedFont(const std::vector<u8>& input, Kernel::PhysicalMemory& output,
98 std::size_t& offset) { 98 std::size_t& offset) {
99 ASSERT_MSG(offset + input.size() + 8 < SHARED_FONT_MEM_SIZE, "Shared fonts exceeds 17mb!"); 99 ASSERT_MSG(offset + input.size() + 8 < SHARED_FONT_MEM_SIZE, "Shared fonts exceeds 17mb!");
100 const u32 KEY = EXPECTED_MAGIC ^ EXPECTED_RESULT; 100 const u32 KEY = EXPECTED_MAGIC ^ EXPECTED_RESULT;
@@ -121,7 +121,7 @@ struct PL_U::Impl {
121 return shared_font_regions.at(index); 121 return shared_font_regions.at(index);
122 } 122 }
123 123
124 void BuildSharedFontsRawRegions(const std::vector<u8>& input) { 124 void BuildSharedFontsRawRegions(const Kernel::PhysicalMemory& input) {
125 // As we can derive the xor key we can just populate the offsets 125 // As we can derive the xor key we can just populate the offsets
126 // based on the shared memory dump 126 // based on the shared memory dump
127 unsigned cur_offset = 0; 127 unsigned cur_offset = 0;
@@ -144,7 +144,7 @@ struct PL_U::Impl {
144 Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem; 144 Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem;
145 145
146 /// Backing memory for the shared font data 146 /// Backing memory for the shared font data
147 std::shared_ptr<std::vector<u8>> shared_font; 147 std::shared_ptr<Kernel::PhysicalMemory> shared_font;
148 148
149 // Automatically populated based on shared_fonts dump or system archives. 149 // Automatically populated based on shared_fonts dump or system archives.
150 std::vector<FontRegion> shared_font_regions; 150 std::vector<FontRegion> shared_font_regions;
@@ -166,7 +166,7 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
166 // Rebuild shared fonts from data ncas 166 // Rebuild shared fonts from data ncas
167 if (nand->HasEntry(static_cast<u64>(FontArchives::Standard), 167 if (nand->HasEntry(static_cast<u64>(FontArchives::Standard),
168 FileSys::ContentRecordType::Data)) { 168 FileSys::ContentRecordType::Data)) {
169 impl->shared_font = std::make_shared<std::vector<u8>>(SHARED_FONT_MEM_SIZE); 169 impl->shared_font = std::make_shared<Kernel::PhysicalMemory>(SHARED_FONT_MEM_SIZE);
170 for (auto font : SHARED_FONTS) { 170 for (auto font : SHARED_FONTS) {
171 const auto nca = 171 const auto nca =
172 nand->GetEntry(static_cast<u64>(font.first), FileSys::ContentRecordType::Data); 172 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>()} {
207 } 207 }
208 208
209 } else { 209 } else {
210 impl->shared_font = std::make_shared<std::vector<u8>>( 210 impl->shared_font = std::make_shared<Kernel::PhysicalMemory>(
211 SHARED_FONT_MEM_SIZE); // Shared memory needs to always be allocated and a fixed size 211 SHARED_FONT_MEM_SIZE); // Shared memory needs to always be allocated and a fixed size
212 212
213 const std::string user_path = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir); 213 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 @@
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/swap.h" 10#include "common/swap.h"
11#include "core/hle/service/nvdrv/nvdata.h"
12
13namespace Core {
14class System;
15}
11 16
12namespace Service::Nvidia::Devices { 17namespace Service::Nvidia::Devices {
13 18
@@ -15,7 +20,7 @@ namespace Service::Nvidia::Devices {
15/// implement the ioctl interface. 20/// implement the ioctl interface.
16class nvdevice { 21class nvdevice {
17public: 22public:
18 nvdevice() = default; 23 explicit nvdevice(Core::System& system) : system{system} {};
19 virtual ~nvdevice() = default; 24 virtual ~nvdevice() = default;
20 union Ioctl { 25 union Ioctl {
21 u32_le raw; 26 u32_le raw;
@@ -33,7 +38,11 @@ public:
33 * @param output A buffer where the output data will be written to. 38 * @param output A buffer where the output data will be written to.
34 * @returns The result code of the ioctl. 39 * @returns The result code of the ioctl.
35 */ 40 */
36 virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) = 0; 41 virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
42 IoctlCtrl& ctrl) = 0;
43
44protected:
45 Core::System& system;
37}; 46};
38 47
39} // namespace Service::Nvidia::Devices 48} // 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 @@
13 13
14namespace Service::Nvidia::Devices { 14namespace Service::Nvidia::Devices {
15 15
16nvdisp_disp0::nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} 16nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
17 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
17nvdisp_disp0 ::~nvdisp_disp0() = default; 18nvdisp_disp0 ::~nvdisp_disp0() = default;
18 19
19u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { 20u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
21 IoctlCtrl& ctrl) {
20 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 22 UNIMPLEMENTED_MSG("Unimplemented ioctl");
21 return 0; 23 return 0;
22} 24}
@@ -34,9 +36,8 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
34 addr, offset, width, height, stride, static_cast<PixelFormat>(format), 36 addr, offset, width, height, stride, static_cast<PixelFormat>(format),
35 transform, crop_rect}; 37 transform, crop_rect};
36 38
37 auto& instance = Core::System::GetInstance(); 39 system.GetPerfStats().EndGameFrame();
38 instance.GetPerfStats().EndGameFrame(); 40 system.GPU().SwapBuffers(&framebuffer);
39 instance.GPU().SwapBuffers(framebuffer);
40} 41}
41 42
42} // namespace Service::Nvidia::Devices 43} // 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;
17 17
18class nvdisp_disp0 final : public nvdevice { 18class nvdisp_disp0 final : public nvdevice {
19public: 19public:
20 explicit nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev); 20 explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
21 ~nvdisp_disp0() override; 21 ~nvdisp_disp0() override;
22 22
23 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; 23 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
24 IoctlCtrl& ctrl) override;
24 25
25 /// Performs a screen flip, drawing the buffer pointed to by the handle. 26 /// Performs a screen flip, drawing the buffer pointed to by the handle.
26 void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, 27 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 {
22}; 22};
23} 23}
24 24
25nvhost_as_gpu::nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} 25nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
26 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
26nvhost_as_gpu::~nvhost_as_gpu() = default; 27nvhost_as_gpu::~nvhost_as_gpu() = default;
27 28
28u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { 29u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
30 IoctlCtrl& ctrl) {
29 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 31 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
30 command.raw, input.size(), output.size()); 32 command.raw, input.size(), output.size());
31 33
@@ -65,7 +67,7 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>&
65 LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, 67 LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages,
66 params.page_size, params.flags); 68 params.page_size, params.flags);
67 69
68 auto& gpu = Core::System::GetInstance().GPU(); 70 auto& gpu = system.GPU();
69 const u64 size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)}; 71 const u64 size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)};
70 if (params.flags & 1) { 72 if (params.flags & 1) {
71 params.offset = gpu.MemoryManager().AllocateSpace(params.offset, size, 1); 73 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)
85 std::vector<IoctlRemapEntry> entries(num_entries); 87 std::vector<IoctlRemapEntry> entries(num_entries);
86 std::memcpy(entries.data(), input.data(), input.size()); 88 std::memcpy(entries.data(), input.data(), input.size());
87 89
88 auto& gpu = Core::System::GetInstance().GPU(); 90 auto& gpu = system.GPU();
89 for (const auto& entry : entries) { 91 for (const auto& entry : entries) {
90 LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", 92 LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
91 entry.offset, entry.nvmap_handle, entry.pages); 93 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
136 // case to prevent unexpected behavior. 138 // case to prevent unexpected behavior.
137 ASSERT(object->id == params.nvmap_handle); 139 ASSERT(object->id == params.nvmap_handle);
138 140
139 auto& gpu = Core::System::GetInstance().GPU(); 141 auto& gpu = system.GPU();
140 142
141 if (params.flags & 1) { 143 if (params.flags & 1) {
142 params.offset = gpu.MemoryManager().MapBufferEx(object->addr, params.offset, object->size); 144 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
173 return 0; 175 return 0;
174 } 176 }
175 177
176 params.offset = Core::System::GetInstance().GPU().MemoryManager().UnmapBuffer(params.offset, 178 params.offset = system.GPU().MemoryManager().UnmapBuffer(params.offset, itr->second.size);
177 itr->second.size);
178 buffer_mappings.erase(itr->second.offset); 179 buffer_mappings.erase(itr->second.offset);
179 180
180 std::memcpy(output.data(), &params, output.size()); 181 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;
17 17
18class nvhost_as_gpu final : public nvdevice { 18class nvhost_as_gpu final : public nvdevice {
19public: 19public:
20 explicit nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev); 20 explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
21 ~nvhost_as_gpu() override; 21 ~nvhost_as_gpu() override;
22 22
23 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; 23 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
24 IoctlCtrl& ctrl) override;
24 25
25private: 26private:
26 enum class IoctlCommand : u32_le { 27 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 @@
7 7
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/core.h"
11#include "core/hle/kernel/readable_event.h"
12#include "core/hle/kernel/writable_event.h"
10#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" 13#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h"
14#include "video_core/gpu.h"
11 15
12namespace Service::Nvidia::Devices { 16namespace Service::Nvidia::Devices {
13 17
14nvhost_ctrl::nvhost_ctrl() = default; 18nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface)
19 : nvdevice(system), events_interface{events_interface} {}
15nvhost_ctrl::~nvhost_ctrl() = default; 20nvhost_ctrl::~nvhost_ctrl() = default;
16 21
17u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { 22u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
23 IoctlCtrl& ctrl) {
18 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 24 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
19 command.raw, input.size(), output.size()); 25 command.raw, input.size(), output.size());
20 26
@@ -22,11 +28,15 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<
22 case IoctlCommand::IocGetConfigCommand: 28 case IoctlCommand::IocGetConfigCommand:
23 return NvOsGetConfigU32(input, output); 29 return NvOsGetConfigU32(input, output);
24 case IoctlCommand::IocCtrlEventWaitCommand: 30 case IoctlCommand::IocCtrlEventWaitCommand:
25 return IocCtrlEventWait(input, output, false); 31 return IocCtrlEventWait(input, output, false, ctrl);
26 case IoctlCommand::IocCtrlEventWaitAsyncCommand: 32 case IoctlCommand::IocCtrlEventWaitAsyncCommand:
27 return IocCtrlEventWait(input, output, true); 33 return IocCtrlEventWait(input, output, true, ctrl);
28 case IoctlCommand::IocCtrlEventRegisterCommand: 34 case IoctlCommand::IocCtrlEventRegisterCommand:
29 return IocCtrlEventRegister(input, output); 35 return IocCtrlEventRegister(input, output);
36 case IoctlCommand::IocCtrlEventUnregisterCommand:
37 return IocCtrlEventUnregister(input, output);
38 case IoctlCommand::IocCtrlEventSignalCommand:
39 return IocCtrlEventSignal(input, output);
30 } 40 }
31 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 41 UNIMPLEMENTED_MSG("Unimplemented ioctl");
32 return 0; 42 return 0;
@@ -41,23 +51,137 @@ u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>&
41} 51}
42 52
43u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, 53u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
44 bool is_async) { 54 bool is_async, IoctlCtrl& ctrl) {
45 IocCtrlEventWaitParams params{}; 55 IocCtrlEventWaitParams params{};
46 std::memcpy(&params, input.data(), sizeof(params)); 56 std::memcpy(&params, input.data(), sizeof(params));
47 LOG_WARNING(Service_NVDRV, 57 LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}",
48 "(STUBBED) called, syncpt_id={}, threshold={}, timeout={}, is_async={}", 58 params.syncpt_id, params.threshold, params.timeout, is_async);
49 params.syncpt_id, params.threshold, params.timeout, is_async);
50 59
51 // TODO(Subv): Implement actual syncpt waiting. 60 if (params.syncpt_id >= MaxSyncPoints) {
52 params.value = 0; 61 return NvResult::BadParameter;
62 }
63
64 auto& gpu = system.GPU();
65 // This is mostly to take into account unimplemented features. As synced
66 // gpu is always synced.
67 if (!gpu.IsAsync()) {
68 return NvResult::Success;
69 }
70 auto lock = gpu.LockSync();
71 const u32 current_syncpoint_value = gpu.GetSyncpointValue(params.syncpt_id);
72 const s32 diff = current_syncpoint_value - params.threshold;
73 if (diff >= 0) {
74 params.value = current_syncpoint_value;
75 std::memcpy(output.data(), &params, sizeof(params));
76 return NvResult::Success;
77 }
78 const u32 target_value = current_syncpoint_value - diff;
79
80 if (!is_async) {
81 params.value = 0;
82 }
83
84 if (params.timeout == 0) {
85 std::memcpy(output.data(), &params, sizeof(params));
86 return NvResult::Timeout;
87 }
88
89 u32 event_id;
90 if (is_async) {
91 event_id = params.value & 0x00FF;
92 if (event_id >= MaxNvEvents) {
93 std::memcpy(output.data(), &params, sizeof(params));
94 return NvResult::BadParameter;
95 }
96 } else {
97 if (ctrl.fresh_call) {
98 const auto result = events_interface.GetFreeEvent();
99 if (result) {
100 event_id = *result;
101 } else {
102 LOG_CRITICAL(Service_NVDRV, "No Free Events available!");
103 event_id = params.value & 0x00FF;
104 }
105 } else {
106 event_id = ctrl.event_id;
107 }
108 }
109
110 EventState status = events_interface.status[event_id];
111 if (event_id < MaxNvEvents || status == EventState::Free || status == EventState::Registered) {
112 events_interface.SetEventStatus(event_id, EventState::Waiting);
113 events_interface.assigned_syncpt[event_id] = params.syncpt_id;
114 events_interface.assigned_value[event_id] = target_value;
115 if (is_async) {
116 params.value = params.syncpt_id << 4;
117 } else {
118 params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000;
119 }
120 params.value |= event_id;
121 events_interface.events[event_id].writable->Clear();
122 gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
123 if (!is_async && ctrl.fresh_call) {
124 ctrl.must_delay = true;
125 ctrl.timeout = params.timeout;
126 ctrl.event_id = event_id;
127 return NvResult::Timeout;
128 }
129 std::memcpy(output.data(), &params, sizeof(params));
130 return NvResult::Timeout;
131 }
53 std::memcpy(output.data(), &params, sizeof(params)); 132 std::memcpy(output.data(), &params, sizeof(params));
54 return 0; 133 return NvResult::BadParameter;
55} 134}
56 135
57u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) { 136u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
58 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 137 IocCtrlEventRegisterParams params{};
59 // TODO(bunnei): Implement this. 138 std::memcpy(&params, input.data(), sizeof(params));
60 return 0; 139 const u32 event_id = params.user_event_id & 0x00FF;
140 LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
141 if (event_id >= MaxNvEvents) {
142 return NvResult::BadParameter;
143 }
144 if (events_interface.registered[event_id]) {
145 return NvResult::BadParameter;
146 }
147 events_interface.RegisterEvent(event_id);
148 return NvResult::Success;
149}
150
151u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output) {
152 IocCtrlEventUnregisterParams params{};
153 std::memcpy(&params, input.data(), sizeof(params));
154 const u32 event_id = params.user_event_id & 0x00FF;
155 LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
156 if (event_id >= MaxNvEvents) {
157 return NvResult::BadParameter;
158 }
159 if (!events_interface.registered[event_id]) {
160 return NvResult::BadParameter;
161 }
162 events_interface.UnregisterEvent(event_id);
163 return NvResult::Success;
164}
165
166u32 nvhost_ctrl::IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output) {
167 IocCtrlEventSignalParams params{};
168 std::memcpy(&params, input.data(), sizeof(params));
169 // TODO(Blinkhawk): This is normally called when an NvEvents timeout on WaitSynchronization
170 // It is believed from RE to cancel the GPU Event. However, better research is required
171 u32 event_id = params.user_event_id & 0x00FF;
172 LOG_WARNING(Service_NVDRV, "(STUBBED) called, user_event_id: {:X}", event_id);
173 if (event_id >= MaxNvEvents) {
174 return NvResult::BadParameter;
175 }
176 if (events_interface.status[event_id] == EventState::Waiting) {
177 auto& gpu = system.GPU();
178 if (gpu.CancelSyncptInterrupt(events_interface.assigned_syncpt[event_id],
179 events_interface.assigned_value[event_id])) {
180 events_interface.LiberateEvent(event_id);
181 events_interface.events[event_id].writable->Signal();
182 }
183 }
184 return NvResult::Success;
61} 185}
62 186
63} // namespace Service::Nvidia::Devices 187} // 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 @@
8#include <vector> 8#include <vector>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/hle/service/nvdrv/devices/nvdevice.h" 10#include "core/hle/service/nvdrv/devices/nvdevice.h"
11#include "core/hle/service/nvdrv/nvdrv.h"
11 12
12namespace Service::Nvidia::Devices { 13namespace Service::Nvidia::Devices {
13 14
14class nvhost_ctrl final : public nvdevice { 15class nvhost_ctrl final : public nvdevice {
15public: 16public:
16 nvhost_ctrl(); 17 explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface);
17 ~nvhost_ctrl() override; 18 ~nvhost_ctrl() override;
18 19
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; 20 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
21 IoctlCtrl& ctrl) override;
20 22
21private: 23private:
22 enum class IoctlCommand : u32_le { 24 enum class IoctlCommand : u32_le {
@@ -132,9 +134,16 @@ private:
132 134
133 u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); 135 u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
134 136
135 u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async); 137 u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async,
138 IoctlCtrl& ctrl);
136 139
137 u32 IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output); 140 u32 IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
141
142 u32 IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
143
144 u32 IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output);
145
146 EventInterface& events_interface;
138}; 147};
139 148
140} // namespace Service::Nvidia::Devices 149} // 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 @@
12 12
13namespace Service::Nvidia::Devices { 13namespace Service::Nvidia::Devices {
14 14
15nvhost_ctrl_gpu::nvhost_ctrl_gpu() = default; 15nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {}
16nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; 16nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;
17 17
18u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { 18u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
19 IoctlCtrl& ctrl) {
19 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 20 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
20 command.raw, input.size(), output.size()); 21 command.raw, input.size(), output.size());
21 22
@@ -185,7 +186,7 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o
185 186
186 IoctlGetGpuTime params{}; 187 IoctlGetGpuTime params{};
187 std::memcpy(&params, input.data(), input.size()); 188 std::memcpy(&params, input.data(), input.size());
188 const auto ns = Core::Timing::CyclesToNs(Core::System::GetInstance().CoreTiming().GetTicks()); 189 const auto ns = Core::Timing::CyclesToNs(system.CoreTiming().GetTicks());
189 params.gpu_time = static_cast<u64_le>(ns.count()); 190 params.gpu_time = static_cast<u64_le>(ns.count());
190 std::memcpy(output.data(), &params, output.size()); 191 std::memcpy(output.data(), &params, output.size());
191 return 0; 192 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 {
13 13
14class nvhost_ctrl_gpu final : public nvdevice { 14class nvhost_ctrl_gpu final : public nvdevice {
15public: 15public:
16 nvhost_ctrl_gpu(); 16 explicit nvhost_ctrl_gpu(Core::System& system);
17 ~nvhost_ctrl_gpu() override; 17 ~nvhost_ctrl_gpu() override;
18 18
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; 19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
20 IoctlCtrl& ctrl) override;
20 21
21private: 22private:
22 enum class IoctlCommand : u32_le { 23 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 @@
13 13
14namespace Service::Nvidia::Devices { 14namespace Service::Nvidia::Devices {
15 15
16nvhost_gpu::nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} 16nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
17 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
17nvhost_gpu::~nvhost_gpu() = default; 18nvhost_gpu::~nvhost_gpu() = default;
18 19
19u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { 20u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
21 IoctlCtrl& ctrl) {
20 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 22 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
21 command.raw, input.size(), output.size()); 23 command.raw, input.size(), output.size());
22 24
@@ -119,8 +121,10 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou
119 params.num_entries, params.flags, params.unk0, params.unk1, params.unk2, 121 params.num_entries, params.flags, params.unk0, params.unk1, params.unk2,
120 params.unk3); 122 params.unk3);
121 123
122 params.fence_out.id = 0; 124 auto& gpu = system.GPU();
123 params.fence_out.value = 0; 125 params.fence_out.id = assigned_syncpoints;
126 params.fence_out.value = gpu.GetSyncpointValue(assigned_syncpoints);
127 assigned_syncpoints++;
124 std::memcpy(output.data(), &params, output.size()); 128 std::memcpy(output.data(), &params, output.size());
125 return 0; 129 return 0;
126} 130}
@@ -143,7 +147,7 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
143 IoctlSubmitGpfifo params{}; 147 IoctlSubmitGpfifo params{};
144 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo)); 148 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
145 LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", 149 LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
146 params.address, params.num_entries, params.flags); 150 params.address, params.num_entries, params.flags.raw);
147 151
148 ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) + 152 ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) +
149 params.num_entries * sizeof(Tegra::CommandListHeader), 153 params.num_entries * sizeof(Tegra::CommandListHeader),
@@ -153,10 +157,18 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
153 std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)], 157 std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)],
154 params.num_entries * sizeof(Tegra::CommandListHeader)); 158 params.num_entries * sizeof(Tegra::CommandListHeader));
155 159
156 Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries)); 160 UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
161 UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
162
163 auto& gpu = system.GPU();
164 u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id);
165 if (params.flags.increment.Value()) {
166 params.fence_out.value += current_syncpoint_value;
167 } else {
168 params.fence_out.value = current_syncpoint_value;
169 }
170 gpu.PushGPUEntries(std::move(entries));
157 171
158 params.fence_out.id = 0;
159 params.fence_out.value = 0;
160 std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo)); 172 std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
161 return 0; 173 return 0;
162} 174}
@@ -168,16 +180,24 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output)
168 IoctlSubmitGpfifo params{}; 180 IoctlSubmitGpfifo params{};
169 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo)); 181 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
170 LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", 182 LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
171 params.address, params.num_entries, params.flags); 183 params.address, params.num_entries, params.flags.raw);
172 184
173 Tegra::CommandList entries(params.num_entries); 185 Tegra::CommandList entries(params.num_entries);
174 Memory::ReadBlock(params.address, entries.data(), 186 Memory::ReadBlock(params.address, entries.data(),
175 params.num_entries * sizeof(Tegra::CommandListHeader)); 187 params.num_entries * sizeof(Tegra::CommandListHeader));
176 188
177 Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries)); 189 UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
190 UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
191
192 auto& gpu = system.GPU();
193 u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id);
194 if (params.flags.increment.Value()) {
195 params.fence_out.value += current_syncpoint_value;
196 } else {
197 params.fence_out.value = current_syncpoint_value;
198 }
199 gpu.PushGPUEntries(std::move(entries));
178 200
179 params.fence_out.id = 0;
180 params.fence_out.value = 0;
181 std::memcpy(output.data(), &params, output.size()); 201 std::memcpy(output.data(), &params, output.size());
182 return 0; 202 return 0;
183} 203}
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 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/swap.h" 11#include "common/swap.h"
12#include "core/hle/service/nvdrv/devices/nvdevice.h" 12#include "core/hle/service/nvdrv/devices/nvdevice.h"
13#include "core/hle/service/nvdrv/nvdata.h"
13 14
14namespace Service::Nvidia::Devices { 15namespace Service::Nvidia::Devices {
15 16
@@ -20,10 +21,11 @@ constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b);
20 21
21class nvhost_gpu final : public nvdevice { 22class nvhost_gpu final : public nvdevice {
22public: 23public:
23 explicit nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev); 24 explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
24 ~nvhost_gpu() override; 25 ~nvhost_gpu() override;
25 26
26 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; 27 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
28 IoctlCtrl& ctrl) override;
27 29
28private: 30private:
29 enum class IoctlCommand : u32_le { 31 enum class IoctlCommand : u32_le {
@@ -113,11 +115,7 @@ private:
113 static_assert(sizeof(IoctlGetErrorNotification) == 16, 115 static_assert(sizeof(IoctlGetErrorNotification) == 16,
114 "IoctlGetErrorNotification is incorrect size"); 116 "IoctlGetErrorNotification is incorrect size");
115 117
116 struct IoctlFence { 118 static_assert(sizeof(Fence) == 8, "Fence is incorrect size");
117 u32_le id;
118 u32_le value;
119 };
120 static_assert(sizeof(IoctlFence) == 8, "IoctlFence is incorrect size");
121 119
122 struct IoctlAllocGpfifoEx { 120 struct IoctlAllocGpfifoEx {
123 u32_le num_entries; 121 u32_le num_entries;
@@ -132,13 +130,13 @@ private:
132 static_assert(sizeof(IoctlAllocGpfifoEx) == 32, "IoctlAllocGpfifoEx is incorrect size"); 130 static_assert(sizeof(IoctlAllocGpfifoEx) == 32, "IoctlAllocGpfifoEx is incorrect size");
133 131
134 struct IoctlAllocGpfifoEx2 { 132 struct IoctlAllocGpfifoEx2 {
135 u32_le num_entries; // in 133 u32_le num_entries; // in
136 u32_le flags; // in 134 u32_le flags; // in
137 u32_le unk0; // in (1 works) 135 u32_le unk0; // in (1 works)
138 IoctlFence fence_out; // out 136 Fence fence_out; // out
139 u32_le unk1; // in 137 u32_le unk1; // in
140 u32_le unk2; // in 138 u32_le unk2; // in
141 u32_le unk3; // in 139 u32_le unk3; // in
142 }; 140 };
143 static_assert(sizeof(IoctlAllocGpfifoEx2) == 32, "IoctlAllocGpfifoEx2 is incorrect size"); 141 static_assert(sizeof(IoctlAllocGpfifoEx2) == 32, "IoctlAllocGpfifoEx2 is incorrect size");
144 142
@@ -153,10 +151,16 @@ private:
153 struct IoctlSubmitGpfifo { 151 struct IoctlSubmitGpfifo {
154 u64_le address; // pointer to gpfifo entry structs 152 u64_le address; // pointer to gpfifo entry structs
155 u32_le num_entries; // number of fence objects being submitted 153 u32_le num_entries; // number of fence objects being submitted
156 u32_le flags; 154 union {
157 IoctlFence fence_out; // returned new fence object for others to wait on 155 u32_le raw;
158 }; 156 BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list
159 static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(IoctlFence), 157 BitField<1, 1, u32_le> add_increment; // append an increment to the list
158 BitField<2, 1, u32_le> new_hw_format; // Mostly ignored
159 BitField<8, 1, u32_le> increment; // increment the returned fence
160 } flags;
161 Fence fence_out; // returned new fence object for others to wait on
162 };
163 static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence),
160 "IoctlSubmitGpfifo is incorrect size"); 164 "IoctlSubmitGpfifo is incorrect size");
161 165
162 struct IoctlGetWaitbase { 166 struct IoctlGetWaitbase {
@@ -184,6 +188,7 @@ private:
184 u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); 188 u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
185 189
186 std::shared_ptr<nvmap> nvmap_dev; 190 std::shared_ptr<nvmap> nvmap_dev;
191 u32 assigned_syncpoints{};
187}; 192};
188 193
189} // namespace Service::Nvidia::Devices 194} // 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 @@
10 10
11namespace Service::Nvidia::Devices { 11namespace Service::Nvidia::Devices {
12 12
13nvhost_nvdec::nvhost_nvdec() = default; 13nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system) {}
14nvhost_nvdec::~nvhost_nvdec() = default; 14nvhost_nvdec::~nvhost_nvdec() = default;
15 15
16u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { 16u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
17 IoctlCtrl& ctrl) {
17 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 18 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
18 command.raw, input.size(), output.size()); 19 command.raw, input.size(), output.size());
19 20
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 {
13 13
14class nvhost_nvdec final : public nvdevice { 14class nvhost_nvdec final : public nvdevice {
15public: 15public:
16 nvhost_nvdec(); 16 explicit nvhost_nvdec(Core::System& system);
17 ~nvhost_nvdec() override; 17 ~nvhost_nvdec() override;
18 18
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; 19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
20 IoctlCtrl& ctrl) override;
20 21
21private: 22private:
22 enum class IoctlCommand : u32_le { 23 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 @@
10 10
11namespace Service::Nvidia::Devices { 11namespace Service::Nvidia::Devices {
12 12
13nvhost_nvjpg::nvhost_nvjpg() = default; 13nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {}
14nvhost_nvjpg::~nvhost_nvjpg() = default; 14nvhost_nvjpg::~nvhost_nvjpg() = default;
15 15
16u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { 16u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
17 IoctlCtrl& ctrl) {
17 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 18 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
18 command.raw, input.size(), output.size()); 19 command.raw, input.size(), output.size());
19 20
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 {
13 13
14class nvhost_nvjpg final : public nvdevice { 14class nvhost_nvjpg final : public nvdevice {
15public: 15public:
16 nvhost_nvjpg(); 16 explicit nvhost_nvjpg(Core::System& system);
17 ~nvhost_nvjpg() override; 17 ~nvhost_nvjpg() override;
18 18
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; 19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
20 IoctlCtrl& ctrl) override;
20 21
21private: 22private:
22 enum class IoctlCommand : u32_le { 23 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 @@
10 10
11namespace Service::Nvidia::Devices { 11namespace Service::Nvidia::Devices {
12 12
13nvhost_vic::nvhost_vic() = default; 13nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system) {}
14nvhost_vic::~nvhost_vic() = default; 14nvhost_vic::~nvhost_vic() = default;
15 15
16u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { 16u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
17 IoctlCtrl& ctrl) {
17 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 18 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
18 command.raw, input.size(), output.size()); 19 command.raw, input.size(), output.size());
19 20
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 {
13 13
14class nvhost_vic final : public nvdevice { 14class nvhost_vic final : public nvdevice {
15public: 15public:
16 nvhost_vic(); 16 explicit nvhost_vic(Core::System& system);
17 ~nvhost_vic() override; 17 ~nvhost_vic() override;
18 18
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; 19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
20 IoctlCtrl& ctrl) override;
20 21
21private: 22private:
22 enum class IoctlCommand : u32_le { 23 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 {
18}; 18};
19} 19}
20 20
21nvmap::nvmap() = default; 21nvmap::nvmap(Core::System& system) : nvdevice(system) {}
22nvmap::~nvmap() = default; 22nvmap::~nvmap() = default;
23 23
24VAddr nvmap::GetObjectAddress(u32 handle) const { 24VAddr nvmap::GetObjectAddress(u32 handle) const {
@@ -28,7 +28,8 @@ VAddr nvmap::GetObjectAddress(u32 handle) const {
28 return object->addr; 28 return object->addr;
29} 29}
30 30
31u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { 31u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
32 IoctlCtrl& ctrl) {
32 switch (static_cast<IoctlCommand>(command.raw)) { 33 switch (static_cast<IoctlCommand>(command.raw)) {
33 case IoctlCommand::Create: 34 case IoctlCommand::Create:
34 return IocCreate(input, output); 35 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 {
16 16
17class nvmap final : public nvdevice { 17class nvmap final : public nvdevice {
18public: 18public:
19 nvmap(); 19 explicit nvmap(Core::System& system);
20 ~nvmap() override; 20 ~nvmap() override;
21 21
22 /// Returns the allocated address of an nvmap object given its handle. 22 /// Returns the allocated address of an nvmap object given its handle.
23 VAddr GetObjectAddress(u32 handle) const; 23 VAddr GetObjectAddress(u32 handle) const;
24 24
25 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; 25 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
26 IoctlCtrl& ctrl) override;
26 27
27 /// Represents an nvmap object. 28 /// Represents an nvmap object.
28 struct Object { 29 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 @@
8#include "core/hle/ipc_helpers.h" 8#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/kernel.h" 9#include "core/hle/kernel/kernel.h"
10#include "core/hle/kernel/readable_event.h" 10#include "core/hle/kernel/readable_event.h"
11#include "core/hle/kernel/thread.h"
11#include "core/hle/kernel/writable_event.h" 12#include "core/hle/kernel/writable_event.h"
12#include "core/hle/service/nvdrv/interface.h" 13#include "core/hle/service/nvdrv/interface.h"
14#include "core/hle/service/nvdrv/nvdata.h"
13#include "core/hle/service/nvdrv/nvdrv.h" 15#include "core/hle/service/nvdrv/nvdrv.h"
14 16
15namespace Service::Nvidia { 17namespace Service::Nvidia {
16 18
19void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
20 nvdrv->SignalSyncpt(syncpoint_id, value);
21}
22
17void NVDRV::Open(Kernel::HLERequestContext& ctx) { 23void NVDRV::Open(Kernel::HLERequestContext& ctx) {
18 LOG_DEBUG(Service_NVDRV, "called"); 24 LOG_DEBUG(Service_NVDRV, "called");
19 25
@@ -36,11 +42,31 @@ void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
36 42
37 std::vector<u8> output(ctx.GetWriteBufferSize()); 43 std::vector<u8> output(ctx.GetWriteBufferSize());
38 44
45 IoctlCtrl ctrl{};
46
47 u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output, ctrl);
48
49 if (ctrl.must_delay) {
50 ctrl.fresh_call = false;
51 ctx.SleepClientThread(
52 "NVServices::DelayedResponse", ctrl.timeout,
53 [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
54 Kernel::ThreadWakeupReason reason) {
55 IoctlCtrl ctrl2{ctrl};
56 std::vector<u8> output2 = output;
57 u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output2, ctrl2);
58 ctx.WriteBuffer(output2);
59 IPC::ResponseBuilder rb{ctx, 3};
60 rb.Push(RESULT_SUCCESS);
61 rb.Push(result);
62 },
63 nvdrv->GetEventWriteable(ctrl.event_id));
64 } else {
65 ctx.WriteBuffer(output);
66 }
39 IPC::ResponseBuilder rb{ctx, 3}; 67 IPC::ResponseBuilder rb{ctx, 3};
40 rb.Push(RESULT_SUCCESS); 68 rb.Push(RESULT_SUCCESS);
41 rb.Push(nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output)); 69 rb.Push(result);
42
43 ctx.WriteBuffer(output);
44} 70}
45 71
46void NVDRV::Close(Kernel::HLERequestContext& ctx) { 72void NVDRV::Close(Kernel::HLERequestContext& ctx) {
@@ -66,13 +92,19 @@ void NVDRV::Initialize(Kernel::HLERequestContext& ctx) {
66void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { 92void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
67 IPC::RequestParser rp{ctx}; 93 IPC::RequestParser rp{ctx};
68 u32 fd = rp.Pop<u32>(); 94 u32 fd = rp.Pop<u32>();
69 u32 event_id = rp.Pop<u32>(); 95 // TODO(Blinkhawk): Figure the meaning of the flag at bit 16
96 u32 event_id = rp.Pop<u32>() & 0x000000FF;
70 LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id); 97 LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id);
71 98
72 IPC::ResponseBuilder rb{ctx, 3, 1}; 99 IPC::ResponseBuilder rb{ctx, 3, 1};
73 rb.Push(RESULT_SUCCESS); 100 rb.Push(RESULT_SUCCESS);
74 rb.PushCopyObjects(query_event.readable); 101 if (event_id < MaxNvEvents) {
75 rb.Push<u32>(0); 102 rb.PushCopyObjects(nvdrv->GetEvent(event_id));
103 rb.Push<u32>(NvResult::Success);
104 } else {
105 rb.Push<u32>(0);
106 rb.Push<u32>(NvResult::BadParameter);
107 }
76} 108}
77 109
78void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) { 110void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) {
@@ -127,10 +159,6 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
127 {13, &NVDRV::FinishInitialize, "FinishInitialize"}, 159 {13, &NVDRV::FinishInitialize, "FinishInitialize"},
128 }; 160 };
129 RegisterHandlers(functions); 161 RegisterHandlers(functions);
130
131 auto& kernel = Core::System::GetInstance().Kernel();
132 query_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
133 "NVDRV::query_event");
134} 162}
135 163
136NVDRV::~NVDRV() = default; 164NVDRV::~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:
19 NVDRV(std::shared_ptr<Module> nvdrv, const char* name); 19 NVDRV(std::shared_ptr<Module> nvdrv, const char* name);
20 ~NVDRV() override; 20 ~NVDRV() override;
21 21
22 void SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value);
23
22private: 24private:
23 void Open(Kernel::HLERequestContext& ctx); 25 void Open(Kernel::HLERequestContext& ctx);
24 void Ioctl(Kernel::HLERequestContext& ctx); 26 void Ioctl(Kernel::HLERequestContext& ctx);
@@ -33,8 +35,6 @@ private:
33 std::shared_ptr<Module> nvdrv; 35 std::shared_ptr<Module> nvdrv;
34 36
35 u64 pid{}; 37 u64 pid{};
36
37 Kernel::EventPair query_event;
38}; 38};
39 39
40} // namespace Service::Nvidia 40} // 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 @@
1#pragma once
2
3#include <array>
4#include "common/common_types.h"
5
6namespace Service::Nvidia {
7
8constexpr u32 MaxSyncPoints = 192;
9constexpr u32 MaxNvEvents = 64;
10
11struct Fence {
12 s32 id;
13 u32 value;
14};
15
16static_assert(sizeof(Fence) == 8, "Fence has wrong size");
17
18struct MultiFence {
19 u32 num_fences;
20 std::array<Fence, 4> fences;
21};
22
23enum NvResult : u32 {
24 Success = 0,
25 BadParameter = 4,
26 Timeout = 5,
27 ResourceError = 15,
28};
29
30enum class EventState {
31 Free = 0,
32 Registered = 1,
33 Waiting = 2,
34 Busy = 3,
35};
36
37struct IoctlCtrl {
38 // First call done to the servioce for services that call itself again after a call.
39 bool fresh_call{true};
40 // Tells the Ioctl Wrapper that it must delay the IPC response and send the thread to sleep
41 bool must_delay{};
42 // Timeout for the delay
43 s64 timeout{};
44 // NV Event Id
45 s32 event_id{-1};
46};
47
48} // 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 @@
4 4
5#include <utility> 5#include <utility>
6 6
7#include <fmt/format.h>
7#include "core/hle/ipc_helpers.h" 8#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/readable_event.h"
10#include "core/hle/kernel/writable_event.h"
8#include "core/hle/service/nvdrv/devices/nvdevice.h" 11#include "core/hle/service/nvdrv/devices/nvdevice.h"
9#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" 12#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
10#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" 13#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
@@ -22,8 +25,9 @@
22 25
23namespace Service::Nvidia { 26namespace Service::Nvidia {
24 27
25void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger) { 28void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
26 auto module_ = std::make_shared<Module>(); 29 Core::System& system) {
30 auto module_ = std::make_shared<Module>(system);
27 std::make_shared<NVDRV>(module_, "nvdrv")->InstallAsService(service_manager); 31 std::make_shared<NVDRV>(module_, "nvdrv")->InstallAsService(service_manager);
28 std::make_shared<NVDRV>(module_, "nvdrv:a")->InstallAsService(service_manager); 32 std::make_shared<NVDRV>(module_, "nvdrv:a")->InstallAsService(service_manager);
29 std::make_shared<NVDRV>(module_, "nvdrv:s")->InstallAsService(service_manager); 33 std::make_shared<NVDRV>(module_, "nvdrv:s")->InstallAsService(service_manager);
@@ -32,17 +36,25 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
32 nvflinger.SetNVDrvInstance(module_); 36 nvflinger.SetNVDrvInstance(module_);
33} 37}
34 38
35Module::Module() { 39Module::Module(Core::System& system) {
36 auto nvmap_dev = std::make_shared<Devices::nvmap>(); 40 auto& kernel = system.Kernel();
37 devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(nvmap_dev); 41 for (u32 i = 0; i < MaxNvEvents; i++) {
38 devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(nvmap_dev); 42 std::string event_label = fmt::format("NVDRV::NvEvent_{}", i);
39 devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(); 43 events_interface.events[i] = Kernel::WritableEvent::CreateEventPair(
44 kernel, Kernel::ResetType::Automatic, event_label);
45 events_interface.status[i] = EventState::Free;
46 events_interface.registered[i] = false;
47 }
48 auto nvmap_dev = std::make_shared<Devices::nvmap>(system);
49 devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev);
50 devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev);
51 devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(system);
40 devices["/dev/nvmap"] = nvmap_dev; 52 devices["/dev/nvmap"] = nvmap_dev;
41 devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(nvmap_dev); 53 devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev);
42 devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(); 54 devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(system, events_interface);
43 devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(); 55 devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system);
44 devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(); 56 devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system);
45 devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(); 57 devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system);
46} 58}
47 59
48Module::~Module() = default; 60Module::~Module() = default;
@@ -59,12 +71,13 @@ u32 Module::Open(const std::string& device_name) {
59 return fd; 71 return fd;
60} 72}
61 73
62u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output) { 74u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output,
75 IoctlCtrl& ctrl) {
63 auto itr = open_files.find(fd); 76 auto itr = open_files.find(fd);
64 ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); 77 ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device");
65 78
66 auto& device = itr->second; 79 auto& device = itr->second;
67 return device->ioctl({command}, input, output); 80 return device->ioctl({command}, input, output, ctrl);
68} 81}
69 82
70ResultCode Module::Close(u32 fd) { 83ResultCode Module::Close(u32 fd) {
@@ -77,4 +90,22 @@ ResultCode Module::Close(u32 fd) {
77 return RESULT_SUCCESS; 90 return RESULT_SUCCESS;
78} 91}
79 92
93void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) {
94 for (u32 i = 0; i < MaxNvEvents; i++) {
95 if (events_interface.assigned_syncpt[i] == syncpoint_id &&
96 events_interface.assigned_value[i] == value) {
97 events_interface.LiberateEvent(i);
98 events_interface.events[i].writable->Signal();
99 }
100 }
101}
102
103Kernel::SharedPtr<Kernel::ReadableEvent> Module::GetEvent(const u32 event_id) const {
104 return events_interface.events[event_id].readable;
105}
106
107Kernel::SharedPtr<Kernel::WritableEvent> Module::GetEventWriteable(const u32 event_id) const {
108 return events_interface.events[event_id].writable;
109}
110
80} // namespace Service::Nvidia 111} // 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 @@
8#include <unordered_map> 8#include <unordered_map>
9#include <vector> 9#include <vector>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/kernel/writable_event.h"
12#include "core/hle/service/nvdrv/nvdata.h"
11#include "core/hle/service/service.h" 13#include "core/hle/service/service.h"
12 14
15namespace Core {
16class System;
17}
18
13namespace Service::NVFlinger { 19namespace Service::NVFlinger {
14class NVFlinger; 20class NVFlinger;
15} 21}
@@ -20,16 +26,72 @@ namespace Devices {
20class nvdevice; 26class nvdevice;
21} 27}
22 28
23struct IoctlFence { 29struct EventInterface {
24 u32 id; 30 // Mask representing currently busy events
25 u32 value; 31 u64 events_mask{};
32 // Each kernel event associated to an NV event
33 std::array<Kernel::EventPair, MaxNvEvents> events;
34 // The status of the current NVEvent
35 std::array<EventState, MaxNvEvents> status{};
36 // Tells if an NVEvent is registered or not
37 std::array<bool, MaxNvEvents> registered{};
38 // When an NVEvent is waiting on GPU interrupt, this is the sync_point
39 // associated with it.
40 std::array<u32, MaxNvEvents> assigned_syncpt{};
41 // This is the value of the GPU interrupt for which the NVEvent is waiting
42 // for.
43 std::array<u32, MaxNvEvents> assigned_value{};
44 // Constant to denote an unasigned syncpoint.
45 static constexpr u32 unassigned_syncpt = 0xFFFFFFFF;
46 std::optional<u32> GetFreeEvent() const {
47 u64 mask = events_mask;
48 for (u32 i = 0; i < MaxNvEvents; i++) {
49 const bool is_free = (mask & 0x1) == 0;
50 if (is_free) {
51 if (status[i] == EventState::Registered || status[i] == EventState::Free) {
52 return {i};
53 }
54 }
55 mask = mask >> 1;
56 }
57 return {};
58 }
59 void SetEventStatus(const u32 event_id, EventState new_status) {
60 EventState old_status = status[event_id];
61 if (old_status == new_status) {
62 return;
63 }
64 status[event_id] = new_status;
65 if (new_status == EventState::Registered) {
66 registered[event_id] = true;
67 }
68 if (new_status == EventState::Waiting || new_status == EventState::Busy) {
69 events_mask |= (1ULL << event_id);
70 }
71 }
72 void RegisterEvent(const u32 event_id) {
73 registered[event_id] = true;
74 if (status[event_id] == EventState::Free) {
75 status[event_id] = EventState::Registered;
76 }
77 }
78 void UnregisterEvent(const u32 event_id) {
79 registered[event_id] = false;
80 if (status[event_id] == EventState::Registered) {
81 status[event_id] = EventState::Free;
82 }
83 }
84 void LiberateEvent(const u32 event_id) {
85 status[event_id] = registered[event_id] ? EventState::Registered : EventState::Free;
86 events_mask &= ~(1ULL << event_id);
87 assigned_syncpt[event_id] = unassigned_syncpt;
88 assigned_value[event_id] = 0;
89 }
26}; 90};
27 91
28static_assert(sizeof(IoctlFence) == 8, "IoctlFence has wrong size");
29
30class Module final { 92class Module final {
31public: 93public:
32 Module(); 94 Module(Core::System& system);
33 ~Module(); 95 ~Module();
34 96
35 /// Returns a pointer to one of the available devices, identified by its name. 97 /// Returns a pointer to one of the available devices, identified by its name.
@@ -44,10 +106,17 @@ public:
44 /// Opens a device node and returns a file descriptor to it. 106 /// Opens a device node and returns a file descriptor to it.
45 u32 Open(const std::string& device_name); 107 u32 Open(const std::string& device_name);
46 /// Sends an ioctl command to the specified file descriptor. 108 /// Sends an ioctl command to the specified file descriptor.
47 u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output); 109 u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output,
110 IoctlCtrl& ctrl);
48 /// Closes a device file descriptor and returns operation success. 111 /// Closes a device file descriptor and returns operation success.
49 ResultCode Close(u32 fd); 112 ResultCode Close(u32 fd);
50 113
114 void SignalSyncpt(const u32 syncpoint_id, const u32 value);
115
116 Kernel::SharedPtr<Kernel::ReadableEvent> GetEvent(u32 event_id) const;
117
118 Kernel::SharedPtr<Kernel::WritableEvent> GetEventWriteable(u32 event_id) const;
119
51private: 120private:
52 /// Id to use for the next open file descriptor. 121 /// Id to use for the next open file descriptor.
53 u32 next_fd = 1; 122 u32 next_fd = 1;
@@ -57,9 +126,12 @@ private:
57 126
58 /// Mapping of device node names to their implementation. 127 /// Mapping of device node names to their implementation.
59 std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices; 128 std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices;
129
130 EventInterface events_interface;
60}; 131};
61 132
62/// Registers all NVDRV services with the specified service manager. 133/// Registers all NVDRV services with the specified service manager.
63void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger); 134void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
135 Core::System& system);
64 136
65} // namespace Service::Nvidia 137} // 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)
34 buffer_wait_event.writable->Signal(); 34 buffer_wait_event.writable->Signal();
35} 35}
36 36
37std::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) { 37std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
38 u32 height) {
38 auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { 39 auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) {
39 // Only consider free buffers. Buffers become free once again after they've been Acquired 40 // Only consider free buffers. Buffers become free once again after they've been Acquired
40 // and Released by the compositor, see the NVFlinger::Compose method. 41 // and Released by the compositor, see the NVFlinger::Compose method.
@@ -51,7 +52,7 @@ std::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
51 } 52 }
52 53
53 itr->status = Buffer::Status::Dequeued; 54 itr->status = Buffer::Status::Dequeued;
54 return itr->slot; 55 return {{itr->slot, &itr->multi_fence}};
55} 56}
56 57
57const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const { 58const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
@@ -63,7 +64,8 @@ const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
63} 64}
64 65
65void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform, 66void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
66 const Common::Rectangle<int>& crop_rect) { 67 const Common::Rectangle<int>& crop_rect, u32 swap_interval,
68 Service::Nvidia::MultiFence& multi_fence) {
67 auto itr = std::find_if(queue.begin(), queue.end(), 69 auto itr = std::find_if(queue.begin(), queue.end(),
68 [&](const Buffer& buffer) { return buffer.slot == slot; }); 70 [&](const Buffer& buffer) { return buffer.slot == slot; });
69 ASSERT(itr != queue.end()); 71 ASSERT(itr != queue.end());
@@ -71,12 +73,21 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
71 itr->status = Buffer::Status::Queued; 73 itr->status = Buffer::Status::Queued;
72 itr->transform = transform; 74 itr->transform = transform;
73 itr->crop_rect = crop_rect; 75 itr->crop_rect = crop_rect;
76 itr->swap_interval = swap_interval;
77 itr->multi_fence = multi_fence;
78 queue_sequence.push_back(slot);
74} 79}
75 80
76std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() { 81std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
77 auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) { 82 auto itr = queue.end();
78 return buffer.status == Buffer::Status::Queued; 83 // Iterate to find a queued buffer matching the requested slot.
79 }); 84 while (itr == queue.end() && !queue_sequence.empty()) {
85 u32 slot = queue_sequence.front();
86 itr = std::find_if(queue.begin(), queue.end(), [&slot](const Buffer& buffer) {
87 return buffer.status == Buffer::Status::Queued && buffer.slot == slot;
88 });
89 queue_sequence.pop_front();
90 }
80 if (itr == queue.end()) 91 if (itr == queue.end())
81 return {}; 92 return {};
82 itr->status = Buffer::Status::Acquired; 93 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 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <list>
7#include <optional> 8#include <optional>
8#include <vector> 9#include <vector>
9 10
@@ -12,6 +13,7 @@
12#include "common/swap.h" 13#include "common/swap.h"
13#include "core/hle/kernel/object.h" 14#include "core/hle/kernel/object.h"
14#include "core/hle/kernel/writable_event.h" 15#include "core/hle/kernel/writable_event.h"
16#include "core/hle/service/nvdrv/nvdata.h"
15 17
16namespace Service::NVFlinger { 18namespace Service::NVFlinger {
17 19
@@ -68,13 +70,17 @@ public:
68 IGBPBuffer igbp_buffer; 70 IGBPBuffer igbp_buffer;
69 BufferTransformFlags transform; 71 BufferTransformFlags transform;
70 Common::Rectangle<int> crop_rect; 72 Common::Rectangle<int> crop_rect;
73 u32 swap_interval;
74 Service::Nvidia::MultiFence multi_fence;
71 }; 75 };
72 76
73 void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer); 77 void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer);
74 std::optional<u32> DequeueBuffer(u32 width, u32 height); 78 std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> DequeueBuffer(u32 width,
79 u32 height);
75 const IGBPBuffer& RequestBuffer(u32 slot) const; 80 const IGBPBuffer& RequestBuffer(u32 slot) const;
76 void QueueBuffer(u32 slot, BufferTransformFlags transform, 81 void QueueBuffer(u32 slot, BufferTransformFlags transform,
77 const Common::Rectangle<int>& crop_rect); 82 const Common::Rectangle<int>& crop_rect, u32 swap_interval,
83 Service::Nvidia::MultiFence& multi_fence);
78 std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer(); 84 std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
79 void ReleaseBuffer(u32 slot); 85 void ReleaseBuffer(u32 slot);
80 u32 Query(QueryType type); 86 u32 Query(QueryType type);
@@ -92,6 +98,7 @@ private:
92 u64 layer_id; 98 u64 layer_id;
93 99
94 std::vector<Buffer> queue; 100 std::vector<Buffer> queue;
101 std::list<u32> queue_sequence;
95 Kernel::EventPair buffer_wait_event; 102 Kernel::EventPair buffer_wait_event;
96}; 103};
97 104
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
37 displays.emplace_back(4, "Null"); 37 displays.emplace_back(4, "Null");
38 38
39 // Schedule the screen composition events 39 // Schedule the screen composition events
40 const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : frame_ticks; 40 composition_event = core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata,
41 41 s64 cycles_late) {
42 composition_event = core_timing.RegisterEvent( 42 Compose();
43 "ScreenComposition", [this, ticks](u64 userdata, s64 cycles_late) { 43 const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks();
44 Compose(); 44 this->core_timing.ScheduleEvent(std::max<s64>(0LL, ticks - cycles_late), composition_event);
45 this->core_timing.ScheduleEvent(ticks - cycles_late, composition_event); 45 });
46 }); 46
47 47 core_timing.ScheduleEvent(frame_ticks, composition_event);
48 core_timing.ScheduleEvent(ticks, composition_event);
49} 48}
50 49
51NVFlinger::~NVFlinger() { 50NVFlinger::~NVFlinger() {
@@ -206,8 +205,14 @@ void NVFlinger::Compose() {
206 igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, 205 igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride,
207 buffer->get().transform, buffer->get().crop_rect); 206 buffer->get().transform, buffer->get().crop_rect);
208 207
208 swap_interval = buffer->get().swap_interval;
209 buffer_queue.ReleaseBuffer(buffer->get().slot); 209 buffer_queue.ReleaseBuffer(buffer->get().slot);
210 } 210 }
211} 211}
212 212
213s64 NVFlinger::GetNextTicks() const {
214 constexpr s64 max_hertz = 120LL;
215 return (Core::Timing::BASE_CLOCK_RATE * (1LL << swap_interval)) / max_hertz;
216}
217
213} // namespace Service::NVFlinger 218} // 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:
74 /// finished. 74 /// finished.
75 void Compose(); 75 void Compose();
76 76
77 s64 GetNextTicks() const;
78
77private: 79private:
78 /// Finds the display identified by the specified ID. 80 /// Finds the display identified by the specified ID.
79 VI::Display* FindDisplay(u64 display_id); 81 VI::Display* FindDisplay(u64 display_id);
@@ -98,6 +100,8 @@ private:
98 /// layers. 100 /// layers.
99 u32 next_buffer_queue_id = 1; 101 u32 next_buffer_queue_id = 1;
100 102
103 u32 swap_interval = 1;
104
101 /// Event that handles screen composition. 105 /// Event that handles screen composition.
102 Core::Timing::EventType* composition_event; 106 Core::Timing::EventType* composition_event;
103 107
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 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/hle/ipc_helpers.h" 5#include "core/hle/ipc_helpers.h"
6#include "core/hle/kernel/kernel.h"
7#include "core/hle/kernel/process.h"
6#include "core/hle/service/pm/pm.h" 8#include "core/hle/service/pm/pm.h"
7#include "core/hle/service/service.h" 9#include "core/hle/service/service.h"
8 10
9namespace Service::PM { 11namespace Service::PM {
10 12
13namespace {
14
15constexpr ResultCode ERROR_PROCESS_NOT_FOUND{ErrorModule::PM, 1};
16
17constexpr u64 NO_PROCESS_FOUND_PID{0};
18
19std::optional<Kernel::SharedPtr<Kernel::Process>> SearchProcessList(
20 const std::vector<Kernel::SharedPtr<Kernel::Process>>& process_list,
21 std::function<bool(const Kernel::SharedPtr<Kernel::Process>&)> predicate) {
22 const auto iter = std::find_if(process_list.begin(), process_list.end(), predicate);
23
24 if (iter == process_list.end()) {
25 return std::nullopt;
26 }
27
28 return *iter;
29}
30
31void GetApplicationPidGeneric(Kernel::HLERequestContext& ctx,
32 const std::vector<Kernel::SharedPtr<Kernel::Process>>& process_list) {
33 const auto process = SearchProcessList(process_list, [](const auto& process) {
34 return process->GetProcessID() == Kernel::Process::ProcessIDMin;
35 });
36
37 IPC::ResponseBuilder rb{ctx, 4};
38 rb.Push(RESULT_SUCCESS);
39 rb.Push(process.has_value() ? (*process)->GetProcessID() : NO_PROCESS_FOUND_PID);
40}
41
42} // Anonymous namespace
43
11class BootMode final : public ServiceFramework<BootMode> { 44class BootMode final : public ServiceFramework<BootMode> {
12public: 45public:
13 explicit BootMode() : ServiceFramework{"pm:bm"} { 46 explicit BootMode() : ServiceFramework{"pm:bm"} {
@@ -41,14 +74,15 @@ private:
41 74
42class DebugMonitor final : public ServiceFramework<DebugMonitor> { 75class DebugMonitor final : public ServiceFramework<DebugMonitor> {
43public: 76public:
44 explicit DebugMonitor() : ServiceFramework{"pm:dmnt"} { 77 explicit DebugMonitor(const Kernel::KernelCore& kernel)
78 : ServiceFramework{"pm:dmnt"}, kernel(kernel) {
45 // clang-format off 79 // clang-format off
46 static const FunctionInfo functions[] = { 80 static const FunctionInfo functions[] = {
47 {0, nullptr, "GetDebugProcesses"}, 81 {0, nullptr, "GetDebugProcesses"},
48 {1, nullptr, "StartDebugProcess"}, 82 {1, nullptr, "StartDebugProcess"},
49 {2, nullptr, "GetTitlePid"}, 83 {2, &DebugMonitor::GetTitlePid, "GetTitlePid"},
50 {3, nullptr, "EnableDebugForTitleId"}, 84 {3, nullptr, "EnableDebugForTitleId"},
51 {4, nullptr, "GetApplicationPid"}, 85 {4, &DebugMonitor::GetApplicationPid, "GetApplicationPid"},
52 {5, nullptr, "EnableDebugForApplication"}, 86 {5, nullptr, "EnableDebugForApplication"},
53 {6, nullptr, "DisableDebug"}, 87 {6, nullptr, "DisableDebug"},
54 }; 88 };
@@ -56,21 +90,77 @@ public:
56 90
57 RegisterHandlers(functions); 91 RegisterHandlers(functions);
58 } 92 }
93
94private:
95 void GetTitlePid(Kernel::HLERequestContext& ctx) {
96 IPC::RequestParser rp{ctx};
97 const auto title_id = rp.PopRaw<u64>();
98
99 LOG_DEBUG(Service_PM, "called, title_id={:016X}", title_id);
100
101 const auto process =
102 SearchProcessList(kernel.GetProcessList(), [title_id](const auto& process) {
103 return process->GetTitleID() == title_id;
104 });
105
106 if (!process.has_value()) {
107 IPC::ResponseBuilder rb{ctx, 2};
108 rb.Push(ERROR_PROCESS_NOT_FOUND);
109 return;
110 }
111
112 IPC::ResponseBuilder rb{ctx, 4};
113 rb.Push(RESULT_SUCCESS);
114 rb.Push((*process)->GetProcessID());
115 }
116
117 void GetApplicationPid(Kernel::HLERequestContext& ctx) {
118 LOG_DEBUG(Service_PM, "called");
119 GetApplicationPidGeneric(ctx, kernel.GetProcessList());
120 }
121
122 const Kernel::KernelCore& kernel;
59}; 123};
60 124
61class Info final : public ServiceFramework<Info> { 125class Info final : public ServiceFramework<Info> {
62public: 126public:
63 explicit Info() : ServiceFramework{"pm:info"} { 127 explicit Info(const std::vector<Kernel::SharedPtr<Kernel::Process>>& process_list)
128 : ServiceFramework{"pm:info"}, process_list(process_list) {
64 static const FunctionInfo functions[] = { 129 static const FunctionInfo functions[] = {
65 {0, nullptr, "GetTitleId"}, 130 {0, &Info::GetTitleId, "GetTitleId"},
66 }; 131 };
67 RegisterHandlers(functions); 132 RegisterHandlers(functions);
68 } 133 }
134
135private:
136 void GetTitleId(Kernel::HLERequestContext& ctx) {
137 IPC::RequestParser rp{ctx};
138 const auto process_id = rp.PopRaw<u64>();
139
140 LOG_DEBUG(Service_PM, "called, process_id={:016X}", process_id);
141
142 const auto process = SearchProcessList(process_list, [process_id](const auto& process) {
143 return process->GetProcessID() == process_id;
144 });
145
146 if (!process.has_value()) {
147 IPC::ResponseBuilder rb{ctx, 2};
148 rb.Push(ERROR_PROCESS_NOT_FOUND);
149 return;
150 }
151
152 IPC::ResponseBuilder rb{ctx, 4};
153 rb.Push(RESULT_SUCCESS);
154 rb.Push((*process)->GetTitleID());
155 }
156
157 const std::vector<Kernel::SharedPtr<Kernel::Process>>& process_list;
69}; 158};
70 159
71class Shell final : public ServiceFramework<Shell> { 160class Shell final : public ServiceFramework<Shell> {
72public: 161public:
73 explicit Shell() : ServiceFramework{"pm:shell"} { 162 explicit Shell(const Kernel::KernelCore& kernel)
163 : ServiceFramework{"pm:shell"}, kernel(kernel) {
74 // clang-format off 164 // clang-format off
75 static const FunctionInfo functions[] = { 165 static const FunctionInfo functions[] = {
76 {0, nullptr, "LaunchProcess"}, 166 {0, nullptr, "LaunchProcess"},
@@ -79,21 +169,31 @@ public:
79 {3, nullptr, "GetProcessEventWaiter"}, 169 {3, nullptr, "GetProcessEventWaiter"},
80 {4, nullptr, "GetProcessEventType"}, 170 {4, nullptr, "GetProcessEventType"},
81 {5, nullptr, "NotifyBootFinished"}, 171 {5, nullptr, "NotifyBootFinished"},
82 {6, nullptr, "GetApplicationPid"}, 172 {6, &Shell::GetApplicationPid, "GetApplicationPid"},
83 {7, nullptr, "BoostSystemMemoryResourceLimit"}, 173 {7, nullptr, "BoostSystemMemoryResourceLimit"},
84 {8, nullptr, "EnableAdditionalSystemThreads"}, 174 {8, nullptr, "EnableAdditionalSystemThreads"},
175 {9, nullptr, "GetUnimplementedEventHandle"},
85 }; 176 };
86 // clang-format on 177 // clang-format on
87 178
88 RegisterHandlers(functions); 179 RegisterHandlers(functions);
89 } 180 }
181
182private:
183 void GetApplicationPid(Kernel::HLERequestContext& ctx) {
184 LOG_DEBUG(Service_PM, "called");
185 GetApplicationPidGeneric(ctx, kernel.GetProcessList());
186 }
187
188 const Kernel::KernelCore& kernel;
90}; 189};
91 190
92void InstallInterfaces(SM::ServiceManager& sm) { 191void InstallInterfaces(Core::System& system) {
93 std::make_shared<BootMode>()->InstallAsService(sm); 192 std::make_shared<BootMode>()->InstallAsService(system.ServiceManager());
94 std::make_shared<DebugMonitor>()->InstallAsService(sm); 193 std::make_shared<DebugMonitor>(system.Kernel())->InstallAsService(system.ServiceManager());
95 std::make_shared<Info>()->InstallAsService(sm); 194 std::make_shared<Info>(system.Kernel().GetProcessList())
96 std::make_shared<Shell>()->InstallAsService(sm); 195 ->InstallAsService(system.ServiceManager());
196 std::make_shared<Shell>(system.Kernel())->InstallAsService(system.ServiceManager());
97} 197}
98 198
99} // namespace Service::PM 199} // 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 @@
4 4
5#pragma once 5#pragma once
6 6
7namespace Service::SM { 7namespace Core {
8class ServiceManager; 8class System;
9} 9}
10 10
11namespace Service::PM { 11namespace Service::PM {
@@ -16,6 +16,6 @@ enum class SystemBootMode {
16}; 16};
17 17
18/// Registers all PM services with the specified service manager. 18/// Registers all PM services with the specified service manager.
19void InstallInterfaces(SM::ServiceManager& service_manager); 19void InstallInterfaces(Core::System& system);
20 20
21} // namespace Service::PM 21} // 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) {
206 AM::InstallInterfaces(*sm, nv_flinger, system); 206 AM::InstallInterfaces(*sm, nv_flinger, system);
207 AOC::InstallInterfaces(*sm); 207 AOC::InstallInterfaces(*sm);
208 APM::InstallInterfaces(system); 208 APM::InstallInterfaces(system);
209 Audio::InstallInterfaces(*sm); 209 Audio::InstallInterfaces(*sm, system);
210 BCAT::InstallInterfaces(*sm); 210 BCAT::InstallInterfaces(*sm);
211 BPC::InstallInterfaces(*sm); 211 BPC::InstallInterfaces(*sm);
212 BtDrv::InstallInterfaces(*sm); 212 BtDrv::InstallInterfaces(*sm);
@@ -236,12 +236,12 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
236 NIM::InstallInterfaces(*sm); 236 NIM::InstallInterfaces(*sm);
237 NPNS::InstallInterfaces(*sm); 237 NPNS::InstallInterfaces(*sm);
238 NS::InstallInterfaces(*sm); 238 NS::InstallInterfaces(*sm);
239 Nvidia::InstallInterfaces(*sm, *nv_flinger); 239 Nvidia::InstallInterfaces(*sm, *nv_flinger, system);
240 PCIe::InstallInterfaces(*sm); 240 PCIe::InstallInterfaces(*sm);
241 PCTL::InstallInterfaces(*sm); 241 PCTL::InstallInterfaces(*sm);
242 PCV::InstallInterfaces(*sm); 242 PCV::InstallInterfaces(*sm);
243 PlayReport::InstallInterfaces(*sm); 243 PlayReport::InstallInterfaces(*sm);
244 PM::InstallInterfaces(*sm); 244 PM::InstallInterfaces(system);
245 PSC::InstallInterfaces(*sm); 245 PSC::InstallInterfaces(*sm);
246 PSM::InstallInterfaces(*sm); 246 PSM::InstallInterfaces(*sm);
247 Set::InstallInterfaces(*sm); 247 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 @@
21#include "core/hle/kernel/readable_event.h" 21#include "core/hle/kernel/readable_event.h"
22#include "core/hle/kernel/thread.h" 22#include "core/hle/kernel/thread.h"
23#include "core/hle/kernel/writable_event.h" 23#include "core/hle/kernel/writable_event.h"
24#include "core/hle/service/nvdrv/nvdata.h"
24#include "core/hle/service/nvdrv/nvdrv.h" 25#include "core/hle/service/nvdrv/nvdrv.h"
25#include "core/hle/service/nvflinger/buffer_queue.h" 26#include "core/hle/service/nvflinger/buffer_queue.h"
26#include "core/hle/service/nvflinger/nvflinger.h" 27#include "core/hle/service/nvflinger/nvflinger.h"
@@ -328,32 +329,22 @@ public:
328 Data data; 329 Data data;
329}; 330};
330 331
331struct BufferProducerFence {
332 u32 is_valid;
333 std::array<Nvidia::IoctlFence, 4> fences;
334};
335static_assert(sizeof(BufferProducerFence) == 36, "BufferProducerFence has wrong size");
336
337class IGBPDequeueBufferResponseParcel : public Parcel { 332class IGBPDequeueBufferResponseParcel : public Parcel {
338public: 333public:
339 explicit IGBPDequeueBufferResponseParcel(u32 slot) : slot(slot) {} 334 explicit IGBPDequeueBufferResponseParcel(u32 slot, Service::Nvidia::MultiFence& multi_fence)
335 : slot(slot), multi_fence(multi_fence) {}
340 ~IGBPDequeueBufferResponseParcel() override = default; 336 ~IGBPDequeueBufferResponseParcel() override = default;
341 337
342protected: 338protected:
343 void SerializeData() override { 339 void SerializeData() override {
344 // TODO(Subv): Find out how this Fence is used.
345 BufferProducerFence fence = {};
346 fence.is_valid = 1;
347 for (auto& fence_ : fence.fences)
348 fence_.id = -1;
349
350 Write(slot); 340 Write(slot);
351 Write<u32_le>(1); 341 Write<u32_le>(1);
352 WriteObject(fence); 342 WriteObject(multi_fence);
353 Write<u32_le>(0); 343 Write<u32_le>(0);
354 } 344 }
355 345
356 u32_le slot; 346 u32_le slot;
347 Service::Nvidia::MultiFence multi_fence;
357}; 348};
358 349
359class IGBPRequestBufferRequestParcel : public Parcel { 350class IGBPRequestBufferRequestParcel : public Parcel {
@@ -400,12 +391,6 @@ public:
400 data = Read<Data>(); 391 data = Read<Data>();
401 } 392 }
402 393
403 struct Fence {
404 u32_le id;
405 u32_le value;
406 };
407 static_assert(sizeof(Fence) == 8, "Fence has wrong size");
408
409 struct Data { 394 struct Data {
410 u32_le slot; 395 u32_le slot;
411 INSERT_PADDING_WORDS(3); 396 INSERT_PADDING_WORDS(3);
@@ -418,15 +403,15 @@ public:
418 s32_le scaling_mode; 403 s32_le scaling_mode;
419 NVFlinger::BufferQueue::BufferTransformFlags transform; 404 NVFlinger::BufferQueue::BufferTransformFlags transform;
420 u32_le sticky_transform; 405 u32_le sticky_transform;
421 INSERT_PADDING_WORDS(2); 406 INSERT_PADDING_WORDS(1);
422 u32_le fence_is_valid; 407 u32_le swap_interval;
423 std::array<Fence, 2> fences; 408 Service::Nvidia::MultiFence multi_fence;
424 409
425 Common::Rectangle<int> GetCropRect() const { 410 Common::Rectangle<int> GetCropRect() const {
426 return {crop_left, crop_top, crop_right, crop_bottom}; 411 return {crop_left, crop_top, crop_right, crop_bottom};
427 } 412 }
428 }; 413 };
429 static_assert(sizeof(Data) == 80, "ParcelData has wrong size"); 414 static_assert(sizeof(Data) == 96, "ParcelData has wrong size");
430 415
431 Data data; 416 Data data;
432}; 417};
@@ -547,11 +532,11 @@ private:
547 IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()}; 532 IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
548 const u32 width{request.data.width}; 533 const u32 width{request.data.width};
549 const u32 height{request.data.height}; 534 const u32 height{request.data.height};
550 std::optional<u32> slot = buffer_queue.DequeueBuffer(width, height); 535 auto result = buffer_queue.DequeueBuffer(width, height);
551 536
552 if (slot) { 537 if (result) {
553 // Buffer is available 538 // Buffer is available
554 IGBPDequeueBufferResponseParcel response{*slot}; 539 IGBPDequeueBufferResponseParcel response{result->first, *result->second};
555 ctx.WriteBuffer(response.Serialize()); 540 ctx.WriteBuffer(response.Serialize());
556 } else { 541 } else {
557 // Wait the current thread until a buffer becomes available 542 // Wait the current thread until a buffer becomes available
@@ -561,10 +546,10 @@ private:
561 Kernel::ThreadWakeupReason reason) { 546 Kernel::ThreadWakeupReason reason) {
562 // Repeat TransactParcel DequeueBuffer when a buffer is available 547 // Repeat TransactParcel DequeueBuffer when a buffer is available
563 auto& buffer_queue = nv_flinger->FindBufferQueue(id); 548 auto& buffer_queue = nv_flinger->FindBufferQueue(id);
564 std::optional<u32> slot = buffer_queue.DequeueBuffer(width, height); 549 auto result = buffer_queue.DequeueBuffer(width, height);
565 ASSERT_MSG(slot != std::nullopt, "Could not dequeue buffer."); 550 ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer.");
566 551
567 IGBPDequeueBufferResponseParcel response{*slot}; 552 IGBPDequeueBufferResponseParcel response{result->first, *result->second};
568 ctx.WriteBuffer(response.Serialize()); 553 ctx.WriteBuffer(response.Serialize());
569 IPC::ResponseBuilder rb{ctx, 2}; 554 IPC::ResponseBuilder rb{ctx, 2};
570 rb.Push(RESULT_SUCCESS); 555 rb.Push(RESULT_SUCCESS);
@@ -582,7 +567,8 @@ private:
582 IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()}; 567 IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};
583 568
584 buffer_queue.QueueBuffer(request.data.slot, request.data.transform, 569 buffer_queue.QueueBuffer(request.data.slot, request.data.transform,
585 request.data.GetCropRect()); 570 request.data.GetCropRect(), request.data.swap_interval,
571 request.data.multi_fence);
586 572
587 IGBPQueueBufferResponseParcel response{1280, 720}; 573 IGBPQueueBufferResponseParcel response{1280, 720};
588 ctx.WriteBuffer(response.Serialize()); 574 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) {
295 } 295 }
296 } 296 }
297 297
298 std::vector<u8> program_image(total_image_size); 298 Kernel::PhysicalMemory program_image(total_image_size);
299 std::size_t current_image_position = 0; 299 std::size_t current_image_position = 0;
300 300
301 Kernel::CodeSet codeset; 301 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) {
69 69
70 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 70 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
71 Kernel::CodeSet codeset; 71 Kernel::CodeSet codeset;
72 std::vector<u8> program_image; 72 Kernel::PhysicalMemory program_image;
73 73
74 const auto load_segment = [&program_image](Kernel::CodeSet::Segment& segment, 74 const auto load_segment = [&program_image](Kernel::CodeSet::Segment& segment,
75 const std::vector<u8>& data, u32 offset) { 75 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,
143 } 143 }
144 144
145 // Build program image 145 // Build program image
146 std::vector<u8> program_image(PageAlignSize(nro_header.file_size)); 146 Kernel::PhysicalMemory program_image(PageAlignSize(nro_header.file_size));
147 std::memcpy(program_image.data(), data.data(), program_image.size()); 147 std::memcpy(program_image.data(), data.data(), program_image.size());
148 if (program_image.size() != PageAlignSize(nro_header.file_size)) { 148 if (program_image.size() != PageAlignSize(nro_header.file_size)) {
149 return {}; 149 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,
89 89
90 // Build program image 90 // Build program image
91 Kernel::CodeSet codeset; 91 Kernel::CodeSet codeset;
92 std::vector<u8> program_image; 92 Kernel::PhysicalMemory program_image;
93 for (std::size_t i = 0; i < nso_header.segments.size(); ++i) { 93 for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
94 std::vector<u8> data = 94 std::vector<u8> data =
95 file.ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset); 95 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() {
85 LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0)); 85 LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0));
86 LogSetting("System_CurrentUser", Settings::values.current_user); 86 LogSetting("System_CurrentUser", Settings::values.current_user);
87 LogSetting("System_LanguageIndex", Settings::values.language_index); 87 LogSetting("System_LanguageIndex", Settings::values.language_index);
88 LogSetting("Core_CpuJitEnabled", Settings::values.cpu_jit_enabled);
89 LogSetting("Core_UseMultiCore", Settings::values.use_multi_core); 88 LogSetting("Core_UseMultiCore", Settings::values.use_multi_core);
90 LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor); 89 LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor);
91 LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit); 90 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 {
378 std::atomic_bool is_device_reload_pending{true}; 378 std::atomic_bool is_device_reload_pending{true};
379 379
380 // Core 380 // Core
381 bool cpu_jit_enabled;
382 bool use_multi_core; 381 bool use_multi_core;
383 382
384 // Data Storage 383 // 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) {
168 AddField(Telemetry::FieldType::UserConfig, "Audio_SinkId", Settings::values.sink_id); 168 AddField(Telemetry::FieldType::UserConfig, "Audio_SinkId", Settings::values.sink_id);
169 AddField(Telemetry::FieldType::UserConfig, "Audio_EnableAudioStretching", 169 AddField(Telemetry::FieldType::UserConfig, "Audio_EnableAudioStretching",
170 Settings::values.enable_audio_stretching); 170 Settings::values.enable_audio_stretching);
171 AddField(Telemetry::FieldType::UserConfig, "Core_UseCpuJit", Settings::values.cpu_jit_enabled);
172 AddField(Telemetry::FieldType::UserConfig, "Core_UseMultiCore", 171 AddField(Telemetry::FieldType::UserConfig, "Core_UseMultiCore",
173 Settings::values.use_multi_core); 172 Settings::values.use_multi_core);
174 AddField(Telemetry::FieldType::UserConfig, "Renderer_ResolutionFactor", 173 AddField(Telemetry::FieldType::UserConfig, "Renderer_ResolutionFactor",