summaryrefslogtreecommitdiff
path: root/src/core/hle
diff options
context:
space:
mode:
authorGravatar Ameer J2023-11-26 21:08:53 -0500
committerGravatar GitHub2023-11-26 21:08:53 -0500
commit1d11fe00a3000efbf6a0a4bb690e0d544a1b7b4a (patch)
treec219aacab776c0a1e3956614b60a01fa2f6164cb /src/core/hle
parentshader_recompiler: Align SSBO offsets in GlobalMemory functions (diff)
parentMerge pull request #11535 from GPUCode/upload_cmdbuf (diff)
downloadyuzu-1d11fe00a3000efbf6a0a4bb690e0d544a1b7b4a.tar.gz
yuzu-1d11fe00a3000efbf6a0a4bb690e0d544a1b7b4a.tar.xz
yuzu-1d11fe00a3000efbf6a0a4bb690e0d544a1b7b4a.zip
Merge branch 'master' into ssbo-align
Diffstat (limited to 'src/core/hle')
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp13
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/k_system_control.h7
-rw-r--r--src/core/hle/kernel/k_capabilities.cpp39
-rw-r--r--src/core/hle/kernel/k_capabilities.h17
-rw-r--r--src/core/hle/kernel/k_device_address_space.cpp4
-rw-r--r--src/core/hle/kernel/k_device_address_space.h10
-rw-r--r--src/core/hle/kernel/k_memory_layout.h8
-rw-r--r--src/core/hle/kernel/k_memory_manager.cpp12
-rw-r--r--src/core/hle/kernel/k_page_table.cpp3519
-rw-r--r--src/core/hle/kernel/k_page_table.h542
-rw-r--r--src/core/hle/kernel/k_page_table_base.cpp5716
-rw-r--r--src/core/hle/kernel/k_page_table_base.h759
-rw-r--r--src/core/hle/kernel/k_process.cpp18
-rw-r--r--src/core/hle/kernel/k_process.h14
-rw-r--r--src/core/hle/kernel/k_process_page_table.h480
-rw-r--r--src/core/hle/kernel/k_server_session.cpp2
-rw-r--r--src/core/hle/kernel/k_system_resource.cpp2
-rw-r--r--src/core/hle/kernel/k_thread_local_page.cpp4
-rw-r--r--src/core/hle/kernel/process_capability.cpp389
-rw-r--r--src/core/hle/kernel/process_capability.h266
-rw-r--r--src/core/hle/kernel/svc/svc_memory.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_physical_memory.cpp9
-rw-r--r--src/core/hle/kernel/svc/svc_process_memory.cpp3
-rw-r--r--src/core/hle/kernel/svc/svc_query_memory.cpp8
-rw-r--r--src/core/hle/result.h31
-rw-r--r--src/core/hle/service/acc/acc.cpp56
-rw-r--r--src/core/hle/service/am/am.cpp91
-rw-r--r--src/core/hle/service/am/am.h3
-rw-r--r--src/core/hle/service/am/applets/applet_cabinet.cpp3
-rw-r--r--src/core/hle/service/am/applets/applet_controller.h2
-rw-r--r--src/core/hle/service/am/applets/applet_web_browser.cpp3
-rw-r--r--src/core/hle/service/am/applets/applets.h24
-rw-r--r--src/core/hle/service/btm/btm.cpp56
-rw-r--r--src/core/hle/service/friend/friend.cpp13
-rw-r--r--src/core/hle/service/hid/controllers/console_six_axis.cpp42
-rw-r--r--src/core/hle/service/hid/controllers/console_six_axis.h43
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.cpp9
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h4
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp10
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h6
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp54
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h6
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp10
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h6
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp11
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h6
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp588
-rw-r--r--src/core/hle/service/hid/controllers/npad.h205
-rw-r--r--src/core/hle/service/hid/controllers/palma.cpp88
-rw-r--r--src/core/hle/service/hid/controllers/palma.h8
-rw-r--r--src/core/hle/service/hid/controllers/seven_six_axis.cpp (renamed from src/core/hle/service/hid/controllers/console_sixaxis.cpp)38
-rw-r--r--src/core/hle/service/hid/controllers/seven_six_axis.h (renamed from src/core/hle/service/hid/controllers/console_sixaxis.h)31
-rw-r--r--src/core/hle/service/hid/controllers/six_axis.cpp413
-rw-r--r--src/core/hle/service/hid/controllers/six_axis.h111
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp11
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h6
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp11
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h6
-rw-r--r--src/core/hle/service/hid/hid.cpp2861
-rw-r--r--src/core/hle/service/hid/hid.h212
-rw-r--r--src/core/hle/service/hid/hid_debug_server.cpp159
-rw-r--r--src/core/hle/service/hid/hid_debug_server.h26
-rw-r--r--src/core/hle/service/hid/hid_firmware_settings.cpp99
-rw-r--r--src/core/hle/service/hid/hid_firmware_settings.h54
-rw-r--r--src/core/hle/service/hid/hid_server.cpp2371
-rw-r--r--src/core/hle/service/hid/hid_server.h149
-rw-r--r--src/core/hle/service/hid/hid_system_server.cpp539
-rw-r--r--src/core/hle/service/hid/hid_system_server.h63
-rw-r--r--src/core/hle/service/hid/hid_util.h146
-rw-r--r--src/core/hle/service/hid/irs.cpp7
-rw-r--r--src/core/hle/service/hid/irs.h5
-rw-r--r--src/core/hle/service/hid/irsensor/clustering_processor.cpp16
-rw-r--r--src/core/hle/service/hid/irsensor/clustering_processor.h9
-rw-r--r--src/core/hle/service/hid/irsensor/image_transfer_processor.cpp2
-rw-r--r--src/core/hle/service/hid/irsensor/moment_processor.cpp123
-rw-r--r--src/core/hle/service/hid/irsensor/moment_processor.h34
-rw-r--r--src/core/hle/service/hid/resource_manager.cpp241
-rw-r--r--src/core/hle/service/hid/resource_manager.h111
-rw-r--r--src/core/hle/service/hid/ring_lifo.h6
-rw-r--r--src/core/hle/service/ldn/ldn.cpp10
-rw-r--r--src/core/hle/service/ldr/ldr.cpp45
-rw-r--r--src/core/hle/service/nfc/common/device_manager.cpp3
-rw-r--r--src/core/hle/service/nvdrv/devices/ioctl_serialization.h159
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp82
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h20
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp42
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h12
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp115
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h29
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp117
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h35
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp15
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp87
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h14
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp7
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp13
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp47
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h12
-rw-r--r--src/core/hle/service/nvnflinger/buffer_item.h2
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp27
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_consumer.h9
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_core.cpp12
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_core.h3
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_producer.cpp19
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_producer.h3
-rw-r--r--src/core/hle/service/nvnflinger/buffer_slot.h2
-rw-r--r--src/core/hle/service/nvnflinger/buffer_transform_flags.h2
-rw-r--r--src/core/hle/service/nvnflinger/consumer_base.cpp20
-rw-r--r--src/core/hle/service/nvnflinger/consumer_base.h2
-rw-r--r--src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp29
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.cpp22
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.h2
-rw-r--r--src/core/hle/service/nvnflinger/status.h2
-rw-r--r--src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp34
-rw-r--r--src/core/hle/service/nvnflinger/ui/graphic_buffer.h25
-rw-r--r--src/core/hle/service/set/set_sys.cpp96
-rw-r--r--src/core/hle/service/set/set_sys.h36
-rw-r--r--src/core/hle/service/sockets/bsd.cpp77
-rw-r--r--src/core/hle/service/sockets/bsd.h2
-rw-r--r--src/core/hle/service/time/clock_types.h5
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp2
122 files changed, 13118 insertions, 9266 deletions
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
index 59364efa1..37fa39a73 100644
--- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
+++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
@@ -222,7 +222,7 @@ Result KSystemControl::AllocateSecureMemory(KernelCore& kernel, KVirtualAddress*
222 }; 222 };
223 223
224 // We succeeded. 224 // We succeeded.
225 *out = KPageTable::GetHeapVirtualAddress(kernel.MemoryLayout(), paddr); 225 *out = KPageTable::GetHeapVirtualAddress(kernel, paddr);
226 R_SUCCEED(); 226 R_SUCCEED();
227} 227}
228 228
@@ -238,8 +238,17 @@ void KSystemControl::FreeSecureMemory(KernelCore& kernel, KVirtualAddress addres
238 ASSERT(Common::IsAligned(size, alignment)); 238 ASSERT(Common::IsAligned(size, alignment));
239 239
240 // Close the secure region's pages. 240 // Close the secure region's pages.
241 kernel.MemoryManager().Close(KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), address), 241 kernel.MemoryManager().Close(KPageTable::GetHeapPhysicalAddress(kernel, address),
242 size / PageSize); 242 size / PageSize);
243} 243}
244 244
245// Insecure Memory.
246KResourceLimit* KSystemControl::GetInsecureMemoryResourceLimit(KernelCore& kernel) {
247 return kernel.GetSystemResourceLimit();
248}
249
250u32 KSystemControl::GetInsecureMemoryPool() {
251 return static_cast<u32>(KMemoryManager::Pool::SystemNonSecure);
252}
253
245} // namespace Kernel::Board::Nintendo::Nx 254} // namespace Kernel::Board::Nintendo::Nx
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h
index ff1feec70..60c5e58b7 100644
--- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h
+++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h
@@ -8,7 +8,8 @@
8 8
9namespace Kernel { 9namespace Kernel {
10class KernelCore; 10class KernelCore;
11} 11class KResourceLimit;
12} // namespace Kernel
12 13
13namespace Kernel::Board::Nintendo::Nx { 14namespace Kernel::Board::Nintendo::Nx {
14 15
@@ -40,6 +41,10 @@ public:
40 u32 pool); 41 u32 pool);
41 static void FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size, 42 static void FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size,
42 u32 pool); 43 u32 pool);
44
45 // Insecure Memory.
46 static KResourceLimit* GetInsecureMemoryResourceLimit(KernelCore& kernel);
47 static u32 GetInsecureMemoryPool();
43}; 48};
44 49
45} // namespace Kernel::Board::Nintendo::Nx 50} // namespace Kernel::Board::Nintendo::Nx
diff --git a/src/core/hle/kernel/k_capabilities.cpp b/src/core/hle/kernel/k_capabilities.cpp
index e7da7a21d..274fee493 100644
--- a/src/core/hle/kernel/k_capabilities.cpp
+++ b/src/core/hle/kernel/k_capabilities.cpp
@@ -4,14 +4,16 @@
4#include "core/hardware_properties.h" 4#include "core/hardware_properties.h"
5#include "core/hle/kernel/k_capabilities.h" 5#include "core/hle/kernel/k_capabilities.h"
6#include "core/hle/kernel/k_memory_layout.h" 6#include "core/hle/kernel/k_memory_layout.h"
7#include "core/hle/kernel/k_page_table.h" 7#include "core/hle/kernel/k_process_page_table.h"
8#include "core/hle/kernel/k_trace.h"
8#include "core/hle/kernel/kernel.h" 9#include "core/hle/kernel/kernel.h"
9#include "core/hle/kernel/svc_results.h" 10#include "core/hle/kernel/svc_results.h"
10#include "core/hle/kernel/svc_version.h" 11#include "core/hle/kernel/svc_version.h"
11 12
12namespace Kernel { 13namespace Kernel {
13 14
14Result KCapabilities::InitializeForKip(std::span<const u32> kern_caps, KPageTable* page_table) { 15Result KCapabilities::InitializeForKip(std::span<const u32> kern_caps,
16 KProcessPageTable* page_table) {
15 // We're initializing an initial process. 17 // We're initializing an initial process.
16 m_svc_access_flags.reset(); 18 m_svc_access_flags.reset();
17 m_irq_access_flags.reset(); 19 m_irq_access_flags.reset();
@@ -41,7 +43,8 @@ Result KCapabilities::InitializeForKip(std::span<const u32> kern_caps, KPageTabl
41 R_RETURN(this->SetCapabilities(kern_caps, page_table)); 43 R_RETURN(this->SetCapabilities(kern_caps, page_table));
42} 44}
43 45
44Result KCapabilities::InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table) { 46Result KCapabilities::InitializeForUser(std::span<const u32> user_caps,
47 KProcessPageTable* page_table) {
45 // We're initializing a user process. 48 // We're initializing a user process.
46 m_svc_access_flags.reset(); 49 m_svc_access_flags.reset();
47 m_irq_access_flags.reset(); 50 m_irq_access_flags.reset();
@@ -121,7 +124,7 @@ Result KCapabilities::SetSyscallMaskCapability(const u32 cap, u32& set_svc) {
121 R_SUCCEED(); 124 R_SUCCEED();
122} 125}
123 126
124Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table) { 127Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KProcessPageTable* page_table) {
125 const auto range_pack = MapRange{cap}; 128 const auto range_pack = MapRange{cap};
126 const auto size_pack = MapRangeSize{size_cap}; 129 const auto size_pack = MapRangeSize{size_cap};
127 130
@@ -142,16 +145,13 @@ Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KPageTable* p
142 ? KMemoryPermission::UserRead 145 ? KMemoryPermission::UserRead
143 : KMemoryPermission::UserReadWrite; 146 : KMemoryPermission::UserReadWrite;
144 if (MapRangeSize{size_cap}.normal) { 147 if (MapRangeSize{size_cap}.normal) {
145 // R_RETURN(page_table->MapStatic(phys_addr, size, perm)); 148 R_RETURN(page_table->MapStatic(phys_addr, size, perm));
146 } else { 149 } else {
147 // R_RETURN(page_table->MapIo(phys_addr, size, perm)); 150 R_RETURN(page_table->MapIo(phys_addr, size, perm));
148 } 151 }
149
150 UNIMPLEMENTED();
151 R_SUCCEED();
152} 152}
153 153
154Result KCapabilities::MapIoPage_(const u32 cap, KPageTable* page_table) { 154Result KCapabilities::MapIoPage_(const u32 cap, KProcessPageTable* page_table) {
155 // Get/validate address/size 155 // Get/validate address/size
156 const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize; 156 const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize;
157 const size_t num_pages = 1; 157 const size_t num_pages = 1;
@@ -160,10 +160,7 @@ Result KCapabilities::MapIoPage_(const u32 cap, KPageTable* page_table) {
160 R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress); 160 R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress);
161 161
162 // Do the mapping. 162 // Do the mapping.
163 // R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission_UserReadWrite)); 163 R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission::UserReadWrite));
164
165 UNIMPLEMENTED();
166 R_SUCCEED();
167} 164}
168 165
169template <typename F> 166template <typename F>
@@ -200,13 +197,11 @@ Result KCapabilities::ProcessMapRegionCapability(const u32 cap, F f) {
200 R_SUCCEED(); 197 R_SUCCEED();
201} 198}
202 199
203Result KCapabilities::MapRegion_(const u32 cap, KPageTable* page_table) { 200Result KCapabilities::MapRegion_(const u32 cap, KProcessPageTable* page_table) {
204 // Map each region into the process's page table. 201 // Map each region into the process's page table.
205 return ProcessMapRegionCapability( 202 return ProcessMapRegionCapability(
206 cap, [](KMemoryRegionType region_type, KMemoryPermission perm) -> Result { 203 cap, [page_table](KMemoryRegionType region_type, KMemoryPermission perm) -> Result {
207 // R_RETURN(page_table->MapRegion(region_type, perm)); 204 R_RETURN(page_table->MapRegion(region_type, perm));
208 UNIMPLEMENTED();
209 R_SUCCEED();
210 }); 205 });
211} 206}
212 207
@@ -280,7 +275,7 @@ Result KCapabilities::SetDebugFlagsCapability(const u32 cap) {
280} 275}
281 276
282Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc, 277Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
283 KPageTable* page_table) { 278 KProcessPageTable* page_table) {
284 // Validate this is a capability we can act on. 279 // Validate this is a capability we can act on.
285 const auto type = GetCapabilityType(cap); 280 const auto type = GetCapabilityType(cap);
286 R_UNLESS(type != CapabilityType::Invalid, ResultInvalidArgument); 281 R_UNLESS(type != CapabilityType::Invalid, ResultInvalidArgument);
@@ -318,7 +313,7 @@ Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
318 } 313 }
319} 314}
320 315
321Result KCapabilities::SetCapabilities(std::span<const u32> caps, KPageTable* page_table) { 316Result KCapabilities::SetCapabilities(std::span<const u32> caps, KProcessPageTable* page_table) {
322 u32 set_flags = 0, set_svc = 0; 317 u32 set_flags = 0, set_svc = 0;
323 318
324 for (size_t i = 0; i < caps.size(); i++) { 319 for (size_t i = 0; i < caps.size(); i++) {
@@ -335,6 +330,8 @@ Result KCapabilities::SetCapabilities(std::span<const u32> caps, KPageTable* pag
335 330
336 // Map the range. 331 // Map the range.
337 R_TRY(this->MapRange_(cap, size_cap, page_table)); 332 R_TRY(this->MapRange_(cap, size_cap, page_table));
333 } else if (GetCapabilityType(cap) == CapabilityType::MapRegion && !IsKTraceEnabled) {
334 continue;
338 } else { 335 } else {
339 R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table)); 336 R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table));
340 } 337 }
diff --git a/src/core/hle/kernel/k_capabilities.h b/src/core/hle/kernel/k_capabilities.h
index ebd4eedb1..013d952ad 100644
--- a/src/core/hle/kernel/k_capabilities.h
+++ b/src/core/hle/kernel/k_capabilities.h
@@ -15,15 +15,15 @@
15 15
16namespace Kernel { 16namespace Kernel {
17 17
18class KPageTable; 18class KProcessPageTable;
19class KernelCore; 19class KernelCore;
20 20
21class KCapabilities { 21class KCapabilities {
22public: 22public:
23 constexpr explicit KCapabilities() = default; 23 constexpr explicit KCapabilities() = default;
24 24
25 Result InitializeForKip(std::span<const u32> kern_caps, KPageTable* page_table); 25 Result InitializeForKip(std::span<const u32> kern_caps, KProcessPageTable* page_table);
26 Result InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table); 26 Result InitializeForUser(std::span<const u32> user_caps, KProcessPageTable* page_table);
27 27
28 static Result CheckCapabilities(KernelCore& kernel, std::span<const u32> user_caps); 28 static Result CheckCapabilities(KernelCore& kernel, std::span<const u32> user_caps);
29 29
@@ -264,9 +264,9 @@ private:
264 264
265 Result SetCorePriorityCapability(const u32 cap); 265 Result SetCorePriorityCapability(const u32 cap);
266 Result SetSyscallMaskCapability(const u32 cap, u32& set_svc); 266 Result SetSyscallMaskCapability(const u32 cap, u32& set_svc);
267 Result MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table); 267 Result MapRange_(const u32 cap, const u32 size_cap, KProcessPageTable* page_table);
268 Result MapIoPage_(const u32 cap, KPageTable* page_table); 268 Result MapIoPage_(const u32 cap, KProcessPageTable* page_table);
269 Result MapRegion_(const u32 cap, KPageTable* page_table); 269 Result MapRegion_(const u32 cap, KProcessPageTable* page_table);
270 Result SetInterruptPairCapability(const u32 cap); 270 Result SetInterruptPairCapability(const u32 cap);
271 Result SetProgramTypeCapability(const u32 cap); 271 Result SetProgramTypeCapability(const u32 cap);
272 Result SetKernelVersionCapability(const u32 cap); 272 Result SetKernelVersionCapability(const u32 cap);
@@ -277,8 +277,9 @@ private:
277 static Result ProcessMapRegionCapability(const u32 cap, F f); 277 static Result ProcessMapRegionCapability(const u32 cap, F f);
278 static Result CheckMapRegion(KernelCore& kernel, const u32 cap); 278 static Result CheckMapRegion(KernelCore& kernel, const u32 cap);
279 279
280 Result SetCapability(const u32 cap, u32& set_flags, u32& set_svc, KPageTable* page_table); 280 Result SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
281 Result SetCapabilities(std::span<const u32> caps, KPageTable* page_table); 281 KProcessPageTable* page_table);
282 Result SetCapabilities(std::span<const u32> caps, KProcessPageTable* page_table);
282 283
283private: 284private:
284 Svc::SvcAccessFlagSet m_svc_access_flags{}; 285 Svc::SvcAccessFlagSet m_svc_access_flags{};
diff --git a/src/core/hle/kernel/k_device_address_space.cpp b/src/core/hle/kernel/k_device_address_space.cpp
index f48896715..f0703f795 100644
--- a/src/core/hle/kernel/k_device_address_space.cpp
+++ b/src/core/hle/kernel/k_device_address_space.cpp
@@ -54,7 +54,7 @@ Result KDeviceAddressSpace::Detach(Svc::DeviceName device_name) {
54 R_SUCCEED(); 54 R_SUCCEED();
55} 55}
56 56
57Result KDeviceAddressSpace::Map(KPageTable* page_table, KProcessAddress process_address, 57Result KDeviceAddressSpace::Map(KProcessPageTable* page_table, KProcessAddress process_address,
58 size_t size, u64 device_address, u32 option, bool is_aligned) { 58 size_t size, u64 device_address, u32 option, bool is_aligned) {
59 // Check that the address falls within the space. 59 // Check that the address falls within the space.
60 R_UNLESS((m_space_address <= device_address && 60 R_UNLESS((m_space_address <= device_address &&
@@ -113,7 +113,7 @@ Result KDeviceAddressSpace::Map(KPageTable* page_table, KProcessAddress process_
113 R_SUCCEED(); 113 R_SUCCEED();
114} 114}
115 115
116Result KDeviceAddressSpace::Unmap(KPageTable* page_table, KProcessAddress process_address, 116Result KDeviceAddressSpace::Unmap(KProcessPageTable* page_table, KProcessAddress process_address,
117 size_t size, u64 device_address) { 117 size_t size, u64 device_address) {
118 // Check that the address falls within the space. 118 // Check that the address falls within the space.
119 R_UNLESS((m_space_address <= device_address && 119 R_UNLESS((m_space_address <= device_address &&
diff --git a/src/core/hle/kernel/k_device_address_space.h b/src/core/hle/kernel/k_device_address_space.h
index 18556e3cc..ff0ec8152 100644
--- a/src/core/hle/kernel/k_device_address_space.h
+++ b/src/core/hle/kernel/k_device_address_space.h
@@ -5,7 +5,7 @@
5 5
6#include <string> 6#include <string>
7 7
8#include "core/hle/kernel/k_page_table.h" 8#include "core/hle/kernel/k_process_page_table.h"
9#include "core/hle/kernel/k_typed_address.h" 9#include "core/hle/kernel/k_typed_address.h"
10#include "core/hle/kernel/slab_helpers.h" 10#include "core/hle/kernel/slab_helpers.h"
11#include "core/hle/result.h" 11#include "core/hle/result.h"
@@ -31,23 +31,23 @@ public:
31 Result Attach(Svc::DeviceName device_name); 31 Result Attach(Svc::DeviceName device_name);
32 Result Detach(Svc::DeviceName device_name); 32 Result Detach(Svc::DeviceName device_name);
33 33
34 Result MapByForce(KPageTable* page_table, KProcessAddress process_address, size_t size, 34 Result MapByForce(KProcessPageTable* page_table, KProcessAddress process_address, size_t size,
35 u64 device_address, u32 option) { 35 u64 device_address, u32 option) {
36 R_RETURN(this->Map(page_table, process_address, size, device_address, option, false)); 36 R_RETURN(this->Map(page_table, process_address, size, device_address, option, false));
37 } 37 }
38 38
39 Result MapAligned(KPageTable* page_table, KProcessAddress process_address, size_t size, 39 Result MapAligned(KProcessPageTable* page_table, KProcessAddress process_address, size_t size,
40 u64 device_address, u32 option) { 40 u64 device_address, u32 option) {
41 R_RETURN(this->Map(page_table, process_address, size, device_address, option, true)); 41 R_RETURN(this->Map(page_table, process_address, size, device_address, option, true));
42 } 42 }
43 43
44 Result Unmap(KPageTable* page_table, KProcessAddress process_address, size_t size, 44 Result Unmap(KProcessPageTable* page_table, KProcessAddress process_address, size_t size,
45 u64 device_address); 45 u64 device_address);
46 46
47 static void Initialize(); 47 static void Initialize();
48 48
49private: 49private:
50 Result Map(KPageTable* page_table, KProcessAddress process_address, size_t size, 50 Result Map(KProcessPageTable* page_table, KProcessAddress process_address, size_t size,
51 u64 device_address, u32 option, bool is_aligned); 51 u64 device_address, u32 option, bool is_aligned);
52 52
53private: 53private:
diff --git a/src/core/hle/kernel/k_memory_layout.h b/src/core/hle/kernel/k_memory_layout.h
index c8122644f..d7adb3169 100644
--- a/src/core/hle/kernel/k_memory_layout.h
+++ b/src/core/hle/kernel/k_memory_layout.h
@@ -394,6 +394,14 @@ private:
394 return region.GetEndAddress(); 394 return region.GetEndAddress();
395 } 395 }
396 396
397public:
398 static const KMemoryRegion* Find(const KMemoryLayout& layout, KVirtualAddress address) {
399 return Find(address, layout.GetVirtualMemoryRegionTree());
400 }
401 static const KMemoryRegion* Find(const KMemoryLayout& layout, KPhysicalAddress address) {
402 return Find(address, layout.GetPhysicalMemoryRegionTree());
403 }
404
397private: 405private:
398 u64 m_linear_phys_to_virt_diff{}; 406 u64 m_linear_phys_to_virt_diff{};
399 u64 m_linear_virt_to_phys_diff{}; 407 u64 m_linear_virt_to_phys_diff{};
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp
index cdc5572d8..0a973ec8c 100644
--- a/src/core/hle/kernel/k_memory_manager.cpp
+++ b/src/core/hle/kernel/k_memory_manager.cpp
@@ -456,8 +456,7 @@ size_t KMemoryManager::Impl::Initialize(KPhysicalAddress address, size_t size,
456} 456}
457 457
458void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) { 458void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) {
459 auto optimize_pa = 459 auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
460 KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
461 auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa); 460 auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
462 461
463 std::memset(optimize_map, 0, CalculateOptimizedProcessOverheadSize(m_heap.GetSize())); 462 std::memset(optimize_map, 0, CalculateOptimizedProcessOverheadSize(m_heap.GetSize()));
@@ -465,8 +464,7 @@ void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) {
465 464
466void KMemoryManager::Impl::TrackUnoptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, 465void KMemoryManager::Impl::TrackUnoptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
467 size_t num_pages) { 466 size_t num_pages) {
468 auto optimize_pa = 467 auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
469 KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
470 auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa); 468 auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
471 469
472 // Get the range we're tracking. 470 // Get the range we're tracking.
@@ -485,8 +483,7 @@ void KMemoryManager::Impl::TrackUnoptimizedAllocation(KernelCore& kernel, KPhysi
485 483
486void KMemoryManager::Impl::TrackOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, 484void KMemoryManager::Impl::TrackOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
487 size_t num_pages) { 485 size_t num_pages) {
488 auto optimize_pa = 486 auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
489 KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
490 auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa); 487 auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
491 488
492 // Get the range we're tracking. 489 // Get the range we're tracking.
@@ -506,8 +503,7 @@ void KMemoryManager::Impl::TrackOptimizedAllocation(KernelCore& kernel, KPhysica
506bool KMemoryManager::Impl::ProcessOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, 503bool KMemoryManager::Impl::ProcessOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
507 size_t num_pages, u8 fill_pattern) { 504 size_t num_pages, u8 fill_pattern) {
508 auto& device_memory = kernel.System().DeviceMemory(); 505 auto& device_memory = kernel.System().DeviceMemory();
509 auto optimize_pa = 506 auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
510 KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
511 auto* optimize_map = device_memory.GetPointer<u64>(optimize_pa); 507 auto* optimize_map = device_memory.GetPointer<u64>(optimize_pa);
512 508
513 // We want to return whether any pages were newly allocated. 509 // We want to return whether any pages were newly allocated.
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
deleted file mode 100644
index 1d47bdf6b..000000000
--- a/src/core/hle/kernel/k_page_table.cpp
+++ /dev/null
@@ -1,3519 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/alignment.h"
5#include "common/assert.h"
6#include "common/literals.h"
7#include "common/scope_exit.h"
8#include "common/settings.h"
9#include "core/core.h"
10#include "core/hle/kernel/k_address_space_info.h"
11#include "core/hle/kernel/k_memory_block.h"
12#include "core/hle/kernel/k_memory_block_manager.h"
13#include "core/hle/kernel/k_page_group.h"
14#include "core/hle/kernel/k_page_table.h"
15#include "core/hle/kernel/k_process.h"
16#include "core/hle/kernel/k_resource_limit.h"
17#include "core/hle/kernel/k_scoped_resource_reservation.h"
18#include "core/hle/kernel/k_system_control.h"
19#include "core/hle/kernel/k_system_resource.h"
20#include "core/hle/kernel/kernel.h"
21#include "core/hle/kernel/svc_results.h"
22#include "core/memory.h"
23
24namespace Kernel {
25
26namespace {
27
28class KScopedLightLockPair {
29 YUZU_NON_COPYABLE(KScopedLightLockPair);
30 YUZU_NON_MOVEABLE(KScopedLightLockPair);
31
32private:
33 KLightLock* m_lower;
34 KLightLock* m_upper;
35
36public:
37 KScopedLightLockPair(KLightLock& lhs, KLightLock& rhs) {
38 // Ensure our locks are in a consistent order.
39 if (std::addressof(lhs) <= std::addressof(rhs)) {
40 m_lower = std::addressof(lhs);
41 m_upper = std::addressof(rhs);
42 } else {
43 m_lower = std::addressof(rhs);
44 m_upper = std::addressof(lhs);
45 }
46
47 // Acquire both locks.
48 m_lower->Lock();
49 if (m_lower != m_upper) {
50 m_upper->Lock();
51 }
52 }
53
54 ~KScopedLightLockPair() {
55 // Unlock the upper lock.
56 if (m_upper != nullptr && m_upper != m_lower) {
57 m_upper->Unlock();
58 }
59
60 // Unlock the lower lock.
61 if (m_lower != nullptr) {
62 m_lower->Unlock();
63 }
64 }
65
66public:
67 // Utility.
68 void TryUnlockHalf(KLightLock& lock) {
69 // Only allow unlocking if the lock is half the pair.
70 if (m_lower != m_upper) {
71 // We want to be sure the lock is one we own.
72 if (m_lower == std::addressof(lock)) {
73 lock.Unlock();
74 m_lower = nullptr;
75 } else if (m_upper == std::addressof(lock)) {
76 lock.Unlock();
77 m_upper = nullptr;
78 }
79 }
80 }
81};
82
83using namespace Common::Literals;
84
85constexpr size_t GetAddressSpaceWidthFromType(Svc::CreateProcessFlag as_type) {
86 switch (as_type) {
87 case Svc::CreateProcessFlag::AddressSpace32Bit:
88 case Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias:
89 return 32;
90 case Svc::CreateProcessFlag::AddressSpace64BitDeprecated:
91 return 36;
92 case Svc::CreateProcessFlag::AddressSpace64Bit:
93 return 39;
94 default:
95 ASSERT(false);
96 return {};
97 }
98}
99
100} // namespace
101
102KPageTable::KPageTable(Core::System& system_)
103 : m_general_lock{system_.Kernel()},
104 m_map_physical_memory_lock{system_.Kernel()}, m_system{system_}, m_kernel{system_.Kernel()} {}
105
106KPageTable::~KPageTable() = default;
107
108Result KPageTable::InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr,
109 bool enable_das_merge, bool from_back,
110 KMemoryManager::Pool pool, KProcessAddress code_addr,
111 size_t code_size, KSystemResource* system_resource,
112 KResourceLimit* resource_limit,
113 Core::Memory::Memory& memory) {
114
115 const auto GetSpaceStart = [this](KAddressSpaceInfo::Type type) {
116 return KAddressSpaceInfo::GetAddressSpaceStart(m_address_space_width, type);
117 };
118 const auto GetSpaceSize = [this](KAddressSpaceInfo::Type type) {
119 return KAddressSpaceInfo::GetAddressSpaceSize(m_address_space_width, type);
120 };
121
122 // Set the tracking memory
123 m_memory = std::addressof(memory);
124
125 // Set our width and heap/alias sizes
126 m_address_space_width = GetAddressSpaceWidthFromType(as_type);
127 const KProcessAddress start = 0;
128 const KProcessAddress end{1ULL << m_address_space_width};
129 size_t alias_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Alias)};
130 size_t heap_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Heap)};
131
132 ASSERT(code_addr < code_addr + code_size);
133 ASSERT(code_addr + code_size - 1 <= end - 1);
134
135 // Adjust heap/alias size if we don't have an alias region
136 if (as_type == Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias) {
137 heap_region_size += alias_region_size;
138 alias_region_size = 0;
139 }
140
141 // Set code regions and determine remaining
142 constexpr size_t RegionAlignment{2_MiB};
143 KProcessAddress process_code_start{};
144 KProcessAddress process_code_end{};
145 size_t stack_region_size{};
146 size_t kernel_map_region_size{};
147
148 if (m_address_space_width == 39) {
149 alias_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Alias);
150 heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Heap);
151 stack_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Stack);
152 kernel_map_region_size = GetSpaceSize(KAddressSpaceInfo::Type::MapSmall);
153 m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::Map39Bit);
154 m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::Map39Bit);
155 m_alias_code_region_start = m_code_region_start;
156 m_alias_code_region_end = m_code_region_end;
157 process_code_start = Common::AlignDown(GetInteger(code_addr), RegionAlignment);
158 process_code_end = Common::AlignUp(GetInteger(code_addr) + code_size, RegionAlignment);
159 } else {
160 stack_region_size = 0;
161 kernel_map_region_size = 0;
162 m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::MapSmall);
163 m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::MapSmall);
164 m_stack_region_start = m_code_region_start;
165 m_alias_code_region_start = m_code_region_start;
166 m_alias_code_region_end = GetSpaceStart(KAddressSpaceInfo::Type::MapLarge) +
167 GetSpaceSize(KAddressSpaceInfo::Type::MapLarge);
168 m_stack_region_end = m_code_region_end;
169 m_kernel_map_region_start = m_code_region_start;
170 m_kernel_map_region_end = m_code_region_end;
171 process_code_start = m_code_region_start;
172 process_code_end = m_code_region_end;
173 }
174
175 // Set other basic fields
176 m_enable_aslr = enable_aslr;
177 m_enable_device_address_space_merge = enable_das_merge;
178 m_address_space_start = start;
179 m_address_space_end = end;
180 m_is_kernel = false;
181 m_memory_block_slab_manager = system_resource->GetMemoryBlockSlabManagerPointer();
182 m_block_info_manager = system_resource->GetBlockInfoManagerPointer();
183 m_resource_limit = resource_limit;
184
185 // Determine the region we can place our undetermineds in
186 KProcessAddress alloc_start{};
187 size_t alloc_size{};
188 if ((process_code_start - m_code_region_start) >= (end - process_code_end)) {
189 alloc_start = m_code_region_start;
190 alloc_size = process_code_start - m_code_region_start;
191 } else {
192 alloc_start = process_code_end;
193 alloc_size = end - process_code_end;
194 }
195 const size_t needed_size =
196 (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size);
197 R_UNLESS(alloc_size >= needed_size, ResultOutOfMemory);
198
199 const size_t remaining_size{alloc_size - needed_size};
200
201 // Determine random placements for each region
202 size_t alias_rnd{}, heap_rnd{}, stack_rnd{}, kmap_rnd{};
203 if (enable_aslr) {
204 alias_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
205 RegionAlignment;
206 heap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
207 RegionAlignment;
208 stack_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
209 RegionAlignment;
210 kmap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
211 RegionAlignment;
212 }
213
214 // Setup heap and alias regions
215 m_alias_region_start = alloc_start + alias_rnd;
216 m_alias_region_end = m_alias_region_start + alias_region_size;
217 m_heap_region_start = alloc_start + heap_rnd;
218 m_heap_region_end = m_heap_region_start + heap_region_size;
219
220 if (alias_rnd <= heap_rnd) {
221 m_heap_region_start += alias_region_size;
222 m_heap_region_end += alias_region_size;
223 } else {
224 m_alias_region_start += heap_region_size;
225 m_alias_region_end += heap_region_size;
226 }
227
228 // Setup stack region
229 if (stack_region_size) {
230 m_stack_region_start = alloc_start + stack_rnd;
231 m_stack_region_end = m_stack_region_start + stack_region_size;
232
233 if (alias_rnd < stack_rnd) {
234 m_stack_region_start += alias_region_size;
235 m_stack_region_end += alias_region_size;
236 } else {
237 m_alias_region_start += stack_region_size;
238 m_alias_region_end += stack_region_size;
239 }
240
241 if (heap_rnd < stack_rnd) {
242 m_stack_region_start += heap_region_size;
243 m_stack_region_end += heap_region_size;
244 } else {
245 m_heap_region_start += stack_region_size;
246 m_heap_region_end += stack_region_size;
247 }
248 }
249
250 // Setup kernel map region
251 if (kernel_map_region_size) {
252 m_kernel_map_region_start = alloc_start + kmap_rnd;
253 m_kernel_map_region_end = m_kernel_map_region_start + kernel_map_region_size;
254
255 if (alias_rnd < kmap_rnd) {
256 m_kernel_map_region_start += alias_region_size;
257 m_kernel_map_region_end += alias_region_size;
258 } else {
259 m_alias_region_start += kernel_map_region_size;
260 m_alias_region_end += kernel_map_region_size;
261 }
262
263 if (heap_rnd < kmap_rnd) {
264 m_kernel_map_region_start += heap_region_size;
265 m_kernel_map_region_end += heap_region_size;
266 } else {
267 m_heap_region_start += kernel_map_region_size;
268 m_heap_region_end += kernel_map_region_size;
269 }
270
271 if (stack_region_size) {
272 if (stack_rnd < kmap_rnd) {
273 m_kernel_map_region_start += stack_region_size;
274 m_kernel_map_region_end += stack_region_size;
275 } else {
276 m_stack_region_start += kernel_map_region_size;
277 m_stack_region_end += kernel_map_region_size;
278 }
279 }
280 }
281
282 // Set heap and fill members.
283 m_current_heap_end = m_heap_region_start;
284 m_max_heap_size = 0;
285 m_mapped_physical_memory_size = 0;
286 m_mapped_unsafe_physical_memory = 0;
287 m_mapped_insecure_memory = 0;
288 m_mapped_ipc_server_memory = 0;
289
290 m_heap_fill_value = 0;
291 m_ipc_fill_value = 0;
292 m_stack_fill_value = 0;
293
294 // Set allocation option.
295 m_allocate_option =
296 KMemoryManager::EncodeOption(pool, from_back ? KMemoryManager::Direction::FromBack
297 : KMemoryManager::Direction::FromFront);
298
299 // Ensure that we regions inside our address space
300 auto IsInAddressSpace = [&](KProcessAddress addr) {
301 return m_address_space_start <= addr && addr <= m_address_space_end;
302 };
303 ASSERT(IsInAddressSpace(m_alias_region_start));
304 ASSERT(IsInAddressSpace(m_alias_region_end));
305 ASSERT(IsInAddressSpace(m_heap_region_start));
306 ASSERT(IsInAddressSpace(m_heap_region_end));
307 ASSERT(IsInAddressSpace(m_stack_region_start));
308 ASSERT(IsInAddressSpace(m_stack_region_end));
309 ASSERT(IsInAddressSpace(m_kernel_map_region_start));
310 ASSERT(IsInAddressSpace(m_kernel_map_region_end));
311
312 // Ensure that we selected regions that don't overlap
313 const KProcessAddress alias_start{m_alias_region_start};
314 const KProcessAddress alias_last{m_alias_region_end - 1};
315 const KProcessAddress heap_start{m_heap_region_start};
316 const KProcessAddress heap_last{m_heap_region_end - 1};
317 const KProcessAddress stack_start{m_stack_region_start};
318 const KProcessAddress stack_last{m_stack_region_end - 1};
319 const KProcessAddress kmap_start{m_kernel_map_region_start};
320 const KProcessAddress kmap_last{m_kernel_map_region_end - 1};
321 ASSERT(alias_last < heap_start || heap_last < alias_start);
322 ASSERT(alias_last < stack_start || stack_last < alias_start);
323 ASSERT(alias_last < kmap_start || kmap_last < alias_start);
324 ASSERT(heap_last < stack_start || stack_last < heap_start);
325 ASSERT(heap_last < kmap_start || kmap_last < heap_start);
326
327 m_current_heap_end = m_heap_region_start;
328 m_max_heap_size = 0;
329 m_mapped_physical_memory_size = 0;
330 m_memory_pool = pool;
331
332 m_page_table_impl = std::make_unique<Common::PageTable>();
333 m_page_table_impl->Resize(m_address_space_width, PageBits);
334
335 // Initialize our memory block manager.
336 R_RETURN(m_memory_block_manager.Initialize(m_address_space_start, m_address_space_end,
337 m_memory_block_slab_manager));
338}
339
340void KPageTable::Finalize() {
341 auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) {
342 if (Settings::IsFastmemEnabled()) {
343 m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size);
344 }
345 };
346
347 // Finalize memory blocks.
348 m_memory_block_manager.Finalize(m_memory_block_slab_manager, std::move(HostUnmapCallback));
349
350 // Release any insecure mapped memory.
351 if (m_mapped_insecure_memory) {
352 UNIMPLEMENTED();
353 }
354
355 // Release any ipc server memory.
356 if (m_mapped_ipc_server_memory) {
357 UNIMPLEMENTED();
358 }
359
360 // Close the backing page table, as the destructor is not called for guest objects.
361 m_page_table_impl.reset();
362}
363
364Result KPageTable::MapProcessCode(KProcessAddress addr, size_t num_pages, KMemoryState state,
365 KMemoryPermission perm) {
366 const u64 size{num_pages * PageSize};
367
368 // Validate the mapping request.
369 R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory);
370
371 // Lock the table.
372 KScopedLightLock lk(m_general_lock);
373
374 // Verify that the destination memory is unmapped.
375 R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free,
376 KMemoryPermission::None, KMemoryPermission::None,
377 KMemoryAttribute::None, KMemoryAttribute::None));
378
379 // Create an update allocator.
380 Result allocator_result{ResultSuccess};
381 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
382 m_memory_block_slab_manager);
383
384 // Allocate and open.
385 KPageGroup pg{m_kernel, m_block_info_manager};
386 R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen(
387 &pg, num_pages,
388 KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, m_allocation_option)));
389
390 R_TRY(Operate(addr, num_pages, pg, OperationType::MapGroup));
391
392 // Update the blocks.
393 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
394 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
395 KMemoryBlockDisableMergeAttribute::None);
396
397 R_SUCCEED();
398}
399
400Result KPageTable::MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address,
401 size_t size) {
402 // Validate the mapping request.
403 R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
404 ResultInvalidMemoryRegion);
405
406 // Lock the table.
407 KScopedLightLock lk(m_general_lock);
408
409 // Verify that the source memory is normal heap.
410 KMemoryState src_state{};
411 KMemoryPermission src_perm{};
412 size_t num_src_allocator_blocks{};
413 R_TRY(this->CheckMemoryState(&src_state, &src_perm, nullptr, &num_src_allocator_blocks,
414 src_address, size, KMemoryState::All, KMemoryState::Normal,
415 KMemoryPermission::All, KMemoryPermission::UserReadWrite,
416 KMemoryAttribute::All, KMemoryAttribute::None));
417
418 // Verify that the destination memory is unmapped.
419 size_t num_dst_allocator_blocks{};
420 R_TRY(this->CheckMemoryState(&num_dst_allocator_blocks, dst_address, size, KMemoryState::All,
421 KMemoryState::Free, KMemoryPermission::None,
422 KMemoryPermission::None, KMemoryAttribute::None,
423 KMemoryAttribute::None));
424
425 // Create an update allocator for the source.
426 Result src_allocator_result{ResultSuccess};
427 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
428 m_memory_block_slab_manager,
429 num_src_allocator_blocks);
430 R_TRY(src_allocator_result);
431
432 // Create an update allocator for the destination.
433 Result dst_allocator_result{ResultSuccess};
434 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
435 m_memory_block_slab_manager,
436 num_dst_allocator_blocks);
437 R_TRY(dst_allocator_result);
438
439 // Map the code memory.
440 {
441 // Determine the number of pages being operated on.
442 const size_t num_pages = size / PageSize;
443
444 // Create page groups for the memory being mapped.
445 KPageGroup pg{m_kernel, m_block_info_manager};
446 AddRegionToPages(src_address, num_pages, pg);
447
448 // We're going to perform an update, so create a helper.
449 KScopedPageTableUpdater updater(this);
450
451 // Reprotect the source as kernel-read/not mapped.
452 const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead |
453 KMemoryPermission::NotMapped);
454 R_TRY(Operate(src_address, num_pages, new_perm, OperationType::ChangePermissions));
455
456 // Ensure that we unprotect the source pages on failure.
457 auto unprot_guard = SCOPE_GUARD({
458 ASSERT(this->Operate(src_address, num_pages, src_perm, OperationType::ChangePermissions)
459 .IsSuccess());
460 });
461
462 // Map the alias pages.
463 const KPageProperties dst_properties = {new_perm, false, false,
464 DisableMergeAttribute::DisableHead};
465 R_TRY(
466 this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_properties, false));
467
468 // We successfully mapped the alias pages, so we don't need to unprotect the src pages on
469 // failure.
470 unprot_guard.Cancel();
471
472 // Apply the memory block updates.
473 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages,
474 src_state, new_perm, KMemoryAttribute::Locked,
475 KMemoryBlockDisableMergeAttribute::Locked,
476 KMemoryBlockDisableMergeAttribute::None);
477 m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages,
478 KMemoryState::AliasCode, new_perm, KMemoryAttribute::None,
479 KMemoryBlockDisableMergeAttribute::Normal,
480 KMemoryBlockDisableMergeAttribute::None);
481 }
482
483 R_SUCCEED();
484}
485
486Result KPageTable::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address,
487 size_t size,
488 ICacheInvalidationStrategy icache_invalidation_strategy) {
489 // Validate the mapping request.
490 R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
491 ResultInvalidMemoryRegion);
492
493 // Lock the table.
494 KScopedLightLock lk(m_general_lock);
495
496 // Verify that the source memory is locked normal heap.
497 size_t num_src_allocator_blocks{};
498 R_TRY(this->CheckMemoryState(std::addressof(num_src_allocator_blocks), src_address, size,
499 KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None,
500 KMemoryPermission::None, KMemoryAttribute::All,
501 KMemoryAttribute::Locked));
502
503 // Verify that the destination memory is aliasable code.
504 size_t num_dst_allocator_blocks{};
505 R_TRY(this->CheckMemoryStateContiguous(
506 std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias,
507 KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
508 KMemoryAttribute::All & ~KMemoryAttribute::PermissionLocked, KMemoryAttribute::None));
509
510 // Determine whether any pages being unmapped are code.
511 bool any_code_pages = false;
512 {
513 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(dst_address);
514 while (true) {
515 // Get the memory info.
516 const KMemoryInfo info = it->GetMemoryInfo();
517
518 // Check if the memory has code flag.
519 if ((info.GetState() & KMemoryState::FlagCode) != KMemoryState::None) {
520 any_code_pages = true;
521 break;
522 }
523
524 // Check if we're done.
525 if (dst_address + size - 1 <= info.GetLastAddress()) {
526 break;
527 }
528
529 // Advance.
530 ++it;
531 }
532 }
533
534 // Ensure that we maintain the instruction cache.
535 bool reprotected_pages = false;
536 SCOPE_EXIT({
537 if (reprotected_pages && any_code_pages) {
538 if (icache_invalidation_strategy == ICacheInvalidationStrategy::InvalidateRange) {
539 m_system.InvalidateCpuInstructionCacheRange(GetInteger(dst_address), size);
540 } else {
541 m_system.InvalidateCpuInstructionCaches();
542 }
543 }
544 });
545
546 // Unmap.
547 {
548 // Determine the number of pages being operated on.
549 const size_t num_pages = size / PageSize;
550
551 // Create an update allocator for the source.
552 Result src_allocator_result{ResultSuccess};
553 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
554 m_memory_block_slab_manager,
555 num_src_allocator_blocks);
556 R_TRY(src_allocator_result);
557
558 // Create an update allocator for the destination.
559 Result dst_allocator_result{ResultSuccess};
560 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
561 m_memory_block_slab_manager,
562 num_dst_allocator_blocks);
563 R_TRY(dst_allocator_result);
564
565 // Unmap the aliased copy of the pages.
566 R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap));
567
568 // Try to set the permissions for the source pages back to what they should be.
569 R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite,
570 OperationType::ChangePermissions));
571
572 // Apply the memory block updates.
573 m_memory_block_manager.Update(
574 std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None,
575 KMemoryPermission::None, KMemoryAttribute::None,
576 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal);
577 m_memory_block_manager.Update(
578 std::addressof(src_allocator), src_address, num_pages, KMemoryState::Normal,
579 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
580 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked);
581
582 // Note that we reprotected pages.
583 reprotected_pages = true;
584 }
585
586 R_SUCCEED();
587}
588
589KProcessAddress KPageTable::FindFreeArea(KProcessAddress region_start, size_t region_num_pages,
590 size_t num_pages, size_t alignment, size_t offset,
591 size_t guard_pages) {
592 KProcessAddress address = 0;
593
594 if (num_pages <= region_num_pages) {
595 if (this->IsAslrEnabled()) {
596 UNIMPLEMENTED();
597 }
598 // Find the first free area.
599 if (address == 0) {
600 address = m_memory_block_manager.FindFreeArea(region_start, region_num_pages, num_pages,
601 alignment, offset, guard_pages);
602 }
603 }
604
605 return address;
606}
607
608Result KPageTable::MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_t num_pages) {
609 ASSERT(this->IsLockedByCurrentThread());
610
611 const size_t size = num_pages * PageSize;
612
613 // We're making a new group, not adding to an existing one.
614 R_UNLESS(pg.empty(), ResultInvalidCurrentMemory);
615
616 // Begin traversal.
617 Common::PageTable::TraversalContext context;
618 Common::PageTable::TraversalEntry next_entry;
619 R_UNLESS(m_page_table_impl->BeginTraversal(next_entry, context, GetInteger(addr)),
620 ResultInvalidCurrentMemory);
621
622 // Prepare tracking variables.
623 KPhysicalAddress cur_addr = next_entry.phys_addr;
624 size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1));
625 size_t tot_size = cur_size;
626
627 // Iterate, adding to group as we go.
628 const auto& memory_layout = m_system.Kernel().MemoryLayout();
629 while (tot_size < size) {
630 R_UNLESS(m_page_table_impl->ContinueTraversal(next_entry, context),
631 ResultInvalidCurrentMemory);
632
633 if (next_entry.phys_addr != (cur_addr + cur_size)) {
634 const size_t cur_pages = cur_size / PageSize;
635
636 R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
637 R_TRY(pg.AddBlock(cur_addr, cur_pages));
638
639 cur_addr = next_entry.phys_addr;
640 cur_size = next_entry.block_size;
641 } else {
642 cur_size += next_entry.block_size;
643 }
644
645 tot_size += next_entry.block_size;
646 }
647
648 // Ensure we add the right amount for the last block.
649 if (tot_size > size) {
650 cur_size -= (tot_size - size);
651 }
652
653 // Add the last block.
654 const size_t cur_pages = cur_size / PageSize;
655 R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
656 R_TRY(pg.AddBlock(cur_addr, cur_pages));
657
658 R_SUCCEED();
659}
660
661bool KPageTable::IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr, size_t num_pages) {
662 ASSERT(this->IsLockedByCurrentThread());
663
664 const size_t size = num_pages * PageSize;
665 const auto& memory_layout = m_system.Kernel().MemoryLayout();
666
667 // Empty groups are necessarily invalid.
668 if (pg.empty()) {
669 return false;
670 }
671
672 // We're going to validate that the group we'd expect is the group we see.
673 auto cur_it = pg.begin();
674 KPhysicalAddress cur_block_address = cur_it->GetAddress();
675 size_t cur_block_pages = cur_it->GetNumPages();
676
677 auto UpdateCurrentIterator = [&]() {
678 if (cur_block_pages == 0) {
679 if ((++cur_it) == pg.end()) {
680 return false;
681 }
682
683 cur_block_address = cur_it->GetAddress();
684 cur_block_pages = cur_it->GetNumPages();
685 }
686 return true;
687 };
688
689 // Begin traversal.
690 Common::PageTable::TraversalContext context;
691 Common::PageTable::TraversalEntry next_entry;
692 if (!m_page_table_impl->BeginTraversal(next_entry, context, GetInteger(addr))) {
693 return false;
694 }
695
696 // Prepare tracking variables.
697 KPhysicalAddress cur_addr = next_entry.phys_addr;
698 size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1));
699 size_t tot_size = cur_size;
700
701 // Iterate, comparing expected to actual.
702 while (tot_size < size) {
703 if (!m_page_table_impl->ContinueTraversal(next_entry, context)) {
704 return false;
705 }
706
707 if (next_entry.phys_addr != (cur_addr + cur_size)) {
708 const size_t cur_pages = cur_size / PageSize;
709
710 if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) {
711 return false;
712 }
713
714 if (!UpdateCurrentIterator()) {
715 return false;
716 }
717
718 if (cur_block_address != cur_addr || cur_block_pages < cur_pages) {
719 return false;
720 }
721
722 cur_block_address += cur_size;
723 cur_block_pages -= cur_pages;
724 cur_addr = next_entry.phys_addr;
725 cur_size = next_entry.block_size;
726 } else {
727 cur_size += next_entry.block_size;
728 }
729
730 tot_size += next_entry.block_size;
731 }
732
733 // Ensure we compare the right amount for the last block.
734 if (tot_size > size) {
735 cur_size -= (tot_size - size);
736 }
737
738 if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) {
739 return false;
740 }
741
742 if (!UpdateCurrentIterator()) {
743 return false;
744 }
745
746 return cur_block_address == cur_addr && cur_block_pages == (cur_size / PageSize);
747}
748
749Result KPageTable::UnmapProcessMemory(KProcessAddress dst_addr, size_t size,
750 KPageTable& src_page_table, KProcessAddress src_addr) {
751 // Acquire the table locks.
752 KScopedLightLockPair lk(src_page_table.m_general_lock, m_general_lock);
753
754 const size_t num_pages{size / PageSize};
755
756 // Check that the memory is mapped in the destination process.
757 size_t num_allocator_blocks;
758 R_TRY(CheckMemoryState(&num_allocator_blocks, dst_addr, size, KMemoryState::All,
759 KMemoryState::SharedCode, KMemoryPermission::UserReadWrite,
760 KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
761 KMemoryAttribute::None));
762
763 // Check that the memory is mapped in the source process.
764 R_TRY(src_page_table.CheckMemoryState(src_addr, size, KMemoryState::FlagCanMapProcess,
765 KMemoryState::FlagCanMapProcess, KMemoryPermission::None,
766 KMemoryPermission::None, KMemoryAttribute::All,
767 KMemoryAttribute::None));
768
769 // Create an update allocator.
770 Result allocator_result{ResultSuccess};
771 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
772 m_memory_block_slab_manager, num_allocator_blocks);
773 R_TRY(allocator_result);
774
775 R_TRY(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
776
777 // Apply the memory block update.
778 m_memory_block_manager.Update(std::addressof(allocator), dst_addr, num_pages,
779 KMemoryState::Free, KMemoryPermission::None,
780 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
781 KMemoryBlockDisableMergeAttribute::Normal);
782
783 m_system.InvalidateCpuInstructionCaches();
784
785 R_SUCCEED();
786}
787
788Result KPageTable::SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed,
789 KProcessAddress address, size_t size,
790 KMemoryPermission test_perm, KMemoryState dst_state) {
791 // Validate pre-conditions.
792 ASSERT(this->IsLockedByCurrentThread());
793 ASSERT(test_perm == KMemoryPermission::UserReadWrite ||
794 test_perm == KMemoryPermission::UserRead);
795
796 // Check that the address is in range.
797 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
798
799 // Get the source permission.
800 const auto src_perm = (test_perm == KMemoryPermission::UserReadWrite)
801 ? KMemoryPermission::KernelReadWrite | KMemoryPermission::NotMapped
802 : KMemoryPermission::UserRead;
803
804 // Get aligned extents.
805 const KProcessAddress aligned_src_start = Common::AlignDown(GetInteger(address), PageSize);
806 const KProcessAddress aligned_src_end = Common::AlignUp(GetInteger(address) + size, PageSize);
807 const KProcessAddress mapping_src_start = Common::AlignUp(GetInteger(address), PageSize);
808 const KProcessAddress mapping_src_end = Common::AlignDown(GetInteger(address) + size, PageSize);
809
810 const auto aligned_src_last = (aligned_src_end)-1;
811 const auto mapping_src_last = (mapping_src_end)-1;
812
813 // Get the test state and attribute mask.
814 KMemoryState test_state;
815 KMemoryAttribute test_attr_mask;
816 switch (dst_state) {
817 case KMemoryState::Ipc:
818 test_state = KMemoryState::FlagCanUseIpc;
819 test_attr_mask =
820 KMemoryAttribute::Uncached | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked;
821 break;
822 case KMemoryState::NonSecureIpc:
823 test_state = KMemoryState::FlagCanUseNonSecureIpc;
824 test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
825 break;
826 case KMemoryState::NonDeviceIpc:
827 test_state = KMemoryState::FlagCanUseNonDeviceIpc;
828 test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
829 break;
830 default:
831 R_THROW(ResultInvalidCombination);
832 }
833
834 // Ensure that on failure, we roll back appropriately.
835 size_t mapped_size = 0;
836 ON_RESULT_FAILURE {
837 if (mapped_size > 0) {
838 this->CleanupForIpcClientOnServerSetupFailure(page_list, mapping_src_start, mapped_size,
839 src_perm);
840 }
841 };
842
843 size_t blocks_needed = 0;
844
845 // Iterate, mapping as needed.
846 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(aligned_src_start);
847 while (true) {
848 const KMemoryInfo info = it->GetMemoryInfo();
849
850 // Validate the current block.
851 R_TRY(this->CheckMemoryState(info, test_state, test_state, test_perm, test_perm,
852 test_attr_mask, KMemoryAttribute::None));
853
854 if (mapping_src_start < mapping_src_end && (mapping_src_start) < info.GetEndAddress() &&
855 info.GetAddress() < GetInteger(mapping_src_end)) {
856 const auto cur_start = info.GetAddress() >= GetInteger(mapping_src_start)
857 ? info.GetAddress()
858 : (mapping_src_start);
859 const auto cur_end = mapping_src_last >= info.GetLastAddress() ? info.GetEndAddress()
860 : (mapping_src_end);
861 const size_t cur_size = cur_end - cur_start;
862
863 if (info.GetAddress() < GetInteger(mapping_src_start)) {
864 ++blocks_needed;
865 }
866 if (mapping_src_last < info.GetLastAddress()) {
867 ++blocks_needed;
868 }
869
870 // Set the permissions on the block, if we need to.
871 if ((info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != src_perm) {
872 R_TRY(Operate(cur_start, cur_size / PageSize, src_perm,
873 OperationType::ChangePermissions));
874 }
875
876 // Note that we mapped this part.
877 mapped_size += cur_size;
878 }
879
880 // If the block is at the end, we're done.
881 if (aligned_src_last <= info.GetLastAddress()) {
882 break;
883 }
884
885 // Advance.
886 ++it;
887 ASSERT(it != m_memory_block_manager.end());
888 }
889
890 if (out_blocks_needed != nullptr) {
891 ASSERT(blocks_needed <= KMemoryBlockManagerUpdateAllocator::MaxBlocks);
892 *out_blocks_needed = blocks_needed;
893 }
894
895 R_SUCCEED();
896}
897
898Result KPageTable::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
899 KProcessAddress src_addr, KMemoryPermission test_perm,
900 KMemoryState dst_state, KPageTable& src_page_table,
901 bool send) {
902 ASSERT(this->IsLockedByCurrentThread());
903 ASSERT(src_page_table.IsLockedByCurrentThread());
904
905 // Check that we can theoretically map.
906 const KProcessAddress region_start = m_alias_region_start;
907 const size_t region_size = m_alias_region_end - m_alias_region_start;
908 R_UNLESS(size < region_size, ResultOutOfAddressSpace);
909
910 // Get aligned source extents.
911 const KProcessAddress src_start = src_addr;
912 const KProcessAddress src_end = src_addr + size;
913 const KProcessAddress aligned_src_start = Common::AlignDown(GetInteger(src_start), PageSize);
914 const KProcessAddress aligned_src_end = Common::AlignUp(GetInteger(src_start) + size, PageSize);
915 const KProcessAddress mapping_src_start = Common::AlignUp(GetInteger(src_start), PageSize);
916 const KProcessAddress mapping_src_end =
917 Common::AlignDown(GetInteger(src_start) + size, PageSize);
918 const size_t aligned_src_size = aligned_src_end - aligned_src_start;
919 const size_t mapping_src_size =
920 (mapping_src_start < mapping_src_end) ? (mapping_src_end - mapping_src_start) : 0;
921
922 // Select a random address to map at.
923 KProcessAddress dst_addr =
924 this->FindFreeArea(region_start, region_size / PageSize, aligned_src_size / PageSize,
925 PageSize, 0, this->GetNumGuardPages());
926
927 R_UNLESS(dst_addr != 0, ResultOutOfAddressSpace);
928
929 // Check that we can perform the operation we're about to perform.
930 ASSERT(this->CanContain(dst_addr, aligned_src_size, dst_state));
931
932 // Create an update allocator.
933 Result allocator_result;
934 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
935 m_memory_block_slab_manager);
936 R_TRY(allocator_result);
937
938 // We're going to perform an update, so create a helper.
939 KScopedPageTableUpdater updater(this);
940
941 // Reserve space for any partial pages we allocate.
942 const size_t unmapped_size = aligned_src_size - mapping_src_size;
943 KScopedResourceReservation memory_reservation(
944 m_resource_limit, LimitableResource::PhysicalMemoryMax, unmapped_size);
945 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
946
947 // Ensure that we manage page references correctly.
948 KPhysicalAddress start_partial_page = 0;
949 KPhysicalAddress end_partial_page = 0;
950 KProcessAddress cur_mapped_addr = dst_addr;
951
952 // If the partial pages are mapped, an extra reference will have been opened. Otherwise, they'll
953 // free on scope exit.
954 SCOPE_EXIT({
955 if (start_partial_page != 0) {
956 m_system.Kernel().MemoryManager().Close(start_partial_page, 1);
957 }
958 if (end_partial_page != 0) {
959 m_system.Kernel().MemoryManager().Close(end_partial_page, 1);
960 }
961 });
962
963 ON_RESULT_FAILURE {
964 if (cur_mapped_addr != dst_addr) {
965 ASSERT(Operate(dst_addr, (cur_mapped_addr - dst_addr) / PageSize,
966 KMemoryPermission::None, OperationType::Unmap)
967 .IsSuccess());
968 }
969 };
970
971 // Allocate the start page as needed.
972 if (aligned_src_start < mapping_src_start) {
973 start_partial_page =
974 m_system.Kernel().MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option);
975 R_UNLESS(start_partial_page != 0, ResultOutOfMemory);
976 }
977
978 // Allocate the end page as needed.
979 if (mapping_src_end < aligned_src_end &&
980 (aligned_src_start < mapping_src_end || aligned_src_start == mapping_src_start)) {
981 end_partial_page =
982 m_system.Kernel().MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option);
983 R_UNLESS(end_partial_page != 0, ResultOutOfMemory);
984 }
985
986 // Get the implementation.
987 auto& src_impl = src_page_table.PageTableImpl();
988
989 // Get the fill value for partial pages.
990 const auto fill_val = m_ipc_fill_value;
991
992 // Begin traversal.
993 Common::PageTable::TraversalContext context;
994 Common::PageTable::TraversalEntry next_entry;
995 bool traverse_valid =
996 src_impl.BeginTraversal(next_entry, context, GetInteger(aligned_src_start));
997 ASSERT(traverse_valid);
998
999 // Prepare tracking variables.
1000 KPhysicalAddress cur_block_addr = next_entry.phys_addr;
1001 size_t cur_block_size =
1002 next_entry.block_size - ((cur_block_addr) & (next_entry.block_size - 1));
1003 size_t tot_block_size = cur_block_size;
1004
1005 // Map the start page, if we have one.
1006 if (start_partial_page != 0) {
1007 // Ensure the page holds correct data.
1008 const KVirtualAddress start_partial_virt =
1009 GetHeapVirtualAddress(m_system.Kernel().MemoryLayout(), start_partial_page);
1010 if (send) {
1011 const size_t partial_offset = src_start - aligned_src_start;
1012 size_t copy_size, clear_size;
1013 if (src_end < mapping_src_start) {
1014 copy_size = size;
1015 clear_size = mapping_src_start - src_end;
1016 } else {
1017 copy_size = mapping_src_start - src_start;
1018 clear_size = 0;
1019 }
1020
1021 std::memset(m_memory->GetPointer<void>(GetInteger(start_partial_virt)), fill_val,
1022 partial_offset);
1023 std::memcpy(
1024 m_memory->GetPointer<void>(GetInteger(start_partial_virt) + partial_offset),
1025 m_memory->GetPointer<void>(GetInteger(GetHeapVirtualAddress(
1026 m_system.Kernel().MemoryLayout(), cur_block_addr)) +
1027 partial_offset),
1028 copy_size);
1029 if (clear_size > 0) {
1030 std::memset(m_memory->GetPointer<void>(GetInteger(start_partial_virt) +
1031 partial_offset + copy_size),
1032 fill_val, clear_size);
1033 }
1034 } else {
1035 std::memset(m_memory->GetPointer<void>(GetInteger(start_partial_virt)), fill_val,
1036 PageSize);
1037 }
1038
1039 // Map the page.
1040 R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, start_partial_page));
1041
1042 // Update tracking extents.
1043 cur_mapped_addr += PageSize;
1044 cur_block_addr += PageSize;
1045 cur_block_size -= PageSize;
1046
1047 // If the block's size was one page, we may need to continue traversal.
1048 if (cur_block_size == 0 && aligned_src_size > PageSize) {
1049 traverse_valid = src_impl.ContinueTraversal(next_entry, context);
1050 ASSERT(traverse_valid);
1051
1052 cur_block_addr = next_entry.phys_addr;
1053 cur_block_size = next_entry.block_size;
1054 tot_block_size += next_entry.block_size;
1055 }
1056 }
1057
1058 // Map the remaining pages.
1059 while (aligned_src_start + tot_block_size < mapping_src_end) {
1060 // Continue the traversal.
1061 traverse_valid = src_impl.ContinueTraversal(next_entry, context);
1062 ASSERT(traverse_valid);
1063
1064 // Process the block.
1065 if (next_entry.phys_addr != cur_block_addr + cur_block_size) {
1066 // Map the block we've been processing so far.
1067 R_TRY(Operate(cur_mapped_addr, cur_block_size / PageSize, test_perm, OperationType::Map,
1068 cur_block_addr));
1069
1070 // Update tracking extents.
1071 cur_mapped_addr += cur_block_size;
1072 cur_block_addr = next_entry.phys_addr;
1073 cur_block_size = next_entry.block_size;
1074 } else {
1075 cur_block_size += next_entry.block_size;
1076 }
1077 tot_block_size += next_entry.block_size;
1078 }
1079
1080 // Handle the last direct-mapped page.
1081 if (const KProcessAddress mapped_block_end =
1082 aligned_src_start + tot_block_size - cur_block_size;
1083 mapped_block_end < mapping_src_end) {
1084 const size_t last_block_size = mapping_src_end - mapped_block_end;
1085
1086 // Map the last block.
1087 R_TRY(Operate(cur_mapped_addr, last_block_size / PageSize, test_perm, OperationType::Map,
1088 cur_block_addr));
1089
1090 // Update tracking extents.
1091 cur_mapped_addr += last_block_size;
1092 cur_block_addr += last_block_size;
1093 if (mapped_block_end + cur_block_size < aligned_src_end &&
1094 cur_block_size == last_block_size) {
1095 traverse_valid = src_impl.ContinueTraversal(next_entry, context);
1096 ASSERT(traverse_valid);
1097
1098 cur_block_addr = next_entry.phys_addr;
1099 }
1100 }
1101
1102 // Map the end page, if we have one.
1103 if (end_partial_page != 0) {
1104 // Ensure the page holds correct data.
1105 const KVirtualAddress end_partial_virt =
1106 GetHeapVirtualAddress(m_system.Kernel().MemoryLayout(), end_partial_page);
1107 if (send) {
1108 const size_t copy_size = src_end - mapping_src_end;
1109 std::memcpy(m_memory->GetPointer<void>(GetInteger(end_partial_virt)),
1110 m_memory->GetPointer<void>(GetInteger(GetHeapVirtualAddress(
1111 m_system.Kernel().MemoryLayout(), cur_block_addr))),
1112 copy_size);
1113 std::memset(m_memory->GetPointer<void>(GetInteger(end_partial_virt) + copy_size),
1114 fill_val, PageSize - copy_size);
1115 } else {
1116 std::memset(m_memory->GetPointer<void>(GetInteger(end_partial_virt)), fill_val,
1117 PageSize);
1118 }
1119
1120 // Map the page.
1121 R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, end_partial_page));
1122 }
1123
1124 // Update memory blocks to reflect our changes
1125 m_memory_block_manager.Update(std::addressof(allocator), dst_addr, aligned_src_size / PageSize,
1126 dst_state, test_perm, KMemoryAttribute::None,
1127 KMemoryBlockDisableMergeAttribute::Normal,
1128 KMemoryBlockDisableMergeAttribute::None);
1129
1130 // Set the output address.
1131 *out_addr = dst_addr + (src_start - aligned_src_start);
1132
1133 // We succeeded.
1134 memory_reservation.Commit();
1135 R_SUCCEED();
1136}
1137
1138Result KPageTable::SetupForIpc(KProcessAddress* out_dst_addr, size_t size, KProcessAddress src_addr,
1139 KPageTable& src_page_table, KMemoryPermission test_perm,
1140 KMemoryState dst_state, bool send) {
1141 // For convenience, alias this.
1142 KPageTable& dst_page_table = *this;
1143
1144 // Acquire the table locks.
1145 KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock);
1146
1147 // We're going to perform an update, so create a helper.
1148 KScopedPageTableUpdater updater(std::addressof(src_page_table));
1149
1150 // Perform client setup.
1151 size_t num_allocator_blocks;
1152 R_TRY(src_page_table.SetupForIpcClient(updater.GetPageList(),
1153 std::addressof(num_allocator_blocks), src_addr, size,
1154 test_perm, dst_state));
1155
1156 // Create an update allocator.
1157 Result allocator_result;
1158 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1159 src_page_table.m_memory_block_slab_manager,
1160 num_allocator_blocks);
1161 R_TRY(allocator_result);
1162
1163 // Get the mapped extents.
1164 const KProcessAddress src_map_start = Common::AlignUp(GetInteger(src_addr), PageSize);
1165 const KProcessAddress src_map_end = Common::AlignDown(GetInteger(src_addr) + size, PageSize);
1166 const size_t src_map_size = src_map_end - src_map_start;
1167
1168 // Ensure that we clean up appropriately if we fail after this.
1169 const auto src_perm = (test_perm == KMemoryPermission::UserReadWrite)
1170 ? KMemoryPermission::KernelReadWrite | KMemoryPermission::NotMapped
1171 : KMemoryPermission::UserRead;
1172 ON_RESULT_FAILURE {
1173 if (src_map_end > src_map_start) {
1174 src_page_table.CleanupForIpcClientOnServerSetupFailure(
1175 updater.GetPageList(), src_map_start, src_map_size, src_perm);
1176 }
1177 };
1178
1179 // Perform server setup.
1180 R_TRY(dst_page_table.SetupForIpcServer(out_dst_addr, size, src_addr, test_perm, dst_state,
1181 src_page_table, send));
1182
1183 // If anything was mapped, ipc-lock the pages.
1184 if (src_map_start < src_map_end) {
1185 // Get the source permission.
1186 src_page_table.m_memory_block_manager.UpdateLock(std::addressof(allocator), src_map_start,
1187 (src_map_end - src_map_start) / PageSize,
1188 &KMemoryBlock::LockForIpc, src_perm);
1189 }
1190
1191 R_SUCCEED();
1192}
1193
1194Result KPageTable::CleanupForIpcServer(KProcessAddress address, size_t size,
1195 KMemoryState dst_state) {
1196 // Validate the address.
1197 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
1198
1199 // Lock the table.
1200 KScopedLightLock lk(m_general_lock);
1201
1202 // Validate the memory state.
1203 size_t num_allocator_blocks;
1204 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
1205 KMemoryState::All, dst_state, KMemoryPermission::UserRead,
1206 KMemoryPermission::UserRead, KMemoryAttribute::All,
1207 KMemoryAttribute::None));
1208
1209 // Create an update allocator.
1210 Result allocator_result;
1211 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1212 m_memory_block_slab_manager, num_allocator_blocks);
1213 R_TRY(allocator_result);
1214
1215 // We're going to perform an update, so create a helper.
1216 KScopedPageTableUpdater updater(this);
1217
1218 // Get aligned extents.
1219 const KProcessAddress aligned_start = Common::AlignDown(GetInteger(address), PageSize);
1220 const KProcessAddress aligned_end = Common::AlignUp(GetInteger(address) + size, PageSize);
1221 const size_t aligned_size = aligned_end - aligned_start;
1222 const size_t aligned_num_pages = aligned_size / PageSize;
1223
1224 // Unmap the pages.
1225 R_TRY(Operate(aligned_start, aligned_num_pages, KMemoryPermission::None, OperationType::Unmap));
1226
1227 // Update memory blocks.
1228 m_memory_block_manager.Update(std::addressof(allocator), aligned_start, aligned_num_pages,
1229 KMemoryState::None, KMemoryPermission::None,
1230 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
1231 KMemoryBlockDisableMergeAttribute::Normal);
1232
1233 // Release from the resource limit as relevant.
1234 const KProcessAddress mapping_start = Common::AlignUp(GetInteger(address), PageSize);
1235 const KProcessAddress mapping_end = Common::AlignDown(GetInteger(address) + size, PageSize);
1236 const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0;
1237 m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, aligned_size - mapping_size);
1238
1239 R_SUCCEED();
1240}
1241
1242Result KPageTable::CleanupForIpcClient(KProcessAddress address, size_t size,
1243 KMemoryState dst_state) {
1244 // Validate the address.
1245 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
1246
1247 // Get aligned source extents.
1248 const KProcessAddress mapping_start = Common::AlignUp(GetInteger(address), PageSize);
1249 const KProcessAddress mapping_end = Common::AlignDown(GetInteger(address) + size, PageSize);
1250 const KProcessAddress mapping_last = mapping_end - 1;
1251 const size_t mapping_size = (mapping_start < mapping_end) ? (mapping_end - mapping_start) : 0;
1252
1253 // If nothing was mapped, we're actually done immediately.
1254 R_SUCCEED_IF(mapping_size == 0);
1255
1256 // Get the test state and attribute mask.
1257 KMemoryState test_state;
1258 KMemoryAttribute test_attr_mask;
1259 switch (dst_state) {
1260 case KMemoryState::Ipc:
1261 test_state = KMemoryState::FlagCanUseIpc;
1262 test_attr_mask =
1263 KMemoryAttribute::Uncached | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked;
1264 break;
1265 case KMemoryState::NonSecureIpc:
1266 test_state = KMemoryState::FlagCanUseNonSecureIpc;
1267 test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
1268 break;
1269 case KMemoryState::NonDeviceIpc:
1270 test_state = KMemoryState::FlagCanUseNonDeviceIpc;
1271 test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
1272 break;
1273 default:
1274 R_THROW(ResultInvalidCombination);
1275 }
1276
1277 // Lock the table.
1278 // NOTE: Nintendo does this *after* creating the updater below, but this does not follow
1279 // convention elsewhere in KPageTable.
1280 KScopedLightLock lk(m_general_lock);
1281
1282 // We're going to perform an update, so create a helper.
1283 KScopedPageTableUpdater updater(this);
1284
1285 // Ensure that on failure, we roll back appropriately.
1286 size_t mapped_size = 0;
1287 ON_RESULT_FAILURE {
1288 if (mapped_size > 0) {
1289 // Determine where the mapping ends.
1290 const auto mapped_end = (mapping_start) + mapped_size;
1291 const auto mapped_last = mapped_end - 1;
1292
1293 // Get current and next iterators.
1294 KMemoryBlockManager::const_iterator start_it =
1295 m_memory_block_manager.FindIterator(mapping_start);
1296 KMemoryBlockManager::const_iterator next_it = start_it;
1297 ++next_it;
1298
1299 // Get the current block info.
1300 KMemoryInfo cur_info = start_it->GetMemoryInfo();
1301
1302 // Create tracking variables.
1303 KProcessAddress cur_address = cur_info.GetAddress();
1304 size_t cur_size = cur_info.GetSize();
1305 bool cur_perm_eq = cur_info.GetPermission() == cur_info.GetOriginalPermission();
1306 bool cur_needs_set_perm = !cur_perm_eq && cur_info.GetIpcLockCount() == 1;
1307 bool first =
1308 cur_info.GetIpcDisableMergeCount() == 1 &&
1309 (cur_info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Locked) ==
1310 KMemoryBlockDisableMergeAttribute::None;
1311
1312 while (((cur_address) + cur_size - 1) < mapped_last) {
1313 // Check that we have a next block.
1314 ASSERT(next_it != m_memory_block_manager.end());
1315
1316 // Get the next info.
1317 const KMemoryInfo next_info = next_it->GetMemoryInfo();
1318
1319 // Check if we can consolidate the next block's permission set with the current one.
1320
1321 const bool next_perm_eq =
1322 next_info.GetPermission() == next_info.GetOriginalPermission();
1323 const bool next_needs_set_perm = !next_perm_eq && next_info.GetIpcLockCount() == 1;
1324 if (cur_perm_eq == next_perm_eq && cur_needs_set_perm == next_needs_set_perm &&
1325 cur_info.GetOriginalPermission() == next_info.GetOriginalPermission()) {
1326 // We can consolidate the reprotection for the current and next block into a
1327 // single call.
1328 cur_size += next_info.GetSize();
1329 } else {
1330 // We have to operate on the current block.
1331 if ((cur_needs_set_perm || first) && !cur_perm_eq) {
1332 ASSERT(Operate(cur_address, cur_size / PageSize, cur_info.GetPermission(),
1333 OperationType::ChangePermissions)
1334 .IsSuccess());
1335 }
1336
1337 // Advance.
1338 cur_address = next_info.GetAddress();
1339 cur_size = next_info.GetSize();
1340 first = false;
1341 }
1342
1343 // Advance.
1344 cur_info = next_info;
1345 cur_perm_eq = next_perm_eq;
1346 cur_needs_set_perm = next_needs_set_perm;
1347 ++next_it;
1348 }
1349
1350 // Process the last block.
1351 if ((first || cur_needs_set_perm) && !cur_perm_eq) {
1352 ASSERT(Operate(cur_address, cur_size / PageSize, cur_info.GetPermission(),
1353 OperationType::ChangePermissions)
1354 .IsSuccess());
1355 }
1356 }
1357 };
1358
1359 // Iterate, reprotecting as needed.
1360 {
1361 // Get current and next iterators.
1362 KMemoryBlockManager::const_iterator start_it =
1363 m_memory_block_manager.FindIterator(mapping_start);
1364 KMemoryBlockManager::const_iterator next_it = start_it;
1365 ++next_it;
1366
1367 // Validate the current block.
1368 KMemoryInfo cur_info = start_it->GetMemoryInfo();
1369 ASSERT(this->CheckMemoryState(cur_info, test_state, test_state, KMemoryPermission::None,
1370 KMemoryPermission::None,
1371 test_attr_mask | KMemoryAttribute::IpcLocked,
1372 KMemoryAttribute::IpcLocked)
1373 .IsSuccess());
1374
1375 // Create tracking variables.
1376 KProcessAddress cur_address = cur_info.GetAddress();
1377 size_t cur_size = cur_info.GetSize();
1378 bool cur_perm_eq = cur_info.GetPermission() == cur_info.GetOriginalPermission();
1379 bool cur_needs_set_perm = !cur_perm_eq && cur_info.GetIpcLockCount() == 1;
1380 bool first =
1381 cur_info.GetIpcDisableMergeCount() == 1 &&
1382 (cur_info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Locked) ==
1383 KMemoryBlockDisableMergeAttribute::None;
1384
1385 while ((cur_address + cur_size - 1) < mapping_last) {
1386 // Check that we have a next block.
1387 ASSERT(next_it != m_memory_block_manager.end());
1388
1389 // Get the next info.
1390 const KMemoryInfo next_info = next_it->GetMemoryInfo();
1391
1392 // Validate the next block.
1393 ASSERT(this->CheckMemoryState(next_info, test_state, test_state,
1394 KMemoryPermission::None, KMemoryPermission::None,
1395 test_attr_mask | KMemoryAttribute::IpcLocked,
1396 KMemoryAttribute::IpcLocked)
1397 .IsSuccess());
1398
1399 // Check if we can consolidate the next block's permission set with the current one.
1400 const bool next_perm_eq =
1401 next_info.GetPermission() == next_info.GetOriginalPermission();
1402 const bool next_needs_set_perm = !next_perm_eq && next_info.GetIpcLockCount() == 1;
1403 if (cur_perm_eq == next_perm_eq && cur_needs_set_perm == next_needs_set_perm &&
1404 cur_info.GetOriginalPermission() == next_info.GetOriginalPermission()) {
1405 // We can consolidate the reprotection for the current and next block into a single
1406 // call.
1407 cur_size += next_info.GetSize();
1408 } else {
1409 // We have to operate on the current block.
1410 if ((cur_needs_set_perm || first) && !cur_perm_eq) {
1411 R_TRY(Operate(cur_address, cur_size / PageSize,
1412 cur_needs_set_perm ? cur_info.GetOriginalPermission()
1413 : cur_info.GetPermission(),
1414 OperationType::ChangePermissions));
1415 }
1416
1417 // Mark that we mapped the block.
1418 mapped_size += cur_size;
1419
1420 // Advance.
1421 cur_address = next_info.GetAddress();
1422 cur_size = next_info.GetSize();
1423 first = false;
1424 }
1425
1426 // Advance.
1427 cur_info = next_info;
1428 cur_perm_eq = next_perm_eq;
1429 cur_needs_set_perm = next_needs_set_perm;
1430 ++next_it;
1431 }
1432
1433 // Process the last block.
1434 const auto lock_count =
1435 cur_info.GetIpcLockCount() +
1436 (next_it != m_memory_block_manager.end()
1437 ? (next_it->GetIpcDisableMergeCount() - next_it->GetIpcLockCount())
1438 : 0);
1439 if ((first || cur_needs_set_perm || (lock_count == 1)) && !cur_perm_eq) {
1440 R_TRY(Operate(cur_address, cur_size / PageSize,
1441 cur_needs_set_perm ? cur_info.GetOriginalPermission()
1442 : cur_info.GetPermission(),
1443 OperationType::ChangePermissions));
1444 }
1445 }
1446
1447 // Create an update allocator.
1448 // NOTE: Guaranteed zero blocks needed here.
1449 Result allocator_result;
1450 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1451 m_memory_block_slab_manager, 0);
1452 R_TRY(allocator_result);
1453
1454 // Unlock the pages.
1455 m_memory_block_manager.UpdateLock(std::addressof(allocator), mapping_start,
1456 mapping_size / PageSize, &KMemoryBlock::UnlockForIpc,
1457 KMemoryPermission::None);
1458
1459 R_SUCCEED();
1460}
1461
1462void KPageTable::CleanupForIpcClientOnServerSetupFailure([[maybe_unused]] PageLinkedList* page_list,
1463 KProcessAddress address, size_t size,
1464 KMemoryPermission prot_perm) {
1465 ASSERT(this->IsLockedByCurrentThread());
1466 ASSERT(Common::IsAligned(GetInteger(address), PageSize));
1467 ASSERT(Common::IsAligned(size, PageSize));
1468
1469 // Get the mapped extents.
1470 const KProcessAddress src_map_start = address;
1471 const KProcessAddress src_map_end = address + size;
1472 const KProcessAddress src_map_last = src_map_end - 1;
1473
1474 // This function is only invoked when there's something to do.
1475 ASSERT(src_map_end > src_map_start);
1476
1477 // Iterate over blocks, fixing permissions.
1478 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(address);
1479 while (true) {
1480 const KMemoryInfo info = it->GetMemoryInfo();
1481
1482 const auto cur_start = info.GetAddress() >= GetInteger(src_map_start)
1483 ? info.GetAddress()
1484 : GetInteger(src_map_start);
1485 const auto cur_end =
1486 src_map_last <= info.GetLastAddress() ? src_map_end : info.GetEndAddress();
1487
1488 // If we can, fix the protections on the block.
1489 if ((info.GetIpcLockCount() == 0 &&
1490 (info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm) ||
1491 (info.GetIpcLockCount() != 0 &&
1492 (info.GetOriginalPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm)) {
1493 // Check if we actually need to fix the protections on the block.
1494 if (cur_end == src_map_end || info.GetAddress() <= GetInteger(src_map_start) ||
1495 (info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm) {
1496 ASSERT(Operate(cur_start, (cur_end - cur_start) / PageSize, info.GetPermission(),
1497 OperationType::ChangePermissions)
1498 .IsSuccess());
1499 }
1500 }
1501
1502 // If we're past the end of the region, we're done.
1503 if (src_map_last <= info.GetLastAddress()) {
1504 break;
1505 }
1506
1507 // Advance.
1508 ++it;
1509 ASSERT(it != m_memory_block_manager.end());
1510 }
1511}
1512
1513Result KPageTable::MapPhysicalMemory(KProcessAddress address, size_t size) {
1514 // Lock the physical memory lock.
1515 KScopedLightLock phys_lk(m_map_physical_memory_lock);
1516
1517 // Calculate the last address for convenience.
1518 const KProcessAddress last_address = address + size - 1;
1519
1520 // Define iteration variables.
1521 KProcessAddress cur_address;
1522 size_t mapped_size;
1523
1524 // The entire mapping process can be retried.
1525 while (true) {
1526 // Check if the memory is already mapped.
1527 {
1528 // Lock the table.
1529 KScopedLightLock lk(m_general_lock);
1530
1531 // Iterate over the memory.
1532 cur_address = address;
1533 mapped_size = 0;
1534
1535 auto it = m_memory_block_manager.FindIterator(cur_address);
1536 while (true) {
1537 // Check that the iterator is valid.
1538 ASSERT(it != m_memory_block_manager.end());
1539
1540 // Get the memory info.
1541 const KMemoryInfo info = it->GetMemoryInfo();
1542
1543 // Check if we're done.
1544 if (last_address <= info.GetLastAddress()) {
1545 if (info.GetState() != KMemoryState::Free) {
1546 mapped_size += (last_address + 1 - cur_address);
1547 }
1548 break;
1549 }
1550
1551 // Track the memory if it's mapped.
1552 if (info.GetState() != KMemoryState::Free) {
1553 mapped_size += KProcessAddress(info.GetEndAddress()) - cur_address;
1554 }
1555
1556 // Advance.
1557 cur_address = info.GetEndAddress();
1558 ++it;
1559 }
1560
1561 // If the size mapped is the size requested, we've nothing to do.
1562 R_SUCCEED_IF(size == mapped_size);
1563 }
1564
1565 // Allocate and map the memory.
1566 {
1567 // Reserve the memory from the process resource limit.
1568 KScopedResourceReservation memory_reservation(
1569 m_resource_limit, LimitableResource::PhysicalMemoryMax, size - mapped_size);
1570 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
1571
1572 // Allocate pages for the new memory.
1573 KPageGroup pg{m_kernel, m_block_info_manager};
1574 R_TRY(m_system.Kernel().MemoryManager().AllocateForProcess(
1575 &pg, (size - mapped_size) / PageSize, m_allocate_option, 0, 0));
1576
1577 // If we fail in the next bit (or retry), we need to cleanup the pages.
1578 // auto pg_guard = SCOPE_GUARD {
1579 // pg.OpenFirst();
1580 // pg.Close();
1581 //};
1582
1583 // Map the memory.
1584 {
1585 // Lock the table.
1586 KScopedLightLock lk(m_general_lock);
1587
1588 size_t num_allocator_blocks = 0;
1589
1590 // Verify that nobody has mapped memory since we first checked.
1591 {
1592 // Iterate over the memory.
1593 size_t checked_mapped_size = 0;
1594 cur_address = address;
1595
1596 auto it = m_memory_block_manager.FindIterator(cur_address);
1597 while (true) {
1598 // Check that the iterator is valid.
1599 ASSERT(it != m_memory_block_manager.end());
1600
1601 // Get the memory info.
1602 const KMemoryInfo info = it->GetMemoryInfo();
1603
1604 const bool is_free = info.GetState() == KMemoryState::Free;
1605 if (is_free) {
1606 if (info.GetAddress() < GetInteger(address)) {
1607 ++num_allocator_blocks;
1608 }
1609 if (last_address < info.GetLastAddress()) {
1610 ++num_allocator_blocks;
1611 }
1612 }
1613
1614 // Check if we're done.
1615 if (last_address <= info.GetLastAddress()) {
1616 if (!is_free) {
1617 checked_mapped_size += (last_address + 1 - cur_address);
1618 }
1619 break;
1620 }
1621
1622 // Track the memory if it's mapped.
1623 if (!is_free) {
1624 checked_mapped_size +=
1625 KProcessAddress(info.GetEndAddress()) - cur_address;
1626 }
1627
1628 // Advance.
1629 cur_address = info.GetEndAddress();
1630 ++it;
1631 }
1632
1633 // If the size now isn't what it was before, somebody mapped or unmapped
1634 // concurrently. If this happened, retry.
1635 if (mapped_size != checked_mapped_size) {
1636 continue;
1637 }
1638 }
1639
1640 // Create an update allocator.
1641 ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks);
1642 Result allocator_result;
1643 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1644 m_memory_block_slab_manager,
1645 num_allocator_blocks);
1646 R_TRY(allocator_result);
1647
1648 // We're going to perform an update, so create a helper.
1649 KScopedPageTableUpdater updater(this);
1650
1651 // Prepare to iterate over the memory.
1652 auto pg_it = pg.begin();
1653 KPhysicalAddress pg_phys_addr = pg_it->GetAddress();
1654 size_t pg_pages = pg_it->GetNumPages();
1655
1656 // Reset the current tracking address, and make sure we clean up on failure.
1657 // pg_guard.Cancel();
1658 cur_address = address;
1659 ON_RESULT_FAILURE {
1660 if (cur_address > address) {
1661 const KProcessAddress last_unmap_address = cur_address - 1;
1662
1663 // Iterate, unmapping the pages.
1664 cur_address = address;
1665
1666 auto it = m_memory_block_manager.FindIterator(cur_address);
1667 while (true) {
1668 // Check that the iterator is valid.
1669 ASSERT(it != m_memory_block_manager.end());
1670
1671 // Get the memory info.
1672 const KMemoryInfo info = it->GetMemoryInfo();
1673
1674 // If the memory state is free, we mapped it and need to unmap it.
1675 if (info.GetState() == KMemoryState::Free) {
1676 // Determine the range to unmap.
1677 const size_t cur_pages =
1678 std::min(KProcessAddress(info.GetEndAddress()) - cur_address,
1679 last_unmap_address + 1 - cur_address) /
1680 PageSize;
1681
1682 // Unmap.
1683 ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None,
1684 OperationType::Unmap)
1685 .IsSuccess());
1686 }
1687
1688 // Check if we're done.
1689 if (last_unmap_address <= info.GetLastAddress()) {
1690 break;
1691 }
1692
1693 // Advance.
1694 cur_address = info.GetEndAddress();
1695 ++it;
1696 }
1697 }
1698
1699 // Release any remaining unmapped memory.
1700 m_system.Kernel().MemoryManager().OpenFirst(pg_phys_addr, pg_pages);
1701 m_system.Kernel().MemoryManager().Close(pg_phys_addr, pg_pages);
1702 for (++pg_it; pg_it != pg.end(); ++pg_it) {
1703 m_system.Kernel().MemoryManager().OpenFirst(pg_it->GetAddress(),
1704 pg_it->GetNumPages());
1705 m_system.Kernel().MemoryManager().Close(pg_it->GetAddress(),
1706 pg_it->GetNumPages());
1707 }
1708 };
1709
1710 auto it = m_memory_block_manager.FindIterator(cur_address);
1711 while (true) {
1712 // Check that the iterator is valid.
1713 ASSERT(it != m_memory_block_manager.end());
1714
1715 // Get the memory info.
1716 const KMemoryInfo info = it->GetMemoryInfo();
1717
1718 // If it's unmapped, we need to map it.
1719 if (info.GetState() == KMemoryState::Free) {
1720 // Determine the range to map.
1721 size_t map_pages =
1722 std::min(KProcessAddress(info.GetEndAddress()) - cur_address,
1723 last_address + 1 - cur_address) /
1724 PageSize;
1725
1726 // While we have pages to map, map them.
1727 {
1728 // Create a page group for the current mapping range.
1729 KPageGroup cur_pg(m_kernel, m_block_info_manager);
1730 {
1731 ON_RESULT_FAILURE_2 {
1732 cur_pg.OpenFirst();
1733 cur_pg.Close();
1734 };
1735
1736 size_t remain_pages = map_pages;
1737 while (remain_pages > 0) {
1738 // Check if we're at the end of the physical block.
1739 if (pg_pages == 0) {
1740 // Ensure there are more pages to map.
1741 ASSERT(pg_it != pg.end());
1742
1743 // Advance our physical block.
1744 ++pg_it;
1745 pg_phys_addr = pg_it->GetAddress();
1746 pg_pages = pg_it->GetNumPages();
1747 }
1748
1749 // Add whatever we can to the current block.
1750 const size_t cur_pages = std::min(pg_pages, remain_pages);
1751 R_TRY(cur_pg.AddBlock(pg_phys_addr +
1752 ((pg_pages - cur_pages) * PageSize),
1753 cur_pages));
1754
1755 // Advance.
1756 remain_pages -= cur_pages;
1757 pg_pages -= cur_pages;
1758 }
1759 }
1760
1761 // Map the pages.
1762 R_TRY(this->Operate(cur_address, map_pages, cur_pg,
1763 OperationType::MapFirstGroup));
1764 }
1765 }
1766
1767 // Check if we're done.
1768 if (last_address <= info.GetLastAddress()) {
1769 break;
1770 }
1771
1772 // Advance.
1773 cur_address = info.GetEndAddress();
1774 ++it;
1775 }
1776
1777 // We succeeded, so commit the memory reservation.
1778 memory_reservation.Commit();
1779
1780 // Increase our tracked mapped size.
1781 m_mapped_physical_memory_size += (size - mapped_size);
1782
1783 // Update the relevant memory blocks.
1784 m_memory_block_manager.UpdateIfMatch(
1785 std::addressof(allocator), address, size / PageSize, KMemoryState::Free,
1786 KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal,
1787 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
1788 address == this->GetAliasRegionStart()
1789 ? KMemoryBlockDisableMergeAttribute::Normal
1790 : KMemoryBlockDisableMergeAttribute::None,
1791 KMemoryBlockDisableMergeAttribute::None);
1792
1793 R_SUCCEED();
1794 }
1795 }
1796 }
1797}
1798
1799Result KPageTable::UnmapPhysicalMemory(KProcessAddress address, size_t size) {
1800 // Lock the physical memory lock.
1801 KScopedLightLock phys_lk(m_map_physical_memory_lock);
1802
1803 // Lock the table.
1804 KScopedLightLock lk(m_general_lock);
1805
1806 // Calculate the last address for convenience.
1807 const KProcessAddress last_address = address + size - 1;
1808
1809 // Define iteration variables.
1810 KProcessAddress map_start_address = 0;
1811 KProcessAddress map_last_address = 0;
1812
1813 KProcessAddress cur_address;
1814 size_t mapped_size;
1815 size_t num_allocator_blocks = 0;
1816
1817 // Check if the memory is mapped.
1818 {
1819 // Iterate over the memory.
1820 cur_address = address;
1821 mapped_size = 0;
1822
1823 auto it = m_memory_block_manager.FindIterator(cur_address);
1824 while (true) {
1825 // Check that the iterator is valid.
1826 ASSERT(it != m_memory_block_manager.end());
1827
1828 // Get the memory info.
1829 const KMemoryInfo info = it->GetMemoryInfo();
1830
1831 // Verify the memory's state.
1832 const bool is_normal = info.GetState() == KMemoryState::Normal &&
1833 info.GetAttribute() == KMemoryAttribute::None;
1834 const bool is_free = info.GetState() == KMemoryState::Free;
1835 R_UNLESS(is_normal || is_free, ResultInvalidCurrentMemory);
1836
1837 if (is_normal) {
1838 R_UNLESS(info.GetAttribute() == KMemoryAttribute::None, ResultInvalidCurrentMemory);
1839
1840 if (map_start_address == 0) {
1841 map_start_address = cur_address;
1842 }
1843 map_last_address =
1844 (last_address >= info.GetLastAddress()) ? info.GetLastAddress() : last_address;
1845
1846 if (info.GetAddress() < GetInteger(address)) {
1847 ++num_allocator_blocks;
1848 }
1849 if (last_address < info.GetLastAddress()) {
1850 ++num_allocator_blocks;
1851 }
1852
1853 mapped_size += (map_last_address + 1 - cur_address);
1854 }
1855
1856 // Check if we're done.
1857 if (last_address <= info.GetLastAddress()) {
1858 break;
1859 }
1860
1861 // Advance.
1862 cur_address = info.GetEndAddress();
1863 ++it;
1864 }
1865
1866 // If there's nothing mapped, we've nothing to do.
1867 R_SUCCEED_IF(mapped_size == 0);
1868 }
1869
1870 // Create an update allocator.
1871 ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks);
1872 Result allocator_result;
1873 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1874 m_memory_block_slab_manager, num_allocator_blocks);
1875 R_TRY(allocator_result);
1876
1877 // We're going to perform an update, so create a helper.
1878 KScopedPageTableUpdater updater(this);
1879
1880 // Separate the mapping.
1881 R_TRY(Operate(map_start_address, (map_last_address + 1 - map_start_address) / PageSize,
1882 KMemoryPermission::None, OperationType::Separate));
1883
1884 // Reset the current tracking address, and make sure we clean up on failure.
1885 cur_address = address;
1886
1887 // Iterate over the memory, unmapping as we go.
1888 auto it = m_memory_block_manager.FindIterator(cur_address);
1889
1890 const auto clear_merge_attr =
1891 (it->GetState() == KMemoryState::Normal &&
1892 it->GetAddress() == this->GetAliasRegionStart() && it->GetAddress() == address)
1893 ? KMemoryBlockDisableMergeAttribute::Normal
1894 : KMemoryBlockDisableMergeAttribute::None;
1895
1896 while (true) {
1897 // Check that the iterator is valid.
1898 ASSERT(it != m_memory_block_manager.end());
1899
1900 // Get the memory info.
1901 const KMemoryInfo info = it->GetMemoryInfo();
1902
1903 // If the memory state is normal, we need to unmap it.
1904 if (info.GetState() == KMemoryState::Normal) {
1905 // Determine the range to unmap.
1906 const size_t cur_pages = std::min(KProcessAddress(info.GetEndAddress()) - cur_address,
1907 last_address + 1 - cur_address) /
1908 PageSize;
1909
1910 // Unmap.
1911 ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap)
1912 .IsSuccess());
1913 }
1914
1915 // Check if we're done.
1916 if (last_address <= info.GetLastAddress()) {
1917 break;
1918 }
1919
1920 // Advance.
1921 cur_address = info.GetEndAddress();
1922 ++it;
1923 }
1924
1925 // Release the memory resource.
1926 m_mapped_physical_memory_size -= mapped_size;
1927 m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, mapped_size);
1928
1929 // Update memory blocks.
1930 m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize,
1931 KMemoryState::Free, KMemoryPermission::None,
1932 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
1933 clear_merge_attr);
1934
1935 // We succeeded.
1936 R_SUCCEED();
1937}
1938
1939Result KPageTable::MapMemory(KProcessAddress dst_address, KProcessAddress src_address,
1940 size_t size) {
1941 // Lock the table.
1942 KScopedLightLock lk(m_general_lock);
1943
1944 // Validate that the source address's state is valid.
1945 KMemoryState src_state;
1946 size_t num_src_allocator_blocks;
1947 R_TRY(this->CheckMemoryState(std::addressof(src_state), nullptr, nullptr,
1948 std::addressof(num_src_allocator_blocks), src_address, size,
1949 KMemoryState::FlagCanAlias, KMemoryState::FlagCanAlias,
1950 KMemoryPermission::All, KMemoryPermission::UserReadWrite,
1951 KMemoryAttribute::All, KMemoryAttribute::None));
1952
1953 // Validate that the dst address's state is valid.
1954 size_t num_dst_allocator_blocks;
1955 R_TRY(this->CheckMemoryState(std::addressof(num_dst_allocator_blocks), dst_address, size,
1956 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
1957 KMemoryPermission::None, KMemoryAttribute::None,
1958 KMemoryAttribute::None));
1959
1960 // Create an update allocator for the source.
1961 Result src_allocator_result;
1962 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
1963 m_memory_block_slab_manager,
1964 num_src_allocator_blocks);
1965 R_TRY(src_allocator_result);
1966
1967 // Create an update allocator for the destination.
1968 Result dst_allocator_result;
1969 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
1970 m_memory_block_slab_manager,
1971 num_dst_allocator_blocks);
1972 R_TRY(dst_allocator_result);
1973
1974 // Map the memory.
1975 {
1976 // Determine the number of pages being operated on.
1977 const size_t num_pages = size / PageSize;
1978
1979 // Create page groups for the memory being unmapped.
1980 KPageGroup pg{m_kernel, m_block_info_manager};
1981
1982 // Create the page group representing the source.
1983 R_TRY(this->MakePageGroup(pg, src_address, num_pages));
1984
1985 // We're going to perform an update, so create a helper.
1986 KScopedPageTableUpdater updater(this);
1987
1988 // Reprotect the source as kernel-read/not mapped.
1989 const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>(
1990 KMemoryPermission::KernelRead | KMemoryPermission::NotMapped);
1991 const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked;
1992 const KPageProperties src_properties = {new_src_perm, false, false,
1993 DisableMergeAttribute::DisableHeadBodyTail};
1994 R_TRY(this->Operate(src_address, num_pages, src_properties.perm,
1995 OperationType::ChangePermissions));
1996
1997 // Ensure that we unprotect the source pages on failure.
1998 ON_RESULT_FAILURE {
1999 const KPageProperties unprotect_properties = {
2000 KMemoryPermission::UserReadWrite, false, false,
2001 DisableMergeAttribute::EnableHeadBodyTail};
2002 ASSERT(this->Operate(src_address, num_pages, unprotect_properties.perm,
2003 OperationType::ChangePermissions) == ResultSuccess);
2004 };
2005
2006 // Map the alias pages.
2007 const KPageProperties dst_map_properties = {KMemoryPermission::UserReadWrite, false, false,
2008 DisableMergeAttribute::DisableHead};
2009 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_map_properties,
2010 false));
2011
2012 // Apply the memory block updates.
2013 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages,
2014 src_state, new_src_perm, new_src_attr,
2015 KMemoryBlockDisableMergeAttribute::Locked,
2016 KMemoryBlockDisableMergeAttribute::None);
2017 m_memory_block_manager.Update(
2018 std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::Stack,
2019 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
2020 KMemoryBlockDisableMergeAttribute::Normal, KMemoryBlockDisableMergeAttribute::None);
2021 }
2022
2023 R_SUCCEED();
2024}
2025
2026Result KPageTable::UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address,
2027 size_t size) {
2028 // Lock the table.
2029 KScopedLightLock lk(m_general_lock);
2030
2031 // Validate that the source address's state is valid.
2032 KMemoryState src_state;
2033 size_t num_src_allocator_blocks;
2034 R_TRY(this->CheckMemoryState(
2035 std::addressof(src_state), nullptr, nullptr, std::addressof(num_src_allocator_blocks),
2036 src_address, size, KMemoryState::FlagCanAlias, KMemoryState::FlagCanAlias,
2037 KMemoryPermission::All, KMemoryPermission::NotMapped | KMemoryPermission::KernelRead,
2038 KMemoryAttribute::All, KMemoryAttribute::Locked));
2039
2040 // Validate that the dst address's state is valid.
2041 KMemoryPermission dst_perm;
2042 size_t num_dst_allocator_blocks;
2043 R_TRY(this->CheckMemoryState(
2044 nullptr, std::addressof(dst_perm), nullptr, std::addressof(num_dst_allocator_blocks),
2045 dst_address, size, KMemoryState::All, KMemoryState::Stack, KMemoryPermission::None,
2046 KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None));
2047
2048 // Create an update allocator for the source.
2049 Result src_allocator_result;
2050 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
2051 m_memory_block_slab_manager,
2052 num_src_allocator_blocks);
2053 R_TRY(src_allocator_result);
2054
2055 // Create an update allocator for the destination.
2056 Result dst_allocator_result;
2057 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
2058 m_memory_block_slab_manager,
2059 num_dst_allocator_blocks);
2060 R_TRY(dst_allocator_result);
2061
2062 // Unmap the memory.
2063 {
2064 // Determine the number of pages being operated on.
2065 const size_t num_pages = size / PageSize;
2066
2067 // Create page groups for the memory being unmapped.
2068 KPageGroup pg{m_kernel, m_block_info_manager};
2069
2070 // Create the page group representing the destination.
2071 R_TRY(this->MakePageGroup(pg, dst_address, num_pages));
2072
2073 // Ensure the page group is the valid for the source.
2074 R_UNLESS(this->IsValidPageGroup(pg, src_address, num_pages), ResultInvalidMemoryRegion);
2075
2076 // We're going to perform an update, so create a helper.
2077 KScopedPageTableUpdater updater(this);
2078
2079 // Unmap the aliased copy of the pages.
2080 const KPageProperties dst_unmap_properties = {KMemoryPermission::None, false, false,
2081 DisableMergeAttribute::None};
2082 R_TRY(
2083 this->Operate(dst_address, num_pages, dst_unmap_properties.perm, OperationType::Unmap));
2084
2085 // Ensure that we re-map the aliased pages on failure.
2086 ON_RESULT_FAILURE {
2087 this->RemapPageGroup(updater.GetPageList(), dst_address, size, pg);
2088 };
2089
2090 // Try to set the permissions for the source pages back to what they should be.
2091 const KPageProperties src_properties = {KMemoryPermission::UserReadWrite, false, false,
2092 DisableMergeAttribute::EnableAndMergeHeadBodyTail};
2093 R_TRY(this->Operate(src_address, num_pages, src_properties.perm,
2094 OperationType::ChangePermissions));
2095
2096 // Apply the memory block updates.
2097 m_memory_block_manager.Update(
2098 std::addressof(src_allocator), src_address, num_pages, src_state,
2099 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
2100 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked);
2101 m_memory_block_manager.Update(
2102 std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None,
2103 KMemoryPermission::None, KMemoryAttribute::None,
2104 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal);
2105 }
2106
2107 R_SUCCEED();
2108}
2109
2110Result KPageTable::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
2111 size_t num_pages, KMemoryPermission perm) {
2112 ASSERT(this->IsLockedByCurrentThread());
2113
2114 // Create a page group to hold the pages we allocate.
2115 KPageGroup pg{m_kernel, m_block_info_manager};
2116
2117 // Allocate the pages.
2118 R_TRY(
2119 m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option));
2120
2121 // Ensure that the page group is closed when we're done working with it.
2122 SCOPE_EXIT({ pg.Close(); });
2123
2124 // Clear all pages.
2125 for (const auto& it : pg) {
2126 std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value,
2127 it.GetSize());
2128 }
2129
2130 // Map the pages.
2131 R_RETURN(this->Operate(address, num_pages, pg, OperationType::MapGroup));
2132}
2133
2134Result KPageTable::MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
2135 const KPageGroup& pg, const KPageProperties properties,
2136 bool reuse_ll) {
2137 ASSERT(this->IsLockedByCurrentThread());
2138
2139 // Note the current address, so that we can iterate.
2140 const KProcessAddress start_address = address;
2141 KProcessAddress cur_address = address;
2142
2143 // Ensure that we clean up on failure.
2144 ON_RESULT_FAILURE {
2145 ASSERT(!reuse_ll);
2146 if (cur_address != start_address) {
2147 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
2148 DisableMergeAttribute::None};
2149 ASSERT(this->Operate(start_address, (cur_address - start_address) / PageSize,
2150 unmap_properties.perm, OperationType::Unmap) == ResultSuccess);
2151 }
2152 };
2153
2154 // Iterate, mapping all pages in the group.
2155 for (const auto& block : pg) {
2156 // Map and advance.
2157 const KPageProperties cur_properties =
2158 (cur_address == start_address)
2159 ? properties
2160 : KPageProperties{properties.perm, properties.io, properties.uncached,
2161 DisableMergeAttribute::None};
2162 this->Operate(cur_address, block.GetNumPages(), cur_properties.perm, OperationType::Map,
2163 block.GetAddress());
2164 cur_address += block.GetSize();
2165 }
2166
2167 // We succeeded!
2168 R_SUCCEED();
2169}
2170
2171void KPageTable::RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
2172 const KPageGroup& pg) {
2173 ASSERT(this->IsLockedByCurrentThread());
2174
2175 // Note the current address, so that we can iterate.
2176 const KProcessAddress start_address = address;
2177 const KProcessAddress last_address = start_address + size - 1;
2178 const KProcessAddress end_address = last_address + 1;
2179
2180 // Iterate over the memory.
2181 auto pg_it = pg.begin();
2182 ASSERT(pg_it != pg.end());
2183
2184 KPhysicalAddress pg_phys_addr = pg_it->GetAddress();
2185 size_t pg_pages = pg_it->GetNumPages();
2186
2187 auto it = m_memory_block_manager.FindIterator(start_address);
2188 while (true) {
2189 // Check that the iterator is valid.
2190 ASSERT(it != m_memory_block_manager.end());
2191
2192 // Get the memory info.
2193 const KMemoryInfo info = it->GetMemoryInfo();
2194
2195 // Determine the range to map.
2196 KProcessAddress map_address = std::max<KProcessAddress>(info.GetAddress(), start_address);
2197 const KProcessAddress map_end_address =
2198 std::min<KProcessAddress>(info.GetEndAddress(), end_address);
2199 ASSERT(map_end_address != map_address);
2200
2201 // Determine if we should disable head merge.
2202 const bool disable_head_merge =
2203 info.GetAddress() >= GetInteger(start_address) &&
2204 True(info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Normal);
2205 const KPageProperties map_properties = {
2206 info.GetPermission(), false, false,
2207 disable_head_merge ? DisableMergeAttribute::DisableHead : DisableMergeAttribute::None};
2208
2209 // While we have pages to map, map them.
2210 size_t map_pages = (map_end_address - map_address) / PageSize;
2211 while (map_pages > 0) {
2212 // Check if we're at the end of the physical block.
2213 if (pg_pages == 0) {
2214 // Ensure there are more pages to map.
2215 ASSERT(pg_it != pg.end());
2216
2217 // Advance our physical block.
2218 ++pg_it;
2219 pg_phys_addr = pg_it->GetAddress();
2220 pg_pages = pg_it->GetNumPages();
2221 }
2222
2223 // Map whatever we can.
2224 const size_t cur_pages = std::min(pg_pages, map_pages);
2225 ASSERT(this->Operate(map_address, map_pages, map_properties.perm, OperationType::Map,
2226 pg_phys_addr) == ResultSuccess);
2227
2228 // Advance.
2229 map_address += cur_pages * PageSize;
2230 map_pages -= cur_pages;
2231
2232 pg_phys_addr += cur_pages * PageSize;
2233 pg_pages -= cur_pages;
2234 }
2235
2236 // Check if we're done.
2237 if (last_address <= info.GetLastAddress()) {
2238 break;
2239 }
2240
2241 // Advance.
2242 ++it;
2243 }
2244
2245 // Check that we re-mapped precisely the page group.
2246 ASSERT((++pg_it) == pg.end());
2247}
2248
2249Result KPageTable::MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
2250 KPhysicalAddress phys_addr, bool is_pa_valid,
2251 KProcessAddress region_start, size_t region_num_pages,
2252 KMemoryState state, KMemoryPermission perm) {
2253 ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize);
2254
2255 // Ensure this is a valid map request.
2256 R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state),
2257 ResultInvalidCurrentMemory);
2258 R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory);
2259
2260 // Lock the table.
2261 KScopedLightLock lk(m_general_lock);
2262
2263 // Find a random address to map at.
2264 KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment,
2265 0, this->GetNumGuardPages());
2266 R_UNLESS(addr != 0, ResultOutOfMemory);
2267 ASSERT(Common::IsAligned(GetInteger(addr), alignment));
2268 ASSERT(this->CanContain(addr, num_pages * PageSize, state));
2269 ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free,
2270 KMemoryPermission::None, KMemoryPermission::None,
2271 KMemoryAttribute::None, KMemoryAttribute::None) == ResultSuccess);
2272
2273 // Create an update allocator.
2274 Result allocator_result;
2275 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2276 m_memory_block_slab_manager);
2277 R_TRY(allocator_result);
2278
2279 // We're going to perform an update, so create a helper.
2280 KScopedPageTableUpdater updater(this);
2281
2282 // Perform mapping operation.
2283 if (is_pa_valid) {
2284 const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
2285 R_TRY(this->Operate(addr, num_pages, properties.perm, OperationType::Map, phys_addr));
2286 } else {
2287 R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), addr, num_pages, perm));
2288 }
2289
2290 // Update the blocks.
2291 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
2292 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2293 KMemoryBlockDisableMergeAttribute::None);
2294
2295 // We successfully mapped the pages.
2296 *out_addr = addr;
2297 R_SUCCEED();
2298}
2299
2300Result KPageTable::MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
2301 KMemoryPermission perm) {
2302 // Check that the map is in range.
2303 const size_t size = num_pages * PageSize;
2304 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
2305
2306 // Lock the table.
2307 KScopedLightLock lk(m_general_lock);
2308
2309 // Check the memory state.
2310 size_t num_allocator_blocks;
2311 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
2312 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
2313 KMemoryPermission::None, KMemoryAttribute::None,
2314 KMemoryAttribute::None));
2315
2316 // Create an update allocator.
2317 Result allocator_result;
2318 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2319 m_memory_block_slab_manager, num_allocator_blocks);
2320 R_TRY(allocator_result);
2321
2322 // We're going to perform an update, so create a helper.
2323 KScopedPageTableUpdater updater(this);
2324
2325 // Map the pages.
2326 R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), address, num_pages, perm));
2327
2328 // Update the blocks.
2329 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm,
2330 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2331 KMemoryBlockDisableMergeAttribute::None);
2332
2333 R_SUCCEED();
2334}
2335
2336Result KPageTable::UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state) {
2337 // Check that the unmap is in range.
2338 const size_t size = num_pages * PageSize;
2339 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
2340
2341 // Lock the table.
2342 KScopedLightLock lk(m_general_lock);
2343
2344 // Check the memory state.
2345 size_t num_allocator_blocks;
2346 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
2347 KMemoryState::All, state, KMemoryPermission::None,
2348 KMemoryPermission::None, KMemoryAttribute::All,
2349 KMemoryAttribute::None));
2350
2351 // Create an update allocator.
2352 Result allocator_result;
2353 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2354 m_memory_block_slab_manager, num_allocator_blocks);
2355 R_TRY(allocator_result);
2356
2357 // We're going to perform an update, so create a helper.
2358 KScopedPageTableUpdater updater(this);
2359
2360 // Perform the unmap.
2361 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
2362 DisableMergeAttribute::None};
2363 R_TRY(this->Operate(address, num_pages, unmap_properties.perm, OperationType::Unmap));
2364
2365 // Update the blocks.
2366 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free,
2367 KMemoryPermission::None, KMemoryAttribute::None,
2368 KMemoryBlockDisableMergeAttribute::None,
2369 KMemoryBlockDisableMergeAttribute::Normal);
2370
2371 R_SUCCEED();
2372}
2373
2374Result KPageTable::MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
2375 KProcessAddress region_start, size_t region_num_pages,
2376 KMemoryState state, KMemoryPermission perm) {
2377 ASSERT(!this->IsLockedByCurrentThread());
2378
2379 // Ensure this is a valid map request.
2380 const size_t num_pages = pg.GetNumPages();
2381 R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state),
2382 ResultInvalidCurrentMemory);
2383 R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory);
2384
2385 // Lock the table.
2386 KScopedLightLock lk(m_general_lock);
2387
2388 // Find a random address to map at.
2389 KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, PageSize,
2390 0, this->GetNumGuardPages());
2391 R_UNLESS(addr != 0, ResultOutOfMemory);
2392 ASSERT(this->CanContain(addr, num_pages * PageSize, state));
2393 ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free,
2394 KMemoryPermission::None, KMemoryPermission::None,
2395 KMemoryAttribute::None, KMemoryAttribute::None) == ResultSuccess);
2396
2397 // Create an update allocator.
2398 Result allocator_result;
2399 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2400 m_memory_block_slab_manager);
2401 R_TRY(allocator_result);
2402
2403 // We're going to perform an update, so create a helper.
2404 KScopedPageTableUpdater updater(this);
2405
2406 // Perform mapping operation.
2407 const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
2408 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
2409
2410 // Update the blocks.
2411 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
2412 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2413 KMemoryBlockDisableMergeAttribute::None);
2414
2415 // We successfully mapped the pages.
2416 *out_addr = addr;
2417 R_SUCCEED();
2418}
2419
2420Result KPageTable::MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMemoryState state,
2421 KMemoryPermission perm) {
2422 ASSERT(!this->IsLockedByCurrentThread());
2423
2424 // Ensure this is a valid map request.
2425 const size_t num_pages = pg.GetNumPages();
2426 const size_t size = num_pages * PageSize;
2427 R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory);
2428
2429 // Lock the table.
2430 KScopedLightLock lk(m_general_lock);
2431
2432 // Check if state allows us to map.
2433 size_t num_allocator_blocks;
2434 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), addr, size,
2435 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
2436 KMemoryPermission::None, KMemoryAttribute::None,
2437 KMemoryAttribute::None));
2438
2439 // Create an update allocator.
2440 Result allocator_result;
2441 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2442 m_memory_block_slab_manager, num_allocator_blocks);
2443 R_TRY(allocator_result);
2444
2445 // We're going to perform an update, so create a helper.
2446 KScopedPageTableUpdater updater(this);
2447
2448 // Perform mapping operation.
2449 const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
2450 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
2451
2452 // Update the blocks.
2453 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
2454 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2455 KMemoryBlockDisableMergeAttribute::None);
2456
2457 // We successfully mapped the pages.
2458 R_SUCCEED();
2459}
2460
2461Result KPageTable::UnmapPageGroup(KProcessAddress address, const KPageGroup& pg,
2462 KMemoryState state) {
2463 ASSERT(!this->IsLockedByCurrentThread());
2464
2465 // Ensure this is a valid unmap request.
2466 const size_t num_pages = pg.GetNumPages();
2467 const size_t size = num_pages * PageSize;
2468 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
2469
2470 // Lock the table.
2471 KScopedLightLock lk(m_general_lock);
2472
2473 // Check if state allows us to unmap.
2474 size_t num_allocator_blocks;
2475 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
2476 KMemoryState::All, state, KMemoryPermission::None,
2477 KMemoryPermission::None, KMemoryAttribute::All,
2478 KMemoryAttribute::None));
2479
2480 // Check that the page group is valid.
2481 R_UNLESS(this->IsValidPageGroup(pg, address, num_pages), ResultInvalidCurrentMemory);
2482
2483 // Create an update allocator.
2484 Result allocator_result;
2485 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2486 m_memory_block_slab_manager, num_allocator_blocks);
2487 R_TRY(allocator_result);
2488
2489 // We're going to perform an update, so create a helper.
2490 KScopedPageTableUpdater updater(this);
2491
2492 // Perform unmapping operation.
2493 const KPageProperties properties = {KMemoryPermission::None, false, false,
2494 DisableMergeAttribute::None};
2495 R_TRY(this->Operate(address, num_pages, properties.perm, OperationType::Unmap));
2496
2497 // Update the blocks.
2498 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free,
2499 KMemoryPermission::None, KMemoryAttribute::None,
2500 KMemoryBlockDisableMergeAttribute::None,
2501 KMemoryBlockDisableMergeAttribute::Normal);
2502
2503 R_SUCCEED();
2504}
2505
2506Result KPageTable::MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages,
2507 KMemoryState state_mask, KMemoryState state,
2508 KMemoryPermission perm_mask, KMemoryPermission perm,
2509 KMemoryAttribute attr_mask, KMemoryAttribute attr) {
2510 // Ensure that the page group isn't null.
2511 ASSERT(out != nullptr);
2512
2513 // Make sure that the region we're mapping is valid for the table.
2514 const size_t size = num_pages * PageSize;
2515 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
2516
2517 // Lock the table.
2518 KScopedLightLock lk(m_general_lock);
2519
2520 // Check if state allows us to create the group.
2521 R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState::FlagReferenceCounted,
2522 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
2523 attr_mask, attr));
2524
2525 // Create a new page group for the region.
2526 R_TRY(this->MakePageGroup(*out, address, num_pages));
2527
2528 R_SUCCEED();
2529}
2530
2531Result KPageTable::SetProcessMemoryPermission(KProcessAddress addr, size_t size,
2532 Svc::MemoryPermission svc_perm) {
2533 const size_t num_pages = size / PageSize;
2534
2535 // Lock the table.
2536 KScopedLightLock lk(m_general_lock);
2537
2538 // Verify we can change the memory permission.
2539 KMemoryState old_state;
2540 KMemoryPermission old_perm;
2541 size_t num_allocator_blocks;
2542 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), nullptr,
2543 std::addressof(num_allocator_blocks), addr, size,
2544 KMemoryState::FlagCode, KMemoryState::FlagCode,
2545 KMemoryPermission::None, KMemoryPermission::None,
2546 KMemoryAttribute::All, KMemoryAttribute::None));
2547
2548 // Determine new perm/state.
2549 const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm);
2550 KMemoryState new_state = old_state;
2551 const bool is_w = (new_perm & KMemoryPermission::UserWrite) == KMemoryPermission::UserWrite;
2552 const bool is_x = (new_perm & KMemoryPermission::UserExecute) == KMemoryPermission::UserExecute;
2553 const bool was_x =
2554 (old_perm & KMemoryPermission::UserExecute) == KMemoryPermission::UserExecute;
2555 ASSERT(!(is_w && is_x));
2556
2557 if (is_w) {
2558 switch (old_state) {
2559 case KMemoryState::Code:
2560 new_state = KMemoryState::CodeData;
2561 break;
2562 case KMemoryState::AliasCode:
2563 new_state = KMemoryState::AliasCodeData;
2564 break;
2565 default:
2566 ASSERT(false);
2567 break;
2568 }
2569 }
2570
2571 // Succeed if there's nothing to do.
2572 R_SUCCEED_IF(old_perm == new_perm && old_state == new_state);
2573
2574 // Create an update allocator.
2575 Result allocator_result{ResultSuccess};
2576 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2577 m_memory_block_slab_manager, num_allocator_blocks);
2578 R_TRY(allocator_result);
2579
2580 // Perform mapping operation.
2581 const auto operation =
2582 was_x ? OperationType::ChangePermissionsAndRefresh : OperationType::ChangePermissions;
2583 R_TRY(Operate(addr, num_pages, new_perm, operation));
2584
2585 // Update the blocks.
2586 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, new_state, new_perm,
2587 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
2588 KMemoryBlockDisableMergeAttribute::None);
2589
2590 // Ensure cache coherency, if we're setting pages as executable.
2591 if (is_x) {
2592 m_system.InvalidateCpuInstructionCacheRange(GetInteger(addr), size);
2593 }
2594
2595 R_SUCCEED();
2596}
2597
2598KMemoryInfo KPageTable::QueryInfoImpl(KProcessAddress addr) {
2599 KScopedLightLock lk(m_general_lock);
2600
2601 return m_memory_block_manager.FindBlock(addr)->GetMemoryInfo();
2602}
2603
2604KMemoryInfo KPageTable::QueryInfo(KProcessAddress addr) {
2605 if (!Contains(addr, 1)) {
2606 return {
2607 .m_address = GetInteger(m_address_space_end),
2608 .m_size = 0 - GetInteger(m_address_space_end),
2609 .m_state = static_cast<KMemoryState>(Svc::MemoryState::Inaccessible),
2610 .m_device_disable_merge_left_count = 0,
2611 .m_device_disable_merge_right_count = 0,
2612 .m_ipc_lock_count = 0,
2613 .m_device_use_count = 0,
2614 .m_ipc_disable_merge_count = 0,
2615 .m_permission = KMemoryPermission::None,
2616 .m_attribute = KMemoryAttribute::None,
2617 .m_original_permission = KMemoryPermission::None,
2618 .m_disable_merge_attribute = KMemoryBlockDisableMergeAttribute::None,
2619 };
2620 }
2621
2622 return QueryInfoImpl(addr);
2623}
2624
2625Result KPageTable::SetMemoryPermission(KProcessAddress addr, size_t size,
2626 Svc::MemoryPermission svc_perm) {
2627 const size_t num_pages = size / PageSize;
2628
2629 // Lock the table.
2630 KScopedLightLock lk(m_general_lock);
2631
2632 // Verify we can change the memory permission.
2633 KMemoryState old_state;
2634 KMemoryPermission old_perm;
2635 size_t num_allocator_blocks;
2636 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), nullptr,
2637 std::addressof(num_allocator_blocks), addr, size,
2638 KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect,
2639 KMemoryPermission::None, KMemoryPermission::None,
2640 KMemoryAttribute::All, KMemoryAttribute::None));
2641
2642 // Determine new perm.
2643 const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm);
2644 R_SUCCEED_IF(old_perm == new_perm);
2645
2646 // Create an update allocator.
2647 Result allocator_result{ResultSuccess};
2648 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2649 m_memory_block_slab_manager, num_allocator_blocks);
2650 R_TRY(allocator_result);
2651
2652 // Perform mapping operation.
2653 R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
2654
2655 // Update the blocks.
2656 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm,
2657 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
2658 KMemoryBlockDisableMergeAttribute::None);
2659
2660 R_SUCCEED();
2661}
2662
2663Result KPageTable::SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mask, u32 attr) {
2664 const size_t num_pages = size / PageSize;
2665 ASSERT((static_cast<KMemoryAttribute>(mask) | KMemoryAttribute::SetMask) ==
2666 KMemoryAttribute::SetMask);
2667
2668 // Lock the table.
2669 KScopedLightLock lk(m_general_lock);
2670
2671 // Verify we can change the memory attribute.
2672 KMemoryState old_state;
2673 KMemoryPermission old_perm;
2674 KMemoryAttribute old_attr;
2675 size_t num_allocator_blocks;
2676 constexpr auto AttributeTestMask =
2677 ~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared);
2678 const KMemoryState state_test_mask =
2679 static_cast<KMemoryState>(((mask & static_cast<u32>(KMemoryAttribute::Uncached))
2680 ? static_cast<u32>(KMemoryState::FlagCanChangeAttribute)
2681 : 0) |
2682 ((mask & static_cast<u32>(KMemoryAttribute::PermissionLocked))
2683 ? static_cast<u32>(KMemoryState::FlagCanPermissionLock)
2684 : 0));
2685 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
2686 std::addressof(old_attr), std::addressof(num_allocator_blocks),
2687 addr, size, state_test_mask, state_test_mask,
2688 KMemoryPermission::None, KMemoryPermission::None,
2689 AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask));
2690
2691 // Create an update allocator.
2692 Result allocator_result{ResultSuccess};
2693 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2694 m_memory_block_slab_manager, num_allocator_blocks);
2695 R_TRY(allocator_result);
2696
2697 // If we need to, perform a change attribute operation.
2698 if (True(KMemoryAttribute::Uncached & static_cast<KMemoryAttribute>(mask))) {
2699 // Perform operation.
2700 R_TRY(this->Operate(addr, num_pages, old_perm,
2701 OperationType::ChangePermissionsAndRefreshAndFlush, 0));
2702 }
2703
2704 // Update the blocks.
2705 m_memory_block_manager.UpdateAttribute(std::addressof(allocator), addr, num_pages,
2706 static_cast<KMemoryAttribute>(mask),
2707 static_cast<KMemoryAttribute>(attr));
2708
2709 R_SUCCEED();
2710}
2711
2712Result KPageTable::SetMaxHeapSize(size_t size) {
2713 // Lock the table.
2714 KScopedLightLock lk(m_general_lock);
2715
2716 // Only process page tables are allowed to set heap size.
2717 ASSERT(!this->IsKernel());
2718
2719 m_max_heap_size = size;
2720
2721 R_SUCCEED();
2722}
2723
2724Result KPageTable::SetHeapSize(u64* out, size_t size) {
2725 // Lock the physical memory mutex.
2726 KScopedLightLock map_phys_mem_lk(m_map_physical_memory_lock);
2727
2728 // Try to perform a reduction in heap, instead of an extension.
2729 KProcessAddress cur_address{};
2730 size_t allocation_size{};
2731 {
2732 // Lock the table.
2733 KScopedLightLock lk(m_general_lock);
2734
2735 // Validate that setting heap size is possible at all.
2736 R_UNLESS(!m_is_kernel, ResultOutOfMemory);
2737 R_UNLESS(size <= static_cast<size_t>(m_heap_region_end - m_heap_region_start),
2738 ResultOutOfMemory);
2739 R_UNLESS(size <= m_max_heap_size, ResultOutOfMemory);
2740
2741 if (size < GetHeapSize()) {
2742 // The size being requested is less than the current size, so we need to free the end of
2743 // the heap.
2744
2745 // Validate memory state.
2746 size_t num_allocator_blocks;
2747 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks),
2748 m_heap_region_start + size, GetHeapSize() - size,
2749 KMemoryState::All, KMemoryState::Normal,
2750 KMemoryPermission::All, KMemoryPermission::UserReadWrite,
2751 KMemoryAttribute::All, KMemoryAttribute::None));
2752
2753 // Create an update allocator.
2754 Result allocator_result{ResultSuccess};
2755 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2756 m_memory_block_slab_manager,
2757 num_allocator_blocks);
2758 R_TRY(allocator_result);
2759
2760 // Unmap the end of the heap.
2761 const auto num_pages = (GetHeapSize() - size) / PageSize;
2762 R_TRY(Operate(m_heap_region_start + size, num_pages, KMemoryPermission::None,
2763 OperationType::Unmap));
2764
2765 // Release the memory from the resource limit.
2766 m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, num_pages * PageSize);
2767
2768 // Apply the memory block update.
2769 m_memory_block_manager.Update(std::addressof(allocator), m_heap_region_start + size,
2770 num_pages, KMemoryState::Free, KMemoryPermission::None,
2771 KMemoryAttribute::None,
2772 KMemoryBlockDisableMergeAttribute::None,
2773 size == 0 ? KMemoryBlockDisableMergeAttribute::Normal
2774 : KMemoryBlockDisableMergeAttribute::None);
2775
2776 // Update the current heap end.
2777 m_current_heap_end = m_heap_region_start + size;
2778
2779 // Set the output.
2780 *out = GetInteger(m_heap_region_start);
2781 R_SUCCEED();
2782 } else if (size == GetHeapSize()) {
2783 // The size requested is exactly the current size.
2784 *out = GetInteger(m_heap_region_start);
2785 R_SUCCEED();
2786 } else {
2787 // We have to allocate memory. Determine how much to allocate and where while the table
2788 // is locked.
2789 cur_address = m_current_heap_end;
2790 allocation_size = size - GetHeapSize();
2791 }
2792 }
2793
2794 // Reserve memory for the heap extension.
2795 KScopedResourceReservation memory_reservation(
2796 m_resource_limit, LimitableResource::PhysicalMemoryMax, allocation_size);
2797 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
2798
2799 // Allocate pages for the heap extension.
2800 KPageGroup pg{m_kernel, m_block_info_manager};
2801 R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen(
2802 &pg, allocation_size / PageSize,
2803 KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option)));
2804
2805 // Clear all the newly allocated pages.
2806 for (const auto& it : pg) {
2807 std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value,
2808 it.GetSize());
2809 }
2810
2811 // Map the pages.
2812 {
2813 // Lock the table.
2814 KScopedLightLock lk(m_general_lock);
2815
2816 // Ensure that the heap hasn't changed since we began executing.
2817 ASSERT(cur_address == m_current_heap_end);
2818
2819 // Check the memory state.
2820 size_t num_allocator_blocks{};
2821 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), m_current_heap_end,
2822 allocation_size, KMemoryState::All, KMemoryState::Free,
2823 KMemoryPermission::None, KMemoryPermission::None,
2824 KMemoryAttribute::None, KMemoryAttribute::None));
2825
2826 // Create an update allocator.
2827 Result allocator_result{ResultSuccess};
2828 KMemoryBlockManagerUpdateAllocator allocator(
2829 std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks);
2830 R_TRY(allocator_result);
2831
2832 // Map the pages.
2833 const auto num_pages = allocation_size / PageSize;
2834 R_TRY(Operate(m_current_heap_end, num_pages, pg, OperationType::MapGroup));
2835
2836 // Clear all the newly allocated pages.
2837 for (size_t cur_page = 0; cur_page < num_pages; ++cur_page) {
2838 std::memset(m_memory->GetPointer(m_current_heap_end + (cur_page * PageSize)), 0,
2839 PageSize);
2840 }
2841
2842 // We succeeded, so commit our memory reservation.
2843 memory_reservation.Commit();
2844
2845 // Apply the memory block update.
2846 m_memory_block_manager.Update(
2847 std::addressof(allocator), m_current_heap_end, num_pages, KMemoryState::Normal,
2848 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
2849 m_heap_region_start == m_current_heap_end ? KMemoryBlockDisableMergeAttribute::Normal
2850 : KMemoryBlockDisableMergeAttribute::None,
2851 KMemoryBlockDisableMergeAttribute::None);
2852
2853 // Update the current heap end.
2854 m_current_heap_end = m_heap_region_start + size;
2855
2856 // Set the output.
2857 *out = GetInteger(m_heap_region_start);
2858 R_SUCCEED();
2859 }
2860}
2861
2862Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address,
2863 size_t size, KMemoryPermission perm,
2864 bool is_aligned, bool check_heap) {
2865 // Lightly validate the range before doing anything else.
2866 const size_t num_pages = size / PageSize;
2867 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
2868
2869 // Lock the table.
2870 KScopedLightLock lk(m_general_lock);
2871
2872 // Check the memory state.
2873 const auto test_state =
2874 (is_aligned ? KMemoryState::FlagCanAlignedDeviceMap : KMemoryState::FlagCanDeviceMap) |
2875 (check_heap ? KMemoryState::FlagReferenceCounted : KMemoryState::None);
2876 size_t num_allocator_blocks;
2877 KMemoryState old_state;
2878 R_TRY(this->CheckMemoryState(std::addressof(old_state), nullptr, nullptr,
2879 std::addressof(num_allocator_blocks), address, size, test_state,
2880 test_state, perm, perm,
2881 KMemoryAttribute::IpcLocked | KMemoryAttribute::Locked,
2882 KMemoryAttribute::None, KMemoryAttribute::DeviceShared));
2883
2884 // Create an update allocator.
2885 Result allocator_result;
2886 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2887 m_memory_block_slab_manager, num_allocator_blocks);
2888 R_TRY(allocator_result);
2889
2890 // Update the memory blocks.
2891 m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages,
2892 &KMemoryBlock::ShareToDevice, KMemoryPermission::None);
2893
2894 // Set whether the locked memory was io.
2895 *out_is_io =
2896 static_cast<Svc::MemoryState>(old_state & KMemoryState::Mask) == Svc::MemoryState::Io;
2897
2898 R_SUCCEED();
2899}
2900
2901Result KPageTable::LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size,
2902 bool check_heap) {
2903 // Lightly validate the range before doing anything else.
2904 const size_t num_pages = size / PageSize;
2905 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
2906
2907 // Lock the table.
2908 KScopedLightLock lk(m_general_lock);
2909
2910 // Check the memory state.
2911 const auto test_state = KMemoryState::FlagCanDeviceMap |
2912 (check_heap ? KMemoryState::FlagReferenceCounted : KMemoryState::None);
2913 size_t num_allocator_blocks;
2914 R_TRY(this->CheckMemoryStateContiguous(
2915 std::addressof(num_allocator_blocks), address, size, test_state, test_state,
2916 KMemoryPermission::None, KMemoryPermission::None,
2917 KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared));
2918
2919 // Create an update allocator.
2920 Result allocator_result;
2921 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2922 m_memory_block_slab_manager, num_allocator_blocks);
2923 R_TRY(allocator_result);
2924
2925 // Update the memory blocks.
2926 const KMemoryBlockManager::MemoryBlockLockFunction lock_func =
2927 m_enable_device_address_space_merge
2928 ? &KMemoryBlock::UpdateDeviceDisableMergeStateForShare
2929 : &KMemoryBlock::UpdateDeviceDisableMergeStateForShareRight;
2930 m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, lock_func,
2931 KMemoryPermission::None);
2932
2933 R_SUCCEED();
2934}
2935
2936Result KPageTable::UnlockForDeviceAddressSpace(KProcessAddress address, size_t size) {
2937 // Lightly validate the range before doing anything else.
2938 const size_t num_pages = size / PageSize;
2939 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
2940
2941 // Lock the table.
2942 KScopedLightLock lk(m_general_lock);
2943
2944 // Check the memory state.
2945 size_t num_allocator_blocks;
2946 R_TRY(this->CheckMemoryStateContiguous(
2947 std::addressof(num_allocator_blocks), address, size, KMemoryState::FlagCanDeviceMap,
2948 KMemoryState::FlagCanDeviceMap, KMemoryPermission::None, KMemoryPermission::None,
2949 KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared));
2950
2951 // Create an update allocator.
2952 Result allocator_result{ResultSuccess};
2953 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2954 m_memory_block_slab_manager, num_allocator_blocks);
2955 R_TRY(allocator_result);
2956
2957 // Update the memory blocks.
2958 m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages,
2959 &KMemoryBlock::UnshareToDevice, KMemoryPermission::None);
2960
2961 R_SUCCEED();
2962}
2963
2964Result KPageTable::LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address,
2965 size_t size) {
2966 R_RETURN(this->LockMemoryAndOpen(
2967 nullptr, out, address, size, KMemoryState::FlagCanIpcUserBuffer,
2968 KMemoryState::FlagCanIpcUserBuffer, KMemoryPermission::All,
2969 KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None,
2970 KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite,
2971 KMemoryAttribute::Locked));
2972}
2973
2974Result KPageTable::UnlockForIpcUserBuffer(KProcessAddress address, size_t size) {
2975 R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanIpcUserBuffer,
2976 KMemoryState::FlagCanIpcUserBuffer, KMemoryPermission::None,
2977 KMemoryPermission::None, KMemoryAttribute::All,
2978 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
2979 KMemoryAttribute::Locked, nullptr));
2980}
2981
2982Result KPageTable::LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size,
2983 KMemoryPermission perm) {
2984 R_RETURN(this->LockMemoryAndOpen(out, nullptr, address, size, KMemoryState::FlagCanTransfer,
2985 KMemoryState::FlagCanTransfer, KMemoryPermission::All,
2986 KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
2987 KMemoryAttribute::None, perm, KMemoryAttribute::Locked));
2988}
2989
2990Result KPageTable::UnlockForTransferMemory(KProcessAddress address, size_t size,
2991 const KPageGroup& pg) {
2992 R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanTransfer,
2993 KMemoryState::FlagCanTransfer, KMemoryPermission::None,
2994 KMemoryPermission::None, KMemoryAttribute::All,
2995 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
2996 KMemoryAttribute::Locked, std::addressof(pg)));
2997}
2998
2999Result KPageTable::LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size) {
3000 R_RETURN(this->LockMemoryAndOpen(
3001 out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
3002 KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
3003 KMemoryAttribute::None, KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite,
3004 KMemoryAttribute::Locked));
3005}
3006
3007Result KPageTable::UnlockForCodeMemory(KProcessAddress addr, size_t size, const KPageGroup& pg) {
3008 R_RETURN(this->UnlockMemory(
3009 addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
3010 KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All,
3011 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, KMemoryAttribute::Locked, &pg));
3012}
3013
3014bool KPageTable::IsRegionContiguous(KProcessAddress addr, u64 size) const {
3015 auto start_ptr = m_system.DeviceMemory().GetPointer<u8>(GetInteger(addr));
3016 for (u64 offset{}; offset < size; offset += PageSize) {
3017 if (start_ptr != m_system.DeviceMemory().GetPointer<u8>(GetInteger(addr) + offset)) {
3018 return false;
3019 }
3020 start_ptr += PageSize;
3021 }
3022 return true;
3023}
3024
3025void KPageTable::AddRegionToPages(KProcessAddress start, size_t num_pages,
3026 KPageGroup& page_linked_list) {
3027 KProcessAddress addr{start};
3028 while (addr < start + (num_pages * PageSize)) {
3029 const KPhysicalAddress paddr{GetPhysicalAddr(addr)};
3030 ASSERT(paddr != 0);
3031 page_linked_list.AddBlock(paddr, 1);
3032 addr += PageSize;
3033 }
3034}
3035
3036KProcessAddress KPageTable::AllocateVirtualMemory(KProcessAddress start, size_t region_num_pages,
3037 u64 needed_num_pages, size_t align) {
3038 if (m_enable_aslr) {
3039 UNIMPLEMENTED();
3040 }
3041 return m_memory_block_manager.FindFreeArea(start, region_num_pages, needed_num_pages, align, 0,
3042 IsKernel() ? 1 : 4);
3043}
3044
3045Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, const KPageGroup& page_group,
3046 OperationType operation) {
3047 ASSERT(this->IsLockedByCurrentThread());
3048
3049 ASSERT(Common::IsAligned(GetInteger(addr), PageSize));
3050 ASSERT(num_pages > 0);
3051 ASSERT(num_pages == page_group.GetNumPages());
3052
3053 switch (operation) {
3054 case OperationType::MapGroup:
3055 case OperationType::MapFirstGroup: {
3056 // We want to maintain a new reference to every page in the group.
3057 KScopedPageGroup spg(page_group, operation != OperationType::MapFirstGroup);
3058
3059 for (const auto& node : page_group) {
3060 const size_t size{node.GetNumPages() * PageSize};
3061
3062 // Map the pages.
3063 m_memory->MapMemoryRegion(*m_page_table_impl, addr, size, node.GetAddress());
3064
3065 addr += size;
3066 }
3067
3068 // We succeeded! We want to persist the reference to the pages.
3069 spg.CancelClose();
3070
3071 break;
3072 }
3073 default:
3074 ASSERT(false);
3075 break;
3076 }
3077
3078 R_SUCCEED();
3079}
3080
3081Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, KMemoryPermission perm,
3082 OperationType operation, KPhysicalAddress map_addr) {
3083 ASSERT(this->IsLockedByCurrentThread());
3084
3085 ASSERT(num_pages > 0);
3086 ASSERT(Common::IsAligned(GetInteger(addr), PageSize));
3087 ASSERT(ContainsPages(addr, num_pages));
3088
3089 switch (operation) {
3090 case OperationType::Unmap: {
3091 // Ensure that any pages we track close on exit.
3092 KPageGroup pages_to_close{m_kernel, this->GetBlockInfoManager()};
3093 SCOPE_EXIT({ pages_to_close.CloseAndReset(); });
3094
3095 this->AddRegionToPages(addr, num_pages, pages_to_close);
3096 m_memory->UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize);
3097 break;
3098 }
3099 case OperationType::Map: {
3100 ASSERT(map_addr);
3101 ASSERT(Common::IsAligned(GetInteger(map_addr), PageSize));
3102 m_memory->MapMemoryRegion(*m_page_table_impl, addr, num_pages * PageSize, map_addr);
3103
3104 // Open references to pages, if we should.
3105 if (IsHeapPhysicalAddress(m_kernel.MemoryLayout(), map_addr)) {
3106 m_kernel.MemoryManager().Open(map_addr, num_pages);
3107 }
3108 break;
3109 }
3110 case OperationType::Separate: {
3111 // HACK: Unimplemented.
3112 break;
3113 }
3114 case OperationType::ChangePermissions:
3115 case OperationType::ChangePermissionsAndRefresh:
3116 case OperationType::ChangePermissionsAndRefreshAndFlush:
3117 break;
3118 default:
3119 ASSERT(false);
3120 break;
3121 }
3122 R_SUCCEED();
3123}
3124
3125void KPageTable::FinalizeUpdate(PageLinkedList* page_list) {
3126 while (page_list->Peek()) {
3127 [[maybe_unused]] auto page = page_list->Pop();
3128
3129 // TODO(bunnei): Free pages once they are allocated in guest memory
3130 // ASSERT(this->GetPageTableManager().IsInPageTableHeap(page));
3131 // ASSERT(this->GetPageTableManager().GetRefCount(page) == 0);
3132 // this->GetPageTableManager().Free(page);
3133 }
3134}
3135
3136KProcessAddress KPageTable::GetRegionAddress(Svc::MemoryState state) const {
3137 switch (state) {
3138 case Svc::MemoryState::Free:
3139 case Svc::MemoryState::Kernel:
3140 return m_address_space_start;
3141 case Svc::MemoryState::Normal:
3142 return m_heap_region_start;
3143 case Svc::MemoryState::Ipc:
3144 case Svc::MemoryState::NonSecureIpc:
3145 case Svc::MemoryState::NonDeviceIpc:
3146 return m_alias_region_start;
3147 case Svc::MemoryState::Stack:
3148 return m_stack_region_start;
3149 case Svc::MemoryState::Static:
3150 case Svc::MemoryState::ThreadLocal:
3151 return m_kernel_map_region_start;
3152 case Svc::MemoryState::Io:
3153 case Svc::MemoryState::Shared:
3154 case Svc::MemoryState::AliasCode:
3155 case Svc::MemoryState::AliasCodeData:
3156 case Svc::MemoryState::Transfered:
3157 case Svc::MemoryState::SharedTransfered:
3158 case Svc::MemoryState::SharedCode:
3159 case Svc::MemoryState::GeneratedCode:
3160 case Svc::MemoryState::CodeOut:
3161 case Svc::MemoryState::Coverage:
3162 case Svc::MemoryState::Insecure:
3163 return m_alias_code_region_start;
3164 case Svc::MemoryState::Code:
3165 case Svc::MemoryState::CodeData:
3166 return m_code_region_start;
3167 default:
3168 UNREACHABLE();
3169 }
3170}
3171
3172size_t KPageTable::GetRegionSize(Svc::MemoryState state) const {
3173 switch (state) {
3174 case Svc::MemoryState::Free:
3175 case Svc::MemoryState::Kernel:
3176 return m_address_space_end - m_address_space_start;
3177 case Svc::MemoryState::Normal:
3178 return m_heap_region_end - m_heap_region_start;
3179 case Svc::MemoryState::Ipc:
3180 case Svc::MemoryState::NonSecureIpc:
3181 case Svc::MemoryState::NonDeviceIpc:
3182 return m_alias_region_end - m_alias_region_start;
3183 case Svc::MemoryState::Stack:
3184 return m_stack_region_end - m_stack_region_start;
3185 case Svc::MemoryState::Static:
3186 case Svc::MemoryState::ThreadLocal:
3187 return m_kernel_map_region_end - m_kernel_map_region_start;
3188 case Svc::MemoryState::Io:
3189 case Svc::MemoryState::Shared:
3190 case Svc::MemoryState::AliasCode:
3191 case Svc::MemoryState::AliasCodeData:
3192 case Svc::MemoryState::Transfered:
3193 case Svc::MemoryState::SharedTransfered:
3194 case Svc::MemoryState::SharedCode:
3195 case Svc::MemoryState::GeneratedCode:
3196 case Svc::MemoryState::CodeOut:
3197 case Svc::MemoryState::Coverage:
3198 case Svc::MemoryState::Insecure:
3199 return m_alias_code_region_end - m_alias_code_region_start;
3200 case Svc::MemoryState::Code:
3201 case Svc::MemoryState::CodeData:
3202 return m_code_region_end - m_code_region_start;
3203 default:
3204 UNREACHABLE();
3205 }
3206}
3207
3208bool KPageTable::CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const {
3209 const KProcessAddress end = addr + size;
3210 const KProcessAddress last = end - 1;
3211
3212 const KProcessAddress region_start = this->GetRegionAddress(state);
3213 const size_t region_size = this->GetRegionSize(state);
3214
3215 const bool is_in_region =
3216 region_start <= addr && addr < end && last <= region_start + region_size - 1;
3217 const bool is_in_heap = !(end <= m_heap_region_start || m_heap_region_end <= addr ||
3218 m_heap_region_start == m_heap_region_end);
3219 const bool is_in_alias = !(end <= m_alias_region_start || m_alias_region_end <= addr ||
3220 m_alias_region_start == m_alias_region_end);
3221 switch (state) {
3222 case Svc::MemoryState::Free:
3223 case Svc::MemoryState::Kernel:
3224 return is_in_region;
3225 case Svc::MemoryState::Io:
3226 case Svc::MemoryState::Static:
3227 case Svc::MemoryState::Code:
3228 case Svc::MemoryState::CodeData:
3229 case Svc::MemoryState::Shared:
3230 case Svc::MemoryState::AliasCode:
3231 case Svc::MemoryState::AliasCodeData:
3232 case Svc::MemoryState::Stack:
3233 case Svc::MemoryState::ThreadLocal:
3234 case Svc::MemoryState::Transfered:
3235 case Svc::MemoryState::SharedTransfered:
3236 case Svc::MemoryState::SharedCode:
3237 case Svc::MemoryState::GeneratedCode:
3238 case Svc::MemoryState::CodeOut:
3239 case Svc::MemoryState::Coverage:
3240 case Svc::MemoryState::Insecure:
3241 return is_in_region && !is_in_heap && !is_in_alias;
3242 case Svc::MemoryState::Normal:
3243 ASSERT(is_in_heap);
3244 return is_in_region && !is_in_alias;
3245 case Svc::MemoryState::Ipc:
3246 case Svc::MemoryState::NonSecureIpc:
3247 case Svc::MemoryState::NonDeviceIpc:
3248 ASSERT(is_in_alias);
3249 return is_in_region && !is_in_heap;
3250 default:
3251 return false;
3252 }
3253}
3254
3255Result KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
3256 KMemoryState state, KMemoryPermission perm_mask,
3257 KMemoryPermission perm, KMemoryAttribute attr_mask,
3258 KMemoryAttribute attr) const {
3259 // Validate the states match expectation.
3260 R_UNLESS((info.m_state & state_mask) == state, ResultInvalidCurrentMemory);
3261 R_UNLESS((info.m_permission & perm_mask) == perm, ResultInvalidCurrentMemory);
3262 R_UNLESS((info.m_attribute & attr_mask) == attr, ResultInvalidCurrentMemory);
3263
3264 R_SUCCEED();
3265}
3266
3267Result KPageTable::CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr,
3268 size_t size, KMemoryState state_mask,
3269 KMemoryState state, KMemoryPermission perm_mask,
3270 KMemoryPermission perm, KMemoryAttribute attr_mask,
3271 KMemoryAttribute attr) const {
3272 ASSERT(this->IsLockedByCurrentThread());
3273
3274 // Get information about the first block.
3275 const KProcessAddress last_addr = addr + size - 1;
3276 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr);
3277 KMemoryInfo info = it->GetMemoryInfo();
3278
3279 // If the start address isn't aligned, we need a block.
3280 const size_t blocks_for_start_align =
3281 (Common::AlignDown(GetInteger(addr), PageSize) != info.GetAddress()) ? 1 : 0;
3282
3283 while (true) {
3284 // Validate against the provided masks.
3285 R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
3286
3287 // Break once we're done.
3288 if (last_addr <= info.GetLastAddress()) {
3289 break;
3290 }
3291
3292 // Advance our iterator.
3293 it++;
3294 ASSERT(it != m_memory_block_manager.cend());
3295 info = it->GetMemoryInfo();
3296 }
3297
3298 // If the end address isn't aligned, we need a block.
3299 const size_t blocks_for_end_align =
3300 (Common::AlignUp(GetInteger(addr) + size, PageSize) != info.GetEndAddress()) ? 1 : 0;
3301
3302 if (out_blocks_needed != nullptr) {
3303 *out_blocks_needed = blocks_for_start_align + blocks_for_end_align;
3304 }
3305
3306 R_SUCCEED();
3307}
3308
3309Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
3310 KMemoryAttribute* out_attr, size_t* out_blocks_needed,
3311 KMemoryBlockManager::const_iterator it,
3312 KProcessAddress last_addr, KMemoryState state_mask,
3313 KMemoryState state, KMemoryPermission perm_mask,
3314 KMemoryPermission perm, KMemoryAttribute attr_mask,
3315 KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
3316 ASSERT(this->IsLockedByCurrentThread());
3317
3318 // Get information about the first block.
3319 KMemoryInfo info = it->GetMemoryInfo();
3320
3321 // Validate all blocks in the range have correct state.
3322 const KMemoryState first_state = info.m_state;
3323 const KMemoryPermission first_perm = info.m_permission;
3324 const KMemoryAttribute first_attr = info.m_attribute;
3325 while (true) {
3326 // Validate the current block.
3327 R_UNLESS(info.m_state == first_state, ResultInvalidCurrentMemory);
3328 R_UNLESS(info.m_permission == first_perm, ResultInvalidCurrentMemory);
3329 R_UNLESS((info.m_attribute | ignore_attr) == (first_attr | ignore_attr),
3330 ResultInvalidCurrentMemory);
3331
3332 // Validate against the provided masks.
3333 R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
3334
3335 // Break once we're done.
3336 if (last_addr <= info.GetLastAddress()) {
3337 break;
3338 }
3339
3340 // Advance our iterator.
3341 it++;
3342 ASSERT(it != m_memory_block_manager.cend());
3343 info = it->GetMemoryInfo();
3344 }
3345
3346 // Write output state.
3347 if (out_state != nullptr) {
3348 *out_state = first_state;
3349 }
3350 if (out_perm != nullptr) {
3351 *out_perm = first_perm;
3352 }
3353 if (out_attr != nullptr) {
3354 *out_attr = static_cast<KMemoryAttribute>(first_attr & ~ignore_attr);
3355 }
3356
3357 // If the end address isn't aligned, we need a block.
3358 if (out_blocks_needed != nullptr) {
3359 const size_t blocks_for_end_align =
3360 (Common::AlignDown(GetInteger(last_addr), PageSize) + PageSize != info.GetEndAddress())
3361 ? 1
3362 : 0;
3363 *out_blocks_needed = blocks_for_end_align;
3364 }
3365
3366 R_SUCCEED();
3367}
3368
3369Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
3370 KMemoryAttribute* out_attr, size_t* out_blocks_needed,
3371 KProcessAddress addr, size_t size, KMemoryState state_mask,
3372 KMemoryState state, KMemoryPermission perm_mask,
3373 KMemoryPermission perm, KMemoryAttribute attr_mask,
3374 KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
3375 ASSERT(this->IsLockedByCurrentThread());
3376
3377 // Check memory state.
3378 const KProcessAddress last_addr = addr + size - 1;
3379 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr);
3380 R_TRY(this->CheckMemoryState(out_state, out_perm, out_attr, out_blocks_needed, it, last_addr,
3381 state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr));
3382
3383 // If the start address isn't aligned, we need a block.
3384 if (out_blocks_needed != nullptr &&
3385 Common::AlignDown(GetInteger(addr), PageSize) != it->GetAddress()) {
3386 ++(*out_blocks_needed);
3387 }
3388
3389 R_SUCCEED();
3390}
3391
3392Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_KPhysicalAddress,
3393 KProcessAddress addr, size_t size, KMemoryState state_mask,
3394 KMemoryState state, KMemoryPermission perm_mask,
3395 KMemoryPermission perm, KMemoryAttribute attr_mask,
3396 KMemoryAttribute attr, KMemoryPermission new_perm,
3397 KMemoryAttribute lock_attr) {
3398 // Validate basic preconditions.
3399 ASSERT((lock_attr & attr) == KMemoryAttribute::None);
3400 ASSERT((lock_attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) ==
3401 KMemoryAttribute::None);
3402
3403 // Validate the lock request.
3404 const size_t num_pages = size / PageSize;
3405 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
3406
3407 // Lock the table.
3408 KScopedLightLock lk(m_general_lock);
3409
3410 // Check that the output page group is empty, if it exists.
3411 if (out_pg) {
3412 ASSERT(out_pg->GetNumPages() == 0);
3413 }
3414
3415 // Check the state.
3416 KMemoryState old_state{};
3417 KMemoryPermission old_perm{};
3418 KMemoryAttribute old_attr{};
3419 size_t num_allocator_blocks{};
3420 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
3421 std::addressof(old_attr), std::addressof(num_allocator_blocks),
3422 addr, size, state_mask | KMemoryState::FlagReferenceCounted,
3423 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
3424 attr_mask, attr));
3425
3426 // Get the physical address, if we're supposed to.
3427 if (out_KPhysicalAddress != nullptr) {
3428 ASSERT(this->GetPhysicalAddressLocked(out_KPhysicalAddress, addr));
3429 }
3430
3431 // Make the page group, if we're supposed to.
3432 if (out_pg != nullptr) {
3433 R_TRY(this->MakePageGroup(*out_pg, addr, num_pages));
3434 }
3435
3436 // Create an update allocator.
3437 Result allocator_result{ResultSuccess};
3438 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
3439 m_memory_block_slab_manager, num_allocator_blocks);
3440 R_TRY(allocator_result);
3441
3442 // Decide on new perm and attr.
3443 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
3444 KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr | lock_attr);
3445
3446 // Update permission, if we need to.
3447 if (new_perm != old_perm) {
3448 R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
3449 }
3450
3451 // Apply the memory block updates.
3452 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm,
3453 new_attr, KMemoryBlockDisableMergeAttribute::Locked,
3454 KMemoryBlockDisableMergeAttribute::None);
3455
3456 // If we have an output page group, open.
3457 if (out_pg) {
3458 out_pg->Open();
3459 }
3460
3461 R_SUCCEED();
3462}
3463
3464Result KPageTable::UnlockMemory(KProcessAddress addr, size_t size, KMemoryState state_mask,
3465 KMemoryState state, KMemoryPermission perm_mask,
3466 KMemoryPermission perm, KMemoryAttribute attr_mask,
3467 KMemoryAttribute attr, KMemoryPermission new_perm,
3468 KMemoryAttribute lock_attr, const KPageGroup* pg) {
3469 // Validate basic preconditions.
3470 ASSERT((attr_mask & lock_attr) == lock_attr);
3471 ASSERT((attr & lock_attr) == lock_attr);
3472
3473 // Validate the unlock request.
3474 const size_t num_pages = size / PageSize;
3475 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
3476
3477 // Lock the table.
3478 KScopedLightLock lk(m_general_lock);
3479
3480 // Check the state.
3481 KMemoryState old_state{};
3482 KMemoryPermission old_perm{};
3483 KMemoryAttribute old_attr{};
3484 size_t num_allocator_blocks{};
3485 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
3486 std::addressof(old_attr), std::addressof(num_allocator_blocks),
3487 addr, size, state_mask | KMemoryState::FlagReferenceCounted,
3488 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
3489 attr_mask, attr));
3490
3491 // Check the page group.
3492 if (pg != nullptr) {
3493 R_UNLESS(this->IsValidPageGroup(*pg, addr, num_pages), ResultInvalidMemoryRegion);
3494 }
3495
3496 // Decide on new perm and attr.
3497 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
3498 KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr & ~lock_attr);
3499
3500 // Create an update allocator.
3501 Result allocator_result{ResultSuccess};
3502 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
3503 m_memory_block_slab_manager, num_allocator_blocks);
3504 R_TRY(allocator_result);
3505
3506 // Update permission, if we need to.
3507 if (new_perm != old_perm) {
3508 R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
3509 }
3510
3511 // Apply the memory block updates.
3512 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm,
3513 new_attr, KMemoryBlockDisableMergeAttribute::None,
3514 KMemoryBlockDisableMergeAttribute::Locked);
3515
3516 R_SUCCEED();
3517}
3518
3519} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 66f16faaf..5541bc13f 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -3,548 +3,14 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include "core/hle/kernel/k_page_table_base.h"
7
8#include "common/common_funcs.h"
9#include "common/page_table.h"
10#include "core/file_sys/program_metadata.h"
11#include "core/hle/kernel/k_dynamic_resource_manager.h"
12#include "core/hle/kernel/k_light_lock.h"
13#include "core/hle/kernel/k_memory_block.h"
14#include "core/hle/kernel/k_memory_block_manager.h"
15#include "core/hle/kernel/k_memory_layout.h"
16#include "core/hle/kernel/k_memory_manager.h"
17#include "core/hle/kernel/k_typed_address.h"
18#include "core/hle/result.h"
19#include "core/memory.h"
20
21namespace Core {
22class System;
23}
24 7
25namespace Kernel { 8namespace Kernel {
26 9
27enum class DisableMergeAttribute : u8 { 10class KPageTable final : public KPageTableBase {
28 None = (0U << 0),
29 DisableHead = (1U << 0),
30 DisableHeadAndBody = (1U << 1),
31 EnableHeadAndBody = (1U << 2),
32 DisableTail = (1U << 3),
33 EnableTail = (1U << 4),
34 EnableAndMergeHeadBodyTail = (1U << 5),
35 EnableHeadBodyTail = EnableHeadAndBody | EnableTail,
36 DisableHeadBodyTail = DisableHeadAndBody | DisableTail,
37};
38
39struct KPageProperties {
40 KMemoryPermission perm;
41 bool io;
42 bool uncached;
43 DisableMergeAttribute disable_merge_attributes;
44};
45static_assert(std::is_trivial_v<KPageProperties>);
46static_assert(sizeof(KPageProperties) == sizeof(u32));
47
48class KBlockInfoManager;
49class KMemoryBlockManager;
50class KResourceLimit;
51class KSystemResource;
52
53class KPageTable final {
54protected:
55 struct PageLinkedList;
56
57public:
58 enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll };
59
60 YUZU_NON_COPYABLE(KPageTable);
61 YUZU_NON_MOVEABLE(KPageTable);
62
63 explicit KPageTable(Core::System& system_);
64 ~KPageTable();
65
66 Result InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr,
67 bool enable_das_merge, bool from_back, KMemoryManager::Pool pool,
68 KProcessAddress code_addr, size_t code_size,
69 KSystemResource* system_resource, KResourceLimit* resource_limit,
70 Core::Memory::Memory& memory);
71
72 void Finalize();
73
74 Result MapProcessCode(KProcessAddress addr, size_t pages_count, KMemoryState state,
75 KMemoryPermission perm);
76 Result MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
77 Result UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size,
78 ICacheInvalidationStrategy icache_invalidation_strategy);
79 Result UnmapProcessMemory(KProcessAddress dst_addr, size_t size, KPageTable& src_page_table,
80 KProcessAddress src_addr);
81 Result MapPhysicalMemory(KProcessAddress addr, size_t size);
82 Result UnmapPhysicalMemory(KProcessAddress addr, size_t size);
83 Result MapMemory(KProcessAddress dst_addr, KProcessAddress src_addr, size_t size);
84 Result UnmapMemory(KProcessAddress dst_addr, KProcessAddress src_addr, size_t size);
85 Result SetProcessMemoryPermission(KProcessAddress addr, size_t size,
86 Svc::MemoryPermission svc_perm);
87 KMemoryInfo QueryInfo(KProcessAddress addr);
88 Result SetMemoryPermission(KProcessAddress addr, size_t size, Svc::MemoryPermission perm);
89 Result SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mask, u32 attr);
90 Result SetMaxHeapSize(size_t size);
91 Result SetHeapSize(u64* out, size_t size);
92 Result LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address, size_t size,
93 KMemoryPermission perm, bool is_aligned, bool check_heap);
94 Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size, bool check_heap);
95
96 Result UnlockForDeviceAddressSpace(KProcessAddress addr, size_t size);
97
98 Result LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address, size_t size);
99 Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size);
100
101 Result SetupForIpc(KProcessAddress* out_dst_addr, size_t size, KProcessAddress src_addr,
102 KPageTable& src_page_table, KMemoryPermission test_perm,
103 KMemoryState dst_state, bool send);
104 Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state);
105 Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state);
106
107 Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size,
108 KMemoryPermission perm);
109 Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg);
110 Result LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size);
111 Result UnlockForCodeMemory(KProcessAddress addr, size_t size, const KPageGroup& pg);
112 Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages,
113 KMemoryState state_mask, KMemoryState state,
114 KMemoryPermission perm_mask, KMemoryPermission perm,
115 KMemoryAttribute attr_mask, KMemoryAttribute attr);
116
117 Common::PageTable& PageTableImpl() {
118 return *m_page_table_impl;
119 }
120
121 const Common::PageTable& PageTableImpl() const {
122 return *m_page_table_impl;
123 }
124
125 KBlockInfoManager* GetBlockInfoManager() {
126 return m_block_info_manager;
127 }
128
129 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
130 KPhysicalAddress phys_addr, KProcessAddress region_start,
131 size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
132 R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, region_start,
133 region_num_pages, state, perm));
134 }
135
136 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
137 KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) {
138 R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
139 this->GetRegionAddress(state),
140 this->GetRegionSize(state) / PageSize, state, perm));
141 }
142
143 Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state,
144 KMemoryPermission perm) {
145 R_RETURN(this->MapPages(out_addr, num_pages, PageSize, 0, false,
146 this->GetRegionAddress(state),
147 this->GetRegionSize(state) / PageSize, state, perm));
148 }
149
150 Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
151 KMemoryPermission perm);
152 Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state);
153
154 Result MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
155 KProcessAddress region_start, size_t region_num_pages, KMemoryState state,
156 KMemoryPermission perm);
157 Result MapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state,
158 KMemoryPermission perm);
159 Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state);
160 void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
161 const KPageGroup& pg);
162
163 KProcessAddress GetRegionAddress(Svc::MemoryState state) const;
164 size_t GetRegionSize(Svc::MemoryState state) const;
165 bool CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const;
166
167 KProcessAddress GetRegionAddress(KMemoryState state) const {
168 return this->GetRegionAddress(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
169 }
170 size_t GetRegionSize(KMemoryState state) const {
171 return this->GetRegionSize(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
172 }
173 bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const {
174 return this->CanContain(addr, size,
175 static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
176 }
177
178protected:
179 struct PageLinkedList {
180 private:
181 struct Node {
182 Node* m_next;
183 std::array<u8, PageSize - sizeof(Node*)> m_buffer;
184 };
185
186 public:
187 constexpr PageLinkedList() = default;
188
189 void Push(Node* n) {
190 ASSERT(Common::IsAligned(reinterpret_cast<uintptr_t>(n), PageSize));
191 n->m_next = m_root;
192 m_root = n;
193 }
194
195 void Push(Core::Memory::Memory& memory, KVirtualAddress addr) {
196 this->Push(memory.GetPointer<Node>(GetInteger(addr)));
197 }
198
199 Node* Peek() const {
200 return m_root;
201 }
202
203 Node* Pop() {
204 Node* const r = m_root;
205
206 m_root = r->m_next;
207 r->m_next = nullptr;
208
209 return r;
210 }
211
212 private:
213 Node* m_root{};
214 };
215 static_assert(std::is_trivially_destructible<PageLinkedList>::value);
216
217private:
218 enum class OperationType : u32 {
219 Map = 0,
220 MapGroup = 1,
221 MapFirstGroup = 2,
222 Unmap = 3,
223 ChangePermissions = 4,
224 ChangePermissionsAndRefresh = 5,
225 ChangePermissionsAndRefreshAndFlush = 6,
226 Separate = 7,
227 };
228
229 static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
230 KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared;
231
232 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
233 KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start,
234 size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
235 bool IsRegionContiguous(KProcessAddress addr, u64 size) const;
236 void AddRegionToPages(KProcessAddress start, size_t num_pages, KPageGroup& page_linked_list);
237 KMemoryInfo QueryInfoImpl(KProcessAddress addr);
238 KProcessAddress AllocateVirtualMemory(KProcessAddress start, size_t region_num_pages,
239 u64 needed_num_pages, size_t align);
240 Result Operate(KProcessAddress addr, size_t num_pages, const KPageGroup& page_group,
241 OperationType operation);
242 Result Operate(KProcessAddress addr, size_t num_pages, KMemoryPermission perm,
243 OperationType operation, KPhysicalAddress map_addr = 0);
244 void FinalizeUpdate(PageLinkedList* page_list);
245
246 KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages,
247 size_t num_pages, size_t alignment, size_t offset,
248 size_t guard_pages);
249
250 Result CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
251 KMemoryState state_mask, KMemoryState state,
252 KMemoryPermission perm_mask, KMemoryPermission perm,
253 KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
254 Result CheckMemoryStateContiguous(KProcessAddress addr, size_t size, KMemoryState state_mask,
255 KMemoryState state, KMemoryPermission perm_mask,
256 KMemoryPermission perm, KMemoryAttribute attr_mask,
257 KMemoryAttribute attr) const {
258 R_RETURN(this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask,
259 perm, attr_mask, attr));
260 }
261
262 Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state,
263 KMemoryPermission perm_mask, KMemoryPermission perm,
264 KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
265 Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
266 KMemoryAttribute* out_attr, size_t* out_blocks_needed,
267 KMemoryBlockManager::const_iterator it, KProcessAddress last_addr,
268 KMemoryState state_mask, KMemoryState state,
269 KMemoryPermission perm_mask, KMemoryPermission perm,
270 KMemoryAttribute attr_mask, KMemoryAttribute attr,
271 KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
272 Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
273 KMemoryAttribute* out_attr, size_t* out_blocks_needed,
274 KProcessAddress addr, size_t size, KMemoryState state_mask,
275 KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
276 KMemoryAttribute attr_mask, KMemoryAttribute attr,
277 KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
278 Result CheckMemoryState(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
279 KMemoryState state_mask, KMemoryState state,
280 KMemoryPermission perm_mask, KMemoryPermission perm,
281 KMemoryAttribute attr_mask, KMemoryAttribute attr,
282 KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
283 R_RETURN(CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
284 state_mask, state, perm_mask, perm, attr_mask, attr,
285 ignore_attr));
286 }
287 Result CheckMemoryState(KProcessAddress addr, size_t size, KMemoryState state_mask,
288 KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
289 KMemoryAttribute attr_mask, KMemoryAttribute attr,
290 KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
291 R_RETURN(this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm,
292 attr_mask, attr, ignore_attr));
293 }
294
295 Result LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_KPhysicalAddress,
296 KProcessAddress addr, size_t size, KMemoryState state_mask,
297 KMemoryState state, KMemoryPermission perm_mask,
298 KMemoryPermission perm, KMemoryAttribute attr_mask,
299 KMemoryAttribute attr, KMemoryPermission new_perm,
300 KMemoryAttribute lock_attr);
301 Result UnlockMemory(KProcessAddress addr, size_t size, KMemoryState state_mask,
302 KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
303 KMemoryAttribute attr_mask, KMemoryAttribute attr,
304 KMemoryPermission new_perm, KMemoryAttribute lock_attr,
305 const KPageGroup* pg);
306
307 Result MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_t num_pages);
308 bool IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr, size_t num_pages);
309
310 bool IsLockedByCurrentThread() const {
311 return m_general_lock.IsLockedByCurrentThread();
312 }
313
314 bool IsHeapPhysicalAddress(const KMemoryLayout& layout, KPhysicalAddress phys_addr) {
315 ASSERT(this->IsLockedByCurrentThread());
316
317 return layout.IsHeapPhysicalAddress(m_cached_physical_heap_region, phys_addr);
318 }
319
320 bool GetPhysicalAddressLocked(KPhysicalAddress* out, KProcessAddress virt_addr) const {
321 ASSERT(this->IsLockedByCurrentThread());
322
323 *out = GetPhysicalAddr(virt_addr);
324
325 return *out != 0;
326 }
327
328 Result SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed,
329 KProcessAddress address, size_t size, KMemoryPermission test_perm,
330 KMemoryState dst_state);
331 Result SetupForIpcServer(KProcessAddress* out_addr, size_t size, KProcessAddress src_addr,
332 KMemoryPermission test_perm, KMemoryState dst_state,
333 KPageTable& src_page_table, bool send);
334 void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, KProcessAddress address,
335 size_t size, KMemoryPermission prot_perm);
336
337 Result AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
338 size_t num_pages, KMemoryPermission perm);
339 Result MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
340 const KPageGroup& pg, const KPageProperties properties, bool reuse_ll);
341
342 mutable KLightLock m_general_lock;
343 mutable KLightLock m_map_physical_memory_lock;
344
345public:
346 constexpr KProcessAddress GetAddressSpaceStart() const {
347 return m_address_space_start;
348 }
349 constexpr KProcessAddress GetAddressSpaceEnd() const {
350 return m_address_space_end;
351 }
352 constexpr size_t GetAddressSpaceSize() const {
353 return m_address_space_end - m_address_space_start;
354 }
355 constexpr KProcessAddress GetHeapRegionStart() const {
356 return m_heap_region_start;
357 }
358 constexpr KProcessAddress GetHeapRegionEnd() const {
359 return m_heap_region_end;
360 }
361 constexpr size_t GetHeapRegionSize() const {
362 return m_heap_region_end - m_heap_region_start;
363 }
364 constexpr KProcessAddress GetAliasRegionStart() const {
365 return m_alias_region_start;
366 }
367 constexpr KProcessAddress GetAliasRegionEnd() const {
368 return m_alias_region_end;
369 }
370 constexpr size_t GetAliasRegionSize() const {
371 return m_alias_region_end - m_alias_region_start;
372 }
373 constexpr KProcessAddress GetStackRegionStart() const {
374 return m_stack_region_start;
375 }
376 constexpr KProcessAddress GetStackRegionEnd() const {
377 return m_stack_region_end;
378 }
379 constexpr size_t GetStackRegionSize() const {
380 return m_stack_region_end - m_stack_region_start;
381 }
382 constexpr KProcessAddress GetKernelMapRegionStart() const {
383 return m_kernel_map_region_start;
384 }
385 constexpr KProcessAddress GetKernelMapRegionEnd() const {
386 return m_kernel_map_region_end;
387 }
388 constexpr KProcessAddress GetCodeRegionStart() const {
389 return m_code_region_start;
390 }
391 constexpr KProcessAddress GetCodeRegionEnd() const {
392 return m_code_region_end;
393 }
394 constexpr KProcessAddress GetAliasCodeRegionStart() const {
395 return m_alias_code_region_start;
396 }
397 constexpr KProcessAddress GetAliasCodeRegionEnd() const {
398 return m_alias_code_region_end;
399 }
400 constexpr size_t GetAliasCodeRegionSize() const {
401 return m_alias_code_region_end - m_alias_code_region_start;
402 }
403 size_t GetNormalMemorySize() const {
404 KScopedLightLock lk(m_general_lock);
405 return GetHeapSize() + m_mapped_physical_memory_size;
406 }
407 constexpr size_t GetAddressSpaceWidth() const {
408 return m_address_space_width;
409 }
410 constexpr size_t GetHeapSize() const {
411 return m_current_heap_end - m_heap_region_start;
412 }
413 constexpr size_t GetNumGuardPages() const {
414 return IsKernel() ? 1 : 4;
415 }
416 KPhysicalAddress GetPhysicalAddr(KProcessAddress addr) const {
417 const auto backing_addr = m_page_table_impl->backing_addr[addr >> PageBits];
418 ASSERT(backing_addr);
419 return backing_addr + GetInteger(addr);
420 }
421 constexpr bool Contains(KProcessAddress addr) const {
422 return m_address_space_start <= addr && addr <= m_address_space_end - 1;
423 }
424 constexpr bool Contains(KProcessAddress addr, size_t size) const {
425 return m_address_space_start <= addr && addr < addr + size &&
426 addr + size - 1 <= m_address_space_end - 1;
427 }
428 constexpr bool IsInAliasRegion(KProcessAddress addr, size_t size) const {
429 return this->Contains(addr, size) && m_alias_region_start <= addr &&
430 addr + size - 1 <= m_alias_region_end - 1;
431 }
432 constexpr bool IsInHeapRegion(KProcessAddress addr, size_t size) const {
433 return this->Contains(addr, size) && m_heap_region_start <= addr &&
434 addr + size - 1 <= m_heap_region_end - 1;
435 }
436
437public: 11public:
438 static KVirtualAddress GetLinearMappedVirtualAddress(const KMemoryLayout& layout, 12 explicit KPageTable(KernelCore& kernel) : KPageTableBase(kernel) {}
439 KPhysicalAddress addr) { 13 ~KPageTable() = default;
440 return layout.GetLinearVirtualAddress(addr);
441 }
442
443 static KPhysicalAddress GetLinearMappedPhysicalAddress(const KMemoryLayout& layout,
444 KVirtualAddress addr) {
445 return layout.GetLinearPhysicalAddress(addr);
446 }
447
448 static KVirtualAddress GetHeapVirtualAddress(const KMemoryLayout& layout,
449 KPhysicalAddress addr) {
450 return GetLinearMappedVirtualAddress(layout, addr);
451 }
452
453 static KPhysicalAddress GetHeapPhysicalAddress(const KMemoryLayout& layout,
454 KVirtualAddress addr) {
455 return GetLinearMappedPhysicalAddress(layout, addr);
456 }
457
458 static KVirtualAddress GetPageTableVirtualAddress(const KMemoryLayout& layout,
459 KPhysicalAddress addr) {
460 return GetLinearMappedVirtualAddress(layout, addr);
461 }
462
463 static KPhysicalAddress GetPageTablePhysicalAddress(const KMemoryLayout& layout,
464 KVirtualAddress addr) {
465 return GetLinearMappedPhysicalAddress(layout, addr);
466 }
467
468private:
469 constexpr bool IsKernel() const {
470 return m_is_kernel;
471 }
472 constexpr bool IsAslrEnabled() const {
473 return m_enable_aslr;
474 }
475
476 constexpr bool ContainsPages(KProcessAddress addr, size_t num_pages) const {
477 return (m_address_space_start <= addr) &&
478 (num_pages <= (m_address_space_end - m_address_space_start) / PageSize) &&
479 (addr + num_pages * PageSize - 1 <= m_address_space_end - 1);
480 }
481
482private:
483 class KScopedPageTableUpdater {
484 private:
485 KPageTable* m_pt{};
486 PageLinkedList m_ll;
487
488 public:
489 explicit KScopedPageTableUpdater(KPageTable* pt) : m_pt(pt) {}
490 explicit KScopedPageTableUpdater(KPageTable& pt) : KScopedPageTableUpdater(&pt) {}
491 ~KScopedPageTableUpdater() {
492 m_pt->FinalizeUpdate(this->GetPageList());
493 }
494
495 PageLinkedList* GetPageList() {
496 return std::addressof(m_ll);
497 }
498 };
499
500private:
501 KProcessAddress m_address_space_start{};
502 KProcessAddress m_address_space_end{};
503 KProcessAddress m_heap_region_start{};
504 KProcessAddress m_heap_region_end{};
505 KProcessAddress m_current_heap_end{};
506 KProcessAddress m_alias_region_start{};
507 KProcessAddress m_alias_region_end{};
508 KProcessAddress m_stack_region_start{};
509 KProcessAddress m_stack_region_end{};
510 KProcessAddress m_kernel_map_region_start{};
511 KProcessAddress m_kernel_map_region_end{};
512 KProcessAddress m_code_region_start{};
513 KProcessAddress m_code_region_end{};
514 KProcessAddress m_alias_code_region_start{};
515 KProcessAddress m_alias_code_region_end{};
516
517 size_t m_max_heap_size{};
518 size_t m_mapped_physical_memory_size{};
519 size_t m_mapped_unsafe_physical_memory{};
520 size_t m_mapped_insecure_memory{};
521 size_t m_mapped_ipc_server_memory{};
522 size_t m_address_space_width{};
523
524 KMemoryBlockManager m_memory_block_manager;
525 u32 m_allocate_option{};
526
527 bool m_is_kernel{};
528 bool m_enable_aslr{};
529 bool m_enable_device_address_space_merge{};
530
531 KMemoryBlockSlabManager* m_memory_block_slab_manager{};
532 KBlockInfoManager* m_block_info_manager{};
533 KResourceLimit* m_resource_limit{};
534
535 u32 m_heap_fill_value{};
536 u32 m_ipc_fill_value{};
537 u32 m_stack_fill_value{};
538 const KMemoryRegion* m_cached_physical_heap_region{};
539
540 KMemoryManager::Pool m_memory_pool{KMemoryManager::Pool::Application};
541 KMemoryManager::Direction m_allocation_option{KMemoryManager::Direction::FromFront};
542
543 std::unique_ptr<Common::PageTable> m_page_table_impl;
544
545 Core::System& m_system;
546 KernelCore& m_kernel;
547 Core::Memory::Memory* m_memory{};
548}; 14};
549 15
550} // namespace Kernel 16} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp
new file mode 100644
index 000000000..47dc8fd35
--- /dev/null
+++ b/src/core/hle/kernel/k_page_table_base.cpp
@@ -0,0 +1,5716 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "common/settings.h"
6#include "core/core.h"
7#include "core/hle/kernel/k_address_space_info.h"
8#include "core/hle/kernel/k_page_table_base.h"
9#include "core/hle/kernel/k_scoped_resource_reservation.h"
10#include "core/hle/kernel/k_system_resource.h"
11
12namespace Kernel {
13
14namespace {
15
16class KScopedLightLockPair {
17 YUZU_NON_COPYABLE(KScopedLightLockPair);
18 YUZU_NON_MOVEABLE(KScopedLightLockPair);
19
20private:
21 KLightLock* m_lower;
22 KLightLock* m_upper;
23
24public:
25 KScopedLightLockPair(KLightLock& lhs, KLightLock& rhs) {
26 // Ensure our locks are in a consistent order.
27 if (std::addressof(lhs) <= std::addressof(rhs)) {
28 m_lower = std::addressof(lhs);
29 m_upper = std::addressof(rhs);
30 } else {
31 m_lower = std::addressof(rhs);
32 m_upper = std::addressof(lhs);
33 }
34
35 // Acquire both locks.
36 m_lower->Lock();
37 if (m_lower != m_upper) {
38 m_upper->Lock();
39 }
40 }
41
42 ~KScopedLightLockPair() {
43 // Unlock the upper lock.
44 if (m_upper != nullptr && m_upper != m_lower) {
45 m_upper->Unlock();
46 }
47
48 // Unlock the lower lock.
49 if (m_lower != nullptr) {
50 m_lower->Unlock();
51 }
52 }
53
54public:
55 // Utility.
56 void TryUnlockHalf(KLightLock& lock) {
57 // Only allow unlocking if the lock is half the pair.
58 if (m_lower != m_upper) {
59 // We want to be sure the lock is one we own.
60 if (m_lower == std::addressof(lock)) {
61 lock.Unlock();
62 m_lower = nullptr;
63 } else if (m_upper == std::addressof(lock)) {
64 lock.Unlock();
65 m_upper = nullptr;
66 }
67 }
68 }
69};
70
71template <typename AddressType>
72void InvalidateInstructionCache(Core::System& system, AddressType addr, u64 size) {
73 system.InvalidateCpuInstructionCacheRange(GetInteger(addr), size);
74}
75
76template <typename AddressType>
77Result InvalidateDataCache(AddressType addr, u64 size) {
78 R_SUCCEED();
79}
80
81template <typename AddressType>
82Result StoreDataCache(AddressType addr, u64 size) {
83 R_SUCCEED();
84}
85
86template <typename AddressType>
87Result FlushDataCache(AddressType addr, u64 size) {
88 R_SUCCEED();
89}
90
91} // namespace
92
93void KPageTableBase::MemoryRange::Open() {
94 // If the range contains heap pages, open them.
95 if (this->IsHeap()) {
96 m_kernel.MemoryManager().Open(this->GetAddress(), this->GetSize() / PageSize);
97 }
98}
99
100void KPageTableBase::MemoryRange::Close() {
101 // If the range contains heap pages, close them.
102 if (this->IsHeap()) {
103 m_kernel.MemoryManager().Close(this->GetAddress(), this->GetSize() / PageSize);
104 }
105}
106
107KPageTableBase::KPageTableBase(KernelCore& kernel)
108 : m_kernel(kernel), m_system(kernel.System()), m_general_lock(kernel),
109 m_map_physical_memory_lock(kernel), m_device_map_lock(kernel) {}
110KPageTableBase::~KPageTableBase() = default;
111
112Result KPageTableBase::InitializeForKernel(bool is_64_bit, KVirtualAddress start,
113 KVirtualAddress end, Core::Memory::Memory& memory) {
114 // Initialize our members.
115 m_address_space_width =
116 static_cast<u32>(is_64_bit ? Common::BitSize<u64>() : Common::BitSize<u32>());
117 m_address_space_start = KProcessAddress(GetInteger(start));
118 m_address_space_end = KProcessAddress(GetInteger(end));
119 m_is_kernel = true;
120 m_enable_aslr = true;
121 m_enable_device_address_space_merge = false;
122
123 m_heap_region_start = 0;
124 m_heap_region_end = 0;
125 m_current_heap_end = 0;
126 m_alias_region_start = 0;
127 m_alias_region_end = 0;
128 m_stack_region_start = 0;
129 m_stack_region_end = 0;
130 m_kernel_map_region_start = 0;
131 m_kernel_map_region_end = 0;
132 m_alias_code_region_start = 0;
133 m_alias_code_region_end = 0;
134 m_code_region_start = 0;
135 m_code_region_end = 0;
136 m_max_heap_size = 0;
137 m_mapped_physical_memory_size = 0;
138 m_mapped_unsafe_physical_memory = 0;
139 m_mapped_insecure_memory = 0;
140 m_mapped_ipc_server_memory = 0;
141
142 m_memory_block_slab_manager =
143 m_kernel.GetSystemSystemResource().GetMemoryBlockSlabManagerPointer();
144 m_block_info_manager = m_kernel.GetSystemSystemResource().GetBlockInfoManagerPointer();
145 m_resource_limit = m_kernel.GetSystemResourceLimit();
146
147 m_allocate_option = KMemoryManager::EncodeOption(KMemoryManager::Pool::System,
148 KMemoryManager::Direction::FromFront);
149 m_heap_fill_value = MemoryFillValue_Zero;
150 m_ipc_fill_value = MemoryFillValue_Zero;
151 m_stack_fill_value = MemoryFillValue_Zero;
152
153 m_cached_physical_linear_region = nullptr;
154 m_cached_physical_heap_region = nullptr;
155
156 // Initialize our implementation.
157 m_impl = std::make_unique<Common::PageTable>();
158 m_impl->Resize(m_address_space_width, PageBits);
159
160 // Set the tracking memory.
161 m_memory = std::addressof(memory);
162
163 // Initialize our memory block manager.
164 R_RETURN(m_memory_block_manager.Initialize(m_address_space_start, m_address_space_end,
165 m_memory_block_slab_manager));
166}
167
168Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr,
169 bool enable_das_merge, bool from_back,
170 KMemoryManager::Pool pool, KProcessAddress code_address,
171 size_t code_size, KSystemResource* system_resource,
172 KResourceLimit* resource_limit,
173 Core::Memory::Memory& memory) {
174 // Calculate region extents.
175 const size_t as_width = GetAddressSpaceWidth(as_type);
176 const KProcessAddress start = 0;
177 const KProcessAddress end = (1ULL << as_width);
178
179 // Validate the region.
180 ASSERT(start <= code_address);
181 ASSERT(code_address < code_address + code_size);
182 ASSERT(code_address + code_size - 1 <= end - 1);
183
184 // Define helpers.
185 auto GetSpaceStart = [&](KAddressSpaceInfo::Type type) {
186 return KAddressSpaceInfo::GetAddressSpaceStart(m_address_space_width, type);
187 };
188 auto GetSpaceSize = [&](KAddressSpaceInfo::Type type) {
189 return KAddressSpaceInfo::GetAddressSpaceSize(m_address_space_width, type);
190 };
191
192 // Set our bit width and heap/alias sizes.
193 m_address_space_width = static_cast<u32>(GetAddressSpaceWidth(as_type));
194 size_t alias_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Alias);
195 size_t heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Heap);
196
197 // Adjust heap/alias size if we don't have an alias region.
198 if ((as_type & Svc::CreateProcessFlag::AddressSpaceMask) ==
199 Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias) {
200 heap_region_size += alias_region_size;
201 alias_region_size = 0;
202 }
203
204 // Set code regions and determine remaining sizes.
205 KProcessAddress process_code_start;
206 KProcessAddress process_code_end;
207 size_t stack_region_size;
208 size_t kernel_map_region_size;
209 if (m_address_space_width == 39) {
210 alias_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Alias);
211 heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Heap);
212 stack_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Stack);
213 kernel_map_region_size = GetSpaceSize(KAddressSpaceInfo::Type::MapSmall);
214 m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::Map39Bit);
215 m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::Map39Bit);
216 m_alias_code_region_start = m_code_region_start;
217 m_alias_code_region_end = m_code_region_end;
218 process_code_start = Common::AlignDown(GetInteger(code_address), RegionAlignment);
219 process_code_end = Common::AlignUp(GetInteger(code_address) + code_size, RegionAlignment);
220 } else {
221 stack_region_size = 0;
222 kernel_map_region_size = 0;
223 m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::MapSmall);
224 m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::MapSmall);
225 m_stack_region_start = m_code_region_start;
226 m_alias_code_region_start = m_code_region_start;
227 m_alias_code_region_end = GetSpaceStart(KAddressSpaceInfo::Type::MapLarge) +
228 GetSpaceSize(KAddressSpaceInfo::Type::MapLarge);
229 m_stack_region_end = m_code_region_end;
230 m_kernel_map_region_start = m_code_region_start;
231 m_kernel_map_region_end = m_code_region_end;
232 process_code_start = m_code_region_start;
233 process_code_end = m_code_region_end;
234 }
235
236 // Set other basic fields.
237 m_enable_aslr = enable_aslr;
238 m_enable_device_address_space_merge = enable_das_merge;
239 m_address_space_start = start;
240 m_address_space_end = end;
241 m_is_kernel = false;
242 m_memory_block_slab_manager = system_resource->GetMemoryBlockSlabManagerPointer();
243 m_block_info_manager = system_resource->GetBlockInfoManagerPointer();
244 m_resource_limit = resource_limit;
245
246 // Determine the region we can place our undetermineds in.
247 KProcessAddress alloc_start;
248 size_t alloc_size;
249 if ((GetInteger(process_code_start) - GetInteger(m_code_region_start)) >=
250 (GetInteger(end) - GetInteger(process_code_end))) {
251 alloc_start = m_code_region_start;
252 alloc_size = GetInteger(process_code_start) - GetInteger(m_code_region_start);
253 } else {
254 alloc_start = process_code_end;
255 alloc_size = GetInteger(end) - GetInteger(process_code_end);
256 }
257 const size_t needed_size =
258 (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size);
259 R_UNLESS(alloc_size >= needed_size, ResultOutOfMemory);
260
261 const size_t remaining_size = alloc_size - needed_size;
262
263 // Determine random placements for each region.
264 size_t alias_rnd = 0, heap_rnd = 0, stack_rnd = 0, kmap_rnd = 0;
265 if (enable_aslr) {
266 alias_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
267 RegionAlignment;
268 heap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
269 RegionAlignment;
270 stack_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
271 RegionAlignment;
272 kmap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
273 RegionAlignment;
274 }
275
276 // Setup heap and alias regions.
277 m_alias_region_start = alloc_start + alias_rnd;
278 m_alias_region_end = m_alias_region_start + alias_region_size;
279 m_heap_region_start = alloc_start + heap_rnd;
280 m_heap_region_end = m_heap_region_start + heap_region_size;
281
282 if (alias_rnd <= heap_rnd) {
283 m_heap_region_start += alias_region_size;
284 m_heap_region_end += alias_region_size;
285 } else {
286 m_alias_region_start += heap_region_size;
287 m_alias_region_end += heap_region_size;
288 }
289
290 // Setup stack region.
291 if (stack_region_size) {
292 m_stack_region_start = alloc_start + stack_rnd;
293 m_stack_region_end = m_stack_region_start + stack_region_size;
294
295 if (alias_rnd < stack_rnd) {
296 m_stack_region_start += alias_region_size;
297 m_stack_region_end += alias_region_size;
298 } else {
299 m_alias_region_start += stack_region_size;
300 m_alias_region_end += stack_region_size;
301 }
302
303 if (heap_rnd < stack_rnd) {
304 m_stack_region_start += heap_region_size;
305 m_stack_region_end += heap_region_size;
306 } else {
307 m_heap_region_start += stack_region_size;
308 m_heap_region_end += stack_region_size;
309 }
310 }
311
312 // Setup kernel map region.
313 if (kernel_map_region_size) {
314 m_kernel_map_region_start = alloc_start + kmap_rnd;
315 m_kernel_map_region_end = m_kernel_map_region_start + kernel_map_region_size;
316
317 if (alias_rnd < kmap_rnd) {
318 m_kernel_map_region_start += alias_region_size;
319 m_kernel_map_region_end += alias_region_size;
320 } else {
321 m_alias_region_start += kernel_map_region_size;
322 m_alias_region_end += kernel_map_region_size;
323 }
324
325 if (heap_rnd < kmap_rnd) {
326 m_kernel_map_region_start += heap_region_size;
327 m_kernel_map_region_end += heap_region_size;
328 } else {
329 m_heap_region_start += kernel_map_region_size;
330 m_heap_region_end += kernel_map_region_size;
331 }
332
333 if (stack_region_size) {
334 if (stack_rnd < kmap_rnd) {
335 m_kernel_map_region_start += stack_region_size;
336 m_kernel_map_region_end += stack_region_size;
337 } else {
338 m_stack_region_start += kernel_map_region_size;
339 m_stack_region_end += kernel_map_region_size;
340 }
341 }
342 }
343
344 // Set heap and fill members.
345 m_current_heap_end = m_heap_region_start;
346 m_max_heap_size = 0;
347 m_mapped_physical_memory_size = 0;
348 m_mapped_unsafe_physical_memory = 0;
349 m_mapped_insecure_memory = 0;
350 m_mapped_ipc_server_memory = 0;
351
352 // const bool fill_memory = KTargetSystem::IsDebugMemoryFillEnabled();
353 const bool fill_memory = false;
354 m_heap_fill_value = fill_memory ? MemoryFillValue_Heap : MemoryFillValue_Zero;
355 m_ipc_fill_value = fill_memory ? MemoryFillValue_Ipc : MemoryFillValue_Zero;
356 m_stack_fill_value = fill_memory ? MemoryFillValue_Stack : MemoryFillValue_Zero;
357
358 // Set allocation option.
359 m_allocate_option =
360 KMemoryManager::EncodeOption(pool, from_back ? KMemoryManager::Direction::FromBack
361 : KMemoryManager::Direction::FromFront);
362
363 // Ensure that we regions inside our address space.
364 auto IsInAddressSpace = [&](KProcessAddress addr) {
365 return m_address_space_start <= addr && addr <= m_address_space_end;
366 };
367 ASSERT(IsInAddressSpace(m_alias_region_start));
368 ASSERT(IsInAddressSpace(m_alias_region_end));
369 ASSERT(IsInAddressSpace(m_heap_region_start));
370 ASSERT(IsInAddressSpace(m_heap_region_end));
371 ASSERT(IsInAddressSpace(m_stack_region_start));
372 ASSERT(IsInAddressSpace(m_stack_region_end));
373 ASSERT(IsInAddressSpace(m_kernel_map_region_start));
374 ASSERT(IsInAddressSpace(m_kernel_map_region_end));
375
376 // Ensure that we selected regions that don't overlap.
377 const KProcessAddress alias_start = m_alias_region_start;
378 const KProcessAddress alias_last = m_alias_region_end - 1;
379 const KProcessAddress heap_start = m_heap_region_start;
380 const KProcessAddress heap_last = m_heap_region_end - 1;
381 const KProcessAddress stack_start = m_stack_region_start;
382 const KProcessAddress stack_last = m_stack_region_end - 1;
383 const KProcessAddress kmap_start = m_kernel_map_region_start;
384 const KProcessAddress kmap_last = m_kernel_map_region_end - 1;
385 ASSERT(alias_last < heap_start || heap_last < alias_start);
386 ASSERT(alias_last < stack_start || stack_last < alias_start);
387 ASSERT(alias_last < kmap_start || kmap_last < alias_start);
388 ASSERT(heap_last < stack_start || stack_last < heap_start);
389 ASSERT(heap_last < kmap_start || kmap_last < heap_start);
390
391 // Initialize our implementation.
392 m_impl = std::make_unique<Common::PageTable>();
393 m_impl->Resize(m_address_space_width, PageBits);
394
395 // Set the tracking memory.
396 m_memory = std::addressof(memory);
397
398 // Initialize our memory block manager.
399 R_RETURN(m_memory_block_manager.Initialize(m_address_space_start, m_address_space_end,
400 m_memory_block_slab_manager));
401}
402
403void KPageTableBase::Finalize() {
404 auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) {
405 if (Settings::IsFastmemEnabled()) {
406 m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size);
407 }
408 };
409
410 // Finalize memory blocks.
411 m_memory_block_manager.Finalize(m_memory_block_slab_manager, std::move(HostUnmapCallback));
412
413 // Free any unsafe mapped memory.
414 if (m_mapped_unsafe_physical_memory) {
415 UNIMPLEMENTED();
416 }
417
418 // Release any insecure mapped memory.
419 if (m_mapped_insecure_memory) {
420 if (auto* const insecure_resource_limit =
421 KSystemControl::GetInsecureMemoryResourceLimit(m_kernel);
422 insecure_resource_limit != nullptr) {
423 insecure_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax,
424 m_mapped_insecure_memory);
425 }
426 }
427
428 // Release any ipc server memory.
429 if (m_mapped_ipc_server_memory) {
430 m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax,
431 m_mapped_ipc_server_memory);
432 }
433
434 // Close the backing page table, as the destructor is not called for guest objects.
435 m_impl.reset();
436}
437
438KProcessAddress KPageTableBase::GetRegionAddress(Svc::MemoryState state) const {
439 switch (state) {
440 case Svc::MemoryState::Free:
441 case Svc::MemoryState::Kernel:
442 return m_address_space_start;
443 case Svc::MemoryState::Normal:
444 return m_heap_region_start;
445 case Svc::MemoryState::Ipc:
446 case Svc::MemoryState::NonSecureIpc:
447 case Svc::MemoryState::NonDeviceIpc:
448 return m_alias_region_start;
449 case Svc::MemoryState::Stack:
450 return m_stack_region_start;
451 case Svc::MemoryState::Static:
452 case Svc::MemoryState::ThreadLocal:
453 return m_kernel_map_region_start;
454 case Svc::MemoryState::Io:
455 case Svc::MemoryState::Shared:
456 case Svc::MemoryState::AliasCode:
457 case Svc::MemoryState::AliasCodeData:
458 case Svc::MemoryState::Transfered:
459 case Svc::MemoryState::SharedTransfered:
460 case Svc::MemoryState::SharedCode:
461 case Svc::MemoryState::GeneratedCode:
462 case Svc::MemoryState::CodeOut:
463 case Svc::MemoryState::Coverage:
464 case Svc::MemoryState::Insecure:
465 return m_alias_code_region_start;
466 case Svc::MemoryState::Code:
467 case Svc::MemoryState::CodeData:
468 return m_code_region_start;
469 default:
470 UNREACHABLE();
471 }
472}
473
474size_t KPageTableBase::GetRegionSize(Svc::MemoryState state) const {
475 switch (state) {
476 case Svc::MemoryState::Free:
477 case Svc::MemoryState::Kernel:
478 return m_address_space_end - m_address_space_start;
479 case Svc::MemoryState::Normal:
480 return m_heap_region_end - m_heap_region_start;
481 case Svc::MemoryState::Ipc:
482 case Svc::MemoryState::NonSecureIpc:
483 case Svc::MemoryState::NonDeviceIpc:
484 return m_alias_region_end - m_alias_region_start;
485 case Svc::MemoryState::Stack:
486 return m_stack_region_end - m_stack_region_start;
487 case Svc::MemoryState::Static:
488 case Svc::MemoryState::ThreadLocal:
489 return m_kernel_map_region_end - m_kernel_map_region_start;
490 case Svc::MemoryState::Io:
491 case Svc::MemoryState::Shared:
492 case Svc::MemoryState::AliasCode:
493 case Svc::MemoryState::AliasCodeData:
494 case Svc::MemoryState::Transfered:
495 case Svc::MemoryState::SharedTransfered:
496 case Svc::MemoryState::SharedCode:
497 case Svc::MemoryState::GeneratedCode:
498 case Svc::MemoryState::CodeOut:
499 case Svc::MemoryState::Coverage:
500 case Svc::MemoryState::Insecure:
501 return m_alias_code_region_end - m_alias_code_region_start;
502 case Svc::MemoryState::Code:
503 case Svc::MemoryState::CodeData:
504 return m_code_region_end - m_code_region_start;
505 default:
506 UNREACHABLE();
507 }
508}
509
510bool KPageTableBase::CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const {
511 const KProcessAddress end = addr + size;
512 const KProcessAddress last = end - 1;
513
514 const KProcessAddress region_start = this->GetRegionAddress(state);
515 const size_t region_size = this->GetRegionSize(state);
516
517 const bool is_in_region =
518 region_start <= addr && addr < end && last <= region_start + region_size - 1;
519 const bool is_in_heap = !(end <= m_heap_region_start || m_heap_region_end <= addr ||
520 m_heap_region_start == m_heap_region_end);
521 const bool is_in_alias = !(end <= m_alias_region_start || m_alias_region_end <= addr ||
522 m_alias_region_start == m_alias_region_end);
523 switch (state) {
524 case Svc::MemoryState::Free:
525 case Svc::MemoryState::Kernel:
526 return is_in_region;
527 case Svc::MemoryState::Io:
528 case Svc::MemoryState::Static:
529 case Svc::MemoryState::Code:
530 case Svc::MemoryState::CodeData:
531 case Svc::MemoryState::Shared:
532 case Svc::MemoryState::AliasCode:
533 case Svc::MemoryState::AliasCodeData:
534 case Svc::MemoryState::Stack:
535 case Svc::MemoryState::ThreadLocal:
536 case Svc::MemoryState::Transfered:
537 case Svc::MemoryState::SharedTransfered:
538 case Svc::MemoryState::SharedCode:
539 case Svc::MemoryState::GeneratedCode:
540 case Svc::MemoryState::CodeOut:
541 case Svc::MemoryState::Coverage:
542 case Svc::MemoryState::Insecure:
543 return is_in_region && !is_in_heap && !is_in_alias;
544 case Svc::MemoryState::Normal:
545 ASSERT(is_in_heap);
546 return is_in_region && !is_in_alias;
547 case Svc::MemoryState::Ipc:
548 case Svc::MemoryState::NonSecureIpc:
549 case Svc::MemoryState::NonDeviceIpc:
550 ASSERT(is_in_alias);
551 return is_in_region && !is_in_heap;
552 default:
553 return false;
554 }
555}
556
557Result KPageTableBase::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
558 KMemoryState state, KMemoryPermission perm_mask,
559 KMemoryPermission perm, KMemoryAttribute attr_mask,
560 KMemoryAttribute attr) const {
561 // Validate the states match expectation.
562 R_UNLESS((info.m_state & state_mask) == state, ResultInvalidCurrentMemory);
563 R_UNLESS((info.m_permission & perm_mask) == perm, ResultInvalidCurrentMemory);
564 R_UNLESS((info.m_attribute & attr_mask) == attr, ResultInvalidCurrentMemory);
565
566 R_SUCCEED();
567}
568
569Result KPageTableBase::CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr,
570 size_t size, KMemoryState state_mask,
571 KMemoryState state, KMemoryPermission perm_mask,
572 KMemoryPermission perm,
573 KMemoryAttribute attr_mask,
574 KMemoryAttribute attr) const {
575 ASSERT(this->IsLockedByCurrentThread());
576
577 // Get information about the first block.
578 const KProcessAddress last_addr = addr + size - 1;
579 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr);
580 KMemoryInfo info = it->GetMemoryInfo();
581
582 // If the start address isn't aligned, we need a block.
583 const size_t blocks_for_start_align =
584 (Common::AlignDown(GetInteger(addr), PageSize) != info.GetAddress()) ? 1 : 0;
585
586 while (true) {
587 // Validate against the provided masks.
588 R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
589
590 // Break once we're done.
591 if (last_addr <= info.GetLastAddress()) {
592 break;
593 }
594
595 // Advance our iterator.
596 it++;
597 ASSERT(it != m_memory_block_manager.cend());
598 info = it->GetMemoryInfo();
599 }
600
601 // If the end address isn't aligned, we need a block.
602 const size_t blocks_for_end_align =
603 (Common::AlignUp(GetInteger(addr) + size, PageSize) != info.GetEndAddress()) ? 1 : 0;
604
605 if (out_blocks_needed != nullptr) {
606 *out_blocks_needed = blocks_for_start_align + blocks_for_end_align;
607 }
608
609 R_SUCCEED();
610}
611
612Result KPageTableBase::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
613 KMemoryAttribute* out_attr, size_t* out_blocks_needed,
614 KMemoryBlockManager::const_iterator it,
615 KProcessAddress last_addr, KMemoryState state_mask,
616 KMemoryState state, KMemoryPermission perm_mask,
617 KMemoryPermission perm, KMemoryAttribute attr_mask,
618 KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
619 ASSERT(this->IsLockedByCurrentThread());
620
621 // Get information about the first block.
622 KMemoryInfo info = it->GetMemoryInfo();
623
624 // Validate all blocks in the range have correct state.
625 const KMemoryState first_state = info.m_state;
626 const KMemoryPermission first_perm = info.m_permission;
627 const KMemoryAttribute first_attr = info.m_attribute;
628 while (true) {
629 // Validate the current block.
630 R_UNLESS(info.m_state == first_state, ResultInvalidCurrentMemory);
631 R_UNLESS(info.m_permission == first_perm, ResultInvalidCurrentMemory);
632 R_UNLESS((info.m_attribute | ignore_attr) == (first_attr | ignore_attr),
633 ResultInvalidCurrentMemory);
634
635 // Validate against the provided masks.
636 R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
637
638 // Break once we're done.
639 if (last_addr <= info.GetLastAddress()) {
640 break;
641 }
642
643 // Advance our iterator.
644 it++;
645 ASSERT(it != m_memory_block_manager.cend());
646 info = it->GetMemoryInfo();
647 }
648
649 // Write output state.
650 if (out_state != nullptr) {
651 *out_state = first_state;
652 }
653 if (out_perm != nullptr) {
654 *out_perm = first_perm;
655 }
656 if (out_attr != nullptr) {
657 *out_attr = first_attr & ~ignore_attr;
658 }
659
660 // If the end address isn't aligned, we need a block.
661 if (out_blocks_needed != nullptr) {
662 const size_t blocks_for_end_align =
663 (Common::AlignDown(GetInteger(last_addr), PageSize) + PageSize != info.GetEndAddress())
664 ? 1
665 : 0;
666 *out_blocks_needed = blocks_for_end_align;
667 }
668
669 R_SUCCEED();
670}
671
672Result KPageTableBase::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
673 KMemoryAttribute* out_attr, size_t* out_blocks_needed,
674 KProcessAddress addr, size_t size, KMemoryState state_mask,
675 KMemoryState state, KMemoryPermission perm_mask,
676 KMemoryPermission perm, KMemoryAttribute attr_mask,
677 KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
678 ASSERT(this->IsLockedByCurrentThread());
679
680 // Check memory state.
681 const KProcessAddress last_addr = addr + size - 1;
682 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr);
683 R_TRY(this->CheckMemoryState(out_state, out_perm, out_attr, out_blocks_needed, it, last_addr,
684 state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr));
685
686 // If the start address isn't aligned, we need a block.
687 if (out_blocks_needed != nullptr &&
688 Common::AlignDown(GetInteger(addr), PageSize) != it->GetAddress()) {
689 ++(*out_blocks_needed);
690 }
691
692 R_SUCCEED();
693}
694
695Result KPageTableBase::LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_paddr,
696 KProcessAddress addr, size_t size, KMemoryState state_mask,
697 KMemoryState state, KMemoryPermission perm_mask,
698 KMemoryPermission perm, KMemoryAttribute attr_mask,
699 KMemoryAttribute attr, KMemoryPermission new_perm,
700 KMemoryAttribute lock_attr) {
701 // Validate basic preconditions.
702 ASSERT(False(lock_attr & attr));
703 ASSERT(False(lock_attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)));
704
705 // Validate the lock request.
706 const size_t num_pages = size / PageSize;
707 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
708
709 // Lock the table.
710 KScopedLightLock lk(m_general_lock);
711
712 // Check that the output page group is empty, if it exists.
713 if (out_pg) {
714 ASSERT(out_pg->GetNumPages() == 0);
715 }
716
717 // Check the state.
718 KMemoryState old_state;
719 KMemoryPermission old_perm;
720 KMemoryAttribute old_attr;
721 size_t num_allocator_blocks;
722 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
723 std::addressof(old_attr), std::addressof(num_allocator_blocks),
724 addr, size, state_mask | KMemoryState::FlagReferenceCounted,
725 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
726 attr_mask, attr));
727
728 // Get the physical address, if we're supposed to.
729 if (out_paddr != nullptr) {
730 ASSERT(this->GetPhysicalAddressLocked(out_paddr, addr));
731 }
732
733 // Make the page group, if we're supposed to.
734 if (out_pg != nullptr) {
735 R_TRY(this->MakePageGroup(*out_pg, addr, num_pages));
736 }
737
738 // Create an update allocator.
739 Result allocator_result;
740 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
741 m_memory_block_slab_manager, num_allocator_blocks);
742 R_TRY(allocator_result);
743
744 // Decide on new perm and attr.
745 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
746 KMemoryAttribute new_attr = old_attr | static_cast<KMemoryAttribute>(lock_attr);
747
748 // Update permission, if we need to.
749 if (new_perm != old_perm) {
750 // We're going to perform an update, so create a helper.
751 KScopedPageTableUpdater updater(this);
752
753 const KPageProperties properties = {new_perm, false,
754 True(old_attr & KMemoryAttribute::Uncached),
755 DisableMergeAttribute::DisableHeadBodyTail};
756 R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, 0, false, properties,
757 OperationType::ChangePermissions, false));
758 }
759
760 // Apply the memory block updates.
761 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm,
762 new_attr, KMemoryBlockDisableMergeAttribute::Locked,
763 KMemoryBlockDisableMergeAttribute::None);
764
765 // If we have an output group, open.
766 if (out_pg) {
767 out_pg->Open();
768 }
769
770 R_SUCCEED();
771}
772
773Result KPageTableBase::UnlockMemory(KProcessAddress addr, size_t size, KMemoryState state_mask,
774 KMemoryState state, KMemoryPermission perm_mask,
775 KMemoryPermission perm, KMemoryAttribute attr_mask,
776 KMemoryAttribute attr, KMemoryPermission new_perm,
777 KMemoryAttribute lock_attr, const KPageGroup* pg) {
778 // Validate basic preconditions.
779 ASSERT((attr_mask & lock_attr) == lock_attr);
780 ASSERT((attr & lock_attr) == lock_attr);
781
782 // Validate the unlock request.
783 const size_t num_pages = size / PageSize;
784 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
785
786 // Lock the table.
787 KScopedLightLock lk(m_general_lock);
788
789 // Check the state.
790 KMemoryState old_state;
791 KMemoryPermission old_perm;
792 KMemoryAttribute old_attr;
793 size_t num_allocator_blocks;
794 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
795 std::addressof(old_attr), std::addressof(num_allocator_blocks),
796 addr, size, state_mask | KMemoryState::FlagReferenceCounted,
797 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
798 attr_mask, attr));
799
800 // Check the page group.
801 if (pg != nullptr) {
802 R_UNLESS(this->IsValidPageGroup(*pg, addr, num_pages), ResultInvalidMemoryRegion);
803 }
804
805 // Decide on new perm and attr.
806 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
807 KMemoryAttribute new_attr = old_attr & ~static_cast<KMemoryAttribute>(lock_attr);
808
809 // Create an update allocator.
810 Result allocator_result;
811 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
812 m_memory_block_slab_manager, num_allocator_blocks);
813 R_TRY(allocator_result);
814
815 // Update permission, if we need to.
816 if (new_perm != old_perm) {
817 // We're going to perform an update, so create a helper.
818 KScopedPageTableUpdater updater(this);
819
820 const KPageProperties properties = {new_perm, false,
821 True(old_attr & KMemoryAttribute::Uncached),
822 DisableMergeAttribute::EnableAndMergeHeadBodyTail};
823 R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, 0, false, properties,
824 OperationType::ChangePermissions, false));
825 }
826
827 // Apply the memory block updates.
828 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm,
829 new_attr, KMemoryBlockDisableMergeAttribute::None,
830 KMemoryBlockDisableMergeAttribute::Locked);
831
832 R_SUCCEED();
833}
834
835Result KPageTableBase::QueryInfoImpl(KMemoryInfo* out_info, Svc::PageInfo* out_page,
836 KProcessAddress address) const {
837 ASSERT(this->IsLockedByCurrentThread());
838 ASSERT(out_info != nullptr);
839 ASSERT(out_page != nullptr);
840
841 const KMemoryBlock* block = m_memory_block_manager.FindBlock(address);
842 R_UNLESS(block != nullptr, ResultInvalidCurrentMemory);
843
844 *out_info = block->GetMemoryInfo();
845 out_page->flags = 0;
846 R_SUCCEED();
847}
848
849Result KPageTableBase::QueryMappingImpl(KProcessAddress* out, KPhysicalAddress address, size_t size,
850 Svc::MemoryState state) const {
851 ASSERT(!this->IsLockedByCurrentThread());
852 ASSERT(out != nullptr);
853
854 const KProcessAddress region_start = this->GetRegionAddress(state);
855 const size_t region_size = this->GetRegionSize(state);
856
857 // Check that the address/size are potentially valid.
858 R_UNLESS((address < address + size), ResultNotFound);
859
860 // Lock the table.
861 KScopedLightLock lk(m_general_lock);
862
863 auto& impl = this->GetImpl();
864
865 // Begin traversal.
866 TraversalContext context;
867 TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0};
868 bool cur_valid = false;
869 TraversalEntry next_entry;
870 bool next_valid;
871 size_t tot_size = 0;
872
873 next_valid =
874 impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), region_start);
875 next_entry.block_size =
876 (next_entry.block_size - (GetInteger(region_start) & (next_entry.block_size - 1)));
877
878 // Iterate, looking for entry.
879 while (true) {
880 if ((!next_valid && !cur_valid) ||
881 (next_valid && cur_valid &&
882 next_entry.phys_addr == cur_entry.phys_addr + cur_entry.block_size)) {
883 cur_entry.block_size += next_entry.block_size;
884 } else {
885 if (cur_valid && cur_entry.phys_addr <= address &&
886 address + size <= cur_entry.phys_addr + cur_entry.block_size) {
887 // Check if this region is valid.
888 const KProcessAddress mapped_address =
889 (region_start + tot_size) + GetInteger(address - cur_entry.phys_addr);
890 if (R_SUCCEEDED(this->CheckMemoryState(
891 mapped_address, size, KMemoryState::Mask, static_cast<KMemoryState>(state),
892 KMemoryPermission::UserRead, KMemoryPermission::UserRead,
893 KMemoryAttribute::None, KMemoryAttribute::None))) {
894 // It is!
895 *out = mapped_address;
896 R_SUCCEED();
897 }
898 }
899
900 // Update tracking variables.
901 tot_size += cur_entry.block_size;
902 cur_entry = next_entry;
903 cur_valid = next_valid;
904 }
905
906 if (cur_entry.block_size + tot_size >= region_size) {
907 break;
908 }
909
910 next_valid = impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
911 }
912
913 // Check the last entry.
914 R_UNLESS(cur_valid, ResultNotFound);
915 R_UNLESS(cur_entry.phys_addr <= address, ResultNotFound);
916 R_UNLESS(address + size <= cur_entry.phys_addr + cur_entry.block_size, ResultNotFound);
917
918 // Check if the last region is valid.
919 const KProcessAddress mapped_address =
920 (region_start + tot_size) + GetInteger(address - cur_entry.phys_addr);
921 R_TRY_CATCH(this->CheckMemoryState(mapped_address, size, KMemoryState::All,
922 static_cast<KMemoryState>(state),
923 KMemoryPermission::UserRead, KMemoryPermission::UserRead,
924 KMemoryAttribute::None, KMemoryAttribute::None)) {
925 R_CONVERT_ALL(ResultNotFound);
926 }
927 R_END_TRY_CATCH;
928
929 // We found the region.
930 *out = mapped_address;
931 R_SUCCEED();
932}
933
934Result KPageTableBase::MapMemory(KProcessAddress dst_address, KProcessAddress src_address,
935 size_t size) {
936 // Lock the table.
937 KScopedLightLock lk(m_general_lock);
938
939 // Validate that the source address's state is valid.
940 KMemoryState src_state;
941 size_t num_src_allocator_blocks;
942 R_TRY(this->CheckMemoryState(std::addressof(src_state), nullptr, nullptr,
943 std::addressof(num_src_allocator_blocks), src_address, size,
944 KMemoryState::FlagCanAlias, KMemoryState::FlagCanAlias,
945 KMemoryPermission::All, KMemoryPermission::UserReadWrite,
946 KMemoryAttribute::All, KMemoryAttribute::None));
947
948 // Validate that the dst address's state is valid.
949 size_t num_dst_allocator_blocks;
950 R_TRY(this->CheckMemoryState(std::addressof(num_dst_allocator_blocks), dst_address, size,
951 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
952 KMemoryPermission::None, KMemoryAttribute::None,
953 KMemoryAttribute::None));
954
955 // Create an update allocator for the source.
956 Result src_allocator_result;
957 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
958 m_memory_block_slab_manager,
959 num_src_allocator_blocks);
960 R_TRY(src_allocator_result);
961
962 // Create an update allocator for the destination.
963 Result dst_allocator_result;
964 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
965 m_memory_block_slab_manager,
966 num_dst_allocator_blocks);
967 R_TRY(dst_allocator_result);
968
969 // Map the memory.
970 {
971 // Determine the number of pages being operated on.
972 const size_t num_pages = size / PageSize;
973
974 // Create page groups for the memory being unmapped.
975 KPageGroup pg(m_kernel, m_block_info_manager);
976
977 // Create the page group representing the source.
978 R_TRY(this->MakePageGroup(pg, src_address, num_pages));
979
980 // We're going to perform an update, so create a helper.
981 KScopedPageTableUpdater updater(this);
982
983 // Reprotect the source as kernel-read/not mapped.
984 const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>(
985 KMemoryPermission::KernelRead | KMemoryPermission::NotMapped);
986 const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked;
987 const KPageProperties src_properties = {new_src_perm, false, false,
988 DisableMergeAttribute::DisableHeadBodyTail};
989 R_TRY(this->Operate(updater.GetPageList(), src_address, num_pages, 0, false, src_properties,
990 OperationType::ChangePermissions, false));
991
992 // Ensure that we unprotect the source pages on failure.
993 ON_RESULT_FAILURE {
994 const KPageProperties unprotect_properties = {
995 KMemoryPermission::UserReadWrite, false, false,
996 DisableMergeAttribute::EnableHeadBodyTail};
997 R_ASSERT(this->Operate(updater.GetPageList(), src_address, num_pages, 0, false,
998 unprotect_properties, OperationType::ChangePermissions, true));
999 };
1000
1001 // Map the alias pages.
1002 const KPageProperties dst_map_properties = {KMemoryPermission::UserReadWrite, false, false,
1003 DisableMergeAttribute::DisableHead};
1004 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_map_properties,
1005 false));
1006
1007 // Apply the memory block updates.
1008 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages,
1009 src_state, new_src_perm, new_src_attr,
1010 KMemoryBlockDisableMergeAttribute::Locked,
1011 KMemoryBlockDisableMergeAttribute::None);
1012 m_memory_block_manager.Update(
1013 std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::Stack,
1014 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
1015 KMemoryBlockDisableMergeAttribute::Normal, KMemoryBlockDisableMergeAttribute::None);
1016 }
1017
1018 R_SUCCEED();
1019}
1020
1021Result KPageTableBase::UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address,
1022 size_t size) {
1023 // Lock the table.
1024 KScopedLightLock lk(m_general_lock);
1025
1026 // Validate that the source address's state is valid.
1027 KMemoryState src_state;
1028 size_t num_src_allocator_blocks;
1029 R_TRY(this->CheckMemoryState(
1030 std::addressof(src_state), nullptr, nullptr, std::addressof(num_src_allocator_blocks),
1031 src_address, size, KMemoryState::FlagCanAlias, KMemoryState::FlagCanAlias,
1032 KMemoryPermission::All, KMemoryPermission::NotMapped | KMemoryPermission::KernelRead,
1033 KMemoryAttribute::All, KMemoryAttribute::Locked));
1034
1035 // Validate that the dst address's state is valid.
1036 KMemoryPermission dst_perm;
1037 size_t num_dst_allocator_blocks;
1038 R_TRY(this->CheckMemoryState(
1039 nullptr, std::addressof(dst_perm), nullptr, std::addressof(num_dst_allocator_blocks),
1040 dst_address, size, KMemoryState::All, KMemoryState::Stack, KMemoryPermission::None,
1041 KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None));
1042
1043 // Create an update allocator for the source.
1044 Result src_allocator_result;
1045 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
1046 m_memory_block_slab_manager,
1047 num_src_allocator_blocks);
1048 R_TRY(src_allocator_result);
1049
1050 // Create an update allocator for the destination.
1051 Result dst_allocator_result;
1052 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
1053 m_memory_block_slab_manager,
1054 num_dst_allocator_blocks);
1055 R_TRY(dst_allocator_result);
1056
1057 // Unmap the memory.
1058 {
1059 // Determine the number of pages being operated on.
1060 const size_t num_pages = size / PageSize;
1061
1062 // Create page groups for the memory being unmapped.
1063 KPageGroup pg(m_kernel, m_block_info_manager);
1064
1065 // Create the page group representing the destination.
1066 R_TRY(this->MakePageGroup(pg, dst_address, num_pages));
1067
1068 // Ensure the page group is the valid for the source.
1069 R_UNLESS(this->IsValidPageGroup(pg, src_address, num_pages), ResultInvalidMemoryRegion);
1070
1071 // We're going to perform an update, so create a helper.
1072 KScopedPageTableUpdater updater(this);
1073
1074 // Unmap the aliased copy of the pages.
1075 const KPageProperties dst_unmap_properties = {KMemoryPermission::None, false, false,
1076 DisableMergeAttribute::None};
1077 R_TRY(this->Operate(updater.GetPageList(), dst_address, num_pages, 0, false,
1078 dst_unmap_properties, OperationType::Unmap, false));
1079
1080 // Ensure that we re-map the aliased pages on failure.
1081 ON_RESULT_FAILURE {
1082 this->RemapPageGroup(updater.GetPageList(), dst_address, size, pg);
1083 };
1084
1085 // Try to set the permissions for the source pages back to what they should be.
1086 const KPageProperties src_properties = {KMemoryPermission::UserReadWrite, false, false,
1087 DisableMergeAttribute::EnableAndMergeHeadBodyTail};
1088 R_TRY(this->Operate(updater.GetPageList(), src_address, num_pages, 0, false, src_properties,
1089 OperationType::ChangePermissions, false));
1090
1091 // Apply the memory block updates.
1092 m_memory_block_manager.Update(
1093 std::addressof(src_allocator), src_address, num_pages, src_state,
1094 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
1095 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked);
1096 m_memory_block_manager.Update(
1097 std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None,
1098 KMemoryPermission::None, KMemoryAttribute::None,
1099 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal);
1100 }
1101
1102 R_SUCCEED();
1103}
1104
1105Result KPageTableBase::MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address,
1106 size_t size) {
1107 // Validate the mapping request.
1108 R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
1109 ResultInvalidMemoryRegion);
1110
1111 // Lock the table.
1112 KScopedLightLock lk(m_general_lock);
1113
1114 // Verify that the source memory is normal heap.
1115 KMemoryState src_state;
1116 KMemoryPermission src_perm;
1117 size_t num_src_allocator_blocks;
1118 R_TRY(this->CheckMemoryState(std::addressof(src_state), std::addressof(src_perm), nullptr,
1119 std::addressof(num_src_allocator_blocks), src_address, size,
1120 KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All,
1121 KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
1122 KMemoryAttribute::None));
1123
1124 // Verify that the destination memory is unmapped.
1125 size_t num_dst_allocator_blocks;
1126 R_TRY(this->CheckMemoryState(std::addressof(num_dst_allocator_blocks), dst_address, size,
1127 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
1128 KMemoryPermission::None, KMemoryAttribute::None,
1129 KMemoryAttribute::None));
1130
1131 // Create an update allocator for the source.
1132 Result src_allocator_result;
1133 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
1134 m_memory_block_slab_manager,
1135 num_src_allocator_blocks);
1136 R_TRY(src_allocator_result);
1137
1138 // Create an update allocator for the destination.
1139 Result dst_allocator_result;
1140 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
1141 m_memory_block_slab_manager,
1142 num_dst_allocator_blocks);
1143 R_TRY(dst_allocator_result);
1144
1145 // Map the code memory.
1146 {
1147 // Determine the number of pages being operated on.
1148 const size_t num_pages = size / PageSize;
1149
1150 // Create page groups for the memory being unmapped.
1151 KPageGroup pg(m_kernel, m_block_info_manager);
1152
1153 // Create the page group representing the source.
1154 R_TRY(this->MakePageGroup(pg, src_address, num_pages));
1155
1156 // We're going to perform an update, so create a helper.
1157 KScopedPageTableUpdater updater(this);
1158
1159 // Reprotect the source as kernel-read/not mapped.
1160 const KMemoryPermission new_perm = static_cast<KMemoryPermission>(
1161 KMemoryPermission::KernelRead | KMemoryPermission::NotMapped);
1162 const KPageProperties src_properties = {new_perm, false, false,
1163 DisableMergeAttribute::DisableHeadBodyTail};
1164 R_TRY(this->Operate(updater.GetPageList(), src_address, num_pages, 0, false, src_properties,
1165 OperationType::ChangePermissions, false));
1166
1167 // Ensure that we unprotect the source pages on failure.
1168 ON_RESULT_FAILURE {
1169 const KPageProperties unprotect_properties = {
1170 src_perm, false, false, DisableMergeAttribute::EnableHeadBodyTail};
1171 R_ASSERT(this->Operate(updater.GetPageList(), src_address, num_pages, 0, false,
1172 unprotect_properties, OperationType::ChangePermissions, true));
1173 };
1174
1175 // Map the alias pages.
1176 const KPageProperties dst_properties = {new_perm, false, false,
1177 DisableMergeAttribute::DisableHead};
1178 R_TRY(
1179 this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_properties, false));
1180
1181 // Apply the memory block updates.
1182 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages,
1183 src_state, new_perm, KMemoryAttribute::Locked,
1184 KMemoryBlockDisableMergeAttribute::Locked,
1185 KMemoryBlockDisableMergeAttribute::None);
1186 m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages,
1187 KMemoryState::AliasCode, new_perm, KMemoryAttribute::None,
1188 KMemoryBlockDisableMergeAttribute::Normal,
1189 KMemoryBlockDisableMergeAttribute::None);
1190 }
1191
1192 R_SUCCEED();
1193}
1194
1195Result KPageTableBase::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address,
1196 size_t size) {
1197 // Validate the mapping request.
1198 R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
1199 ResultInvalidMemoryRegion);
1200
1201 // Lock the table.
1202 KScopedLightLock lk(m_general_lock);
1203
1204 // Verify that the source memory is locked normal heap.
1205 size_t num_src_allocator_blocks;
1206 R_TRY(this->CheckMemoryState(std::addressof(num_src_allocator_blocks), src_address, size,
1207 KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None,
1208 KMemoryPermission::None, KMemoryAttribute::All,
1209 KMemoryAttribute::Locked));
1210
1211 // Verify that the destination memory is aliasable code.
1212 size_t num_dst_allocator_blocks;
1213 R_TRY(this->CheckMemoryStateContiguous(
1214 std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias,
1215 KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
1216 KMemoryAttribute::All & ~KMemoryAttribute::PermissionLocked, KMemoryAttribute::None));
1217
1218 // Determine whether any pages being unmapped are code.
1219 bool any_code_pages = false;
1220 {
1221 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(dst_address);
1222 while (true) {
1223 // Get the memory info.
1224 const KMemoryInfo info = it->GetMemoryInfo();
1225
1226 // Check if the memory has code flag.
1227 if (True(info.GetState() & KMemoryState::FlagCode)) {
1228 any_code_pages = true;
1229 break;
1230 }
1231
1232 // Check if we're done.
1233 if (dst_address + size - 1 <= info.GetLastAddress()) {
1234 break;
1235 }
1236
1237 // Advance.
1238 ++it;
1239 }
1240 }
1241
1242 // Ensure that we maintain the instruction cache.
1243 bool reprotected_pages = false;
1244 SCOPE_EXIT({
1245 if (reprotected_pages && any_code_pages) {
1246 InvalidateInstructionCache(m_system, dst_address, size);
1247 }
1248 });
1249
1250 // Unmap.
1251 {
1252 // Determine the number of pages being operated on.
1253 const size_t num_pages = size / PageSize;
1254
1255 // Create page groups for the memory being unmapped.
1256 KPageGroup pg(m_kernel, m_block_info_manager);
1257
1258 // Create the page group representing the destination.
1259 R_TRY(this->MakePageGroup(pg, dst_address, num_pages));
1260
1261 // Verify that the page group contains the same pages as the source.
1262 R_UNLESS(this->IsValidPageGroup(pg, src_address, num_pages), ResultInvalidMemoryRegion);
1263
1264 // Create an update allocator for the source.
1265 Result src_allocator_result;
1266 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
1267 m_memory_block_slab_manager,
1268 num_src_allocator_blocks);
1269 R_TRY(src_allocator_result);
1270
1271 // Create an update allocator for the destination.
1272 Result dst_allocator_result;
1273 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
1274 m_memory_block_slab_manager,
1275 num_dst_allocator_blocks);
1276 R_TRY(dst_allocator_result);
1277
1278 // We're going to perform an update, so create a helper.
1279 KScopedPageTableUpdater updater(this);
1280
1281 // Unmap the aliased copy of the pages.
1282 const KPageProperties dst_unmap_properties = {KMemoryPermission::None, false, false,
1283 DisableMergeAttribute::None};
1284 R_TRY(this->Operate(updater.GetPageList(), dst_address, num_pages, 0, false,
1285 dst_unmap_properties, OperationType::Unmap, false));
1286
1287 // Ensure that we re-map the aliased pages on failure.
1288 ON_RESULT_FAILURE {
1289 this->RemapPageGroup(updater.GetPageList(), dst_address, size, pg);
1290 };
1291
1292 // Try to set the permissions for the source pages back to what they should be.
1293 const KPageProperties src_properties = {KMemoryPermission::UserReadWrite, false, false,
1294 DisableMergeAttribute::EnableAndMergeHeadBodyTail};
1295 R_TRY(this->Operate(updater.GetPageList(), src_address, num_pages, 0, false, src_properties,
1296 OperationType::ChangePermissions, false));
1297
1298 // Apply the memory block updates.
1299 m_memory_block_manager.Update(
1300 std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None,
1301 KMemoryPermission::None, KMemoryAttribute::None,
1302 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal);
1303 m_memory_block_manager.Update(
1304 std::addressof(src_allocator), src_address, num_pages, KMemoryState::Normal,
1305 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
1306 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked);
1307
1308 // Note that we reprotected pages.
1309 reprotected_pages = true;
1310 }
1311
1312 R_SUCCEED();
1313}
1314
1315Result KPageTableBase::MapInsecureMemory(KProcessAddress address, size_t size) {
1316 // Get the insecure memory resource limit and pool.
1317 auto* const insecure_resource_limit = KSystemControl::GetInsecureMemoryResourceLimit(m_kernel);
1318 const auto insecure_pool =
1319 static_cast<KMemoryManager::Pool>(KSystemControl::GetInsecureMemoryPool());
1320
1321 // Reserve the insecure memory.
1322 // NOTE: ResultOutOfMemory is returned here instead of the usual LimitReached.
1323 KScopedResourceReservation memory_reservation(insecure_resource_limit,
1324 Svc::LimitableResource::PhysicalMemoryMax, size);
1325 R_UNLESS(memory_reservation.Succeeded(), ResultOutOfMemory);
1326
1327 // Allocate pages for the insecure memory.
1328 KPageGroup pg(m_kernel, m_block_info_manager);
1329 R_TRY(m_kernel.MemoryManager().AllocateAndOpen(
1330 std::addressof(pg), size / PageSize,
1331 KMemoryManager::EncodeOption(insecure_pool, KMemoryManager::Direction::FromFront)));
1332
1333 // Close the opened pages when we're done with them.
1334 // If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed
1335 // automatically.
1336 SCOPE_EXIT({ pg.Close(); });
1337
1338 // Clear all the newly allocated pages.
1339 for (const auto& it : pg) {
1340 std::memset(GetHeapVirtualPointer(m_kernel, it.GetAddress()),
1341 static_cast<u32>(m_heap_fill_value), it.GetSize());
1342 }
1343
1344 // Lock the table.
1345 KScopedLightLock lk(m_general_lock);
1346
1347 // Validate that the address's state is valid.
1348 size_t num_allocator_blocks;
1349 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
1350 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
1351 KMemoryPermission::None, KMemoryAttribute::None,
1352 KMemoryAttribute::None));
1353
1354 // Create an update allocator.
1355 Result allocator_result;
1356 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1357 m_memory_block_slab_manager, num_allocator_blocks);
1358 R_TRY(allocator_result);
1359
1360 // We're going to perform an update, so create a helper.
1361 KScopedPageTableUpdater updater(this);
1362
1363 // Map the pages.
1364 const size_t num_pages = size / PageSize;
1365 const KPageProperties map_properties = {KMemoryPermission::UserReadWrite, false, false,
1366 DisableMergeAttribute::DisableHead};
1367 R_TRY(this->Operate(updater.GetPageList(), address, num_pages, pg, map_properties,
1368 OperationType::MapGroup, false));
1369
1370 // Apply the memory block update.
1371 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages,
1372 KMemoryState::Insecure, KMemoryPermission::UserReadWrite,
1373 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
1374 KMemoryBlockDisableMergeAttribute::None);
1375
1376 // Update our mapped insecure size.
1377 m_mapped_insecure_memory += size;
1378
1379 // Commit the memory reservation.
1380 memory_reservation.Commit();
1381
1382 // We succeeded.
1383 R_SUCCEED();
1384}
1385
1386Result KPageTableBase::UnmapInsecureMemory(KProcessAddress address, size_t size) {
1387 // Lock the table.
1388 KScopedLightLock lk(m_general_lock);
1389
1390 // Check the memory state.
1391 size_t num_allocator_blocks;
1392 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
1393 KMemoryState::All, KMemoryState::Insecure, KMemoryPermission::All,
1394 KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
1395 KMemoryAttribute::None));
1396
1397 // Create an update allocator.
1398 Result allocator_result;
1399 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1400 m_memory_block_slab_manager, num_allocator_blocks);
1401 R_TRY(allocator_result);
1402
1403 // We're going to perform an update, so create a helper.
1404 KScopedPageTableUpdater updater(this);
1405
1406 // Unmap the memory.
1407 const size_t num_pages = size / PageSize;
1408 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
1409 DisableMergeAttribute::None};
1410 R_TRY(this->Operate(updater.GetPageList(), address, num_pages, 0, false, unmap_properties,
1411 OperationType::Unmap, false));
1412
1413 // Apply the memory block update.
1414 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free,
1415 KMemoryPermission::None, KMemoryAttribute::None,
1416 KMemoryBlockDisableMergeAttribute::None,
1417 KMemoryBlockDisableMergeAttribute::Normal);
1418
1419 // Update our mapped insecure size.
1420 m_mapped_insecure_memory -= size;
1421
1422 // Release the insecure memory from the insecure limit.
1423 if (auto* const insecure_resource_limit =
1424 KSystemControl::GetInsecureMemoryResourceLimit(m_kernel);
1425 insecure_resource_limit != nullptr) {
1426 insecure_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, size);
1427 }
1428
1429 R_SUCCEED();
1430}
1431
1432KProcessAddress KPageTableBase::FindFreeArea(KProcessAddress region_start, size_t region_num_pages,
1433 size_t num_pages, size_t alignment, size_t offset,
1434 size_t guard_pages) const {
1435 KProcessAddress address = 0;
1436
1437 if (num_pages <= region_num_pages) {
1438 if (this->IsAslrEnabled()) {
1439 // Try to directly find a free area up to 8 times.
1440 for (size_t i = 0; i < 8; i++) {
1441 const size_t random_offset =
1442 KSystemControl::GenerateRandomRange(
1443 0, (region_num_pages - num_pages - guard_pages) * PageSize / alignment) *
1444 alignment;
1445 const KProcessAddress candidate =
1446 Common::AlignDown(GetInteger(region_start + random_offset), alignment) + offset;
1447
1448 KMemoryInfo info;
1449 Svc::PageInfo page_info;
1450 R_ASSERT(this->QueryInfoImpl(std::addressof(info), std::addressof(page_info),
1451 candidate));
1452
1453 if (info.m_state != KMemoryState::Free) {
1454 continue;
1455 }
1456 if (!(region_start <= candidate)) {
1457 continue;
1458 }
1459 if (!(info.GetAddress() + guard_pages * PageSize <= GetInteger(candidate))) {
1460 continue;
1461 }
1462 if (!(candidate + (num_pages + guard_pages) * PageSize - 1 <=
1463 info.GetLastAddress())) {
1464 continue;
1465 }
1466 if (!(candidate + (num_pages + guard_pages) * PageSize - 1 <=
1467 region_start + region_num_pages * PageSize - 1)) {
1468 continue;
1469 }
1470
1471 address = candidate;
1472 break;
1473 }
1474 // Fall back to finding the first free area with a random offset.
1475 if (address == 0) {
1476 // NOTE: Nintendo does not account for guard pages here.
1477 // This may theoretically cause an offset to be chosen that cannot be mapped.
1478 // We will account for guard pages.
1479 const size_t offset_pages = KSystemControl::GenerateRandomRange(
1480 0, region_num_pages - num_pages - guard_pages);
1481 address = m_memory_block_manager.FindFreeArea(
1482 region_start + offset_pages * PageSize, region_num_pages - offset_pages,
1483 num_pages, alignment, offset, guard_pages);
1484 }
1485 }
1486 // Find the first free area.
1487 if (address == 0) {
1488 address = m_memory_block_manager.FindFreeArea(region_start, region_num_pages, num_pages,
1489 alignment, offset, guard_pages);
1490 }
1491 }
1492
1493 return address;
1494}
1495
1496size_t KPageTableBase::GetSize(KMemoryState state) const {
1497 // Lock the table.
1498 KScopedLightLock lk(m_general_lock);
1499
1500 // Iterate, counting blocks with the desired state.
1501 size_t total_size = 0;
1502 for (KMemoryBlockManager::const_iterator it =
1503 m_memory_block_manager.FindIterator(m_address_space_start);
1504 it != m_memory_block_manager.end(); ++it) {
1505 // Get the memory info.
1506 const KMemoryInfo info = it->GetMemoryInfo();
1507 if (info.GetState() == state) {
1508 total_size += info.GetSize();
1509 }
1510 }
1511
1512 return total_size;
1513}
1514
1515size_t KPageTableBase::GetCodeSize() const {
1516 return this->GetSize(KMemoryState::Code);
1517}
1518
1519size_t KPageTableBase::GetCodeDataSize() const {
1520 return this->GetSize(KMemoryState::CodeData);
1521}
1522
1523size_t KPageTableBase::GetAliasCodeSize() const {
1524 return this->GetSize(KMemoryState::AliasCode);
1525}
1526
1527size_t KPageTableBase::GetAliasCodeDataSize() const {
1528 return this->GetSize(KMemoryState::AliasCodeData);
1529}
1530
1531Result KPageTableBase::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
1532 size_t num_pages, KMemoryPermission perm) {
1533 ASSERT(this->IsLockedByCurrentThread());
1534
1535 // Create a page group to hold the pages we allocate.
1536 KPageGroup pg(m_kernel, m_block_info_manager);
1537
1538 // Allocate the pages.
1539 R_TRY(
1540 m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option));
1541
1542 // Ensure that the page group is closed when we're done working with it.
1543 SCOPE_EXIT({ pg.Close(); });
1544
1545 // Clear all pages.
1546 for (const auto& it : pg) {
1547 std::memset(GetHeapVirtualPointer(m_kernel, it.GetAddress()),
1548 static_cast<u32>(m_heap_fill_value), it.GetSize());
1549 }
1550
1551 // Map the pages.
1552 const KPageProperties properties = {perm, false, false, DisableMergeAttribute::None};
1553 R_RETURN(this->Operate(page_list, address, num_pages, pg, properties, OperationType::MapGroup,
1554 false));
1555}
1556
1557Result KPageTableBase::MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
1558 const KPageGroup& pg, const KPageProperties properties,
1559 bool reuse_ll) {
1560 ASSERT(this->IsLockedByCurrentThread());
1561
1562 // Note the current address, so that we can iterate.
1563 const KProcessAddress start_address = address;
1564 KProcessAddress cur_address = address;
1565
1566 // Ensure that we clean up on failure.
1567 ON_RESULT_FAILURE {
1568 ASSERT(!reuse_ll);
1569 if (cur_address != start_address) {
1570 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
1571 DisableMergeAttribute::None};
1572 R_ASSERT(this->Operate(page_list, start_address,
1573 (cur_address - start_address) / PageSize, 0, false,
1574 unmap_properties, OperationType::Unmap, true));
1575 }
1576 };
1577
1578 // Iterate, mapping all pages in the group.
1579 for (const auto& block : pg) {
1580 // Map and advance.
1581 const KPageProperties cur_properties =
1582 (cur_address == start_address)
1583 ? properties
1584 : KPageProperties{properties.perm, properties.io, properties.uncached,
1585 DisableMergeAttribute::None};
1586 R_TRY(this->Operate(page_list, cur_address, block.GetNumPages(), block.GetAddress(), true,
1587 cur_properties, OperationType::Map, reuse_ll));
1588 cur_address += block.GetSize();
1589 }
1590
1591 // We succeeded!
1592 R_SUCCEED();
1593}
1594
1595void KPageTableBase::RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
1596 const KPageGroup& pg) {
1597 ASSERT(this->IsLockedByCurrentThread());
1598
1599 // Note the current address, so that we can iterate.
1600 const KProcessAddress start_address = address;
1601 const KProcessAddress last_address = start_address + size - 1;
1602 const KProcessAddress end_address = last_address + 1;
1603
1604 // Iterate over the memory.
1605 auto pg_it = pg.begin();
1606 ASSERT(pg_it != pg.end());
1607
1608 KPhysicalAddress pg_phys_addr = pg_it->GetAddress();
1609 size_t pg_pages = pg_it->GetNumPages();
1610
1611 auto it = m_memory_block_manager.FindIterator(start_address);
1612 while (true) {
1613 // Check that the iterator is valid.
1614 ASSERT(it != m_memory_block_manager.end());
1615
1616 // Get the memory info.
1617 const KMemoryInfo info = it->GetMemoryInfo();
1618
1619 // Determine the range to map.
1620 KProcessAddress map_address = std::max<u64>(info.GetAddress(), GetInteger(start_address));
1621 const KProcessAddress map_end_address =
1622 std::min<u64>(info.GetEndAddress(), GetInteger(end_address));
1623 ASSERT(map_end_address != map_address);
1624
1625 // Determine if we should disable head merge.
1626 const bool disable_head_merge =
1627 info.GetAddress() >= GetInteger(start_address) &&
1628 True(info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Normal);
1629 const KPageProperties map_properties = {
1630 info.GetPermission(), false, false,
1631 disable_head_merge ? DisableMergeAttribute::DisableHead : DisableMergeAttribute::None};
1632
1633 // While we have pages to map, map them.
1634 size_t map_pages = (map_end_address - map_address) / PageSize;
1635 while (map_pages > 0) {
1636 // Check if we're at the end of the physical block.
1637 if (pg_pages == 0) {
1638 // Ensure there are more pages to map.
1639 ASSERT(pg_it != pg.end());
1640
1641 // Advance our physical block.
1642 ++pg_it;
1643 pg_phys_addr = pg_it->GetAddress();
1644 pg_pages = pg_it->GetNumPages();
1645 }
1646
1647 // Map whatever we can.
1648 const size_t cur_pages = std::min(pg_pages, map_pages);
1649 R_ASSERT(this->Operate(page_list, map_address, map_pages, pg_phys_addr, true,
1650 map_properties, OperationType::Map, true));
1651
1652 // Advance.
1653 map_address += cur_pages * PageSize;
1654 map_pages -= cur_pages;
1655
1656 pg_phys_addr += cur_pages * PageSize;
1657 pg_pages -= cur_pages;
1658 }
1659
1660 // Check if we're done.
1661 if (last_address <= info.GetLastAddress()) {
1662 break;
1663 }
1664
1665 // Advance.
1666 ++it;
1667 }
1668
1669 // Check that we re-mapped precisely the page group.
1670 ASSERT((++pg_it) == pg.end());
1671}
1672
1673Result KPageTableBase::MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_t num_pages) {
1674 ASSERT(this->IsLockedByCurrentThread());
1675
1676 const size_t size = num_pages * PageSize;
1677
1678 // We're making a new group, not adding to an existing one.
1679 R_UNLESS(pg.empty(), ResultInvalidCurrentMemory);
1680
1681 auto& impl = this->GetImpl();
1682
1683 // Begin traversal.
1684 TraversalContext context;
1685 TraversalEntry next_entry;
1686 R_UNLESS(impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), addr),
1687 ResultInvalidCurrentMemory);
1688
1689 // Prepare tracking variables.
1690 KPhysicalAddress cur_addr = next_entry.phys_addr;
1691 size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
1692 size_t tot_size = cur_size;
1693
1694 // Iterate, adding to group as we go.
1695 while (tot_size < size) {
1696 R_UNLESS(impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)),
1697 ResultInvalidCurrentMemory);
1698
1699 if (next_entry.phys_addr != (cur_addr + cur_size)) {
1700 const size_t cur_pages = cur_size / PageSize;
1701
1702 R_UNLESS(IsHeapPhysicalAddress(cur_addr), ResultInvalidCurrentMemory);
1703 R_TRY(pg.AddBlock(cur_addr, cur_pages));
1704
1705 cur_addr = next_entry.phys_addr;
1706 cur_size = next_entry.block_size;
1707 } else {
1708 cur_size += next_entry.block_size;
1709 }
1710
1711 tot_size += next_entry.block_size;
1712 }
1713
1714 // Ensure we add the right amount for the last block.
1715 if (tot_size > size) {
1716 cur_size -= (tot_size - size);
1717 }
1718
1719 // add the last block.
1720 const size_t cur_pages = cur_size / PageSize;
1721 R_UNLESS(IsHeapPhysicalAddress(cur_addr), ResultInvalidCurrentMemory);
1722 R_TRY(pg.AddBlock(cur_addr, cur_pages));
1723
1724 R_SUCCEED();
1725}
1726
1727bool KPageTableBase::IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr,
1728 size_t num_pages) {
1729 ASSERT(this->IsLockedByCurrentThread());
1730
1731 const size_t size = num_pages * PageSize;
1732
1733 // Empty groups are necessarily invalid.
1734 if (pg.empty()) {
1735 return false;
1736 }
1737
1738 auto& impl = this->GetImpl();
1739
1740 // We're going to validate that the group we'd expect is the group we see.
1741 auto cur_it = pg.begin();
1742 KPhysicalAddress cur_block_address = cur_it->GetAddress();
1743 size_t cur_block_pages = cur_it->GetNumPages();
1744
1745 auto UpdateCurrentIterator = [&]() {
1746 if (cur_block_pages == 0) {
1747 if ((++cur_it) == pg.end()) {
1748 return false;
1749 }
1750
1751 cur_block_address = cur_it->GetAddress();
1752 cur_block_pages = cur_it->GetNumPages();
1753 }
1754 return true;
1755 };
1756
1757 // Begin traversal.
1758 TraversalContext context;
1759 TraversalEntry next_entry;
1760 if (!impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), addr)) {
1761 return false;
1762 }
1763
1764 // Prepare tracking variables.
1765 KPhysicalAddress cur_addr = next_entry.phys_addr;
1766 size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
1767 size_t tot_size = cur_size;
1768
1769 // Iterate, comparing expected to actual.
1770 while (tot_size < size) {
1771 if (!impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context))) {
1772 return false;
1773 }
1774
1775 if (next_entry.phys_addr != (cur_addr + cur_size)) {
1776 const size_t cur_pages = cur_size / PageSize;
1777
1778 if (!IsHeapPhysicalAddress(cur_addr)) {
1779 return false;
1780 }
1781
1782 if (!UpdateCurrentIterator()) {
1783 return false;
1784 }
1785
1786 if (cur_block_address != cur_addr || cur_block_pages < cur_pages) {
1787 return false;
1788 }
1789
1790 cur_block_address += cur_size;
1791 cur_block_pages -= cur_pages;
1792 cur_addr = next_entry.phys_addr;
1793 cur_size = next_entry.block_size;
1794 } else {
1795 cur_size += next_entry.block_size;
1796 }
1797
1798 tot_size += next_entry.block_size;
1799 }
1800
1801 // Ensure we compare the right amount for the last block.
1802 if (tot_size > size) {
1803 cur_size -= (tot_size - size);
1804 }
1805
1806 if (!IsHeapPhysicalAddress(cur_addr)) {
1807 return false;
1808 }
1809
1810 if (!UpdateCurrentIterator()) {
1811 return false;
1812 }
1813
1814 return cur_block_address == cur_addr && cur_block_pages == (cur_size / PageSize);
1815}
1816
1817Result KPageTableBase::GetContiguousMemoryRangeWithState(
1818 MemoryRange* out, KProcessAddress address, size_t size, KMemoryState state_mask,
1819 KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
1820 KMemoryAttribute attr_mask, KMemoryAttribute attr) {
1821 ASSERT(this->IsLockedByCurrentThread());
1822
1823 auto& impl = this->GetImpl();
1824
1825 // Begin a traversal.
1826 TraversalContext context;
1827 TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0};
1828 R_UNLESS(impl.BeginTraversal(std::addressof(cur_entry), std::addressof(context), address),
1829 ResultInvalidCurrentMemory);
1830
1831 // Traverse until we have enough size or we aren't contiguous any more.
1832 const KPhysicalAddress phys_address = cur_entry.phys_addr;
1833 size_t contig_size;
1834 for (contig_size =
1835 cur_entry.block_size - (GetInteger(phys_address) & (cur_entry.block_size - 1));
1836 contig_size < size; contig_size += cur_entry.block_size) {
1837 if (!impl.ContinueTraversal(std::addressof(cur_entry), std::addressof(context))) {
1838 break;
1839 }
1840 if (cur_entry.phys_addr != phys_address + contig_size) {
1841 break;
1842 }
1843 }
1844
1845 // Take the minimum size for our region.
1846 size = std::min(size, contig_size);
1847
1848 // Check that the memory is contiguous (modulo the reference count bit).
1849 const KMemoryState test_state_mask = state_mask | KMemoryState::FlagReferenceCounted;
1850 const bool is_heap = R_SUCCEEDED(this->CheckMemoryStateContiguous(
1851 address, size, test_state_mask, state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
1852 attr_mask, attr));
1853 if (!is_heap) {
1854 R_TRY(this->CheckMemoryStateContiguous(address, size, test_state_mask, state, perm_mask,
1855 perm, attr_mask, attr));
1856 }
1857
1858 // The memory is contiguous, so set the output range.
1859 out->Set(phys_address, size, is_heap);
1860 R_SUCCEED();
1861}
1862
1863Result KPageTableBase::SetMemoryPermission(KProcessAddress addr, size_t size,
1864 Svc::MemoryPermission svc_perm) {
1865 const size_t num_pages = size / PageSize;
1866
1867 // Lock the table.
1868 KScopedLightLock lk(m_general_lock);
1869
1870 // Verify we can change the memory permission.
1871 KMemoryState old_state;
1872 KMemoryPermission old_perm;
1873 size_t num_allocator_blocks;
1874 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), nullptr,
1875 std::addressof(num_allocator_blocks), addr, size,
1876 KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect,
1877 KMemoryPermission::None, KMemoryPermission::None,
1878 KMemoryAttribute::All, KMemoryAttribute::None));
1879
1880 // Determine new perm.
1881 const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm);
1882 R_SUCCEED_IF(old_perm == new_perm);
1883
1884 // Create an update allocator.
1885 Result allocator_result;
1886 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1887 m_memory_block_slab_manager, num_allocator_blocks);
1888 R_TRY(allocator_result);
1889
1890 // We're going to perform an update, so create a helper.
1891 KScopedPageTableUpdater updater(this);
1892
1893 // Perform mapping operation.
1894 const KPageProperties properties = {new_perm, false, false, DisableMergeAttribute::None};
1895 R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, 0, false, properties,
1896 OperationType::ChangePermissions, false));
1897
1898 // Update the blocks.
1899 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm,
1900 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
1901 KMemoryBlockDisableMergeAttribute::None);
1902
1903 R_SUCCEED();
1904}
1905
1906Result KPageTableBase::SetProcessMemoryPermission(KProcessAddress addr, size_t size,
1907 Svc::MemoryPermission svc_perm) {
1908 const size_t num_pages = size / PageSize;
1909
1910 // Lock the table.
1911 KScopedLightLock lk(m_general_lock);
1912
1913 // Verify we can change the memory permission.
1914 KMemoryState old_state;
1915 KMemoryPermission old_perm;
1916 size_t num_allocator_blocks;
1917 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), nullptr,
1918 std::addressof(num_allocator_blocks), addr, size,
1919 KMemoryState::FlagCode, KMemoryState::FlagCode,
1920 KMemoryPermission::None, KMemoryPermission::None,
1921 KMemoryAttribute::All, KMemoryAttribute::None));
1922
1923 // Make a new page group for the region.
1924 KPageGroup pg(m_kernel, m_block_info_manager);
1925
1926 // Determine new perm/state.
1927 const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm);
1928 KMemoryState new_state = old_state;
1929 const bool is_w = (new_perm & KMemoryPermission::UserWrite) == KMemoryPermission::UserWrite;
1930 const bool is_x = (new_perm & KMemoryPermission::UserExecute) == KMemoryPermission::UserExecute;
1931 const bool was_x =
1932 (old_perm & KMemoryPermission::UserExecute) == KMemoryPermission::UserExecute;
1933 ASSERT(!(is_w && is_x));
1934
1935 if (is_w) {
1936 switch (old_state) {
1937 case KMemoryState::Code:
1938 new_state = KMemoryState::CodeData;
1939 break;
1940 case KMemoryState::AliasCode:
1941 new_state = KMemoryState::AliasCodeData;
1942 break;
1943 default:
1944 UNREACHABLE();
1945 }
1946 }
1947
1948 // Create a page group, if we're setting execute permissions.
1949 if (is_x) {
1950 R_TRY(this->MakePageGroup(pg, GetInteger(addr), num_pages));
1951 }
1952
1953 // Succeed if there's nothing to do.
1954 R_SUCCEED_IF(old_perm == new_perm && old_state == new_state);
1955
1956 // Create an update allocator.
1957 Result allocator_result;
1958 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1959 m_memory_block_slab_manager, num_allocator_blocks);
1960 R_TRY(allocator_result);
1961
1962 // We're going to perform an update, so create a helper.
1963 KScopedPageTableUpdater updater(this);
1964
1965 // Perform mapping operation.
1966 const KPageProperties properties = {new_perm, false, false, DisableMergeAttribute::None};
1967 const auto operation = was_x ? OperationType::ChangePermissionsAndRefreshAndFlush
1968 : OperationType::ChangePermissions;
1969 R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, 0, false, properties, operation,
1970 false));
1971
1972 // Update the blocks.
1973 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, new_state, new_perm,
1974 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
1975 KMemoryBlockDisableMergeAttribute::None);
1976
1977 // Ensure cache coherency, if we're setting pages as executable.
1978 if (is_x) {
1979 for (const auto& block : pg) {
1980 StoreDataCache(GetHeapVirtualPointer(m_kernel, block.GetAddress()), block.GetSize());
1981 }
1982 InvalidateInstructionCache(m_system, addr, size);
1983 }
1984
1985 R_SUCCEED();
1986}
1987
1988Result KPageTableBase::SetMemoryAttribute(KProcessAddress addr, size_t size, KMemoryAttribute mask,
1989 KMemoryAttribute attr) {
1990 const size_t num_pages = size / PageSize;
1991 ASSERT((mask | KMemoryAttribute::SetMask) == KMemoryAttribute::SetMask);
1992
1993 // Lock the table.
1994 KScopedLightLock lk(m_general_lock);
1995
1996 // Verify we can change the memory attribute.
1997 KMemoryState old_state;
1998 KMemoryPermission old_perm;
1999 KMemoryAttribute old_attr;
2000 size_t num_allocator_blocks;
2001 constexpr KMemoryAttribute AttributeTestMask =
2002 ~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared);
2003 const KMemoryState state_test_mask =
2004 (True(mask & KMemoryAttribute::Uncached) ? KMemoryState::FlagCanChangeAttribute
2005 : KMemoryState::None) |
2006 (True(mask & KMemoryAttribute::PermissionLocked) ? KMemoryState::FlagCanPermissionLock
2007 : KMemoryState::None);
2008 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
2009 std::addressof(old_attr), std::addressof(num_allocator_blocks),
2010 addr, size, state_test_mask, state_test_mask,
2011 KMemoryPermission::None, KMemoryPermission::None,
2012 AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask));
2013
2014 // Create an update allocator.
2015 Result allocator_result;
2016 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2017 m_memory_block_slab_manager, num_allocator_blocks);
2018 R_TRY(allocator_result);
2019
2020 // We're going to perform an update, so create a helper.
2021 KScopedPageTableUpdater updater(this);
2022
2023 // If we need to, perform a change attribute operation.
2024 if (True(mask & KMemoryAttribute::Uncached)) {
2025 // Determine the new attribute.
2026 const KMemoryAttribute new_attr =
2027 static_cast<KMemoryAttribute>(((old_attr & ~mask) | (attr & mask)));
2028
2029 // Perform operation.
2030 const KPageProperties properties = {old_perm, false,
2031 True(new_attr & KMemoryAttribute::Uncached),
2032 DisableMergeAttribute::None};
2033 R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, 0, false, properties,
2034 OperationType::ChangePermissionsAndRefreshAndFlush, false));
2035 }
2036
2037 // Update the blocks.
2038 m_memory_block_manager.UpdateAttribute(std::addressof(allocator), addr, num_pages, mask, attr);
2039
2040 R_SUCCEED();
2041}
2042
2043Result KPageTableBase::SetHeapSize(KProcessAddress* out, size_t size) {
2044 // Lock the physical memory mutex.
2045 KScopedLightLock map_phys_mem_lk(m_map_physical_memory_lock);
2046
2047 // Try to perform a reduction in heap, instead of an extension.
2048 KProcessAddress cur_address;
2049 size_t allocation_size;
2050 {
2051 // Lock the table.
2052 KScopedLightLock lk(m_general_lock);
2053
2054 // Validate that setting heap size is possible at all.
2055 R_UNLESS(!m_is_kernel, ResultOutOfMemory);
2056 R_UNLESS(size <= static_cast<size_t>(m_heap_region_end - m_heap_region_start),
2057 ResultOutOfMemory);
2058 R_UNLESS(size <= m_max_heap_size, ResultOutOfMemory);
2059
2060 if (size < static_cast<size_t>(m_current_heap_end - m_heap_region_start)) {
2061 // The size being requested is less than the current size, so we need to free the end of
2062 // the heap.
2063
2064 // Validate memory state.
2065 size_t num_allocator_blocks;
2066 R_TRY(this->CheckMemoryState(
2067 std::addressof(num_allocator_blocks), m_heap_region_start + size,
2068 (m_current_heap_end - m_heap_region_start) - size, KMemoryState::All,
2069 KMemoryState::Normal, KMemoryPermission::All, KMemoryPermission::UserReadWrite,
2070 KMemoryAttribute::All, KMemoryAttribute::None));
2071
2072 // Create an update allocator.
2073 Result allocator_result;
2074 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2075 m_memory_block_slab_manager,
2076 num_allocator_blocks);
2077 R_TRY(allocator_result);
2078
2079 // We're going to perform an update, so create a helper.
2080 KScopedPageTableUpdater updater(this);
2081
2082 // Unmap the end of the heap.
2083 const size_t num_pages = ((m_current_heap_end - m_heap_region_start) - size) / PageSize;
2084 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
2085 DisableMergeAttribute::None};
2086 R_TRY(this->Operate(updater.GetPageList(), m_heap_region_start + size, num_pages, 0,
2087 false, unmap_properties, OperationType::Unmap, false));
2088
2089 // Release the memory from the resource limit.
2090 m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax,
2091 num_pages * PageSize);
2092
2093 // Apply the memory block update.
2094 m_memory_block_manager.Update(std::addressof(allocator), m_heap_region_start + size,
2095 num_pages, KMemoryState::Free, KMemoryPermission::None,
2096 KMemoryAttribute::None,
2097 KMemoryBlockDisableMergeAttribute::None,
2098 size == 0 ? KMemoryBlockDisableMergeAttribute::Normal
2099 : KMemoryBlockDisableMergeAttribute::None);
2100
2101 // Update the current heap end.
2102 m_current_heap_end = m_heap_region_start + size;
2103
2104 // Set the output.
2105 *out = m_heap_region_start;
2106 R_SUCCEED();
2107 } else if (size == static_cast<size_t>(m_current_heap_end - m_heap_region_start)) {
2108 // The size requested is exactly the current size.
2109 *out = m_heap_region_start;
2110 R_SUCCEED();
2111 } else {
2112 // We have to allocate memory. Determine how much to allocate and where while the table
2113 // is locked.
2114 cur_address = m_current_heap_end;
2115 allocation_size = size - (m_current_heap_end - m_heap_region_start);
2116 }
2117 }
2118
2119 // Reserve memory for the heap extension.
2120 KScopedResourceReservation memory_reservation(
2121 m_resource_limit, Svc::LimitableResource::PhysicalMemoryMax, allocation_size);
2122 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
2123
2124 // Allocate pages for the heap extension.
2125 KPageGroup pg(m_kernel, m_block_info_manager);
2126 R_TRY(m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), allocation_size / PageSize,
2127 m_allocate_option));
2128
2129 // Close the opened pages when we're done with them.
2130 // If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed
2131 // automatically.
2132 SCOPE_EXIT({ pg.Close(); });
2133
2134 // Clear all the newly allocated pages.
2135 for (const auto& it : pg) {
2136 std::memset(GetHeapVirtualPointer(m_kernel, it.GetAddress()), m_heap_fill_value,
2137 it.GetSize());
2138 }
2139
2140 // Map the pages.
2141 {
2142 // Lock the table.
2143 KScopedLightLock lk(m_general_lock);
2144
2145 // Ensure that the heap hasn't changed since we began executing.
2146 ASSERT(cur_address == m_current_heap_end);
2147
2148 // Check the memory state.
2149 size_t num_allocator_blocks;
2150 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), m_current_heap_end,
2151 allocation_size, KMemoryState::All, KMemoryState::Free,
2152 KMemoryPermission::None, KMemoryPermission::None,
2153 KMemoryAttribute::None, KMemoryAttribute::None));
2154
2155 // Create an update allocator.
2156 Result allocator_result;
2157 KMemoryBlockManagerUpdateAllocator allocator(
2158 std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks);
2159 R_TRY(allocator_result);
2160
2161 // We're going to perform an update, so create a helper.
2162 KScopedPageTableUpdater updater(this);
2163
2164 // Map the pages.
2165 const size_t num_pages = allocation_size / PageSize;
2166 const KPageProperties map_properties = {KMemoryPermission::UserReadWrite, false, false,
2167 (m_current_heap_end == m_heap_region_start)
2168 ? DisableMergeAttribute::DisableHead
2169 : DisableMergeAttribute::None};
2170 R_TRY(this->Operate(updater.GetPageList(), m_current_heap_end, num_pages, pg,
2171 map_properties, OperationType::MapGroup, false));
2172
2173 // We succeeded, so commit our memory reservation.
2174 memory_reservation.Commit();
2175
2176 // Apply the memory block update.
2177 m_memory_block_manager.Update(
2178 std::addressof(allocator), m_current_heap_end, num_pages, KMemoryState::Normal,
2179 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
2180 m_heap_region_start == m_current_heap_end ? KMemoryBlockDisableMergeAttribute::Normal
2181 : KMemoryBlockDisableMergeAttribute::None,
2182 KMemoryBlockDisableMergeAttribute::None);
2183
2184 // Update the current heap end.
2185 m_current_heap_end = m_heap_region_start + size;
2186
2187 // Set the output.
2188 *out = m_heap_region_start;
2189 R_SUCCEED();
2190 }
2191}
2192
2193Result KPageTableBase::SetMaxHeapSize(size_t size) {
2194 // Lock the table.
2195 KScopedLightLock lk(m_general_lock);
2196
2197 // Only process page tables are allowed to set heap size.
2198 ASSERT(!this->IsKernel());
2199
2200 m_max_heap_size = size;
2201
2202 R_SUCCEED();
2203}
2204
2205Result KPageTableBase::QueryInfo(KMemoryInfo* out_info, Svc::PageInfo* out_page_info,
2206 KProcessAddress addr) const {
2207 // If the address is invalid, create a fake block.
2208 if (!this->Contains(addr, 1)) {
2209 *out_info = {
2210 .m_address = GetInteger(m_address_space_end),
2211 .m_size = 0 - GetInteger(m_address_space_end),
2212 .m_state = static_cast<KMemoryState>(Svc::MemoryState::Inaccessible),
2213 .m_device_disable_merge_left_count = 0,
2214 .m_device_disable_merge_right_count = 0,
2215 .m_ipc_lock_count = 0,
2216 .m_device_use_count = 0,
2217 .m_ipc_disable_merge_count = 0,
2218 .m_permission = KMemoryPermission::None,
2219 .m_attribute = KMemoryAttribute::None,
2220 .m_original_permission = KMemoryPermission::None,
2221 .m_disable_merge_attribute = KMemoryBlockDisableMergeAttribute::None,
2222 };
2223 out_page_info->flags = 0;
2224
2225 R_SUCCEED();
2226 }
2227
2228 // Otherwise, lock the table and query.
2229 KScopedLightLock lk(m_general_lock);
2230 R_RETURN(this->QueryInfoImpl(out_info, out_page_info, addr));
2231}
2232
2233Result KPageTableBase::QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out,
2234 KProcessAddress address) const {
2235 // Lock the table.
2236 KScopedLightLock lk(m_general_lock);
2237
2238 // Align the address down to page size.
2239 address = Common::AlignDown(GetInteger(address), PageSize);
2240
2241 // Verify that we can query the address.
2242 KMemoryInfo info;
2243 Svc::PageInfo page_info;
2244 R_TRY(this->QueryInfoImpl(std::addressof(info), std::addressof(page_info), address));
2245
2246 // Check the memory state.
2247 R_TRY(this->CheckMemoryState(info, KMemoryState::FlagCanQueryPhysical,
2248 KMemoryState::FlagCanQueryPhysical,
2249 KMemoryPermission::UserReadExecute, KMemoryPermission::UserRead,
2250 KMemoryAttribute::None, KMemoryAttribute::None));
2251
2252 // Prepare to traverse.
2253 KPhysicalAddress phys_addr;
2254 size_t phys_size;
2255
2256 KProcessAddress virt_addr = info.GetAddress();
2257 KProcessAddress end_addr = info.GetEndAddress();
2258
2259 // Perform traversal.
2260 {
2261 // Begin traversal.
2262 TraversalContext context;
2263 TraversalEntry next_entry;
2264 bool traverse_valid =
2265 m_impl->BeginTraversal(std::addressof(next_entry), std::addressof(context), virt_addr);
2266 R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
2267
2268 // Set tracking variables.
2269 phys_addr = next_entry.phys_addr;
2270 phys_size = next_entry.block_size - (GetInteger(phys_addr) & (next_entry.block_size - 1));
2271
2272 // Iterate.
2273 while (true) {
2274 // Continue the traversal.
2275 traverse_valid =
2276 m_impl->ContinueTraversal(std::addressof(next_entry), std::addressof(context));
2277 if (!traverse_valid) {
2278 break;
2279 }
2280
2281 if (next_entry.phys_addr != (phys_addr + phys_size)) {
2282 // Check if we're done.
2283 if (virt_addr <= address && address <= virt_addr + phys_size - 1) {
2284 break;
2285 }
2286
2287 // Advance.
2288 phys_addr = next_entry.phys_addr;
2289 virt_addr += next_entry.block_size;
2290 phys_size =
2291 next_entry.block_size - (GetInteger(phys_addr) & (next_entry.block_size - 1));
2292 } else {
2293 phys_size += next_entry.block_size;
2294 }
2295
2296 // Check if we're done.
2297 if (end_addr < virt_addr + phys_size) {
2298 break;
2299 }
2300 }
2301 ASSERT(virt_addr <= address && address <= virt_addr + phys_size - 1);
2302
2303 // Ensure we use the right size.
2304 if (end_addr < virt_addr + phys_size) {
2305 phys_size = end_addr - virt_addr;
2306 }
2307 }
2308
2309 // Set the output.
2310 out->physical_address = GetInteger(phys_addr);
2311 out->virtual_address = GetInteger(virt_addr);
2312 out->size = phys_size;
2313 R_SUCCEED();
2314}
2315
2316Result KPageTableBase::MapIoImpl(KProcessAddress* out, PageLinkedList* page_list,
2317 KPhysicalAddress phys_addr, size_t size, KMemoryState state,
2318 KMemoryPermission perm) {
2319 // Check pre-conditions.
2320 ASSERT(this->IsLockedByCurrentThread());
2321 ASSERT(Common::IsAligned(GetInteger(phys_addr), PageSize));
2322 ASSERT(Common::IsAligned(size, PageSize));
2323 ASSERT(size > 0);
2324
2325 R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress);
2326 const size_t num_pages = size / PageSize;
2327 const KPhysicalAddress last = phys_addr + size - 1;
2328
2329 // Get region extents.
2330 const KProcessAddress region_start = m_kernel_map_region_start;
2331 const size_t region_size = m_kernel_map_region_end - m_kernel_map_region_start;
2332 const size_t region_num_pages = region_size / PageSize;
2333
2334 ASSERT(this->CanContain(region_start, region_size, state));
2335
2336 // Locate the memory region.
2337 const KMemoryRegion* region = KMemoryLayout::Find(m_kernel.MemoryLayout(), phys_addr);
2338 R_UNLESS(region != nullptr, ResultInvalidAddress);
2339
2340 ASSERT(region->Contains(GetInteger(phys_addr)));
2341
2342 // Ensure that the region is mappable.
2343 const bool is_rw = perm == KMemoryPermission::UserReadWrite;
2344 while (true) {
2345 // Check that the region exists.
2346 R_UNLESS(region != nullptr, ResultInvalidAddress);
2347
2348 // Check the region attributes.
2349 R_UNLESS(!region->IsDerivedFrom(KMemoryRegionType_Dram), ResultInvalidAddress);
2350 R_UNLESS(!region->HasTypeAttribute(KMemoryRegionAttr_UserReadOnly) || !is_rw,
2351 ResultInvalidAddress);
2352 R_UNLESS(!region->HasTypeAttribute(KMemoryRegionAttr_NoUserMap), ResultInvalidAddress);
2353
2354 // Check if we're done.
2355 if (GetInteger(last) <= region->GetLastAddress()) {
2356 break;
2357 }
2358
2359 // Advance.
2360 region = region->GetNext();
2361 };
2362
2363 // Select an address to map at.
2364 KProcessAddress addr = 0;
2365 {
2366 const size_t alignment = 4_KiB;
2367 const KPhysicalAddress aligned_phys =
2368 Common::AlignUp(GetInteger(phys_addr), alignment) + alignment - 1;
2369 R_UNLESS(aligned_phys > phys_addr, ResultInvalidAddress);
2370
2371 const KPhysicalAddress last_aligned_paddr =
2372 Common::AlignDown(GetInteger(last) + 1, alignment) - 1;
2373 R_UNLESS((last_aligned_paddr <= last && aligned_phys <= last_aligned_paddr),
2374 ResultInvalidAddress);
2375
2376 addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0,
2377 this->GetNumGuardPages());
2378 R_UNLESS(addr != 0, ResultOutOfMemory);
2379 }
2380
2381 // Check that we can map IO here.
2382 ASSERT(this->CanContain(addr, size, state));
2383 R_ASSERT(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free,
2384 KMemoryPermission::None, KMemoryPermission::None,
2385 KMemoryAttribute::None, KMemoryAttribute::None));
2386
2387 // Perform mapping operation.
2388 const KPageProperties properties = {perm, state == KMemoryState::IoRegister, false,
2389 DisableMergeAttribute::DisableHead};
2390 R_TRY(this->Operate(page_list, addr, num_pages, phys_addr, true, properties, OperationType::Map,
2391 false));
2392
2393 // Set the output address.
2394 *out = addr;
2395
2396 R_SUCCEED();
2397}
2398
2399Result KPageTableBase::MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) {
2400 // Lock the table.
2401 KScopedLightLock lk(m_general_lock);
2402
2403 // Create an update allocator.
2404 Result allocator_result;
2405 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2406 m_memory_block_slab_manager);
2407 R_TRY(allocator_result);
2408
2409 // We're going to perform an update, so create a helper.
2410 KScopedPageTableUpdater updater(this);
2411
2412 // Map the io memory.
2413 KProcessAddress addr;
2414 R_TRY(this->MapIoImpl(std::addressof(addr), updater.GetPageList(), phys_addr, size,
2415 KMemoryState::IoRegister, perm));
2416
2417 // Update the blocks.
2418 m_memory_block_manager.Update(std::addressof(allocator), addr, size / PageSize,
2419 KMemoryState::IoRegister, perm, KMemoryAttribute::Locked,
2420 KMemoryBlockDisableMergeAttribute::Normal,
2421 KMemoryBlockDisableMergeAttribute::None);
2422
2423 // We successfully mapped the pages.
2424 R_SUCCEED();
2425}
2426
2427Result KPageTableBase::MapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr,
2428 size_t size, Svc::MemoryMapping mapping,
2429 Svc::MemoryPermission svc_perm) {
2430 const size_t num_pages = size / PageSize;
2431
2432 // Lock the table.
2433 KScopedLightLock lk(m_general_lock);
2434
2435 // Validate the memory state.
2436 size_t num_allocator_blocks;
2437 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), dst_address, size,
2438 KMemoryState::All, KMemoryState::None, KMemoryPermission::None,
2439 KMemoryPermission::None, KMemoryAttribute::None,
2440 KMemoryAttribute::None));
2441
2442 // Create an update allocator.
2443 Result allocator_result;
2444 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2445 m_memory_block_slab_manager, num_allocator_blocks);
2446 R_TRY(allocator_result);
2447
2448 // We're going to perform an update, so create a helper.
2449 KScopedPageTableUpdater updater(this);
2450
2451 // Perform mapping operation.
2452 const KMemoryPermission perm = ConvertToKMemoryPermission(svc_perm);
2453 const KPageProperties properties = {perm, mapping == Svc::MemoryMapping::IoRegister,
2454 mapping == Svc::MemoryMapping::Uncached,
2455 DisableMergeAttribute::DisableHead};
2456 R_TRY(this->Operate(updater.GetPageList(), dst_address, num_pages, phys_addr, true, properties,
2457 OperationType::Map, false));
2458
2459 // Update the blocks.
2460 const auto state =
2461 mapping == Svc::MemoryMapping::Memory ? KMemoryState::IoMemory : KMemoryState::IoRegister;
2462 m_memory_block_manager.Update(
2463 std::addressof(allocator), dst_address, num_pages, state, perm, KMemoryAttribute::Locked,
2464 KMemoryBlockDisableMergeAttribute::Normal, KMemoryBlockDisableMergeAttribute::None);
2465
2466 // We successfully mapped the pages.
2467 R_SUCCEED();
2468}
2469
2470Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr,
2471 size_t size, Svc::MemoryMapping mapping) {
2472 const size_t num_pages = size / PageSize;
2473
2474 // Lock the table.
2475 KScopedLightLock lk(m_general_lock);
2476
2477 // Validate the memory state.
2478 KMemoryState old_state;
2479 KMemoryPermission old_perm;
2480 KMemoryAttribute old_attr;
2481 size_t num_allocator_blocks;
2482 R_TRY(this->CheckMemoryState(
2483 std::addressof(old_state), std::addressof(old_perm), std::addressof(old_attr),
2484 std::addressof(num_allocator_blocks), dst_address, size, KMemoryState::All,
2485 mapping == Svc::MemoryMapping::Memory ? KMemoryState::IoMemory : KMemoryState::IoRegister,
2486 KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All,
2487 KMemoryAttribute::Locked));
2488
2489 // Validate that the region being unmapped corresponds to the physical range described.
2490 {
2491 // Get the impl.
2492 auto& impl = this->GetImpl();
2493
2494 // Begin traversal.
2495 TraversalContext context;
2496 TraversalEntry next_entry;
2497 ASSERT(
2498 impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_address));
2499
2500 // Check that the physical region matches.
2501 R_UNLESS(next_entry.phys_addr == phys_addr, ResultInvalidMemoryRegion);
2502
2503 // Iterate.
2504 for (size_t checked_size =
2505 next_entry.block_size - (GetInteger(phys_addr) & (next_entry.block_size - 1));
2506 checked_size < size; checked_size += next_entry.block_size) {
2507 // Continue the traversal.
2508 ASSERT(impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)));
2509
2510 // Check that the physical region matches.
2511 R_UNLESS(next_entry.phys_addr == phys_addr + checked_size, ResultInvalidMemoryRegion);
2512 }
2513 }
2514
2515 // Create an update allocator.
2516 Result allocator_result;
2517 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2518 m_memory_block_slab_manager, num_allocator_blocks);
2519 R_TRY(allocator_result);
2520
2521 // We're going to perform an update, so create a helper.
2522 KScopedPageTableUpdater updater(this);
2523
2524 // If the region being unmapped is Memory, synchronize.
2525 if (mapping == Svc::MemoryMapping::Memory) {
2526 // Change the region to be uncached.
2527 const KPageProperties properties = {old_perm, false, true, DisableMergeAttribute::None};
2528 R_ASSERT(this->Operate(updater.GetPageList(), dst_address, num_pages, 0, false, properties,
2529 OperationType::ChangePermissionsAndRefresh, false));
2530
2531 // Temporarily unlock ourselves, so that other operations can occur while we flush the
2532 // region.
2533 m_general_lock.Unlock();
2534 SCOPE_EXIT({ m_general_lock.Lock(); });
2535
2536 // Flush the region.
2537 R_ASSERT(FlushDataCache(dst_address, size));
2538 }
2539
2540 // Perform the unmap.
2541 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
2542 DisableMergeAttribute::None};
2543 R_ASSERT(this->Operate(updater.GetPageList(), dst_address, num_pages, 0, false,
2544 unmap_properties, OperationType::Unmap, false));
2545
2546 // Update the blocks.
2547 m_memory_block_manager.Update(std::addressof(allocator), dst_address, num_pages,
2548 KMemoryState::Free, KMemoryPermission::None,
2549 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
2550 KMemoryBlockDisableMergeAttribute::Normal);
2551
2552 R_SUCCEED();
2553}
2554
2555Result KPageTableBase::MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) {
2556 ASSERT(Common::IsAligned(GetInteger(phys_addr), PageSize));
2557 ASSERT(Common::IsAligned(size, PageSize));
2558 ASSERT(size > 0);
2559 R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress);
2560 const size_t num_pages = size / PageSize;
2561 const KPhysicalAddress last = phys_addr + size - 1;
2562
2563 // Get region extents.
2564 const KProcessAddress region_start = this->GetRegionAddress(KMemoryState::Static);
2565 const size_t region_size = this->GetRegionSize(KMemoryState::Static);
2566 const size_t region_num_pages = region_size / PageSize;
2567
2568 // Locate the memory region.
2569 const KMemoryRegion* region = KMemoryLayout::Find(m_kernel.MemoryLayout(), phys_addr);
2570 R_UNLESS(region != nullptr, ResultInvalidAddress);
2571
2572 ASSERT(region->Contains(GetInteger(phys_addr)));
2573 R_UNLESS(GetInteger(last) <= region->GetLastAddress(), ResultInvalidAddress);
2574
2575 // Check the region attributes.
2576 const bool is_rw = perm == KMemoryPermission::UserReadWrite;
2577 R_UNLESS(region->IsDerivedFrom(KMemoryRegionType_Dram), ResultInvalidAddress);
2578 R_UNLESS(!region->HasTypeAttribute(KMemoryRegionAttr_NoUserMap), ResultInvalidAddress);
2579 R_UNLESS(!region->HasTypeAttribute(KMemoryRegionAttr_UserReadOnly) || !is_rw,
2580 ResultInvalidAddress);
2581
2582 // Lock the table.
2583 KScopedLightLock lk(m_general_lock);
2584
2585 // Select an address to map at.
2586 KProcessAddress addr = 0;
2587 {
2588 const size_t alignment = 4_KiB;
2589 const KPhysicalAddress aligned_phys =
2590 Common::AlignUp(GetInteger(phys_addr), alignment) + alignment - 1;
2591 R_UNLESS(aligned_phys > phys_addr, ResultInvalidAddress);
2592
2593 const KPhysicalAddress last_aligned_paddr =
2594 Common::AlignDown(GetInteger(last) + 1, alignment) - 1;
2595 R_UNLESS((last_aligned_paddr <= last && aligned_phys <= last_aligned_paddr),
2596 ResultInvalidAddress);
2597
2598 addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0,
2599 this->GetNumGuardPages());
2600 R_UNLESS(addr != 0, ResultOutOfMemory);
2601 }
2602
2603 // Check that we can map static here.
2604 ASSERT(this->CanContain(addr, size, KMemoryState::Static));
2605 R_ASSERT(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free,
2606 KMemoryPermission::None, KMemoryPermission::None,
2607 KMemoryAttribute::None, KMemoryAttribute::None));
2608
2609 // Create an update allocator.
2610 Result allocator_result;
2611 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2612 m_memory_block_slab_manager);
2613 R_TRY(allocator_result);
2614
2615 // We're going to perform an update, so create a helper.
2616 KScopedPageTableUpdater updater(this);
2617
2618 // Perform mapping operation.
2619 const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
2620 R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, phys_addr, true, properties,
2621 OperationType::Map, false));
2622
2623 // Update the blocks.
2624 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, KMemoryState::Static,
2625 perm, KMemoryAttribute::None,
2626 KMemoryBlockDisableMergeAttribute::Normal,
2627 KMemoryBlockDisableMergeAttribute::None);
2628
2629 // We successfully mapped the pages.
2630 R_SUCCEED();
2631}
2632
2633Result KPageTableBase::MapRegion(KMemoryRegionType region_type, KMemoryPermission perm) {
2634 // Get the memory region.
2635 const KMemoryRegion* region =
2636 m_kernel.MemoryLayout().GetPhysicalMemoryRegionTree().FindFirstDerived(region_type);
2637 R_UNLESS(region != nullptr, ResultOutOfRange);
2638
2639 // Check that the region is valid.
2640 ASSERT(region->GetEndAddress() != 0);
2641
2642 // Map the region.
2643 R_TRY_CATCH(this->MapStatic(region->GetAddress(), region->GetSize(), perm)){
2644 R_CONVERT(ResultInvalidAddress, ResultOutOfRange)} R_END_TRY_CATCH;
2645
2646 R_SUCCEED();
2647}
2648
2649Result KPageTableBase::MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
2650 KPhysicalAddress phys_addr, bool is_pa_valid,
2651 KProcessAddress region_start, size_t region_num_pages,
2652 KMemoryState state, KMemoryPermission perm) {
2653 ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize);
2654
2655 // Ensure this is a valid map request.
2656 R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state),
2657 ResultInvalidCurrentMemory);
2658 R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory);
2659
2660 // Lock the table.
2661 KScopedLightLock lk(m_general_lock);
2662
2663 // Find a random address to map at.
2664 KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment,
2665 0, this->GetNumGuardPages());
2666 R_UNLESS(addr != 0, ResultOutOfMemory);
2667 ASSERT(Common::IsAligned(GetInteger(addr), alignment));
2668 ASSERT(this->CanContain(addr, num_pages * PageSize, state));
2669 R_ASSERT(this->CheckMemoryState(
2670 addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
2671 KMemoryPermission::None, KMemoryAttribute::None, KMemoryAttribute::None));
2672
2673 // Create an update allocator.
2674 Result allocator_result;
2675 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2676 m_memory_block_slab_manager);
2677 R_TRY(allocator_result);
2678
2679 // We're going to perform an update, so create a helper.
2680 KScopedPageTableUpdater updater(this);
2681
2682 // Perform mapping operation.
2683 if (is_pa_valid) {
2684 const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
2685 R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, phys_addr, true, properties,
2686 OperationType::Map, false));
2687 } else {
2688 R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), addr, num_pages, perm));
2689 }
2690
2691 // Update the blocks.
2692 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
2693 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2694 KMemoryBlockDisableMergeAttribute::None);
2695
2696 // We successfully mapped the pages.
2697 *out_addr = addr;
2698 R_SUCCEED();
2699}
2700
2701Result KPageTableBase::MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
2702 KMemoryPermission perm) {
2703 // Check that the map is in range.
2704 const size_t size = num_pages * PageSize;
2705 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
2706
2707 // Lock the table.
2708 KScopedLightLock lk(m_general_lock);
2709
2710 // Check the memory state.
2711 size_t num_allocator_blocks;
2712 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
2713 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
2714 KMemoryPermission::None, KMemoryAttribute::None,
2715 KMemoryAttribute::None));
2716
2717 // Create an update allocator.
2718 Result allocator_result;
2719 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2720 m_memory_block_slab_manager, num_allocator_blocks);
2721 R_TRY(allocator_result);
2722
2723 // We're going to perform an update, so create a helper.
2724 KScopedPageTableUpdater updater(this);
2725
2726 // Map the pages.
2727 R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), address, num_pages, perm));
2728
2729 // Update the blocks.
2730 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm,
2731 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2732 KMemoryBlockDisableMergeAttribute::None);
2733
2734 R_SUCCEED();
2735}
2736
2737Result KPageTableBase::UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state) {
2738 // Check that the unmap is in range.
2739 const size_t size = num_pages * PageSize;
2740 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
2741
2742 // Lock the table.
2743 KScopedLightLock lk(m_general_lock);
2744
2745 // Check the memory state.
2746 size_t num_allocator_blocks;
2747 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
2748 KMemoryState::All, state, KMemoryPermission::None,
2749 KMemoryPermission::None, KMemoryAttribute::All,
2750 KMemoryAttribute::None));
2751
2752 // Create an update allocator.
2753 Result allocator_result;
2754 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2755 m_memory_block_slab_manager, num_allocator_blocks);
2756 R_TRY(allocator_result);
2757
2758 // We're going to perform an update, so create a helper.
2759 KScopedPageTableUpdater updater(this);
2760
2761 // Perform the unmap.
2762 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
2763 DisableMergeAttribute::None};
2764 R_TRY(this->Operate(updater.GetPageList(), address, num_pages, 0, false, unmap_properties,
2765 OperationType::Unmap, false));
2766
2767 // Update the blocks.
2768 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free,
2769 KMemoryPermission::None, KMemoryAttribute::None,
2770 KMemoryBlockDisableMergeAttribute::None,
2771 KMemoryBlockDisableMergeAttribute::Normal);
2772
2773 R_SUCCEED();
2774}
2775
2776Result KPageTableBase::MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
2777 KProcessAddress region_start, size_t region_num_pages,
2778 KMemoryState state, KMemoryPermission perm) {
2779 ASSERT(!this->IsLockedByCurrentThread());
2780
2781 // Ensure this is a valid map request.
2782 const size_t num_pages = pg.GetNumPages();
2783 R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state),
2784 ResultInvalidCurrentMemory);
2785 R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory);
2786
2787 // Lock the table.
2788 KScopedLightLock lk(m_general_lock);
2789
2790 // Find a random address to map at.
2791 KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, PageSize,
2792 0, this->GetNumGuardPages());
2793 R_UNLESS(addr != 0, ResultOutOfMemory);
2794 ASSERT(this->CanContain(addr, num_pages * PageSize, state));
2795 R_ASSERT(this->CheckMemoryState(
2796 addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
2797 KMemoryPermission::None, KMemoryAttribute::None, KMemoryAttribute::None));
2798
2799 // Create an update allocator.
2800 Result allocator_result;
2801 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2802 m_memory_block_slab_manager);
2803 R_TRY(allocator_result);
2804
2805 // We're going to perform an update, so create a helper.
2806 KScopedPageTableUpdater updater(this);
2807
2808 // Perform mapping operation.
2809 const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
2810 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
2811
2812 // Update the blocks.
2813 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
2814 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2815 KMemoryBlockDisableMergeAttribute::None);
2816
2817 // We successfully mapped the pages.
2818 *out_addr = addr;
2819 R_SUCCEED();
2820}
2821
2822Result KPageTableBase::MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMemoryState state,
2823 KMemoryPermission perm) {
2824 ASSERT(!this->IsLockedByCurrentThread());
2825
2826 // Ensure this is a valid map request.
2827 const size_t num_pages = pg.GetNumPages();
2828 const size_t size = num_pages * PageSize;
2829 R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory);
2830
2831 // Lock the table.
2832 KScopedLightLock lk(m_general_lock);
2833
2834 // Check if state allows us to map.
2835 size_t num_allocator_blocks;
2836 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), addr, size,
2837 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
2838 KMemoryPermission::None, KMemoryAttribute::None,
2839 KMemoryAttribute::None));
2840
2841 // Create an update allocator.
2842 Result allocator_result;
2843 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2844 m_memory_block_slab_manager, num_allocator_blocks);
2845 R_TRY(allocator_result);
2846
2847 // We're going to perform an update, so create a helper.
2848 KScopedPageTableUpdater updater(this);
2849
2850 // Perform mapping operation.
2851 const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
2852 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
2853
2854 // Update the blocks.
2855 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
2856 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2857 KMemoryBlockDisableMergeAttribute::None);
2858
2859 // We successfully mapped the pages.
2860 R_SUCCEED();
2861}
2862
2863Result KPageTableBase::UnmapPageGroup(KProcessAddress address, const KPageGroup& pg,
2864 KMemoryState state) {
2865 ASSERT(!this->IsLockedByCurrentThread());
2866
2867 // Ensure this is a valid unmap request.
2868 const size_t num_pages = pg.GetNumPages();
2869 const size_t size = num_pages * PageSize;
2870 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
2871
2872 // Lock the table.
2873 KScopedLightLock lk(m_general_lock);
2874
2875 // Check if state allows us to unmap.
2876 size_t num_allocator_blocks;
2877 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
2878 KMemoryState::All, state, KMemoryPermission::None,
2879 KMemoryPermission::None, KMemoryAttribute::All,
2880 KMemoryAttribute::None));
2881
2882 // Check that the page group is valid.
2883 R_UNLESS(this->IsValidPageGroup(pg, address, num_pages), ResultInvalidCurrentMemory);
2884
2885 // Create an update allocator.
2886 Result allocator_result;
2887 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2888 m_memory_block_slab_manager, num_allocator_blocks);
2889 R_TRY(allocator_result);
2890
2891 // We're going to perform an update, so create a helper.
2892 KScopedPageTableUpdater updater(this);
2893
2894 // Perform unmapping operation.
2895 const KPageProperties properties = {KMemoryPermission::None, false, false,
2896 DisableMergeAttribute::None};
2897 R_TRY(this->Operate(updater.GetPageList(), address, num_pages, 0, false, properties,
2898 OperationType::Unmap, false));
2899
2900 // Update the blocks.
2901 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free,
2902 KMemoryPermission::None, KMemoryAttribute::None,
2903 KMemoryBlockDisableMergeAttribute::None,
2904 KMemoryBlockDisableMergeAttribute::Normal);
2905
2906 R_SUCCEED();
2907}
2908
2909Result KPageTableBase::MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address,
2910 size_t num_pages, KMemoryState state_mask,
2911 KMemoryState state, KMemoryPermission perm_mask,
2912 KMemoryPermission perm, KMemoryAttribute attr_mask,
2913 KMemoryAttribute attr) {
2914 // Ensure that the page group isn't null.
2915 ASSERT(out != nullptr);
2916
2917 // Make sure that the region we're mapping is valid for the table.
2918 const size_t size = num_pages * PageSize;
2919 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
2920
2921 // Lock the table.
2922 KScopedLightLock lk(m_general_lock);
2923
2924 // Check if state allows us to create the group.
2925 R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState::FlagReferenceCounted,
2926 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
2927 attr_mask, attr));
2928
2929 // Create a new page group for the region.
2930 R_TRY(this->MakePageGroup(*out, address, num_pages));
2931
2932 // Open a new reference to the pages in the group.
2933 out->Open();
2934
2935 R_SUCCEED();
2936}
2937
2938Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_t size) {
2939 // Check that the region is in range.
2940 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
2941
2942 // Lock the table.
2943 KScopedLightLock lk(m_general_lock);
2944
2945 // Check the memory state.
2946 R_TRY(this->CheckMemoryStateContiguous(
2947 address, size, KMemoryState::FlagReferenceCounted, KMemoryState::FlagReferenceCounted,
2948 KMemoryPermission::UserReadWrite, KMemoryPermission::UserReadWrite,
2949 KMemoryAttribute::Uncached, KMemoryAttribute::None));
2950
2951 // Get the impl.
2952 auto& impl = this->GetImpl();
2953
2954 // Begin traversal.
2955 TraversalContext context;
2956 TraversalEntry next_entry;
2957 bool traverse_valid =
2958 impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), address);
2959 R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
2960
2961 // Prepare tracking variables.
2962 KPhysicalAddress cur_addr = next_entry.phys_addr;
2963 size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
2964 size_t tot_size = cur_size;
2965
2966 // Iterate.
2967 while (tot_size < size) {
2968 // Continue the traversal.
2969 traverse_valid =
2970 impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
2971 R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
2972
2973 if (next_entry.phys_addr != (cur_addr + cur_size)) {
2974 // Check that the pages are linearly mapped.
2975 R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory);
2976
2977 // Invalidate the block.
2978 if (cur_size > 0) {
2979 // NOTE: Nintendo does not check the result of invalidation.
2980 InvalidateDataCache(GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size);
2981 }
2982
2983 // Advance.
2984 cur_addr = next_entry.phys_addr;
2985 cur_size = next_entry.block_size;
2986 } else {
2987 cur_size += next_entry.block_size;
2988 }
2989
2990 tot_size += next_entry.block_size;
2991 }
2992
2993 // Ensure we use the right size for the last block.
2994 if (tot_size > size) {
2995 cur_size -= (tot_size - size);
2996 }
2997
2998 // Check that the last block is linearly mapped.
2999 R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory);
3000
3001 // Invalidate the last block.
3002 if (cur_size > 0) {
3003 // NOTE: Nintendo does not check the result of invalidation.
3004 InvalidateDataCache(GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size);
3005 }
3006
3007 R_SUCCEED();
3008}
3009
3010Result KPageTableBase::InvalidateCurrentProcessDataCache(KProcessAddress address, size_t size) {
3011 // Check pre-condition: this is being called on the current process.
3012 ASSERT(this == std::addressof(GetCurrentProcess(m_kernel).GetPageTable().GetBasePageTable()));
3013
3014 // Check that the region is in range.
3015 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
3016
3017 // Lock the table.
3018 KScopedLightLock lk(m_general_lock);
3019
3020 // Check the memory state.
3021 R_TRY(this->CheckMemoryStateContiguous(
3022 address, size, KMemoryState::FlagReferenceCounted, KMemoryState::FlagReferenceCounted,
3023 KMemoryPermission::UserReadWrite, KMemoryPermission::UserReadWrite,
3024 KMemoryAttribute::Uncached, KMemoryAttribute::None));
3025
3026 // Invalidate the data cache.
3027 R_RETURN(InvalidateDataCache(address, size));
3028}
3029
3030Result KPageTableBase::ReadDebugMemory(KProcessAddress dst_address, KProcessAddress src_address,
3031 size_t size) {
3032 // Lightly validate the region is in range.
3033 R_UNLESS(this->Contains(src_address, size), ResultInvalidCurrentMemory);
3034
3035 // Lock the table.
3036 KScopedLightLock lk(m_general_lock);
3037
3038 // Require that the memory either be user readable or debuggable.
3039 const bool can_read = R_SUCCEEDED(this->CheckMemoryStateContiguous(
3040 src_address, size, KMemoryState::None, KMemoryState::None, KMemoryPermission::UserRead,
3041 KMemoryPermission::UserRead, KMemoryAttribute::None, KMemoryAttribute::None));
3042 if (!can_read) {
3043 const bool can_debug = R_SUCCEEDED(this->CheckMemoryStateContiguous(
3044 src_address, size, KMemoryState::FlagCanDebug, KMemoryState::FlagCanDebug,
3045 KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::None,
3046 KMemoryAttribute::None));
3047 R_UNLESS(can_debug, ResultInvalidCurrentMemory);
3048 }
3049
3050 // Get the impl.
3051 auto& impl = this->GetImpl();
3052 auto& dst_memory = GetCurrentMemory(m_system.Kernel());
3053
3054 // Begin traversal.
3055 TraversalContext context;
3056 TraversalEntry next_entry;
3057 bool traverse_valid =
3058 impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_address);
3059 R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
3060
3061 // Prepare tracking variables.
3062 KPhysicalAddress cur_addr = next_entry.phys_addr;
3063 size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
3064 size_t tot_size = cur_size;
3065
3066 auto PerformCopy = [&]() -> Result {
3067 // Ensure the address is linear mapped.
3068 R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory);
3069
3070 // Copy as much aligned data as we can.
3071 if (cur_size >= sizeof(u32)) {
3072 const size_t copy_size = Common::AlignDown(cur_size, sizeof(u32));
3073 const void* copy_src = GetLinearMappedVirtualPointer(m_kernel, cur_addr);
3074 FlushDataCache(copy_src, copy_size);
3075 R_UNLESS(dst_memory.WriteBlock(dst_address, copy_src, copy_size), ResultInvalidPointer);
3076
3077 dst_address += copy_size;
3078 cur_addr += copy_size;
3079 cur_size -= copy_size;
3080 }
3081
3082 // Copy remaining data.
3083 if (cur_size > 0) {
3084 const void* copy_src = GetLinearMappedVirtualPointer(m_kernel, cur_addr);
3085 FlushDataCache(copy_src, cur_size);
3086 R_UNLESS(dst_memory.WriteBlock(dst_address, copy_src, cur_size), ResultInvalidPointer);
3087 }
3088
3089 R_SUCCEED();
3090 };
3091
3092 // Iterate.
3093 while (tot_size < size) {
3094 // Continue the traversal.
3095 traverse_valid =
3096 impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
3097 ASSERT(traverse_valid);
3098
3099 if (next_entry.phys_addr != (cur_addr + cur_size)) {
3100 // Perform copy.
3101 R_TRY(PerformCopy());
3102
3103 // Advance.
3104 dst_address += cur_size;
3105
3106 cur_addr = next_entry.phys_addr;
3107 cur_size = next_entry.block_size;
3108 } else {
3109 cur_size += next_entry.block_size;
3110 }
3111
3112 tot_size += next_entry.block_size;
3113 }
3114
3115 // Ensure we use the right size for the last block.
3116 if (tot_size > size) {
3117 cur_size -= (tot_size - size);
3118 }
3119
3120 // Perform copy for the last block.
3121 R_TRY(PerformCopy());
3122
3123 R_SUCCEED();
3124}
3125
3126Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAddress src_address,
3127 size_t size) {
3128 // Lightly validate the region is in range.
3129 R_UNLESS(this->Contains(dst_address, size), ResultInvalidCurrentMemory);
3130
3131 // Lock the table.
3132 KScopedLightLock lk(m_general_lock);
3133
3134 // Require that the memory either be user writable or debuggable.
3135 const bool can_read = R_SUCCEEDED(this->CheckMemoryStateContiguous(
3136 dst_address, size, KMemoryState::None, KMemoryState::None, KMemoryPermission::UserReadWrite,
3137 KMemoryPermission::UserReadWrite, KMemoryAttribute::None, KMemoryAttribute::None));
3138 if (!can_read) {
3139 const bool can_debug = R_SUCCEEDED(this->CheckMemoryStateContiguous(
3140 dst_address, size, KMemoryState::FlagCanDebug, KMemoryState::FlagCanDebug,
3141 KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::None,
3142 KMemoryAttribute::None));
3143 R_UNLESS(can_debug, ResultInvalidCurrentMemory);
3144 }
3145
3146 // Get the impl.
3147 auto& impl = this->GetImpl();
3148 auto& src_memory = GetCurrentMemory(m_system.Kernel());
3149
3150 // Begin traversal.
3151 TraversalContext context;
3152 TraversalEntry next_entry;
3153 bool traverse_valid =
3154 impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_address);
3155 R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
3156
3157 // Prepare tracking variables.
3158 KPhysicalAddress cur_addr = next_entry.phys_addr;
3159 size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
3160 size_t tot_size = cur_size;
3161
3162 auto PerformCopy = [&]() -> Result {
3163 // Ensure the address is linear mapped.
3164 R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory);
3165
3166 // Copy as much aligned data as we can.
3167 if (cur_size >= sizeof(u32)) {
3168 const size_t copy_size = Common::AlignDown(cur_size, sizeof(u32));
3169 void* copy_dst = GetLinearMappedVirtualPointer(m_kernel, cur_addr);
3170 R_UNLESS(src_memory.ReadBlock(src_address, copy_dst, copy_size),
3171 ResultInvalidCurrentMemory);
3172
3173 StoreDataCache(GetLinearMappedVirtualPointer(m_kernel, cur_addr), copy_size);
3174
3175 src_address += copy_size;
3176 cur_addr += copy_size;
3177 cur_size -= copy_size;
3178 }
3179
3180 // Copy remaining data.
3181 if (cur_size > 0) {
3182 void* copy_dst = GetLinearMappedVirtualPointer(m_kernel, cur_addr);
3183 R_UNLESS(src_memory.ReadBlock(src_address, copy_dst, cur_size),
3184 ResultInvalidCurrentMemory);
3185
3186 StoreDataCache(GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size);
3187 }
3188
3189 R_SUCCEED();
3190 };
3191
3192 // Iterate.
3193 while (tot_size < size) {
3194 // Continue the traversal.
3195 traverse_valid =
3196 impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
3197 ASSERT(traverse_valid);
3198
3199 if (next_entry.phys_addr != (cur_addr + cur_size)) {
3200 // Perform copy.
3201 R_TRY(PerformCopy());
3202
3203 // Advance.
3204 src_address += cur_size;
3205
3206 cur_addr = next_entry.phys_addr;
3207 cur_size = next_entry.block_size;
3208 } else {
3209 cur_size += next_entry.block_size;
3210 }
3211
3212 tot_size += next_entry.block_size;
3213 }
3214
3215 // Ensure we use the right size for the last block.
3216 if (tot_size > size) {
3217 cur_size -= (tot_size - size);
3218 }
3219
3220 // Perform copy for the last block.
3221 R_TRY(PerformCopy());
3222
3223 // Invalidate the instruction cache, as this svc allows modifying executable pages.
3224 InvalidateInstructionCache(m_system, dst_address, size);
3225
3226 R_SUCCEED();
3227}
3228
3229Result KPageTableBase::ReadIoMemoryImpl(KProcessAddress dst_addr, KPhysicalAddress phys_addr,
3230 size_t size, KMemoryState state) {
3231 // Check pre-conditions.
3232 ASSERT(this->IsLockedByCurrentThread());
3233
3234 // Determine the mapping extents.
3235 const KPhysicalAddress map_start = Common::AlignDown(GetInteger(phys_addr), PageSize);
3236 const KPhysicalAddress map_end = Common::AlignUp(GetInteger(phys_addr) + size, PageSize);
3237 const size_t map_size = map_end - map_start;
3238
3239 // Get the memory reference to write into.
3240 auto& dst_memory = GetCurrentMemory(m_kernel);
3241
3242 // We're going to perform an update, so create a helper.
3243 KScopedPageTableUpdater updater(this);
3244
3245 // Temporarily map the io memory.
3246 KProcessAddress io_addr;
3247 R_TRY(this->MapIoImpl(std::addressof(io_addr), updater.GetPageList(), map_start, map_size,
3248 state, KMemoryPermission::UserRead));
3249
3250 // Ensure we unmap the io memory when we're done with it.
3251 const KPageProperties unmap_properties =
3252 KPageProperties{KMemoryPermission::None, false, false, DisableMergeAttribute::None};
3253 SCOPE_EXIT({
3254 R_ASSERT(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, 0, false,
3255 unmap_properties, OperationType::Unmap, true));
3256 });
3257
3258 // Read the memory.
3259 const KProcessAddress read_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1));
3260 dst_memory.CopyBlock(dst_addr, read_addr, size);
3261
3262 R_SUCCEED();
3263}
3264
3265Result KPageTableBase::WriteIoMemoryImpl(KPhysicalAddress phys_addr, KProcessAddress src_addr,
3266 size_t size, KMemoryState state) {
3267 // Check pre-conditions.
3268 ASSERT(this->IsLockedByCurrentThread());
3269
3270 // Determine the mapping extents.
3271 const KPhysicalAddress map_start = Common::AlignDown(GetInteger(phys_addr), PageSize);
3272 const KPhysicalAddress map_end = Common::AlignUp(GetInteger(phys_addr) + size, PageSize);
3273 const size_t map_size = map_end - map_start;
3274
3275 // Get the memory reference to read from.
3276 auto& src_memory = GetCurrentMemory(m_kernel);
3277
3278 // We're going to perform an update, so create a helper.
3279 KScopedPageTableUpdater updater(this);
3280
3281 // Temporarily map the io memory.
3282 KProcessAddress io_addr;
3283 R_TRY(this->MapIoImpl(std::addressof(io_addr), updater.GetPageList(), map_start, map_size,
3284 state, KMemoryPermission::UserReadWrite));
3285
3286 // Ensure we unmap the io memory when we're done with it.
3287 const KPageProperties unmap_properties =
3288 KPageProperties{KMemoryPermission::None, false, false, DisableMergeAttribute::None};
3289 SCOPE_EXIT({
3290 R_ASSERT(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, 0, false,
3291 unmap_properties, OperationType::Unmap, true));
3292 });
3293
3294 // Write the memory.
3295 const KProcessAddress write_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1));
3296 R_UNLESS(src_memory.CopyBlock(write_addr, src_addr, size), ResultInvalidPointer);
3297
3298 R_SUCCEED();
3299}
3300
3301Result KPageTableBase::ReadDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address,
3302 size_t size, KMemoryState state) {
3303 // Lightly validate the range before doing anything else.
3304 R_UNLESS(this->Contains(src_address, size), ResultInvalidCurrentMemory);
3305
3306 // We need to lock both this table, and the current process's table, so set up some aliases.
3307 KPageTableBase& src_page_table = *this;
3308 KPageTableBase& dst_page_table = GetCurrentProcess(m_kernel).GetPageTable().GetBasePageTable();
3309
3310 // Acquire the table locks.
3311 KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock);
3312
3313 // Check that the desired range is readable io memory.
3314 R_TRY(this->CheckMemoryStateContiguous(src_address, size, KMemoryState::All, state,
3315 KMemoryPermission::UserRead, KMemoryPermission::UserRead,
3316 KMemoryAttribute::None, KMemoryAttribute::None));
3317
3318 // Read the memory.
3319 KProcessAddress dst = dst_address;
3320 const KProcessAddress last_address = src_address + size - 1;
3321 while (src_address <= last_address) {
3322 // Get the current physical address.
3323 KPhysicalAddress phys_addr;
3324 ASSERT(src_page_table.GetPhysicalAddressLocked(std::addressof(phys_addr), src_address));
3325
3326 // Determine the current read size.
3327 const size_t cur_size =
3328 std::min<size_t>(last_address - src_address + 1,
3329 Common::AlignDown(GetInteger(src_address) + PageSize, PageSize) -
3330 GetInteger(src_address));
3331
3332 // Read.
3333 R_TRY(dst_page_table.ReadIoMemoryImpl(dst, phys_addr, cur_size, state));
3334
3335 // Advance.
3336 src_address += cur_size;
3337 dst += cur_size;
3338 }
3339
3340 R_SUCCEED();
3341}
3342
3343Result KPageTableBase::WriteDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address,
3344 size_t size, KMemoryState state) {
3345 // Lightly validate the range before doing anything else.
3346 R_UNLESS(this->Contains(dst_address, size), ResultInvalidCurrentMemory);
3347
3348 // We need to lock both this table, and the current process's table, so set up some aliases.
3349 KPageTableBase& src_page_table = *this;
3350 KPageTableBase& dst_page_table = GetCurrentProcess(m_kernel).GetPageTable().GetBasePageTable();
3351
3352 // Acquire the table locks.
3353 KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock);
3354
3355 // Check that the desired range is writable io memory.
3356 R_TRY(this->CheckMemoryStateContiguous(
3357 dst_address, size, KMemoryState::All, state, KMemoryPermission::UserReadWrite,
3358 KMemoryPermission::UserReadWrite, KMemoryAttribute::None, KMemoryAttribute::None));
3359
3360 // Read the memory.
3361 KProcessAddress src = src_address;
3362 const KProcessAddress last_address = dst_address + size - 1;
3363 while (dst_address <= last_address) {
3364 // Get the current physical address.
3365 KPhysicalAddress phys_addr;
3366 ASSERT(src_page_table.GetPhysicalAddressLocked(std::addressof(phys_addr), dst_address));
3367
3368 // Determine the current read size.
3369 const size_t cur_size =
3370 std::min<size_t>(last_address - dst_address + 1,
3371 Common::AlignDown(GetInteger(dst_address) + PageSize, PageSize) -
3372 GetInteger(dst_address));
3373
3374 // Read.
3375 R_TRY(dst_page_table.WriteIoMemoryImpl(phys_addr, src, cur_size, state));
3376
3377 // Advance.
3378 dst_address += cur_size;
3379 src += cur_size;
3380 }
3381
3382 R_SUCCEED();
3383}
3384
3385Result KPageTableBase::LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address,
3386 size_t size, KMemoryPermission perm,
3387 bool is_aligned, bool check_heap) {
3388 // Lightly validate the range before doing anything else.
3389 const size_t num_pages = size / PageSize;
3390 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
3391
3392 // Lock the table.
3393 KScopedLightLock lk(m_general_lock);
3394
3395 // Check the memory state.
3396 const KMemoryState test_state =
3397 (is_aligned ? KMemoryState::FlagCanAlignedDeviceMap : KMemoryState::FlagCanDeviceMap) |
3398 (check_heap ? KMemoryState::FlagReferenceCounted : KMemoryState::None);
3399 size_t num_allocator_blocks;
3400 KMemoryState old_state;
3401 R_TRY(this->CheckMemoryState(std::addressof(old_state), nullptr, nullptr,
3402 std::addressof(num_allocator_blocks), address, size, test_state,
3403 test_state, perm, perm,
3404 KMemoryAttribute::IpcLocked | KMemoryAttribute::Locked,
3405 KMemoryAttribute::None, KMemoryAttribute::DeviceShared));
3406
3407 // Create an update allocator.
3408 Result allocator_result;
3409 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
3410 m_memory_block_slab_manager, num_allocator_blocks);
3411 R_TRY(allocator_result);
3412
3413 // Update the memory blocks.
3414 m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages,
3415 &KMemoryBlock::ShareToDevice, KMemoryPermission::None);
3416
3417 // Set whether the locked memory was io.
3418 *out_is_io =
3419 static_cast<Svc::MemoryState>(old_state & KMemoryState::Mask) == Svc::MemoryState::Io;
3420
3421 R_SUCCEED();
3422}
3423
3424Result KPageTableBase::LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size,
3425 bool check_heap) {
3426 // Lightly validate the range before doing anything else.
3427 const size_t num_pages = size / PageSize;
3428 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
3429
3430 // Lock the table.
3431 KScopedLightLock lk(m_general_lock);
3432
3433 // Check the memory state.
3434 const KMemoryState test_state =
3435 KMemoryState::FlagCanDeviceMap |
3436 (check_heap ? KMemoryState::FlagReferenceCounted : KMemoryState::None);
3437 size_t num_allocator_blocks;
3438 R_TRY(this->CheckMemoryStateContiguous(
3439 std::addressof(num_allocator_blocks), address, size, test_state, test_state,
3440 KMemoryPermission::None, KMemoryPermission::None,
3441 KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared));
3442
3443 // Create an update allocator.
3444 Result allocator_result;
3445 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
3446 m_memory_block_slab_manager, num_allocator_blocks);
3447 R_TRY(allocator_result);
3448
3449 // Update the memory blocks.
3450 const KMemoryBlockManager::MemoryBlockLockFunction lock_func =
3451 m_enable_device_address_space_merge
3452 ? &KMemoryBlock::UpdateDeviceDisableMergeStateForShare
3453 : &KMemoryBlock::UpdateDeviceDisableMergeStateForShareRight;
3454 m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, lock_func,
3455 KMemoryPermission::None);
3456
3457 R_SUCCEED();
3458}
3459
3460Result KPageTableBase::UnlockForDeviceAddressSpace(KProcessAddress address, size_t size) {
3461 // Lightly validate the range before doing anything else.
3462 const size_t num_pages = size / PageSize;
3463 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
3464
3465 // Lock the table.
3466 KScopedLightLock lk(m_general_lock);
3467
3468 // Check the memory state.
3469 size_t num_allocator_blocks;
3470 R_TRY(this->CheckMemoryStateContiguous(
3471 std::addressof(num_allocator_blocks), address, size, KMemoryState::FlagCanDeviceMap,
3472 KMemoryState::FlagCanDeviceMap, KMemoryPermission::None, KMemoryPermission::None,
3473 KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared));
3474
3475 // Create an update allocator.
3476 Result allocator_result;
3477 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
3478 m_memory_block_slab_manager, num_allocator_blocks);
3479 R_TRY(allocator_result);
3480
3481 // Update the memory blocks.
3482 m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages,
3483 &KMemoryBlock::UnshareToDevice, KMemoryPermission::None);
3484
3485 R_SUCCEED();
3486}
3487
3488Result KPageTableBase::UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size) {
3489 // Lightly validate the range before doing anything else.
3490 const size_t num_pages = size / PageSize;
3491 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
3492
3493 // Lock the table.
3494 KScopedLightLock lk(m_general_lock);
3495
3496 // Check memory state.
3497 size_t allocator_num_blocks = 0;
3498 R_TRY(this->CheckMemoryStateContiguous(
3499 std::addressof(allocator_num_blocks), address, size, KMemoryState::FlagCanDeviceMap,
3500 KMemoryState::FlagCanDeviceMap, KMemoryPermission::None, KMemoryPermission::None,
3501 KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared));
3502
3503 // Create an update allocator for the region.
3504 Result allocator_result;
3505 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
3506 m_memory_block_slab_manager, allocator_num_blocks);
3507 R_TRY(allocator_result);
3508
3509 // Update the memory blocks.
3510 m_memory_block_manager.UpdateLock(
3511 std::addressof(allocator), address, num_pages,
3512 m_enable_device_address_space_merge
3513 ? &KMemoryBlock::UpdateDeviceDisableMergeStateForUnshare
3514 : &KMemoryBlock::UpdateDeviceDisableMergeStateForUnshareRight,
3515 KMemoryPermission::None);
3516
3517 R_SUCCEED();
3518}
3519
3520Result KPageTableBase::OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange* out,
3521 KProcessAddress address, size_t size,
3522 KMemoryPermission perm,
3523 bool is_aligned) {
3524 // Lock the table.
3525 KScopedLightLock lk(m_general_lock);
3526
3527 // Get the range.
3528 const KMemoryState test_state =
3529 (is_aligned ? KMemoryState::FlagCanAlignedDeviceMap : KMemoryState::FlagCanDeviceMap);
3530 R_TRY(this->GetContiguousMemoryRangeWithState(
3531 out, address, size, test_state, test_state, perm, perm,
3532 KMemoryAttribute::IpcLocked | KMemoryAttribute::Locked, KMemoryAttribute::None));
3533
3534 // We got the range, so open it.
3535 out->Open();
3536
3537 R_SUCCEED();
3538}
3539
3540Result KPageTableBase::OpenMemoryRangeForUnmapDeviceAddressSpace(MemoryRange* out,
3541 KProcessAddress address,
3542 size_t size) {
3543 // Lock the table.
3544 KScopedLightLock lk(m_general_lock);
3545
3546 // Get the range.
3547 R_TRY(this->GetContiguousMemoryRangeWithState(
3548 out, address, size, KMemoryState::FlagCanDeviceMap, KMemoryState::FlagCanDeviceMap,
3549 KMemoryPermission::None, KMemoryPermission::None,
3550 KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared));
3551
3552 // We got the range, so open it.
3553 out->Open();
3554
3555 R_SUCCEED();
3556}
3557
3558Result KPageTableBase::LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address,
3559 size_t size) {
3560 R_RETURN(this->LockMemoryAndOpen(
3561 nullptr, out, address, size, KMemoryState::FlagCanIpcUserBuffer,
3562 KMemoryState::FlagCanIpcUserBuffer, KMemoryPermission::All,
3563 KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None,
3564 static_cast<KMemoryPermission>(KMemoryPermission::NotMapped |
3565 KMemoryPermission::KernelReadWrite),
3566 KMemoryAttribute::Locked));
3567}
3568
3569Result KPageTableBase::UnlockForIpcUserBuffer(KProcessAddress address, size_t size) {
3570 R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanIpcUserBuffer,
3571 KMemoryState::FlagCanIpcUserBuffer, KMemoryPermission::None,
3572 KMemoryPermission::None, KMemoryAttribute::All,
3573 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
3574 KMemoryAttribute::Locked, nullptr));
3575}
3576
3577Result KPageTableBase::LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size,
3578 KMemoryPermission perm) {
3579 R_RETURN(this->LockMemoryAndOpen(out, nullptr, address, size, KMemoryState::FlagCanTransfer,
3580 KMemoryState::FlagCanTransfer, KMemoryPermission::All,
3581 KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
3582 KMemoryAttribute::None, perm, KMemoryAttribute::Locked));
3583}
3584
3585Result KPageTableBase::UnlockForTransferMemory(KProcessAddress address, size_t size,
3586 const KPageGroup& pg) {
3587 R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanTransfer,
3588 KMemoryState::FlagCanTransfer, KMemoryPermission::None,
3589 KMemoryPermission::None, KMemoryAttribute::All,
3590 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
3591 KMemoryAttribute::Locked, std::addressof(pg)));
3592}
3593
3594Result KPageTableBase::LockForCodeMemory(KPageGroup* out, KProcessAddress address, size_t size) {
3595 R_RETURN(this->LockMemoryAndOpen(
3596 out, nullptr, address, size, KMemoryState::FlagCanCodeMemory,
3597 KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite,
3598 KMemoryAttribute::All, KMemoryAttribute::None,
3599 static_cast<KMemoryPermission>(KMemoryPermission::NotMapped |
3600 KMemoryPermission::KernelReadWrite),
3601 KMemoryAttribute::Locked));
3602}
3603
3604Result KPageTableBase::UnlockForCodeMemory(KProcessAddress address, size_t size,
3605 const KPageGroup& pg) {
3606 R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanCodeMemory,
3607 KMemoryState::FlagCanCodeMemory, KMemoryPermission::None,
3608 KMemoryPermission::None, KMemoryAttribute::All,
3609 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
3610 KMemoryAttribute::Locked, std::addressof(pg)));
3611}
3612
3613Result KPageTableBase::OpenMemoryRangeForProcessCacheOperation(MemoryRange* out,
3614 KProcessAddress address,
3615 size_t size) {
3616 // Lock the table.
3617 KScopedLightLock lk(m_general_lock);
3618
3619 // Get the range.
3620 R_TRY(this->GetContiguousMemoryRangeWithState(
3621 out, address, size, KMemoryState::FlagReferenceCounted, KMemoryState::FlagReferenceCounted,
3622 KMemoryPermission::UserRead, KMemoryPermission::UserRead, KMemoryAttribute::Uncached,
3623 KMemoryAttribute::None));
3624
3625 // We got the range, so open it.
3626 out->Open();
3627
3628 R_SUCCEED();
3629}
3630
3631Result KPageTableBase::CopyMemoryFromLinearToUser(
3632 KProcessAddress dst_addr, size_t size, KProcessAddress src_addr, KMemoryState src_state_mask,
3633 KMemoryState src_state, KMemoryPermission src_test_perm, KMemoryAttribute src_attr_mask,
3634 KMemoryAttribute src_attr) {
3635 // Lightly validate the range before doing anything else.
3636 R_UNLESS(this->Contains(src_addr, size), ResultInvalidCurrentMemory);
3637
3638 // Get the destination memory reference.
3639 auto& dst_memory = GetCurrentMemory(m_kernel);
3640
3641 // Copy the memory.
3642 {
3643 // Lock the table.
3644 KScopedLightLock lk(m_general_lock);
3645
3646 // Check memory state.
3647 R_TRY(this->CheckMemoryStateContiguous(
3648 src_addr, size, src_state_mask, src_state, src_test_perm, src_test_perm,
3649 src_attr_mask | KMemoryAttribute::Uncached, src_attr));
3650
3651 auto& impl = this->GetImpl();
3652
3653 // Begin traversal.
3654 TraversalContext context;
3655 TraversalEntry next_entry;
3656 bool traverse_valid =
3657 impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_addr);
3658 ASSERT(traverse_valid);
3659
3660 // Prepare tracking variables.
3661 KPhysicalAddress cur_addr = next_entry.phys_addr;
3662 size_t cur_size =
3663 next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
3664 size_t tot_size = cur_size;
3665
3666 auto PerformCopy = [&]() -> Result {
3667 // Ensure the address is linear mapped.
3668 R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory);
3669
3670 // Copy as much aligned data as we can.
3671 if (cur_size >= sizeof(u32)) {
3672 const size_t copy_size = Common::AlignDown(cur_size, sizeof(u32));
3673 R_UNLESS(dst_memory.WriteBlock(dst_addr,
3674 GetLinearMappedVirtualPointer(m_kernel, cur_addr),
3675 copy_size),
3676 ResultInvalidCurrentMemory);
3677
3678 dst_addr += copy_size;
3679 cur_addr += copy_size;
3680 cur_size -= copy_size;
3681 }
3682
3683 // Copy remaining data.
3684 if (cur_size > 0) {
3685 R_UNLESS(dst_memory.WriteBlock(
3686 dst_addr, GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size),
3687 ResultInvalidCurrentMemory);
3688 }
3689
3690 R_SUCCEED();
3691 };
3692
3693 // Iterate.
3694 while (tot_size < size) {
3695 // Continue the traversal.
3696 traverse_valid =
3697 impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
3698 ASSERT(traverse_valid);
3699
3700 if (next_entry.phys_addr != (cur_addr + cur_size)) {
3701 // Perform copy.
3702 R_TRY(PerformCopy());
3703
3704 // Advance.
3705 dst_addr += cur_size;
3706
3707 cur_addr = next_entry.phys_addr;
3708 cur_size = next_entry.block_size;
3709 } else {
3710 cur_size += next_entry.block_size;
3711 }
3712
3713 tot_size += next_entry.block_size;
3714 }
3715
3716 // Ensure we use the right size for the last block.
3717 if (tot_size > size) {
3718 cur_size -= (tot_size - size);
3719 }
3720
3721 // Perform copy for the last block.
3722 R_TRY(PerformCopy());
3723 }
3724
3725 R_SUCCEED();
3726}
3727
3728Result KPageTableBase::CopyMemoryFromLinearToKernel(
3729 void* buffer, size_t size, KProcessAddress src_addr, KMemoryState src_state_mask,
3730 KMemoryState src_state, KMemoryPermission src_test_perm, KMemoryAttribute src_attr_mask,
3731 KMemoryAttribute src_attr) {
3732 // Lightly validate the range before doing anything else.
3733 R_UNLESS(this->Contains(src_addr, size), ResultInvalidCurrentMemory);
3734
3735 // Copy the memory.
3736 {
3737 // Lock the table.
3738 KScopedLightLock lk(m_general_lock);
3739
3740 // Check memory state.
3741 R_TRY(this->CheckMemoryStateContiguous(
3742 src_addr, size, src_state_mask, src_state, src_test_perm, src_test_perm,
3743 src_attr_mask | KMemoryAttribute::Uncached, src_attr));
3744
3745 auto& impl = this->GetImpl();
3746
3747 // Begin traversal.
3748 TraversalContext context;
3749 TraversalEntry next_entry;
3750 bool traverse_valid =
3751 impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_addr);
3752 ASSERT(traverse_valid);
3753
3754 // Prepare tracking variables.
3755 KPhysicalAddress cur_addr = next_entry.phys_addr;
3756 size_t cur_size =
3757 next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
3758 size_t tot_size = cur_size;
3759
3760 auto PerformCopy = [&]() -> Result {
3761 // Ensure the address is linear mapped.
3762 R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory);
3763
3764 // Copy the data.
3765 std::memcpy(buffer, GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size);
3766
3767 R_SUCCEED();
3768 };
3769
3770 // Iterate.
3771 while (tot_size < size) {
3772 // Continue the traversal.
3773 traverse_valid =
3774 impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
3775 ASSERT(traverse_valid);
3776
3777 if (next_entry.phys_addr != (cur_addr + cur_size)) {
3778 // Perform copy.
3779 R_TRY(PerformCopy());
3780
3781 // Advance.
3782 buffer = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(buffer) + cur_size);
3783
3784 cur_addr = next_entry.phys_addr;
3785 cur_size = next_entry.block_size;
3786 } else {
3787 cur_size += next_entry.block_size;
3788 }
3789
3790 tot_size += next_entry.block_size;
3791 }
3792
3793 // Ensure we use the right size for the last block.
3794 if (tot_size > size) {
3795 cur_size -= (tot_size - size);
3796 }
3797
3798 // Perform copy for the last block.
3799 R_TRY(PerformCopy());
3800 }
3801
3802 R_SUCCEED();
3803}
3804
3805Result KPageTableBase::CopyMemoryFromUserToLinear(
3806 KProcessAddress dst_addr, size_t size, KMemoryState dst_state_mask, KMemoryState dst_state,
3807 KMemoryPermission dst_test_perm, KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
3808 KProcessAddress src_addr) {
3809 // Lightly validate the range before doing anything else.
3810 R_UNLESS(this->Contains(dst_addr, size), ResultInvalidCurrentMemory);
3811
3812 // Get the source memory reference.
3813 auto& src_memory = GetCurrentMemory(m_kernel);
3814
3815 // Copy the memory.
3816 {
3817 // Lock the table.
3818 KScopedLightLock lk(m_general_lock);
3819
3820 // Check memory state.
3821 R_TRY(this->CheckMemoryStateContiguous(
3822 dst_addr, size, dst_state_mask, dst_state, dst_test_perm, dst_test_perm,
3823 dst_attr_mask | KMemoryAttribute::Uncached, dst_attr));
3824
3825 auto& impl = this->GetImpl();
3826
3827 // Begin traversal.
3828 TraversalContext context;
3829 TraversalEntry next_entry;
3830 bool traverse_valid =
3831 impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_addr);
3832 ASSERT(traverse_valid);
3833
3834 // Prepare tracking variables.
3835 KPhysicalAddress cur_addr = next_entry.phys_addr;
3836 size_t cur_size =
3837 next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
3838 size_t tot_size = cur_size;
3839
3840 auto PerformCopy = [&]() -> Result {
3841 // Ensure the address is linear mapped.
3842 R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory);
3843
3844 // Copy as much aligned data as we can.
3845 if (cur_size >= sizeof(u32)) {
3846 const size_t copy_size = Common::AlignDown(cur_size, sizeof(u32));
3847 R_UNLESS(src_memory.ReadBlock(src_addr,
3848 GetLinearMappedVirtualPointer(m_kernel, cur_addr),
3849 copy_size),
3850 ResultInvalidCurrentMemory);
3851 src_addr += copy_size;
3852 cur_addr += copy_size;
3853 cur_size -= copy_size;
3854 }
3855
3856 // Copy remaining data.
3857 if (cur_size > 0) {
3858 R_UNLESS(src_memory.ReadBlock(
3859 src_addr, GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size),
3860 ResultInvalidCurrentMemory);
3861 }
3862
3863 R_SUCCEED();
3864 };
3865
3866 // Iterate.
3867 while (tot_size < size) {
3868 // Continue the traversal.
3869 traverse_valid =
3870 impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
3871 ASSERT(traverse_valid);
3872
3873 if (next_entry.phys_addr != (cur_addr + cur_size)) {
3874 // Perform copy.
3875 R_TRY(PerformCopy());
3876
3877 // Advance.
3878 src_addr += cur_size;
3879
3880 cur_addr = next_entry.phys_addr;
3881 cur_size = next_entry.block_size;
3882 } else {
3883 cur_size += next_entry.block_size;
3884 }
3885
3886 tot_size += next_entry.block_size;
3887 }
3888
3889 // Ensure we use the right size for the last block.
3890 if (tot_size > size) {
3891 cur_size -= (tot_size - size);
3892 }
3893
3894 // Perform copy for the last block.
3895 R_TRY(PerformCopy());
3896 }
3897
3898 R_SUCCEED();
3899}
3900
3901Result KPageTableBase::CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, size_t size,
3902 KMemoryState dst_state_mask,
3903 KMemoryState dst_state,
3904 KMemoryPermission dst_test_perm,
3905 KMemoryAttribute dst_attr_mask,
3906 KMemoryAttribute dst_attr, void* buffer) {
3907 // Lightly validate the range before doing anything else.
3908 R_UNLESS(this->Contains(dst_addr, size), ResultInvalidCurrentMemory);
3909
3910 // Copy the memory.
3911 {
3912 // Lock the table.
3913 KScopedLightLock lk(m_general_lock);
3914
3915 // Check memory state.
3916 R_TRY(this->CheckMemoryStateContiguous(
3917 dst_addr, size, dst_state_mask, dst_state, dst_test_perm, dst_test_perm,
3918 dst_attr_mask | KMemoryAttribute::Uncached, dst_attr));
3919
3920 auto& impl = this->GetImpl();
3921
3922 // Begin traversal.
3923 TraversalContext context;
3924 TraversalEntry next_entry;
3925 bool traverse_valid =
3926 impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_addr);
3927 ASSERT(traverse_valid);
3928
3929 // Prepare tracking variables.
3930 KPhysicalAddress cur_addr = next_entry.phys_addr;
3931 size_t cur_size =
3932 next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1));
3933 size_t tot_size = cur_size;
3934
3935 auto PerformCopy = [&]() -> Result {
3936 // Ensure the address is linear mapped.
3937 R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory);
3938
3939 // Copy the data.
3940 std::memcpy(GetLinearMappedVirtualPointer(m_kernel, cur_addr), buffer, cur_size);
3941
3942 R_SUCCEED();
3943 };
3944
3945 // Iterate.
3946 while (tot_size < size) {
3947 // Continue the traversal.
3948 traverse_valid =
3949 impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
3950 ASSERT(traverse_valid);
3951
3952 if (next_entry.phys_addr != (cur_addr + cur_size)) {
3953 // Perform copy.
3954 R_TRY(PerformCopy());
3955
3956 // Advance.
3957 buffer = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(buffer) + cur_size);
3958
3959 cur_addr = next_entry.phys_addr;
3960 cur_size = next_entry.block_size;
3961 } else {
3962 cur_size += next_entry.block_size;
3963 }
3964
3965 tot_size += next_entry.block_size;
3966 }
3967
3968 // Ensure we use the right size for the last block.
3969 if (tot_size > size) {
3970 cur_size -= (tot_size - size);
3971 }
3972
3973 // Perform copy for the last block.
3974 R_TRY(PerformCopy());
3975 }
3976
3977 R_SUCCEED();
3978}
3979
3980Result KPageTableBase::CopyMemoryFromHeapToHeap(
3981 KPageTableBase& dst_page_table, KProcessAddress dst_addr, size_t size,
3982 KMemoryState dst_state_mask, KMemoryState dst_state, KMemoryPermission dst_test_perm,
3983 KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, KProcessAddress src_addr,
3984 KMemoryState src_state_mask, KMemoryState src_state, KMemoryPermission src_test_perm,
3985 KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) {
3986 // For convenience, alias this.
3987 KPageTableBase& src_page_table = *this;
3988
3989 // Lightly validate the ranges before doing anything else.
3990 R_UNLESS(src_page_table.Contains(src_addr, size), ResultInvalidCurrentMemory);
3991 R_UNLESS(dst_page_table.Contains(dst_addr, size), ResultInvalidCurrentMemory);
3992
3993 // Copy the memory.
3994 {
3995 // Acquire the table locks.
3996 KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock);
3997
3998 // Check memory state.
3999 R_TRY(src_page_table.CheckMemoryStateContiguous(
4000 src_addr, size, src_state_mask, src_state, src_test_perm, src_test_perm,
4001 src_attr_mask | KMemoryAttribute::Uncached, src_attr));
4002 R_TRY(dst_page_table.CheckMemoryStateContiguous(
4003 dst_addr, size, dst_state_mask, dst_state, dst_test_perm, dst_test_perm,
4004 dst_attr_mask | KMemoryAttribute::Uncached, dst_attr));
4005
4006 // Get implementations.
4007 auto& src_impl = src_page_table.GetImpl();
4008 auto& dst_impl = dst_page_table.GetImpl();
4009
4010 // Prepare for traversal.
4011 TraversalContext src_context;
4012 TraversalContext dst_context;
4013 TraversalEntry src_next_entry;
4014 TraversalEntry dst_next_entry;
4015 bool traverse_valid;
4016
4017 // Begin traversal.
4018 traverse_valid = src_impl.BeginTraversal(std::addressof(src_next_entry),
4019 std::addressof(src_context), src_addr);
4020 ASSERT(traverse_valid);
4021 traverse_valid = dst_impl.BeginTraversal(std::addressof(dst_next_entry),
4022 std::addressof(dst_context), dst_addr);
4023 ASSERT(traverse_valid);
4024
4025 // Prepare tracking variables.
4026 KPhysicalAddress cur_src_block_addr = src_next_entry.phys_addr;
4027 KPhysicalAddress cur_dst_block_addr = dst_next_entry.phys_addr;
4028 size_t cur_src_size = src_next_entry.block_size -
4029 (GetInteger(cur_src_block_addr) & (src_next_entry.block_size - 1));
4030 size_t cur_dst_size = dst_next_entry.block_size -
4031 (GetInteger(cur_dst_block_addr) & (dst_next_entry.block_size - 1));
4032
4033 // Adjust the initial block sizes.
4034 src_next_entry.block_size = cur_src_size;
4035 dst_next_entry.block_size = cur_dst_size;
4036
4037 // Before we get any crazier, succeed if there's nothing to do.
4038 R_SUCCEED_IF(size == 0);
4039
4040 // We're going to manage dual traversal via an offset against the total size.
4041 KPhysicalAddress cur_src_addr = cur_src_block_addr;
4042 KPhysicalAddress cur_dst_addr = cur_dst_block_addr;
4043 size_t cur_min_size = std::min<size_t>(cur_src_size, cur_dst_size);
4044
4045 // Iterate.
4046 size_t ofs = 0;
4047 while (ofs < size) {
4048 // Determine how much we can copy this iteration.
4049 const size_t cur_copy_size = std::min<size_t>(cur_min_size, size - ofs);
4050
4051 // If we need to advance the traversals, do so.
4052 bool updated_src = false, updated_dst = false, skip_copy = false;
4053 if (ofs + cur_copy_size != size) {
4054 if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) {
4055 // Continue the src traversal.
4056 traverse_valid = src_impl.ContinueTraversal(std::addressof(src_next_entry),
4057 std::addressof(src_context));
4058 ASSERT(traverse_valid);
4059
4060 // Update source.
4061 updated_src = cur_src_addr + cur_min_size != src_next_entry.phys_addr;
4062 }
4063
4064 if (cur_dst_addr + cur_min_size ==
4065 dst_next_entry.phys_addr + dst_next_entry.block_size) {
4066 // Continue the dst traversal.
4067 traverse_valid = dst_impl.ContinueTraversal(std::addressof(dst_next_entry),
4068 std::addressof(dst_context));
4069 ASSERT(traverse_valid);
4070
4071 // Update destination.
4072 updated_dst = cur_dst_addr + cur_min_size != dst_next_entry.phys_addr;
4073 }
4074
4075 // If we didn't update either of source/destination, skip the copy this iteration.
4076 if (!updated_src && !updated_dst) {
4077 skip_copy = true;
4078
4079 // Update the source block address.
4080 cur_src_block_addr = src_next_entry.phys_addr;
4081 }
4082 }
4083
4084 // Do the copy, unless we're skipping it.
4085 if (!skip_copy) {
4086 // We need both ends of the copy to be heap blocks.
4087 R_UNLESS(IsHeapPhysicalAddress(cur_src_addr), ResultInvalidCurrentMemory);
4088 R_UNLESS(IsHeapPhysicalAddress(cur_dst_addr), ResultInvalidCurrentMemory);
4089
4090 // Copy the data.
4091 std::memcpy(GetHeapVirtualPointer(m_kernel, cur_dst_addr),
4092 GetHeapVirtualPointer(m_kernel, cur_src_addr), cur_copy_size);
4093
4094 // Update.
4095 cur_src_block_addr = src_next_entry.phys_addr;
4096 cur_src_addr = updated_src ? cur_src_block_addr : cur_src_addr + cur_copy_size;
4097 cur_dst_block_addr = dst_next_entry.phys_addr;
4098 cur_dst_addr = updated_dst ? cur_dst_block_addr : cur_dst_addr + cur_copy_size;
4099
4100 // Advance offset.
4101 ofs += cur_copy_size;
4102 }
4103
4104 // Update min size.
4105 cur_src_size = src_next_entry.block_size;
4106 cur_dst_size = dst_next_entry.block_size;
4107 cur_min_size = std::min<size_t>(cur_src_block_addr - cur_src_addr + cur_src_size,
4108 cur_dst_block_addr - cur_dst_addr + cur_dst_size);
4109 }
4110 }
4111
4112 R_SUCCEED();
4113}
4114
4115Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination(
4116 KPageTableBase& dst_page_table, KProcessAddress dst_addr, size_t size,
4117 KMemoryState dst_state_mask, KMemoryState dst_state, KMemoryPermission dst_test_perm,
4118 KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, KProcessAddress src_addr,
4119 KMemoryState src_state_mask, KMemoryState src_state, KMemoryPermission src_test_perm,
4120 KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) {
4121 // For convenience, alias this.
4122 KPageTableBase& src_page_table = *this;
4123
4124 // Lightly validate the ranges before doing anything else.
4125 R_UNLESS(src_page_table.Contains(src_addr, size), ResultInvalidCurrentMemory);
4126 R_UNLESS(dst_page_table.Contains(dst_addr, size), ResultInvalidCurrentMemory);
4127
4128 // Copy the memory.
4129 {
4130 // Acquire the table locks.
4131 KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock);
4132
4133 // Check memory state for source.
4134 R_TRY(src_page_table.CheckMemoryStateContiguous(
4135 src_addr, size, src_state_mask, src_state, src_test_perm, src_test_perm,
4136 src_attr_mask | KMemoryAttribute::Uncached, src_attr));
4137
4138 // Destination state is intentionally unchecked.
4139
4140 // Get implementations.
4141 auto& src_impl = src_page_table.GetImpl();
4142 auto& dst_impl = dst_page_table.GetImpl();
4143
4144 // Prepare for traversal.
4145 TraversalContext src_context;
4146 TraversalContext dst_context;
4147 TraversalEntry src_next_entry;
4148 TraversalEntry dst_next_entry;
4149 bool traverse_valid;
4150
4151 // Begin traversal.
4152 traverse_valid = src_impl.BeginTraversal(std::addressof(src_next_entry),
4153 std::addressof(src_context), src_addr);
4154 ASSERT(traverse_valid);
4155 traverse_valid = dst_impl.BeginTraversal(std::addressof(dst_next_entry),
4156 std::addressof(dst_context), dst_addr);
4157 ASSERT(traverse_valid);
4158
4159 // Prepare tracking variables.
4160 KPhysicalAddress cur_src_block_addr = src_next_entry.phys_addr;
4161 KPhysicalAddress cur_dst_block_addr = dst_next_entry.phys_addr;
4162 size_t cur_src_size = src_next_entry.block_size -
4163 (GetInteger(cur_src_block_addr) & (src_next_entry.block_size - 1));
4164 size_t cur_dst_size = dst_next_entry.block_size -
4165 (GetInteger(cur_dst_block_addr) & (dst_next_entry.block_size - 1));
4166
4167 // Adjust the initial block sizes.
4168 src_next_entry.block_size = cur_src_size;
4169 dst_next_entry.block_size = cur_dst_size;
4170
4171 // Before we get any crazier, succeed if there's nothing to do.
4172 R_SUCCEED_IF(size == 0);
4173
4174 // We're going to manage dual traversal via an offset against the total size.
4175 KPhysicalAddress cur_src_addr = cur_src_block_addr;
4176 KPhysicalAddress cur_dst_addr = cur_dst_block_addr;
4177 size_t cur_min_size = std::min<size_t>(cur_src_size, cur_dst_size);
4178
4179 // Iterate.
4180 size_t ofs = 0;
4181 while (ofs < size) {
4182 // Determine how much we can copy this iteration.
4183 const size_t cur_copy_size = std::min<size_t>(cur_min_size, size - ofs);
4184
4185 // If we need to advance the traversals, do so.
4186 bool updated_src = false, updated_dst = false, skip_copy = false;
4187 if (ofs + cur_copy_size != size) {
4188 if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) {
4189 // Continue the src traversal.
4190 traverse_valid = src_impl.ContinueTraversal(std::addressof(src_next_entry),
4191 std::addressof(src_context));
4192 ASSERT(traverse_valid);
4193
4194 // Update source.
4195 updated_src = cur_src_addr + cur_min_size != src_next_entry.phys_addr;
4196 }
4197
4198 if (cur_dst_addr + cur_min_size ==
4199 dst_next_entry.phys_addr + dst_next_entry.block_size) {
4200 // Continue the dst traversal.
4201 traverse_valid = dst_impl.ContinueTraversal(std::addressof(dst_next_entry),
4202 std::addressof(dst_context));
4203 ASSERT(traverse_valid);
4204
4205 // Update destination.
4206 updated_dst = cur_dst_addr + cur_min_size != dst_next_entry.phys_addr;
4207 }
4208
4209 // If we didn't update either of source/destination, skip the copy this iteration.
4210 if (!updated_src && !updated_dst) {
4211 skip_copy = true;
4212
4213 // Update the source block address.
4214 cur_src_block_addr = src_next_entry.phys_addr;
4215 }
4216 }
4217
4218 // Do the copy, unless we're skipping it.
4219 if (!skip_copy) {
4220 // We need both ends of the copy to be heap blocks.
4221 R_UNLESS(IsHeapPhysicalAddress(cur_src_addr), ResultInvalidCurrentMemory);
4222 R_UNLESS(IsHeapPhysicalAddress(cur_dst_addr), ResultInvalidCurrentMemory);
4223
4224 // Copy the data.
4225 std::memcpy(GetHeapVirtualPointer(m_kernel, cur_dst_addr),
4226 GetHeapVirtualPointer(m_kernel, cur_src_addr), cur_copy_size);
4227
4228 // Update.
4229 cur_src_block_addr = src_next_entry.phys_addr;
4230 cur_src_addr = updated_src ? cur_src_block_addr : cur_src_addr + cur_copy_size;
4231 cur_dst_block_addr = dst_next_entry.phys_addr;
4232 cur_dst_addr = updated_dst ? cur_dst_block_addr : cur_dst_addr + cur_copy_size;
4233
4234 // Advance offset.
4235 ofs += cur_copy_size;
4236 }
4237
4238 // Update min size.
4239 cur_src_size = src_next_entry.block_size;
4240 cur_dst_size = dst_next_entry.block_size;
4241 cur_min_size = std::min<size_t>(cur_src_block_addr - cur_src_addr + cur_src_size,
4242 cur_dst_block_addr - cur_dst_addr + cur_dst_size);
4243 }
4244 }
4245
4246 R_SUCCEED();
4247}
4248
4249Result KPageTableBase::SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed,
4250 KProcessAddress address, size_t size,
4251 KMemoryPermission test_perm, KMemoryState dst_state) {
4252 // Validate pre-conditions.
4253 ASSERT(this->IsLockedByCurrentThread());
4254 ASSERT(test_perm == KMemoryPermission::UserReadWrite ||
4255 test_perm == KMemoryPermission::UserRead);
4256
4257 // Check that the address is in range.
4258 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
4259
4260 // Get the source permission.
4261 const auto src_perm = static_cast<KMemoryPermission>(
4262 (test_perm == KMemoryPermission::UserReadWrite)
4263 ? KMemoryPermission::KernelReadWrite | KMemoryPermission::NotMapped
4264 : KMemoryPermission::UserRead);
4265
4266 // Get aligned extents.
4267 const KProcessAddress aligned_src_start = Common::AlignDown(GetInteger(address), PageSize);
4268 const KProcessAddress aligned_src_end = Common::AlignUp(GetInteger(address) + size, PageSize);
4269 const KProcessAddress mapping_src_start = Common::AlignUp(GetInteger(address), PageSize);
4270 const KProcessAddress mapping_src_end = Common::AlignDown(GetInteger(address) + size, PageSize);
4271
4272 const auto aligned_src_last = GetInteger(aligned_src_end) - 1;
4273 const auto mapping_src_last = GetInteger(mapping_src_end) - 1;
4274
4275 // Get the test state and attribute mask.
4276 KMemoryState test_state;
4277 KMemoryAttribute test_attr_mask;
4278 switch (dst_state) {
4279 case KMemoryState::Ipc:
4280 test_state = KMemoryState::FlagCanUseIpc;
4281 test_attr_mask =
4282 KMemoryAttribute::Uncached | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked;
4283 break;
4284 case KMemoryState::NonSecureIpc:
4285 test_state = KMemoryState::FlagCanUseNonSecureIpc;
4286 test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
4287 break;
4288 case KMemoryState::NonDeviceIpc:
4289 test_state = KMemoryState::FlagCanUseNonDeviceIpc;
4290 test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
4291 break;
4292 default:
4293 R_THROW(ResultInvalidCombination);
4294 }
4295
4296 // Ensure that on failure, we roll back appropriately.
4297 size_t mapped_size = 0;
4298 ON_RESULT_FAILURE {
4299 if (mapped_size > 0) {
4300 this->CleanupForIpcClientOnServerSetupFailure(page_list, mapping_src_start, mapped_size,
4301 src_perm);
4302 }
4303 };
4304
4305 size_t blocks_needed = 0;
4306
4307 // Iterate, mapping as needed.
4308 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(aligned_src_start);
4309 while (true) {
4310 const KMemoryInfo info = it->GetMemoryInfo();
4311
4312 // Validate the current block.
4313 R_TRY(this->CheckMemoryState(info, test_state, test_state, test_perm, test_perm,
4314 test_attr_mask, KMemoryAttribute::None));
4315
4316 if (mapping_src_start < mapping_src_end &&
4317 GetInteger(mapping_src_start) < info.GetEndAddress() &&
4318 info.GetAddress() < GetInteger(mapping_src_end)) {
4319 const auto cur_start = info.GetAddress() >= GetInteger(mapping_src_start)
4320 ? info.GetAddress()
4321 : GetInteger(mapping_src_start);
4322 const auto cur_end = mapping_src_last >= info.GetLastAddress()
4323 ? info.GetEndAddress()
4324 : GetInteger(mapping_src_end);
4325 const size_t cur_size = cur_end - cur_start;
4326
4327 if (info.GetAddress() < GetInteger(mapping_src_start)) {
4328 ++blocks_needed;
4329 }
4330 if (mapping_src_last < info.GetLastAddress()) {
4331 ++blocks_needed;
4332 }
4333
4334 // Set the permissions on the block, if we need to.
4335 if ((info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != src_perm) {
4336 const DisableMergeAttribute head_body_attr =
4337 (GetInteger(mapping_src_start) >= info.GetAddress())
4338 ? DisableMergeAttribute::DisableHeadAndBody
4339 : DisableMergeAttribute::None;
4340 const DisableMergeAttribute tail_attr = (cur_end == GetInteger(mapping_src_end))
4341 ? DisableMergeAttribute::DisableTail
4342 : DisableMergeAttribute::None;
4343 const KPageProperties properties = {
4344 src_perm, false, false,
4345 static_cast<DisableMergeAttribute>(head_body_attr | tail_attr)};
4346 R_TRY(this->Operate(page_list, cur_start, cur_size / PageSize, 0, false, properties,
4347 OperationType::ChangePermissions, false));
4348 }
4349
4350 // Note that we mapped this part.
4351 mapped_size += cur_size;
4352 }
4353
4354 // If the block is at the end, we're done.
4355 if (aligned_src_last <= info.GetLastAddress()) {
4356 break;
4357 }
4358
4359 // Advance.
4360 ++it;
4361 ASSERT(it != m_memory_block_manager.end());
4362 }
4363
4364 if (out_blocks_needed != nullptr) {
4365 ASSERT(blocks_needed <= KMemoryBlockManagerUpdateAllocator::MaxBlocks);
4366 *out_blocks_needed = blocks_needed;
4367 }
4368
4369 R_SUCCEED();
4370}
4371
4372Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
4373 KProcessAddress src_addr, KMemoryPermission test_perm,
4374 KMemoryState dst_state, KPageTableBase& src_page_table,
4375 bool send) {
4376 ASSERT(this->IsLockedByCurrentThread());
4377 ASSERT(src_page_table.IsLockedByCurrentThread());
4378
4379 // Check that we can theoretically map.
4380 const KProcessAddress region_start = m_alias_region_start;
4381 const size_t region_size = m_alias_region_end - m_alias_region_start;
4382 R_UNLESS(size < region_size, ResultOutOfAddressSpace);
4383
4384 // Get aligned source extents.
4385 const KProcessAddress src_start = src_addr;
4386 const KProcessAddress src_end = src_addr + size;
4387 const KProcessAddress aligned_src_start = Common::AlignDown(GetInteger(src_start), PageSize);
4388 const KProcessAddress aligned_src_end = Common::AlignUp(GetInteger(src_start) + size, PageSize);
4389 const KProcessAddress mapping_src_start = Common::AlignUp(GetInteger(src_start), PageSize);
4390 const KProcessAddress mapping_src_end =
4391 Common::AlignDown(GetInteger(src_start) + size, PageSize);
4392 const size_t aligned_src_size = aligned_src_end - aligned_src_start;
4393 const size_t mapping_src_size =
4394 (mapping_src_start < mapping_src_end) ? (mapping_src_end - mapping_src_start) : 0;
4395
4396 // Select a random address to map at.
4397 KProcessAddress dst_addr = 0;
4398 {
4399 const size_t alignment = 4_KiB;
4400 const size_t offset = GetInteger(aligned_src_start) & (alignment - 1);
4401
4402 dst_addr =
4403 this->FindFreeArea(region_start, region_size / PageSize, aligned_src_size / PageSize,
4404 alignment, offset, this->GetNumGuardPages());
4405 R_UNLESS(dst_addr != 0, ResultOutOfAddressSpace);
4406 }
4407
4408 // Check that we can perform the operation we're about to perform.
4409 ASSERT(this->CanContain(dst_addr, aligned_src_size, dst_state));
4410
4411 // Create an update allocator.
4412 Result allocator_result;
4413 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
4414 m_memory_block_slab_manager);
4415 R_TRY(allocator_result);
4416
4417 // We're going to perform an update, so create a helper.
4418 KScopedPageTableUpdater updater(this);
4419
4420 // Reserve space for any partial pages we allocate.
4421 const size_t unmapped_size = aligned_src_size - mapping_src_size;
4422 KScopedResourceReservation memory_reservation(
4423 m_resource_limit, Svc::LimitableResource::PhysicalMemoryMax, unmapped_size);
4424 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
4425
4426 // Ensure that we manage page references correctly.
4427 KPhysicalAddress start_partial_page = 0;
4428 KPhysicalAddress end_partial_page = 0;
4429 KProcessAddress cur_mapped_addr = dst_addr;
4430
4431 // If the partial pages are mapped, an extra reference will have been opened. Otherwise, they'll
4432 // free on scope exit.
4433 SCOPE_EXIT({
4434 if (start_partial_page != 0) {
4435 m_kernel.MemoryManager().Close(start_partial_page, 1);
4436 }
4437 if (end_partial_page != 0) {
4438 m_kernel.MemoryManager().Close(end_partial_page, 1);
4439 }
4440 });
4441
4442 ON_RESULT_FAILURE {
4443 if (cur_mapped_addr != dst_addr) {
4444 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
4445 DisableMergeAttribute::None};
4446 R_ASSERT(this->Operate(updater.GetPageList(), dst_addr,
4447 (cur_mapped_addr - dst_addr) / PageSize, 0, false,
4448 unmap_properties, OperationType::Unmap, true));
4449 }
4450 };
4451
4452 // Allocate the start page as needed.
4453 if (aligned_src_start < mapping_src_start) {
4454 start_partial_page =
4455 m_kernel.MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option);
4456 R_UNLESS(start_partial_page != 0, ResultOutOfMemory);
4457 }
4458
4459 // Allocate the end page as needed.
4460 if (mapping_src_end < aligned_src_end &&
4461 (aligned_src_start < mapping_src_end || aligned_src_start == mapping_src_start)) {
4462 end_partial_page =
4463 m_kernel.MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option);
4464 R_UNLESS(end_partial_page != 0, ResultOutOfMemory);
4465 }
4466
4467 // Get the implementation.
4468 auto& src_impl = src_page_table.GetImpl();
4469
4470 // Get the fill value for partial pages.
4471 const auto fill_val = m_ipc_fill_value;
4472
4473 // Begin traversal.
4474 TraversalContext context;
4475 TraversalEntry next_entry;
4476 bool traverse_valid = src_impl.BeginTraversal(std::addressof(next_entry),
4477 std::addressof(context), aligned_src_start);
4478 ASSERT(traverse_valid);
4479
4480 // Prepare tracking variables.
4481 KPhysicalAddress cur_block_addr = next_entry.phys_addr;
4482 size_t cur_block_size =
4483 next_entry.block_size - (GetInteger(cur_block_addr) & (next_entry.block_size - 1));
4484 size_t tot_block_size = cur_block_size;
4485
4486 // Map the start page, if we have one.
4487 if (start_partial_page != 0) {
4488 // Ensure the page holds correct data.
4489 u8* const start_partial_virt = GetHeapVirtualPointer(m_kernel, start_partial_page);
4490 if (send) {
4491 const size_t partial_offset = src_start - aligned_src_start;
4492 size_t copy_size, clear_size;
4493 if (src_end < mapping_src_start) {
4494 copy_size = size;
4495 clear_size = mapping_src_start - src_end;
4496 } else {
4497 copy_size = mapping_src_start - src_start;
4498 clear_size = 0;
4499 }
4500
4501 std::memset(start_partial_virt, fill_val, partial_offset);
4502 std::memcpy(start_partial_virt + partial_offset,
4503 GetHeapVirtualPointer(m_kernel, cur_block_addr) + partial_offset,
4504 copy_size);
4505 if (clear_size > 0) {
4506 std::memset(start_partial_virt + partial_offset + copy_size, fill_val, clear_size);
4507 }
4508 } else {
4509 std::memset(start_partial_virt, fill_val, PageSize);
4510 }
4511
4512 // Map the page.
4513 const KPageProperties start_map_properties = {test_perm, false, false,
4514 DisableMergeAttribute::DisableHead};
4515 R_TRY(this->Operate(updater.GetPageList(), cur_mapped_addr, 1, start_partial_page, true,
4516 start_map_properties, OperationType::Map, false));
4517
4518 // Update tracking extents.
4519 cur_mapped_addr += PageSize;
4520 cur_block_addr += PageSize;
4521 cur_block_size -= PageSize;
4522
4523 // If the block's size was one page, we may need to continue traversal.
4524 if (cur_block_size == 0 && aligned_src_size > PageSize) {
4525 traverse_valid =
4526 src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
4527 ASSERT(traverse_valid);
4528
4529 cur_block_addr = next_entry.phys_addr;
4530 cur_block_size = next_entry.block_size;
4531 tot_block_size += next_entry.block_size;
4532 }
4533 }
4534
4535 // Map the remaining pages.
4536 while (aligned_src_start + tot_block_size < mapping_src_end) {
4537 // Continue the traversal.
4538 traverse_valid =
4539 src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
4540 ASSERT(traverse_valid);
4541
4542 // Process the block.
4543 if (next_entry.phys_addr != cur_block_addr + cur_block_size) {
4544 // Map the block we've been processing so far.
4545 const KPageProperties map_properties = {test_perm, false, false,
4546 (cur_mapped_addr == dst_addr)
4547 ? DisableMergeAttribute::DisableHead
4548 : DisableMergeAttribute::None};
4549 R_TRY(this->Operate(updater.GetPageList(), cur_mapped_addr, cur_block_size / PageSize,
4550 cur_block_addr, true, map_properties, OperationType::Map, false));
4551
4552 // Update tracking extents.
4553 cur_mapped_addr += cur_block_size;
4554 cur_block_addr = next_entry.phys_addr;
4555 cur_block_size = next_entry.block_size;
4556 } else {
4557 cur_block_size += next_entry.block_size;
4558 }
4559 tot_block_size += next_entry.block_size;
4560 }
4561
4562 // Handle the last direct-mapped page.
4563 if (const KProcessAddress mapped_block_end =
4564 aligned_src_start + tot_block_size - cur_block_size;
4565 mapped_block_end < mapping_src_end) {
4566 const size_t last_block_size = mapping_src_end - mapped_block_end;
4567
4568 // Map the last block.
4569 const KPageProperties map_properties = {test_perm, false, false,
4570 (cur_mapped_addr == dst_addr)
4571 ? DisableMergeAttribute::DisableHead
4572 : DisableMergeAttribute::None};
4573 R_TRY(this->Operate(updater.GetPageList(), cur_mapped_addr, last_block_size / PageSize,
4574 cur_block_addr, true, map_properties, OperationType::Map, false));
4575
4576 // Update tracking extents.
4577 cur_mapped_addr += last_block_size;
4578 cur_block_addr += last_block_size;
4579 if (mapped_block_end + cur_block_size < aligned_src_end &&
4580 cur_block_size == last_block_size) {
4581 traverse_valid =
4582 src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
4583 ASSERT(traverse_valid);
4584
4585 cur_block_addr = next_entry.phys_addr;
4586 }
4587 }
4588
4589 // Map the end page, if we have one.
4590 if (end_partial_page != 0) {
4591 // Ensure the page holds correct data.
4592 u8* const end_partial_virt = GetHeapVirtualPointer(m_kernel, end_partial_page);
4593 if (send) {
4594 const size_t copy_size = src_end - mapping_src_end;
4595 std::memcpy(end_partial_virt, GetHeapVirtualPointer(m_kernel, cur_block_addr),
4596 copy_size);
4597 std::memset(end_partial_virt + copy_size, fill_val, PageSize - copy_size);
4598 } else {
4599 std::memset(end_partial_virt, fill_val, PageSize);
4600 }
4601
4602 // Map the page.
4603 const KPageProperties map_properties = {test_perm, false, false,
4604 (cur_mapped_addr == dst_addr)
4605 ? DisableMergeAttribute::DisableHead
4606 : DisableMergeAttribute::None};
4607 R_TRY(this->Operate(updater.GetPageList(), cur_mapped_addr, 1, end_partial_page, true,
4608 map_properties, OperationType::Map, false));
4609 }
4610
4611 // Update memory blocks to reflect our changes
4612 m_memory_block_manager.Update(std::addressof(allocator), dst_addr, aligned_src_size / PageSize,
4613 dst_state, test_perm, KMemoryAttribute::None,
4614 KMemoryBlockDisableMergeAttribute::Normal,
4615 KMemoryBlockDisableMergeAttribute::None);
4616
4617 // Set the output address.
4618 *out_addr = dst_addr + (src_start - aligned_src_start);
4619
4620 // We succeeded.
4621 memory_reservation.Commit();
4622 R_SUCCEED();
4623}
4624
4625Result KPageTableBase::SetupForIpc(KProcessAddress* out_dst_addr, size_t size,
4626 KProcessAddress src_addr, KPageTableBase& src_page_table,
4627 KMemoryPermission test_perm, KMemoryState dst_state, bool send) {
4628 // For convenience, alias this.
4629 KPageTableBase& dst_page_table = *this;
4630
4631 // Acquire the table locks.
4632 KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock);
4633
4634 // We're going to perform an update, so create a helper.
4635 KScopedPageTableUpdater updater(std::addressof(src_page_table));
4636
4637 // Perform client setup.
4638 size_t num_allocator_blocks;
4639 R_TRY(src_page_table.SetupForIpcClient(updater.GetPageList(),
4640 std::addressof(num_allocator_blocks), src_addr, size,
4641 test_perm, dst_state));
4642
4643 // Create an update allocator.
4644 Result allocator_result;
4645 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
4646 src_page_table.m_memory_block_slab_manager,
4647 num_allocator_blocks);
4648 R_TRY(allocator_result);
4649
4650 // Get the mapped extents.
4651 const KProcessAddress src_map_start = Common::AlignUp(GetInteger(src_addr), PageSize);
4652 const KProcessAddress src_map_end = Common::AlignDown(GetInteger(src_addr) + size, PageSize);
4653 const size_t src_map_size = src_map_end - src_map_start;
4654
4655 // Ensure that we clean up appropriately if we fail after this.
4656 const auto src_perm = static_cast<KMemoryPermission>(
4657 (test_perm == KMemoryPermission::UserReadWrite)
4658 ? KMemoryPermission::KernelReadWrite | KMemoryPermission::NotMapped
4659 : KMemoryPermission::UserRead);
4660 ON_RESULT_FAILURE {
4661 if (src_map_end > src_map_start) {
4662 src_page_table.CleanupForIpcClientOnServerSetupFailure(
4663 updater.GetPageList(), src_map_start, src_map_size, src_perm);
4664 }
4665 };
4666
4667 // Perform server setup.
4668 R_TRY(dst_page_table.SetupForIpcServer(out_dst_addr, size, src_addr, test_perm, dst_state,
4669 src_page_table, send));
4670
4671 // If anything was mapped, ipc-lock the pages.
4672 if (src_map_start < src_map_end) {
4673 // Get the source permission.
4674 src_page_table.m_memory_block_manager.UpdateLock(std::addressof(allocator), src_map_start,
4675 (src_map_end - src_map_start) / PageSize,
4676 &KMemoryBlock::LockForIpc, src_perm);
4677 }
4678
4679 R_SUCCEED();
4680}
4681
4682Result KPageTableBase::CleanupForIpcServer(KProcessAddress address, size_t size,
4683 KMemoryState dst_state) {
4684 // Validate the address.
4685 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
4686
4687 // Lock the table.
4688 KScopedLightLock lk(m_general_lock);
4689
4690 // Validate the memory state.
4691 size_t num_allocator_blocks;
4692 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
4693 KMemoryState::All, dst_state, KMemoryPermission::UserRead,
4694 KMemoryPermission::UserRead, KMemoryAttribute::All,
4695 KMemoryAttribute::None));
4696
4697 // Create an update allocator.
4698 Result allocator_result;
4699 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
4700 m_memory_block_slab_manager, num_allocator_blocks);
4701 R_TRY(allocator_result);
4702
4703 // We're going to perform an update, so create a helper.
4704 KScopedPageTableUpdater updater(this);
4705
4706 // Get aligned extents.
4707 const KProcessAddress aligned_start = Common::AlignDown(GetInteger(address), PageSize);
4708 const KProcessAddress aligned_end = Common::AlignUp(GetInteger(address) + size, PageSize);
4709 const size_t aligned_size = aligned_end - aligned_start;
4710 const size_t aligned_num_pages = aligned_size / PageSize;
4711
4712 // Unmap the pages.
4713 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
4714 DisableMergeAttribute::None};
4715 R_TRY(this->Operate(updater.GetPageList(), aligned_start, aligned_num_pages, 0, false,
4716 unmap_properties, OperationType::Unmap, false));
4717
4718 // Update memory blocks.
4719 m_memory_block_manager.Update(std::addressof(allocator), aligned_start, aligned_num_pages,
4720 KMemoryState::None, KMemoryPermission::None,
4721 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
4722 KMemoryBlockDisableMergeAttribute::Normal);
4723
4724 // Release from the resource limit as relevant.
4725 const KProcessAddress mapping_start = Common::AlignUp(GetInteger(address), PageSize);
4726 const KProcessAddress mapping_end = Common::AlignDown(GetInteger(address) + size, PageSize);
4727 const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0;
4728 m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax,
4729 aligned_size - mapping_size);
4730
4731 R_SUCCEED();
4732}
4733
4734Result KPageTableBase::CleanupForIpcClient(KProcessAddress address, size_t size,
4735 KMemoryState dst_state) {
4736 // Validate the address.
4737 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
4738
4739 // Get aligned source extents.
4740 const KProcessAddress mapping_start = Common::AlignUp(GetInteger(address), PageSize);
4741 const KProcessAddress mapping_end = Common::AlignDown(GetInteger(address) + size, PageSize);
4742 const KProcessAddress mapping_last = mapping_end - 1;
4743 const size_t mapping_size = (mapping_start < mapping_end) ? (mapping_end - mapping_start) : 0;
4744
4745 // If nothing was mapped, we're actually done immediately.
4746 R_SUCCEED_IF(mapping_size == 0);
4747
4748 // Get the test state and attribute mask.
4749 KMemoryState test_state;
4750 KMemoryAttribute test_attr_mask;
4751 switch (dst_state) {
4752 case KMemoryState::Ipc:
4753 test_state = KMemoryState::FlagCanUseIpc;
4754 test_attr_mask =
4755 KMemoryAttribute::Uncached | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked;
4756 break;
4757 case KMemoryState::NonSecureIpc:
4758 test_state = KMemoryState::FlagCanUseNonSecureIpc;
4759 test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
4760 break;
4761 case KMemoryState::NonDeviceIpc:
4762 test_state = KMemoryState::FlagCanUseNonDeviceIpc;
4763 test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
4764 break;
4765 default:
4766 R_THROW(ResultInvalidCombination);
4767 }
4768
4769 // Lock the table.
4770 // NOTE: Nintendo does this *after* creating the updater below, but this does not follow
4771 // convention elsewhere in KPageTableBase.
4772 KScopedLightLock lk(m_general_lock);
4773
4774 // We're going to perform an update, so create a helper.
4775 KScopedPageTableUpdater updater(this);
4776
4777 // Ensure that on failure, we roll back appropriately.
4778 size_t mapped_size = 0;
4779 ON_RESULT_FAILURE {
4780 if (mapped_size > 0) {
4781 // Determine where the mapping ends.
4782 const auto mapped_end = GetInteger(mapping_start) + mapped_size;
4783 const auto mapped_last = mapped_end - 1;
4784
4785 // Get current and next iterators.
4786 KMemoryBlockManager::const_iterator start_it =
4787 m_memory_block_manager.FindIterator(mapping_start);
4788 KMemoryBlockManager::const_iterator next_it = start_it;
4789 ++next_it;
4790
4791 // Get the current block info.
4792 KMemoryInfo cur_info = start_it->GetMemoryInfo();
4793
4794 // Create tracking variables.
4795 KProcessAddress cur_address = cur_info.GetAddress();
4796 size_t cur_size = cur_info.GetSize();
4797 bool cur_perm_eq = cur_info.GetPermission() == cur_info.GetOriginalPermission();
4798 bool cur_needs_set_perm = !cur_perm_eq && cur_info.GetIpcLockCount() == 1;
4799 bool first = cur_info.GetIpcDisableMergeCount() == 1 &&
4800 False(cur_info.GetDisableMergeAttribute() &
4801 KMemoryBlockDisableMergeAttribute::Locked);
4802
4803 while ((GetInteger(cur_address) + cur_size - 1) < mapped_last) {
4804 // Check that we have a next block.
4805 ASSERT(next_it != m_memory_block_manager.end());
4806
4807 // Get the next info.
4808 const KMemoryInfo next_info = next_it->GetMemoryInfo();
4809
4810 // Check if we can consolidate the next block's permission set with the current one.
4811 const bool next_perm_eq =
4812 next_info.GetPermission() == next_info.GetOriginalPermission();
4813 const bool next_needs_set_perm = !next_perm_eq && next_info.GetIpcLockCount() == 1;
4814 if (cur_perm_eq == next_perm_eq && cur_needs_set_perm == next_needs_set_perm &&
4815 cur_info.GetOriginalPermission() == next_info.GetOriginalPermission()) {
4816 // We can consolidate the reprotection for the current and next block into a
4817 // single call.
4818 cur_size += next_info.GetSize();
4819 } else {
4820 // We have to operate on the current block.
4821 if ((cur_needs_set_perm || first) && !cur_perm_eq) {
4822 const KPageProperties properties = {
4823 cur_info.GetPermission(), false, false,
4824 first ? DisableMergeAttribute::EnableAndMergeHeadBodyTail
4825 : DisableMergeAttribute::None};
4826 R_ASSERT(this->Operate(updater.GetPageList(), cur_address,
4827 cur_size / PageSize, 0, false, properties,
4828 OperationType::ChangePermissions, true));
4829 }
4830
4831 // Advance.
4832 cur_address = next_info.GetAddress();
4833 cur_size = next_info.GetSize();
4834 first = false;
4835 }
4836
4837 // Advance.
4838 cur_info = next_info;
4839 cur_perm_eq = next_perm_eq;
4840 cur_needs_set_perm = next_needs_set_perm;
4841 ++next_it;
4842 }
4843
4844 // Process the last block.
4845 if ((first || cur_needs_set_perm) && !cur_perm_eq) {
4846 const KPageProperties properties = {
4847 cur_info.GetPermission(), false, false,
4848 first ? DisableMergeAttribute::EnableAndMergeHeadBodyTail
4849 : DisableMergeAttribute::None};
4850 R_ASSERT(this->Operate(updater.GetPageList(), cur_address, cur_size / PageSize, 0,
4851 false, properties, OperationType::ChangePermissions, true));
4852 }
4853 }
4854 };
4855
4856 // Iterate, reprotecting as needed.
4857 {
4858 // Get current and next iterators.
4859 KMemoryBlockManager::const_iterator start_it =
4860 m_memory_block_manager.FindIterator(mapping_start);
4861 KMemoryBlockManager::const_iterator next_it = start_it;
4862 ++next_it;
4863
4864 // Validate the current block.
4865 KMemoryInfo cur_info = start_it->GetMemoryInfo();
4866 R_ASSERT(this->CheckMemoryState(
4867 cur_info, test_state, test_state, KMemoryPermission::None, KMemoryPermission::None,
4868 test_attr_mask | KMemoryAttribute::IpcLocked, KMemoryAttribute::IpcLocked));
4869
4870 // Create tracking variables.
4871 KProcessAddress cur_address = cur_info.GetAddress();
4872 size_t cur_size = cur_info.GetSize();
4873 bool cur_perm_eq = cur_info.GetPermission() == cur_info.GetOriginalPermission();
4874 bool cur_needs_set_perm = !cur_perm_eq && cur_info.GetIpcLockCount() == 1;
4875 bool first =
4876 cur_info.GetIpcDisableMergeCount() == 1 &&
4877 False(cur_info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Locked);
4878
4879 while ((cur_address + cur_size - 1) < mapping_last) {
4880 // Check that we have a next block.
4881 ASSERT(next_it != m_memory_block_manager.end());
4882
4883 // Get the next info.
4884 const KMemoryInfo next_info = next_it->GetMemoryInfo();
4885
4886 // Validate the next block.
4887 R_ASSERT(this->CheckMemoryState(
4888 next_info, test_state, test_state, KMemoryPermission::None, KMemoryPermission::None,
4889 test_attr_mask | KMemoryAttribute::IpcLocked, KMemoryAttribute::IpcLocked));
4890
4891 // Check if we can consolidate the next block's permission set with the current one.
4892 const bool next_perm_eq =
4893 next_info.GetPermission() == next_info.GetOriginalPermission();
4894 const bool next_needs_set_perm = !next_perm_eq && next_info.GetIpcLockCount() == 1;
4895 if (cur_perm_eq == next_perm_eq && cur_needs_set_perm == next_needs_set_perm &&
4896 cur_info.GetOriginalPermission() == next_info.GetOriginalPermission()) {
4897 // We can consolidate the reprotection for the current and next block into a single
4898 // call.
4899 cur_size += next_info.GetSize();
4900 } else {
4901 // We have to operate on the current block.
4902 if ((cur_needs_set_perm || first) && !cur_perm_eq) {
4903 const KPageProperties properties = {
4904 cur_needs_set_perm ? cur_info.GetOriginalPermission()
4905 : cur_info.GetPermission(),
4906 false, false,
4907 first ? DisableMergeAttribute::EnableHeadAndBody
4908 : DisableMergeAttribute::None};
4909 R_TRY(this->Operate(updater.GetPageList(), cur_address, cur_size / PageSize, 0,
4910 false, properties, OperationType::ChangePermissions,
4911 false));
4912 }
4913
4914 // Mark that we mapped the block.
4915 mapped_size += cur_size;
4916
4917 // Advance.
4918 cur_address = next_info.GetAddress();
4919 cur_size = next_info.GetSize();
4920 first = false;
4921 }
4922
4923 // Advance.
4924 cur_info = next_info;
4925 cur_perm_eq = next_perm_eq;
4926 cur_needs_set_perm = next_needs_set_perm;
4927 ++next_it;
4928 }
4929
4930 // Process the last block.
4931 const auto lock_count =
4932 cur_info.GetIpcLockCount() +
4933 (next_it != m_memory_block_manager.end()
4934 ? (next_it->GetIpcDisableMergeCount() - next_it->GetIpcLockCount())
4935 : 0);
4936 if ((first || cur_needs_set_perm || (lock_count == 1)) && !cur_perm_eq) {
4937 const DisableMergeAttribute head_body_attr =
4938 first ? DisableMergeAttribute::EnableHeadAndBody : DisableMergeAttribute::None;
4939 const DisableMergeAttribute tail_attr =
4940 lock_count == 1 ? DisableMergeAttribute::EnableTail : DisableMergeAttribute::None;
4941 const KPageProperties properties = {
4942 cur_needs_set_perm ? cur_info.GetOriginalPermission() : cur_info.GetPermission(),
4943 false, false, static_cast<DisableMergeAttribute>(head_body_attr | tail_attr)};
4944 R_TRY(this->Operate(updater.GetPageList(), cur_address, cur_size / PageSize, 0, false,
4945 properties, OperationType::ChangePermissions, false));
4946 }
4947 }
4948
4949 // Create an update allocator.
4950 // NOTE: Guaranteed zero blocks needed here.
4951 Result allocator_result;
4952 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
4953 m_memory_block_slab_manager, 0);
4954 R_TRY(allocator_result);
4955
4956 // Unlock the pages.
4957 m_memory_block_manager.UpdateLock(std::addressof(allocator), mapping_start,
4958 mapping_size / PageSize, &KMemoryBlock::UnlockForIpc,
4959 KMemoryPermission::None);
4960
4961 R_SUCCEED();
4962}
4963
4964void KPageTableBase::CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list,
4965 KProcessAddress address, size_t size,
4966 KMemoryPermission prot_perm) {
4967 ASSERT(this->IsLockedByCurrentThread());
4968 ASSERT(Common::IsAligned(GetInteger(address), PageSize));
4969 ASSERT(Common::IsAligned(size, PageSize));
4970
4971 // Get the mapped extents.
4972 const KProcessAddress src_map_start = address;
4973 const KProcessAddress src_map_end = address + size;
4974 const KProcessAddress src_map_last = src_map_end - 1;
4975
4976 // This function is only invoked when there's something to do.
4977 ASSERT(src_map_end > src_map_start);
4978
4979 // Iterate over blocks, fixing permissions.
4980 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(address);
4981 while (true) {
4982 const KMemoryInfo info = it->GetMemoryInfo();
4983
4984 const auto cur_start = info.GetAddress() >= GetInteger(src_map_start)
4985 ? info.GetAddress()
4986 : GetInteger(src_map_start);
4987 const auto cur_end =
4988 src_map_last <= info.GetLastAddress() ? src_map_end : info.GetEndAddress();
4989
4990 // If we can, fix the protections on the block.
4991 if ((info.GetIpcLockCount() == 0 &&
4992 (info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm) ||
4993 (info.GetIpcLockCount() != 0 &&
4994 (info.GetOriginalPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm)) {
4995 // Check if we actually need to fix the protections on the block.
4996 if (cur_end == src_map_end || info.GetAddress() <= GetInteger(src_map_start) ||
4997 (info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm) {
4998 const bool start_nc = (info.GetAddress() == GetInteger(src_map_start))
4999 ? (False(info.GetDisableMergeAttribute() &
5000 (KMemoryBlockDisableMergeAttribute::Locked |
5001 KMemoryBlockDisableMergeAttribute::IpcLeft)))
5002 : info.GetAddress() <= GetInteger(src_map_start);
5003
5004 const DisableMergeAttribute head_body_attr =
5005 start_nc ? DisableMergeAttribute::EnableHeadAndBody
5006 : DisableMergeAttribute::None;
5007 DisableMergeAttribute tail_attr;
5008 if (cur_end == src_map_end && info.GetEndAddress() == src_map_end) {
5009 auto next_it = it;
5010 ++next_it;
5011
5012 const auto lock_count =
5013 info.GetIpcLockCount() +
5014 (next_it != m_memory_block_manager.end()
5015 ? (next_it->GetIpcDisableMergeCount() - next_it->GetIpcLockCount())
5016 : 0);
5017 tail_attr = lock_count == 0 ? DisableMergeAttribute::EnableTail
5018 : DisableMergeAttribute::None;
5019 } else {
5020 tail_attr = DisableMergeAttribute::None;
5021 }
5022
5023 const KPageProperties properties = {
5024 info.GetPermission(), false, false,
5025 static_cast<DisableMergeAttribute>(head_body_attr | tail_attr)};
5026 R_ASSERT(this->Operate(page_list, cur_start, (cur_end - cur_start) / PageSize, 0,
5027 false, properties, OperationType::ChangePermissions, true));
5028 }
5029 }
5030
5031 // If we're past the end of the region, we're done.
5032 if (src_map_last <= info.GetLastAddress()) {
5033 break;
5034 }
5035
5036 // Advance.
5037 ++it;
5038 ASSERT(it != m_memory_block_manager.end());
5039 }
5040}
5041
5042Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) {
5043 // Lock the physical memory lock.
5044 KScopedLightLock phys_lk(m_map_physical_memory_lock);
5045
5046 // Calculate the last address for convenience.
5047 const KProcessAddress last_address = address + size - 1;
5048
5049 // Define iteration variables.
5050 KProcessAddress cur_address;
5051 size_t mapped_size;
5052
5053 // The entire mapping process can be retried.
5054 while (true) {
5055 // Check if the memory is already mapped.
5056 {
5057 // Lock the table.
5058 KScopedLightLock lk(m_general_lock);
5059
5060 // Iterate over the memory.
5061 cur_address = address;
5062 mapped_size = 0;
5063
5064 auto it = m_memory_block_manager.FindIterator(cur_address);
5065 while (true) {
5066 // Check that the iterator is valid.
5067 ASSERT(it != m_memory_block_manager.end());
5068
5069 // Get the memory info.
5070 const KMemoryInfo info = it->GetMemoryInfo();
5071
5072 // Check if we're done.
5073 if (last_address <= info.GetLastAddress()) {
5074 if (info.GetState() != KMemoryState::Free) {
5075 mapped_size += (last_address + 1 - cur_address);
5076 }
5077 break;
5078 }
5079
5080 // Track the memory if it's mapped.
5081 if (info.GetState() != KMemoryState::Free) {
5082 mapped_size += KProcessAddress(info.GetEndAddress()) - cur_address;
5083 }
5084
5085 // Advance.
5086 cur_address = info.GetEndAddress();
5087 ++it;
5088 }
5089
5090 // If the size mapped is the size requested, we've nothing to do.
5091 R_SUCCEED_IF(size == mapped_size);
5092 }
5093
5094 // Allocate and map the memory.
5095 {
5096 // Reserve the memory from the process resource limit.
5097 KScopedResourceReservation memory_reservation(
5098 m_resource_limit, Svc::LimitableResource::PhysicalMemoryMax, size - mapped_size);
5099 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
5100
5101 // Allocate pages for the new memory.
5102 KPageGroup pg(m_kernel, m_block_info_manager);
5103 R_TRY(m_kernel.MemoryManager().AllocateForProcess(
5104 std::addressof(pg), (size - mapped_size) / PageSize, m_allocate_option,
5105 GetCurrentProcess(m_kernel).GetId(), m_heap_fill_value));
5106
5107 // If we fail in the next bit (or retry), we need to cleanup the pages.
5108 auto pg_guard = SCOPE_GUARD({
5109 pg.OpenFirst();
5110 pg.Close();
5111 });
5112
5113 // Map the memory.
5114 {
5115 // Lock the table.
5116 KScopedLightLock lk(m_general_lock);
5117
5118 size_t num_allocator_blocks = 0;
5119
5120 // Verify that nobody has mapped memory since we first checked.
5121 {
5122 // Iterate over the memory.
5123 size_t checked_mapped_size = 0;
5124 cur_address = address;
5125
5126 auto it = m_memory_block_manager.FindIterator(cur_address);
5127 while (true) {
5128 // Check that the iterator is valid.
5129 ASSERT(it != m_memory_block_manager.end());
5130
5131 // Get the memory info.
5132 const KMemoryInfo info = it->GetMemoryInfo();
5133
5134 const bool is_free = info.GetState() == KMemoryState::Free;
5135 if (is_free) {
5136 if (info.GetAddress() < GetInteger(address)) {
5137 ++num_allocator_blocks;
5138 }
5139 if (last_address < info.GetLastAddress()) {
5140 ++num_allocator_blocks;
5141 }
5142 }
5143
5144 // Check if we're done.
5145 if (last_address <= info.GetLastAddress()) {
5146 if (!is_free) {
5147 checked_mapped_size += (last_address + 1 - cur_address);
5148 }
5149 break;
5150 }
5151
5152 // Track the memory if it's mapped.
5153 if (!is_free) {
5154 checked_mapped_size +=
5155 KProcessAddress(info.GetEndAddress()) - cur_address;
5156 }
5157
5158 // Advance.
5159 cur_address = info.GetEndAddress();
5160 ++it;
5161 }
5162
5163 // If the size now isn't what it was before, somebody mapped or unmapped
5164 // concurrently. If this happened, retry.
5165 if (mapped_size != checked_mapped_size) {
5166 continue;
5167 }
5168 }
5169
5170 // Create an update allocator.
5171 ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks);
5172 Result allocator_result;
5173 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
5174 m_memory_block_slab_manager,
5175 num_allocator_blocks);
5176 R_TRY(allocator_result);
5177
5178 // We're going to perform an update, so create a helper.
5179 KScopedPageTableUpdater updater(this);
5180
5181 // Prepare to iterate over the memory.
5182 auto pg_it = pg.begin();
5183 KPhysicalAddress pg_phys_addr = pg_it->GetAddress();
5184 size_t pg_pages = pg_it->GetNumPages();
5185
5186 // Reset the current tracking address, and make sure we clean up on failure.
5187 pg_guard.Cancel();
5188 cur_address = address;
5189 ON_RESULT_FAILURE {
5190 if (cur_address > address) {
5191 const KProcessAddress last_unmap_address = cur_address - 1;
5192
5193 // Iterate, unmapping the pages.
5194 cur_address = address;
5195
5196 auto it = m_memory_block_manager.FindIterator(cur_address);
5197 while (true) {
5198 // Check that the iterator is valid.
5199 ASSERT(it != m_memory_block_manager.end());
5200
5201 // Get the memory info.
5202 const KMemoryInfo info = it->GetMemoryInfo();
5203
5204 // If the memory state is free, we mapped it and need to unmap it.
5205 if (info.GetState() == KMemoryState::Free) {
5206 // Determine the range to unmap.
5207 const KPageProperties unmap_properties = {
5208 KMemoryPermission::None, false, false,
5209 DisableMergeAttribute::None};
5210 const size_t cur_pages =
5211 std::min(KProcessAddress(info.GetEndAddress()) - cur_address,
5212 last_unmap_address + 1 - cur_address) /
5213 PageSize;
5214
5215 // Unmap.
5216 R_ASSERT(this->Operate(updater.GetPageList(), cur_address,
5217 cur_pages, 0, false, unmap_properties,
5218 OperationType::Unmap, true));
5219 }
5220
5221 // Check if we're done.
5222 if (last_unmap_address <= info.GetLastAddress()) {
5223 break;
5224 }
5225
5226 // Advance.
5227 cur_address = info.GetEndAddress();
5228 ++it;
5229 }
5230 }
5231
5232 // Release any remaining unmapped memory.
5233 m_kernel.MemoryManager().OpenFirst(pg_phys_addr, pg_pages);
5234 m_kernel.MemoryManager().Close(pg_phys_addr, pg_pages);
5235 for (++pg_it; pg_it != pg.end(); ++pg_it) {
5236 m_kernel.MemoryManager().OpenFirst(pg_it->GetAddress(),
5237 pg_it->GetNumPages());
5238 m_kernel.MemoryManager().Close(pg_it->GetAddress(), pg_it->GetNumPages());
5239 }
5240 };
5241
5242 auto it = m_memory_block_manager.FindIterator(cur_address);
5243 while (true) {
5244 // Check that the iterator is valid.
5245 ASSERT(it != m_memory_block_manager.end());
5246
5247 // Get the memory info.
5248 const KMemoryInfo info = it->GetMemoryInfo();
5249
5250 // If it's unmapped, we need to map it.
5251 if (info.GetState() == KMemoryState::Free) {
5252 // Determine the range to map.
5253 const KPageProperties map_properties = {
5254 KMemoryPermission::UserReadWrite, false, false,
5255 cur_address == this->GetAliasRegionStart()
5256 ? DisableMergeAttribute::DisableHead
5257 : DisableMergeAttribute::None};
5258 size_t map_pages =
5259 std::min(KProcessAddress(info.GetEndAddress()) - cur_address,
5260 last_address + 1 - cur_address) /
5261 PageSize;
5262
5263 // While we have pages to map, map them.
5264 {
5265 // Create a page group for the current mapping range.
5266 KPageGroup cur_pg(m_kernel, m_block_info_manager);
5267 {
5268 ON_RESULT_FAILURE_2 {
5269 cur_pg.OpenFirst();
5270 cur_pg.Close();
5271 };
5272
5273 size_t remain_pages = map_pages;
5274 while (remain_pages > 0) {
5275 // Check if we're at the end of the physical block.
5276 if (pg_pages == 0) {
5277 // Ensure there are more pages to map.
5278 ASSERT(pg_it != pg.end());
5279
5280 // Advance our physical block.
5281 ++pg_it;
5282 pg_phys_addr = pg_it->GetAddress();
5283 pg_pages = pg_it->GetNumPages();
5284 }
5285
5286 // Add whatever we can to the current block.
5287 const size_t cur_pages = std::min(pg_pages, remain_pages);
5288 R_TRY(cur_pg.AddBlock(pg_phys_addr +
5289 ((pg_pages - cur_pages) * PageSize),
5290 cur_pages));
5291
5292 // Advance.
5293 remain_pages -= cur_pages;
5294 pg_pages -= cur_pages;
5295 }
5296 }
5297
5298 // Map the papges.
5299 R_TRY(this->Operate(updater.GetPageList(), cur_address, map_pages,
5300 cur_pg, map_properties,
5301 OperationType::MapFirstGroup, false));
5302 }
5303 }
5304
5305 // Check if we're done.
5306 if (last_address <= info.GetLastAddress()) {
5307 break;
5308 }
5309
5310 // Advance.
5311 cur_address = info.GetEndAddress();
5312 ++it;
5313 }
5314
5315 // We succeeded, so commit the memory reservation.
5316 memory_reservation.Commit();
5317
5318 // Increase our tracked mapped size.
5319 m_mapped_physical_memory_size += (size - mapped_size);
5320
5321 // Update the relevant memory blocks.
5322 m_memory_block_manager.UpdateIfMatch(
5323 std::addressof(allocator), address, size / PageSize, KMemoryState::Free,
5324 KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal,
5325 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
5326 address == this->GetAliasRegionStart()
5327 ? KMemoryBlockDisableMergeAttribute::Normal
5328 : KMemoryBlockDisableMergeAttribute::None,
5329 KMemoryBlockDisableMergeAttribute::None);
5330
5331 R_SUCCEED();
5332 }
5333 }
5334 }
5335}
5336
5337Result KPageTableBase::UnmapPhysicalMemory(KProcessAddress address, size_t size) {
5338 // Lock the physical memory lock.
5339 KScopedLightLock phys_lk(m_map_physical_memory_lock);
5340
5341 // Lock the table.
5342 KScopedLightLock lk(m_general_lock);
5343
5344 // Calculate the last address for convenience.
5345 const KProcessAddress last_address = address + size - 1;
5346
5347 // Define iteration variables.
5348 KProcessAddress map_start_address = 0;
5349 KProcessAddress map_last_address = 0;
5350
5351 KProcessAddress cur_address;
5352 size_t mapped_size;
5353 size_t num_allocator_blocks = 0;
5354
5355 // Check if the memory is mapped.
5356 {
5357 // Iterate over the memory.
5358 cur_address = address;
5359 mapped_size = 0;
5360
5361 auto it = m_memory_block_manager.FindIterator(cur_address);
5362 while (true) {
5363 // Check that the iterator is valid.
5364 ASSERT(it != m_memory_block_manager.end());
5365
5366 // Get the memory info.
5367 const KMemoryInfo info = it->GetMemoryInfo();
5368
5369 // Verify the memory's state.
5370 const bool is_normal = info.GetState() == KMemoryState::Normal &&
5371 info.GetAttribute() == KMemoryAttribute::None;
5372 const bool is_free = info.GetState() == KMemoryState::Free;
5373 R_UNLESS(is_normal || is_free, ResultInvalidCurrentMemory);
5374
5375 if (is_normal) {
5376 R_UNLESS(info.GetAttribute() == KMemoryAttribute::None, ResultInvalidCurrentMemory);
5377
5378 if (map_start_address == 0) {
5379 map_start_address = cur_address;
5380 }
5381 map_last_address =
5382 (last_address >= info.GetLastAddress()) ? info.GetLastAddress() : last_address;
5383
5384 if (info.GetAddress() < GetInteger(address)) {
5385 ++num_allocator_blocks;
5386 }
5387 if (last_address < info.GetLastAddress()) {
5388 ++num_allocator_blocks;
5389 }
5390
5391 mapped_size += (map_last_address + 1 - cur_address);
5392 }
5393
5394 // Check if we're done.
5395 if (last_address <= info.GetLastAddress()) {
5396 break;
5397 }
5398
5399 // Advance.
5400 cur_address = info.GetEndAddress();
5401 ++it;
5402 }
5403
5404 // If there's nothing mapped, we've nothing to do.
5405 R_SUCCEED_IF(mapped_size == 0);
5406 }
5407
5408 // Create an update allocator.
5409 ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks);
5410 Result allocator_result;
5411 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
5412 m_memory_block_slab_manager, num_allocator_blocks);
5413 R_TRY(allocator_result);
5414
5415 // We're going to perform an update, so create a helper.
5416 KScopedPageTableUpdater updater(this);
5417
5418 // Separate the mapping.
5419 const KPageProperties sep_properties = {KMemoryPermission::None, false, false,
5420 DisableMergeAttribute::None};
5421 R_TRY(this->Operate(updater.GetPageList(), map_start_address,
5422 (map_last_address + 1 - map_start_address) / PageSize, 0, false,
5423 sep_properties, OperationType::Separate, false));
5424
5425 // Reset the current tracking address, and make sure we clean up on failure.
5426 cur_address = address;
5427
5428 // Iterate over the memory, unmapping as we go.
5429 auto it = m_memory_block_manager.FindIterator(cur_address);
5430
5431 const auto clear_merge_attr =
5432 (it->GetState() == KMemoryState::Normal &&
5433 it->GetAddress() == this->GetAliasRegionStart() && it->GetAddress() == address)
5434 ? KMemoryBlockDisableMergeAttribute::Normal
5435 : KMemoryBlockDisableMergeAttribute::None;
5436
5437 while (true) {
5438 // Check that the iterator is valid.
5439 ASSERT(it != m_memory_block_manager.end());
5440
5441 // Get the memory info.
5442 const KMemoryInfo info = it->GetMemoryInfo();
5443
5444 // If the memory state is normal, we need to unmap it.
5445 if (info.GetState() == KMemoryState::Normal) {
5446 // Determine the range to unmap.
5447 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
5448 DisableMergeAttribute::None};
5449 const size_t cur_pages = std::min(KProcessAddress(info.GetEndAddress()) - cur_address,
5450 last_address + 1 - cur_address) /
5451 PageSize;
5452
5453 // Unmap.
5454 R_ASSERT(this->Operate(updater.GetPageList(), cur_address, cur_pages, 0, false,
5455 unmap_properties, OperationType::Unmap, false));
5456 }
5457
5458 // Check if we're done.
5459 if (last_address <= info.GetLastAddress()) {
5460 break;
5461 }
5462
5463 // Advance.
5464 cur_address = info.GetEndAddress();
5465 ++it;
5466 }
5467
5468 // Release the memory resource.
5469 m_mapped_physical_memory_size -= mapped_size;
5470 m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, mapped_size);
5471
5472 // Update memory blocks.
5473 m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize,
5474 KMemoryState::Free, KMemoryPermission::None,
5475 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
5476 clear_merge_attr);
5477
5478 // We succeeded.
5479 R_SUCCEED();
5480}
5481
5482Result KPageTableBase::MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size) {
5483 UNIMPLEMENTED();
5484 R_THROW(ResultNotImplemented);
5485}
5486
5487Result KPageTableBase::UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size) {
5488 UNIMPLEMENTED();
5489 R_THROW(ResultNotImplemented);
5490}
5491
5492Result KPageTableBase::UnmapProcessMemory(KProcessAddress dst_address, size_t size,
5493 KPageTableBase& src_page_table,
5494 KProcessAddress src_address) {
5495 // We need to lock both this table, and the current process's table, so set up an alias.
5496 KPageTableBase& dst_page_table = *this;
5497
5498 // Acquire the table locks.
5499 KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock);
5500
5501 // Check that the memory is mapped in the destination process.
5502 size_t num_allocator_blocks;
5503 R_TRY(dst_page_table.CheckMemoryState(
5504 std::addressof(num_allocator_blocks), dst_address, size, KMemoryState::All,
5505 KMemoryState::SharedCode, KMemoryPermission::UserReadWrite,
5506 KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None));
5507
5508 // Check that the memory is mapped in the source process.
5509 R_TRY(src_page_table.CheckMemoryState(src_address, size, KMemoryState::FlagCanMapProcess,
5510 KMemoryState::FlagCanMapProcess, KMemoryPermission::None,
5511 KMemoryPermission::None, KMemoryAttribute::All,
5512 KMemoryAttribute::None));
5513
5514 // Validate that the memory ranges are compatible.
5515 {
5516 // Define a helper type.
5517 struct ContiguousRangeInfo {
5518 public:
5519 KPageTableBase& m_pt;
5520 TraversalContext m_context;
5521 TraversalEntry m_entry;
5522 KPhysicalAddress m_phys_addr;
5523 size_t m_cur_size;
5524 size_t m_remaining_size;
5525
5526 public:
5527 ContiguousRangeInfo(KPageTableBase& pt, KProcessAddress address, size_t size)
5528 : m_pt(pt), m_remaining_size(size) {
5529 // Begin a traversal.
5530 ASSERT(m_pt.GetImpl().BeginTraversal(std::addressof(m_entry),
5531 std::addressof(m_context), address));
5532
5533 // Setup tracking fields.
5534 m_phys_addr = m_entry.phys_addr;
5535 m_cur_size = std::min<size_t>(
5536 m_remaining_size,
5537 m_entry.block_size - (GetInteger(m_phys_addr) & (m_entry.block_size - 1)));
5538
5539 // Consume the whole contiguous block.
5540 this->DetermineContiguousBlockExtents();
5541 }
5542
5543 void ContinueTraversal() {
5544 // Update our remaining size.
5545 m_remaining_size = m_remaining_size - m_cur_size;
5546
5547 // Update our tracking fields.
5548 if (m_remaining_size > 0) {
5549 m_phys_addr = m_entry.phys_addr;
5550 m_cur_size = std::min<size_t>(m_remaining_size, m_entry.block_size);
5551
5552 // Consume the whole contiguous block.
5553 this->DetermineContiguousBlockExtents();
5554 }
5555 }
5556
5557 private:
5558 void DetermineContiguousBlockExtents() {
5559 // Continue traversing until we're not contiguous, or we have enough.
5560 while (m_cur_size < m_remaining_size) {
5561 ASSERT(m_pt.GetImpl().ContinueTraversal(std::addressof(m_entry),
5562 std::addressof(m_context)));
5563
5564 // If we're not contiguous, we're done.
5565 if (m_entry.phys_addr != m_phys_addr + m_cur_size) {
5566 break;
5567 }
5568
5569 // Update our current size.
5570 m_cur_size = std::min(m_remaining_size, m_cur_size + m_entry.block_size);
5571 }
5572 }
5573 };
5574
5575 // Create ranges for both tables.
5576 ContiguousRangeInfo src_range(src_page_table, src_address, size);
5577 ContiguousRangeInfo dst_range(dst_page_table, dst_address, size);
5578
5579 // Validate the ranges.
5580 while (src_range.m_remaining_size > 0 && dst_range.m_remaining_size > 0) {
5581 R_UNLESS(src_range.m_phys_addr == dst_range.m_phys_addr, ResultInvalidMemoryRegion);
5582 R_UNLESS(src_range.m_cur_size == dst_range.m_cur_size, ResultInvalidMemoryRegion);
5583
5584 src_range.ContinueTraversal();
5585 dst_range.ContinueTraversal();
5586 }
5587 }
5588
5589 // We no longer need to hold our lock on the source page table.
5590 lk.TryUnlockHalf(src_page_table.m_general_lock);
5591
5592 // Create an update allocator.
5593 Result allocator_result;
5594 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
5595 m_memory_block_slab_manager, num_allocator_blocks);
5596 R_TRY(allocator_result);
5597
5598 // We're going to perform an update, so create a helper.
5599 KScopedPageTableUpdater updater(this);
5600
5601 // Unmap the memory.
5602 const size_t num_pages = size / PageSize;
5603 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
5604 DisableMergeAttribute::None};
5605 R_TRY(this->Operate(updater.GetPageList(), dst_address, num_pages, 0, false, unmap_properties,
5606 OperationType::Unmap, false));
5607
5608 // Apply the memory block update.
5609 m_memory_block_manager.Update(std::addressof(allocator), dst_address, num_pages,
5610 KMemoryState::Free, KMemoryPermission::None,
5611 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
5612 KMemoryBlockDisableMergeAttribute::Normal);
5613
5614 R_SUCCEED();
5615}
5616
5617Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_addr,
5618 size_t num_pages, KPhysicalAddress phys_addr, bool is_pa_valid,
5619 const KPageProperties properties, OperationType operation,
5620 bool reuse_ll) {
5621 ASSERT(this->IsLockedByCurrentThread());
5622 ASSERT(num_pages > 0);
5623 ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize));
5624 ASSERT(this->ContainsPages(virt_addr, num_pages));
5625
5626 // As we don't allocate page entries in guest memory, we don't need to allocate them from
5627 // or free them to the page list, and so it goes unused (along with page properties).
5628
5629 switch (operation) {
5630 case OperationType::Unmap: {
5631 // Ensure that any pages we track are closed on exit.
5632 KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager());
5633 SCOPE_EXIT({ pages_to_close.CloseAndReset(); });
5634
5635 // Make a page group representing the region to unmap.
5636 this->MakePageGroup(pages_to_close, virt_addr, num_pages);
5637
5638 // Unmap.
5639 m_memory->UnmapRegion(*m_impl, virt_addr, num_pages * PageSize);
5640
5641 R_SUCCEED();
5642 }
5643 case OperationType::Map: {
5644 ASSERT(virt_addr != 0);
5645 ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize));
5646 m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr);
5647
5648 // Open references to pages, if we should.
5649 if (this->IsHeapPhysicalAddress(phys_addr)) {
5650 m_kernel.MemoryManager().Open(phys_addr, num_pages);
5651 }
5652
5653 R_SUCCEED();
5654 }
5655 case OperationType::Separate: {
5656 // TODO: Unimplemented.
5657 R_SUCCEED();
5658 }
5659 case OperationType::ChangePermissions:
5660 case OperationType::ChangePermissionsAndRefresh:
5661 case OperationType::ChangePermissionsAndRefreshAndFlush:
5662 R_SUCCEED();
5663 default:
5664 UNREACHABLE();
5665 }
5666}
5667
5668Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_addr,
5669 size_t num_pages, const KPageGroup& page_group,
5670 const KPageProperties properties, OperationType operation,
5671 bool reuse_ll) {
5672 ASSERT(this->IsLockedByCurrentThread());
5673 ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize));
5674 ASSERT(num_pages > 0);
5675 ASSERT(num_pages == page_group.GetNumPages());
5676
5677 // As we don't allocate page entries in guest memory, we don't need to allocate them from
5678 // the page list, and so it goes unused (along with page properties).
5679
5680 switch (operation) {
5681 case OperationType::MapGroup:
5682 case OperationType::MapFirstGroup: {
5683 // We want to maintain a new reference to every page in the group.
5684 KScopedPageGroup spg(page_group, operation != OperationType::MapFirstGroup);
5685
5686 for (const auto& node : page_group) {
5687 const size_t size{node.GetNumPages() * PageSize};
5688
5689 // Map the pages.
5690 m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress());
5691
5692 virt_addr += size;
5693 }
5694
5695 // We succeeded! We want to persist the reference to the pages.
5696 spg.CancelClose();
5697
5698 R_SUCCEED();
5699 }
5700 default:
5701 UNREACHABLE();
5702 }
5703}
5704
5705void KPageTableBase::FinalizeUpdate(PageLinkedList* page_list) {
5706 while (page_list->Peek()) {
5707 [[maybe_unused]] auto page = page_list->Pop();
5708
5709 // TODO: Free page entries once they are allocated in guest memory.
5710 // ASSERT(this->GetPageTableManager().IsInPageTableHeap(page));
5711 // ASSERT(this->GetPageTableManager().GetRefCount(page) == 0);
5712 // this->GetPageTableManager().Free(page);
5713 }
5714}
5715
5716} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table_base.h b/src/core/hle/kernel/k_page_table_base.h
new file mode 100644
index 000000000..ee2c41e67
--- /dev/null
+++ b/src/core/hle/kernel/k_page_table_base.h
@@ -0,0 +1,759 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7
8#include "common/common_funcs.h"
9#include "common/page_table.h"
10#include "core/core.h"
11#include "core/hle/kernel/k_dynamic_resource_manager.h"
12#include "core/hle/kernel/k_light_lock.h"
13#include "core/hle/kernel/k_memory_block.h"
14#include "core/hle/kernel/k_memory_block_manager.h"
15#include "core/hle/kernel/k_memory_layout.h"
16#include "core/hle/kernel/k_memory_manager.h"
17#include "core/hle/kernel/k_typed_address.h"
18#include "core/hle/kernel/kernel.h"
19#include "core/hle/result.h"
20#include "core/memory.h"
21
22namespace Kernel {
23
24enum class DisableMergeAttribute : u8 {
25 None = (0U << 0),
26
27 DisableHead = (1U << 0),
28 DisableHeadAndBody = (1U << 1),
29 EnableHeadAndBody = (1U << 2),
30 DisableTail = (1U << 3),
31 EnableTail = (1U << 4),
32 EnableAndMergeHeadBodyTail = (1U << 5),
33
34 EnableHeadBodyTail = EnableHeadAndBody | EnableTail,
35 DisableHeadBodyTail = DisableHeadAndBody | DisableTail,
36};
37DECLARE_ENUM_FLAG_OPERATORS(DisableMergeAttribute);
38
39struct KPageProperties {
40 KMemoryPermission perm;
41 bool io;
42 bool uncached;
43 DisableMergeAttribute disable_merge_attributes;
44};
45static_assert(std::is_trivial_v<KPageProperties>);
46static_assert(sizeof(KPageProperties) == sizeof(u32));
47
48class KResourceLimit;
49class KSystemResource;
50
51class KPageTableBase {
52 YUZU_NON_COPYABLE(KPageTableBase);
53 YUZU_NON_MOVEABLE(KPageTableBase);
54
55public:
56 using TraversalEntry = Common::PageTable::TraversalEntry;
57 using TraversalContext = Common::PageTable::TraversalContext;
58
59 class MemoryRange {
60 private:
61 KernelCore& m_kernel;
62 KPhysicalAddress m_address;
63 size_t m_size;
64 bool m_heap;
65
66 public:
67 explicit MemoryRange(KernelCore& kernel)
68 : m_kernel(kernel), m_address(0), m_size(0), m_heap(false) {}
69
70 void Set(KPhysicalAddress address, size_t size, bool heap) {
71 m_address = address;
72 m_size = size;
73 m_heap = heap;
74 }
75
76 KPhysicalAddress GetAddress() const {
77 return m_address;
78 }
79 size_t GetSize() const {
80 return m_size;
81 }
82 bool IsHeap() const {
83 return m_heap;
84 }
85
86 void Open();
87 void Close();
88 };
89
90protected:
91 enum MemoryFillValue : u8 {
92 MemoryFillValue_Zero = 0,
93 MemoryFillValue_Stack = 'X',
94 MemoryFillValue_Ipc = 'Y',
95 MemoryFillValue_Heap = 'Z',
96 };
97
98 enum class OperationType {
99 Map = 0,
100 MapGroup = 1,
101 MapFirstGroup = 2,
102 Unmap = 3,
103 ChangePermissions = 4,
104 ChangePermissionsAndRefresh = 5,
105 ChangePermissionsAndRefreshAndFlush = 6,
106 Separate = 7,
107 };
108
109 static constexpr size_t MaxPhysicalMapAlignment = 1_GiB;
110 static constexpr size_t RegionAlignment = 2_MiB;
111 static_assert(RegionAlignment == KernelAslrAlignment);
112
113 struct PageLinkedList {
114 private:
115 struct Node {
116 Node* m_next;
117 std::array<u8, PageSize - sizeof(Node*)> m_buffer;
118 };
119 static_assert(std::is_trivial_v<Node>);
120
121 private:
122 Node* m_root{};
123
124 public:
125 constexpr PageLinkedList() : m_root(nullptr) {}
126
127 void Push(Node* n) {
128 ASSERT(Common::IsAligned(reinterpret_cast<uintptr_t>(n), PageSize));
129 n->m_next = m_root;
130 m_root = n;
131 }
132
133 Node* Peek() const {
134 return m_root;
135 }
136
137 Node* Pop() {
138 Node* const r = m_root;
139
140 m_root = r->m_next;
141 r->m_next = nullptr;
142
143 return r;
144 }
145 };
146 static_assert(std::is_trivially_destructible_v<PageLinkedList>);
147
148 static constexpr auto DefaultMemoryIgnoreAttr =
149 KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared;
150
151 static constexpr size_t GetAddressSpaceWidth(Svc::CreateProcessFlag as_type) {
152 switch (static_cast<Svc::CreateProcessFlag>(as_type &
153 Svc::CreateProcessFlag::AddressSpaceMask)) {
154 case Svc::CreateProcessFlag::AddressSpace64Bit:
155 return 39;
156 case Svc::CreateProcessFlag::AddressSpace64BitDeprecated:
157 return 36;
158 case Svc::CreateProcessFlag::AddressSpace32Bit:
159 case Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias:
160 return 32;
161 default:
162 UNREACHABLE();
163 }
164 }
165
166private:
167 class KScopedPageTableUpdater {
168 private:
169 KPageTableBase* m_pt;
170 PageLinkedList m_ll;
171
172 public:
173 explicit KScopedPageTableUpdater(KPageTableBase* pt) : m_pt(pt), m_ll() {}
174 explicit KScopedPageTableUpdater(KPageTableBase& pt)
175 : KScopedPageTableUpdater(std::addressof(pt)) {}
176 ~KScopedPageTableUpdater() {
177 m_pt->FinalizeUpdate(this->GetPageList());
178 }
179
180 PageLinkedList* GetPageList() {
181 return std::addressof(m_ll);
182 }
183 };
184
185private:
186 KernelCore& m_kernel;
187 Core::System& m_system;
188 KProcessAddress m_address_space_start{};
189 KProcessAddress m_address_space_end{};
190 KProcessAddress m_heap_region_start{};
191 KProcessAddress m_heap_region_end{};
192 KProcessAddress m_current_heap_end{};
193 KProcessAddress m_alias_region_start{};
194 KProcessAddress m_alias_region_end{};
195 KProcessAddress m_stack_region_start{};
196 KProcessAddress m_stack_region_end{};
197 KProcessAddress m_kernel_map_region_start{};
198 KProcessAddress m_kernel_map_region_end{};
199 KProcessAddress m_alias_code_region_start{};
200 KProcessAddress m_alias_code_region_end{};
201 KProcessAddress m_code_region_start{};
202 KProcessAddress m_code_region_end{};
203 size_t m_max_heap_size{};
204 size_t m_mapped_physical_memory_size{};
205 size_t m_mapped_unsafe_physical_memory{};
206 size_t m_mapped_insecure_memory{};
207 size_t m_mapped_ipc_server_memory{};
208 mutable KLightLock m_general_lock;
209 mutable KLightLock m_map_physical_memory_lock;
210 KLightLock m_device_map_lock;
211 std::unique_ptr<Common::PageTable> m_impl{};
212 Core::Memory::Memory* m_memory{};
213 KMemoryBlockManager m_memory_block_manager{};
214 u32 m_allocate_option{};
215 u32 m_address_space_width{};
216 bool m_is_kernel{};
217 bool m_enable_aslr{};
218 bool m_enable_device_address_space_merge{};
219 KMemoryBlockSlabManager* m_memory_block_slab_manager{};
220 KBlockInfoManager* m_block_info_manager{};
221 KResourceLimit* m_resource_limit{};
222 const KMemoryRegion* m_cached_physical_linear_region{};
223 const KMemoryRegion* m_cached_physical_heap_region{};
224 MemoryFillValue m_heap_fill_value{};
225 MemoryFillValue m_ipc_fill_value{};
226 MemoryFillValue m_stack_fill_value{};
227
228public:
229 explicit KPageTableBase(KernelCore& kernel);
230 ~KPageTableBase();
231
232 Result InitializeForKernel(bool is_64_bit, KVirtualAddress start, KVirtualAddress end,
233 Core::Memory::Memory& memory);
234 Result InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr,
235 bool enable_device_address_space_merge, bool from_back,
236 KMemoryManager::Pool pool, KProcessAddress code_address,
237 size_t code_size, KSystemResource* system_resource,
238 KResourceLimit* resource_limit, Core::Memory::Memory& memory);
239
240 void Finalize();
241
242 bool IsKernel() const {
243 return m_is_kernel;
244 }
245 bool IsAslrEnabled() const {
246 return m_enable_aslr;
247 }
248
249 bool Contains(KProcessAddress addr) const {
250 return m_address_space_start <= addr && addr <= m_address_space_end - 1;
251 }
252
253 bool Contains(KProcessAddress addr, size_t size) const {
254 return m_address_space_start <= addr && addr < addr + size &&
255 addr + size - 1 <= m_address_space_end - 1;
256 }
257
258 bool IsInAliasRegion(KProcessAddress addr, size_t size) const {
259 return this->Contains(addr, size) && m_alias_region_start <= addr &&
260 addr + size - 1 <= m_alias_region_end - 1;
261 }
262
263 bool IsInHeapRegion(KProcessAddress addr, size_t size) const {
264 return this->Contains(addr, size) && m_heap_region_start <= addr &&
265 addr + size - 1 <= m_heap_region_end - 1;
266 }
267
268 bool IsInUnsafeAliasRegion(KProcessAddress addr, size_t size) const {
269 // Even though Unsafe physical memory is KMemoryState_Normal, it must be mapped inside the
270 // alias code region.
271 return this->CanContain(addr, size, Svc::MemoryState::AliasCode);
272 }
273
274 KScopedLightLock AcquireDeviceMapLock() {
275 return KScopedLightLock(m_device_map_lock);
276 }
277
278 KProcessAddress GetRegionAddress(Svc::MemoryState state) const;
279 size_t GetRegionSize(Svc::MemoryState state) const;
280 bool CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const;
281
282 KProcessAddress GetRegionAddress(KMemoryState state) const {
283 return this->GetRegionAddress(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
284 }
285 size_t GetRegionSize(KMemoryState state) const {
286 return this->GetRegionSize(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
287 }
288 bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const {
289 return this->CanContain(addr, size,
290 static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
291 }
292
293public:
294 Core::Memory::Memory& GetMemory() {
295 return *m_memory;
296 }
297
298 Core::Memory::Memory& GetMemory() const {
299 return *m_memory;
300 }
301
302 Common::PageTable& GetImpl() {
303 return *m_impl;
304 }
305
306 Common::PageTable& GetImpl() const {
307 return *m_impl;
308 }
309
310 size_t GetNumGuardPages() const {
311 return this->IsKernel() ? 1 : 4;
312 }
313
314protected:
315 // NOTE: These three functions (Operate, Operate, FinalizeUpdate) are virtual functions
316 // in Nintendo's kernel. We devirtualize them, since KPageTable is the only derived
317 // class, and this avoids unnecessary virtual function calls.
318 Result Operate(PageLinkedList* page_list, KProcessAddress virt_addr, size_t num_pages,
319 KPhysicalAddress phys_addr, bool is_pa_valid, const KPageProperties properties,
320 OperationType operation, bool reuse_ll);
321 Result Operate(PageLinkedList* page_list, KProcessAddress virt_addr, size_t num_pages,
322 const KPageGroup& page_group, const KPageProperties properties,
323 OperationType operation, bool reuse_ll);
324 void FinalizeUpdate(PageLinkedList* page_list);
325
326 bool IsLockedByCurrentThread() const {
327 return m_general_lock.IsLockedByCurrentThread();
328 }
329
330 bool IsLinearMappedPhysicalAddress(KPhysicalAddress phys_addr) {
331 ASSERT(this->IsLockedByCurrentThread());
332
333 return m_kernel.MemoryLayout().IsLinearMappedPhysicalAddress(
334 m_cached_physical_linear_region, phys_addr);
335 }
336
337 bool IsLinearMappedPhysicalAddress(KPhysicalAddress phys_addr, size_t size) {
338 ASSERT(this->IsLockedByCurrentThread());
339
340 return m_kernel.MemoryLayout().IsLinearMappedPhysicalAddress(
341 m_cached_physical_linear_region, phys_addr, size);
342 }
343
344 bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr) {
345 ASSERT(this->IsLockedByCurrentThread());
346
347 return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region,
348 phys_addr);
349 }
350
351 bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr, size_t size) {
352 ASSERT(this->IsLockedByCurrentThread());
353
354 return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region,
355 phys_addr, size);
356 }
357
358 bool IsHeapPhysicalAddressForFinalize(KPhysicalAddress phys_addr) {
359 ASSERT(!this->IsLockedByCurrentThread());
360
361 return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region,
362 phys_addr);
363 }
364
365 bool ContainsPages(KProcessAddress addr, size_t num_pages) const {
366 return (m_address_space_start <= addr) &&
367 (num_pages <= (m_address_space_end - m_address_space_start) / PageSize) &&
368 (addr + num_pages * PageSize - 1 <= m_address_space_end - 1);
369 }
370
371private:
372 KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages,
373 size_t num_pages, size_t alignment, size_t offset,
374 size_t guard_pages) const;
375
376 Result CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
377 KMemoryState state_mask, KMemoryState state,
378 KMemoryPermission perm_mask, KMemoryPermission perm,
379 KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
380 Result CheckMemoryStateContiguous(KProcessAddress addr, size_t size, KMemoryState state_mask,
381 KMemoryState state, KMemoryPermission perm_mask,
382 KMemoryPermission perm, KMemoryAttribute attr_mask,
383 KMemoryAttribute attr) const {
384 R_RETURN(this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask,
385 perm, attr_mask, attr));
386 }
387
388 Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state,
389 KMemoryPermission perm_mask, KMemoryPermission perm,
390 KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
391 Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
392 KMemoryAttribute* out_attr, size_t* out_blocks_needed,
393 KMemoryBlockManager::const_iterator it, KProcessAddress last_addr,
394 KMemoryState state_mask, KMemoryState state,
395 KMemoryPermission perm_mask, KMemoryPermission perm,
396 KMemoryAttribute attr_mask, KMemoryAttribute attr,
397 KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
398 Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
399 KMemoryAttribute* out_attr, size_t* out_blocks_needed,
400 KProcessAddress addr, size_t size, KMemoryState state_mask,
401 KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
402 KMemoryAttribute attr_mask, KMemoryAttribute attr,
403 KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
404 Result CheckMemoryState(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
405 KMemoryState state_mask, KMemoryState state,
406 KMemoryPermission perm_mask, KMemoryPermission perm,
407 KMemoryAttribute attr_mask, KMemoryAttribute attr,
408 KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
409 R_RETURN(this->CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
410 state_mask, state, perm_mask, perm, attr_mask, attr,
411 ignore_attr));
412 }
413 Result CheckMemoryState(KProcessAddress addr, size_t size, KMemoryState state_mask,
414 KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
415 KMemoryAttribute attr_mask, KMemoryAttribute attr,
416 KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
417 R_RETURN(this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm,
418 attr_mask, attr, ignore_attr));
419 }
420
421 Result LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_paddr, KProcessAddress addr,
422 size_t size, KMemoryState state_mask, KMemoryState state,
423 KMemoryPermission perm_mask, KMemoryPermission perm,
424 KMemoryAttribute attr_mask, KMemoryAttribute attr,
425 KMemoryPermission new_perm, KMemoryAttribute lock_attr);
426 Result UnlockMemory(KProcessAddress addr, size_t size, KMemoryState state_mask,
427 KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
428 KMemoryAttribute attr_mask, KMemoryAttribute attr,
429 KMemoryPermission new_perm, KMemoryAttribute lock_attr,
430 const KPageGroup* pg);
431
432 Result QueryInfoImpl(KMemoryInfo* out_info, Svc::PageInfo* out_page,
433 KProcessAddress address) const;
434
435 Result QueryMappingImpl(KProcessAddress* out, KPhysicalAddress address, size_t size,
436 Svc::MemoryState state) const;
437
438 Result AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
439 size_t num_pages, KMemoryPermission perm);
440 Result MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
441 const KPageGroup& pg, const KPageProperties properties, bool reuse_ll);
442
443 void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
444 const KPageGroup& pg);
445
446 Result MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_t num_pages);
447 bool IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr, size_t num_pages);
448
449 Result GetContiguousMemoryRangeWithState(MemoryRange* out, KProcessAddress address, size_t size,
450 KMemoryState state_mask, KMemoryState state,
451 KMemoryPermission perm_mask, KMemoryPermission perm,
452 KMemoryAttribute attr_mask, KMemoryAttribute attr);
453
454 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
455 KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start,
456 size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
457
458 Result MapIoImpl(KProcessAddress* out, PageLinkedList* page_list, KPhysicalAddress phys_addr,
459 size_t size, KMemoryState state, KMemoryPermission perm);
460 Result ReadIoMemoryImpl(KProcessAddress dst_addr, KPhysicalAddress phys_addr, size_t size,
461 KMemoryState state);
462 Result WriteIoMemoryImpl(KPhysicalAddress phys_addr, KProcessAddress src_addr, size_t size,
463 KMemoryState state);
464
465 Result SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed,
466 KProcessAddress address, size_t size, KMemoryPermission test_perm,
467 KMemoryState dst_state);
468 Result SetupForIpcServer(KProcessAddress* out_addr, size_t size, KProcessAddress src_addr,
469 KMemoryPermission test_perm, KMemoryState dst_state,
470 KPageTableBase& src_page_table, bool send);
471 void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, KProcessAddress address,
472 size_t size, KMemoryPermission prot_perm);
473
474 size_t GetSize(KMemoryState state) const;
475
476 bool GetPhysicalAddressLocked(KPhysicalAddress* out, KProcessAddress virt_addr) const {
477 // Validate pre-conditions.
478 ASSERT(this->IsLockedByCurrentThread());
479
480 return this->GetImpl().GetPhysicalAddress(out, virt_addr);
481 }
482
483public:
484 bool GetPhysicalAddress(KPhysicalAddress* out, KProcessAddress virt_addr) const {
485 // Validate pre-conditions.
486 ASSERT(!this->IsLockedByCurrentThread());
487
488 // Acquire exclusive access to the table while doing address translation.
489 KScopedLightLock lk(m_general_lock);
490
491 return this->GetPhysicalAddressLocked(out, virt_addr);
492 }
493
494 KBlockInfoManager* GetBlockInfoManager() const {
495 return m_block_info_manager;
496 }
497
498 Result SetMemoryPermission(KProcessAddress addr, size_t size, Svc::MemoryPermission perm);
499 Result SetProcessMemoryPermission(KProcessAddress addr, size_t size,
500 Svc::MemoryPermission perm);
501 Result SetMemoryAttribute(KProcessAddress addr, size_t size, KMemoryAttribute mask,
502 KMemoryAttribute attr);
503 Result SetHeapSize(KProcessAddress* out, size_t size);
504 Result SetMaxHeapSize(size_t size);
505 Result QueryInfo(KMemoryInfo* out_info, Svc::PageInfo* out_page_info,
506 KProcessAddress addr) const;
507 Result QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out, KProcessAddress address) const;
508 Result QueryStaticMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) const {
509 R_RETURN(this->QueryMappingImpl(out, address, size, Svc::MemoryState::Static));
510 }
511 Result QueryIoMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) const {
512 R_RETURN(this->QueryMappingImpl(out, address, size, Svc::MemoryState::Io));
513 }
514 Result MapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
515 Result UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
516 Result MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
517 Result UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
518 Result MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm);
519 Result MapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size,
520 Svc::MemoryMapping mapping, Svc::MemoryPermission perm);
521 Result UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size,
522 Svc::MemoryMapping mapping);
523 Result MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm);
524 Result MapRegion(KMemoryRegionType region_type, KMemoryPermission perm);
525 Result MapInsecureMemory(KProcessAddress address, size_t size);
526 Result UnmapInsecureMemory(KProcessAddress address, size_t size);
527
528 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
529 KPhysicalAddress phys_addr, KProcessAddress region_start,
530 size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
531 R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, region_start,
532 region_num_pages, state, perm));
533 }
534
535 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
536 KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) {
537 R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
538 this->GetRegionAddress(state),
539 this->GetRegionSize(state) / PageSize, state, perm));
540 }
541
542 Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state,
543 KMemoryPermission perm) {
544 R_RETURN(this->MapPages(out_addr, num_pages, PageSize, 0, false,
545 this->GetRegionAddress(state),
546 this->GetRegionSize(state) / PageSize, state, perm));
547 }
548
549 Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
550 KMemoryPermission perm);
551 Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state);
552
553 Result MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
554 KProcessAddress region_start, size_t region_num_pages, KMemoryState state,
555 KMemoryPermission perm);
556 Result MapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state,
557 KMemoryPermission perm);
558 Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state);
559
560 Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages,
561 KMemoryState state_mask, KMemoryState state,
562 KMemoryPermission perm_mask, KMemoryPermission perm,
563 KMemoryAttribute attr_mask, KMemoryAttribute attr);
564
565 Result InvalidateProcessDataCache(KProcessAddress address, size_t size);
566 Result InvalidateCurrentProcessDataCache(KProcessAddress address, size_t size);
567
568 Result ReadDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
569 Result ReadDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size,
570 KMemoryState state);
571
572 Result WriteDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
573 Result WriteDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size,
574 KMemoryState state);
575
576 Result LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address, size_t size,
577 KMemoryPermission perm, bool is_aligned, bool check_heap);
578 Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size, bool check_heap);
579
580 Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size);
581 Result UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size);
582
583 Result OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange* out,
584 KProcessAddress address, size_t size,
585 KMemoryPermission perm, bool is_aligned);
586 Result OpenMemoryRangeForUnmapDeviceAddressSpace(MemoryRange* out, KProcessAddress address,
587 size_t size);
588
589 Result LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address, size_t size);
590 Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size);
591
592 Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size,
593 KMemoryPermission perm);
594 Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg);
595 Result LockForCodeMemory(KPageGroup* out, KProcessAddress address, size_t size);
596 Result UnlockForCodeMemory(KProcessAddress address, size_t size, const KPageGroup& pg);
597
598 Result OpenMemoryRangeForProcessCacheOperation(MemoryRange* out, KProcessAddress address,
599 size_t size);
600
601 Result CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size,
602 KProcessAddress src_addr, KMemoryState src_state_mask,
603 KMemoryState src_state, KMemoryPermission src_test_perm,
604 KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr);
605 Result CopyMemoryFromLinearToKernel(void* buffer, size_t size, KProcessAddress src_addr,
606 KMemoryState src_state_mask, KMemoryState src_state,
607 KMemoryPermission src_test_perm,
608 KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr);
609 Result CopyMemoryFromUserToLinear(KProcessAddress dst_addr, size_t size,
610 KMemoryState dst_state_mask, KMemoryState dst_state,
611 KMemoryPermission dst_test_perm,
612 KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
613 KProcessAddress src_addr);
614 Result CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, size_t size,
615 KMemoryState dst_state_mask, KMemoryState dst_state,
616 KMemoryPermission dst_test_perm,
617 KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
618 void* buffer);
619 Result CopyMemoryFromHeapToHeap(KPageTableBase& dst_page_table, KProcessAddress dst_addr,
620 size_t size, KMemoryState dst_state_mask,
621 KMemoryState dst_state, KMemoryPermission dst_test_perm,
622 KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
623 KProcessAddress src_addr, KMemoryState src_state_mask,
624 KMemoryState src_state, KMemoryPermission src_test_perm,
625 KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr);
626 Result CopyMemoryFromHeapToHeapWithoutCheckDestination(
627 KPageTableBase& dst_page_table, KProcessAddress dst_addr, size_t size,
628 KMemoryState dst_state_mask, KMemoryState dst_state, KMemoryPermission dst_test_perm,
629 KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, KProcessAddress src_addr,
630 KMemoryState src_state_mask, KMemoryState src_state, KMemoryPermission src_test_perm,
631 KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr);
632
633 Result SetupForIpc(KProcessAddress* out_dst_addr, size_t size, KProcessAddress src_addr,
634 KPageTableBase& src_page_table, KMemoryPermission test_perm,
635 KMemoryState dst_state, bool send);
636 Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state);
637 Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state);
638
639 Result MapPhysicalMemory(KProcessAddress address, size_t size);
640 Result UnmapPhysicalMemory(KProcessAddress address, size_t size);
641
642 Result MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size);
643 Result UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size);
644
645 Result UnmapProcessMemory(KProcessAddress dst_address, size_t size, KPageTableBase& src_pt,
646 KProcessAddress src_address);
647
648public:
649 KProcessAddress GetAddressSpaceStart() const {
650 return m_address_space_start;
651 }
652 KProcessAddress GetHeapRegionStart() const {
653 return m_heap_region_start;
654 }
655 KProcessAddress GetAliasRegionStart() const {
656 return m_alias_region_start;
657 }
658 KProcessAddress GetStackRegionStart() const {
659 return m_stack_region_start;
660 }
661 KProcessAddress GetKernelMapRegionStart() const {
662 return m_kernel_map_region_start;
663 }
664 KProcessAddress GetCodeRegionStart() const {
665 return m_code_region_start;
666 }
667 KProcessAddress GetAliasCodeRegionStart() const {
668 return m_alias_code_region_start;
669 }
670
671 size_t GetAddressSpaceSize() const {
672 return m_address_space_end - m_address_space_start;
673 }
674 size_t GetHeapRegionSize() const {
675 return m_heap_region_end - m_heap_region_start;
676 }
677 size_t GetAliasRegionSize() const {
678 return m_alias_region_end - m_alias_region_start;
679 }
680 size_t GetStackRegionSize() const {
681 return m_stack_region_end - m_stack_region_start;
682 }
683 size_t GetKernelMapRegionSize() const {
684 return m_kernel_map_region_end - m_kernel_map_region_start;
685 }
686 size_t GetCodeRegionSize() const {
687 return m_code_region_end - m_code_region_start;
688 }
689 size_t GetAliasCodeRegionSize() const {
690 return m_alias_code_region_end - m_alias_code_region_start;
691 }
692
693 size_t GetNormalMemorySize() const {
694 // Lock the table.
695 KScopedLightLock lk(m_general_lock);
696
697 return (m_current_heap_end - m_heap_region_start) + m_mapped_physical_memory_size;
698 }
699
700 size_t GetCodeSize() const;
701 size_t GetCodeDataSize() const;
702 size_t GetAliasCodeSize() const;
703 size_t GetAliasCodeDataSize() const;
704
705 u32 GetAllocateOption() const {
706 return m_allocate_option;
707 }
708
709 u32 GetAddressSpaceWidth() const {
710 return m_address_space_width;
711 }
712
713public:
714 // Linear mapped
715 static u8* GetLinearMappedVirtualPointer(KernelCore& kernel, KPhysicalAddress addr) {
716 return kernel.System().DeviceMemory().GetPointer<u8>(addr);
717 }
718
719 static KPhysicalAddress GetLinearMappedPhysicalAddress(KernelCore& kernel,
720 KVirtualAddress addr) {
721 return kernel.MemoryLayout().GetLinearPhysicalAddress(addr);
722 }
723
724 static KVirtualAddress GetLinearMappedVirtualAddress(KernelCore& kernel,
725 KPhysicalAddress addr) {
726 return kernel.MemoryLayout().GetLinearVirtualAddress(addr);
727 }
728
729 // Heap
730 static u8* GetHeapVirtualPointer(KernelCore& kernel, KPhysicalAddress addr) {
731 return kernel.System().DeviceMemory().GetPointer<u8>(addr);
732 }
733
734 static KPhysicalAddress GetHeapPhysicalAddress(KernelCore& kernel, KVirtualAddress addr) {
735 return GetLinearMappedPhysicalAddress(kernel, addr);
736 }
737
738 static KVirtualAddress GetHeapVirtualAddress(KernelCore& kernel, KPhysicalAddress addr) {
739 return GetLinearMappedVirtualAddress(kernel, addr);
740 }
741
742 // Member heap
743 u8* GetHeapVirtualPointer(KPhysicalAddress addr) {
744 return GetHeapVirtualPointer(m_kernel, addr);
745 }
746
747 KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress addr) {
748 return GetHeapPhysicalAddress(m_kernel, addr);
749 }
750
751 KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress addr) {
752 return GetHeapVirtualAddress(m_kernel, addr);
753 }
754
755 // TODO: GetPageTableVirtualAddress
756 // TODO: GetPageTablePhysicalAddress
757};
758
759} // namespace Kernel
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 1f4b0755d..3cfb414e5 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -298,9 +298,9 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa
298 const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr); 298 const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr);
299 const bool enable_das_merge = 299 const bool enable_das_merge =
300 False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); 300 False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
301 R_TRY(m_page_table.InitializeForProcess( 301 R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
302 as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, 302 params.code_address, params.code_num_pages * PageSize,
303 params.code_num_pages * PageSize, m_system_resource, res_limit, this->GetMemory())); 303 m_system_resource, res_limit, this->GetMemory()));
304 } 304 }
305 ON_RESULT_FAILURE_2 { 305 ON_RESULT_FAILURE_2 {
306 m_page_table.Finalize(); 306 m_page_table.Finalize();
@@ -391,9 +391,9 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params,
391 const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr); 391 const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr);
392 const bool enable_das_merge = 392 const bool enable_das_merge =
393 False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); 393 False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
394 R_TRY(m_page_table.InitializeForProcess(as_type, enable_aslr, enable_das_merge, 394 R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
395 !enable_aslr, pool, params.code_address, code_size, 395 params.code_address, code_size, m_system_resource, res_limit,
396 m_system_resource, res_limit, this->GetMemory())); 396 this->GetMemory()));
397 } 397 }
398 ON_RESULT_FAILURE_2 { 398 ON_RESULT_FAILURE_2 {
399 m_page_table.Finalize(); 399 m_page_table.Finalize();
@@ -1122,9 +1122,9 @@ Result KProcess::GetThreadList(s32* out_num_threads, KProcessAddress out_thread_
1122void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {} 1122void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {}
1123 1123
1124KProcess::KProcess(KernelCore& kernel) 1124KProcess::KProcess(KernelCore& kernel)
1125 : KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel.System()}, 1125 : KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel}, m_state_lock{kernel},
1126 m_state_lock{kernel}, m_list_lock{kernel}, m_cond_var{kernel.System()}, 1126 m_list_lock{kernel}, m_cond_var{kernel.System()}, m_address_arbiter{kernel.System()},
1127 m_address_arbiter{kernel.System()}, m_handle_table{kernel} {} 1127 m_handle_table{kernel} {}
1128KProcess::~KProcess() = default; 1128KProcess::~KProcess() = default;
1129 1129
1130Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, 1130Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index f9f755afa..8339465fd 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -5,13 +5,14 @@
5 5
6#include <map> 6#include <map>
7 7
8#include "core/file_sys/program_metadata.h"
8#include "core/hle/kernel/code_set.h" 9#include "core/hle/kernel/code_set.h"
9#include "core/hle/kernel/k_address_arbiter.h" 10#include "core/hle/kernel/k_address_arbiter.h"
10#include "core/hle/kernel/k_capabilities.h" 11#include "core/hle/kernel/k_capabilities.h"
11#include "core/hle/kernel/k_condition_variable.h" 12#include "core/hle/kernel/k_condition_variable.h"
12#include "core/hle/kernel/k_handle_table.h" 13#include "core/hle/kernel/k_handle_table.h"
13#include "core/hle/kernel/k_page_table.h"
14#include "core/hle/kernel/k_page_table_manager.h" 14#include "core/hle/kernel/k_page_table_manager.h"
15#include "core/hle/kernel/k_process_page_table.h"
15#include "core/hle/kernel/k_system_resource.h" 16#include "core/hle/kernel/k_system_resource.h"
16#include "core/hle/kernel/k_thread.h" 17#include "core/hle/kernel/k_thread.h"
17#include "core/hle/kernel/k_thread_local_page.h" 18#include "core/hle/kernel/k_thread_local_page.h"
@@ -65,7 +66,7 @@ private:
65 using TLPIterator = TLPTree::iterator; 66 using TLPIterator = TLPTree::iterator;
66 67
67private: 68private:
68 KPageTable m_page_table; 69 KProcessPageTable m_page_table;
69 std::atomic<size_t> m_used_kernel_memory_size{}; 70 std::atomic<size_t> m_used_kernel_memory_size{};
70 TLPTree m_fully_used_tlp_tree{}; 71 TLPTree m_fully_used_tlp_tree{};
71 TLPTree m_partially_used_tlp_tree{}; 72 TLPTree m_partially_used_tlp_tree{};
@@ -254,9 +255,8 @@ public:
254 return m_is_hbl; 255 return m_is_hbl;
255 } 256 }
256 257
257 Kernel::KMemoryManager::Direction GetAllocateOption() const { 258 u32 GetAllocateOption() const {
258 // TODO: property of the KPageTableBase 259 return m_page_table.GetAllocateOption();
259 return KMemoryManager::Direction::FromFront;
260 } 260 }
261 261
262 ThreadList& GetThreadList() { 262 ThreadList& GetThreadList() {
@@ -295,10 +295,10 @@ public:
295 return m_list_lock; 295 return m_list_lock;
296 } 296 }
297 297
298 KPageTable& GetPageTable() { 298 KProcessPageTable& GetPageTable() {
299 return m_page_table; 299 return m_page_table;
300 } 300 }
301 const KPageTable& GetPageTable() const { 301 const KProcessPageTable& GetPageTable() const {
302 return m_page_table; 302 return m_page_table;
303 } 303 }
304 304
diff --git a/src/core/hle/kernel/k_process_page_table.h b/src/core/hle/kernel/k_process_page_table.h
new file mode 100644
index 000000000..b7ae5abd0
--- /dev/null
+++ b/src/core/hle/kernel/k_process_page_table.h
@@ -0,0 +1,480 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/kernel/k_page_table.h"
7#include "core/hle/kernel/k_scoped_lock.h"
8#include "core/hle/kernel/svc_types.h"
9
10namespace Core {
11class ARM_Interface;
12}
13
14namespace Kernel {
15
16class KProcessPageTable {
17private:
18 KPageTable m_page_table;
19
20public:
21 KProcessPageTable(KernelCore& kernel) : m_page_table(kernel) {}
22
23 Result Initialize(Svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge,
24 bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address,
25 size_t code_size, KSystemResource* system_resource,
26 KResourceLimit* resource_limit, Core::Memory::Memory& memory) {
27 R_RETURN(m_page_table.InitializeForProcess(as_type, enable_aslr, enable_das_merge,
28 from_back, pool, code_address, code_size,
29 system_resource, resource_limit, memory));
30 }
31
32 void Finalize() {
33 m_page_table.Finalize();
34 }
35
36 Core::Memory::Memory& GetMemory() {
37 return m_page_table.GetMemory();
38 }
39
40 Core::Memory::Memory& GetMemory() const {
41 return m_page_table.GetMemory();
42 }
43
44 Common::PageTable& GetImpl() {
45 return m_page_table.GetImpl();
46 }
47
48 Common::PageTable& GetImpl() const {
49 return m_page_table.GetImpl();
50 }
51
52 size_t GetNumGuardPages() const {
53 return m_page_table.GetNumGuardPages();
54 }
55
56 KScopedLightLock AcquireDeviceMapLock() {
57 return m_page_table.AcquireDeviceMapLock();
58 }
59
60 Result SetMemoryPermission(KProcessAddress addr, size_t size, Svc::MemoryPermission perm) {
61 R_RETURN(m_page_table.SetMemoryPermission(addr, size, perm));
62 }
63
64 Result SetProcessMemoryPermission(KProcessAddress addr, size_t size,
65 Svc::MemoryPermission perm) {
66 R_RETURN(m_page_table.SetProcessMemoryPermission(addr, size, perm));
67 }
68
69 Result SetMemoryAttribute(KProcessAddress addr, size_t size, KMemoryAttribute mask,
70 KMemoryAttribute attr) {
71 R_RETURN(m_page_table.SetMemoryAttribute(addr, size, mask, attr));
72 }
73
74 Result SetHeapSize(KProcessAddress* out, size_t size) {
75 R_RETURN(m_page_table.SetHeapSize(out, size));
76 }
77
78 Result SetMaxHeapSize(size_t size) {
79 R_RETURN(m_page_table.SetMaxHeapSize(size));
80 }
81
82 Result QueryInfo(KMemoryInfo* out_info, Svc::PageInfo* out_page_info,
83 KProcessAddress addr) const {
84 R_RETURN(m_page_table.QueryInfo(out_info, out_page_info, addr));
85 }
86
87 Result QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out, KProcessAddress address) {
88 R_RETURN(m_page_table.QueryPhysicalAddress(out, address));
89 }
90
91 Result QueryStaticMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) {
92 R_RETURN(m_page_table.QueryStaticMapping(out, address, size));
93 }
94
95 Result QueryIoMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) {
96 R_RETURN(m_page_table.QueryIoMapping(out, address, size));
97 }
98
99 Result MapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
100 R_RETURN(m_page_table.MapMemory(dst_address, src_address, size));
101 }
102
103 Result UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
104 R_RETURN(m_page_table.UnmapMemory(dst_address, src_address, size));
105 }
106
107 Result MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
108 R_RETURN(m_page_table.MapCodeMemory(dst_address, src_address, size));
109 }
110
111 Result UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
112 R_RETURN(m_page_table.UnmapCodeMemory(dst_address, src_address, size));
113 }
114
115 Result MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) {
116 R_RETURN(m_page_table.MapIo(phys_addr, size, perm));
117 }
118
119 Result MapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size,
120 Svc::MemoryMapping mapping, Svc::MemoryPermission perm) {
121 R_RETURN(m_page_table.MapIoRegion(dst_address, phys_addr, size, mapping, perm));
122 }
123
124 Result UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size,
125 Svc::MemoryMapping mapping) {
126 R_RETURN(m_page_table.UnmapIoRegion(dst_address, phys_addr, size, mapping));
127 }
128
129 Result MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) {
130 R_RETURN(m_page_table.MapStatic(phys_addr, size, perm));
131 }
132
133 Result MapRegion(KMemoryRegionType region_type, KMemoryPermission perm) {
134 R_RETURN(m_page_table.MapRegion(region_type, perm));
135 }
136
137 Result MapInsecureMemory(KProcessAddress address, size_t size) {
138 R_RETURN(m_page_table.MapInsecureMemory(address, size));
139 }
140
141 Result UnmapInsecureMemory(KProcessAddress address, size_t size) {
142 R_RETURN(m_page_table.UnmapInsecureMemory(address, size));
143 }
144
145 Result MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMemoryState state,
146 KMemoryPermission perm) {
147 R_RETURN(m_page_table.MapPageGroup(addr, pg, state, perm));
148 }
149
150 Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state) {
151 R_RETURN(m_page_table.UnmapPageGroup(address, pg, state));
152 }
153
154 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
155 KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) {
156 R_RETURN(m_page_table.MapPages(out_addr, num_pages, alignment, phys_addr, state, perm));
157 }
158
159 Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state,
160 KMemoryPermission perm) {
161 R_RETURN(m_page_table.MapPages(out_addr, num_pages, state, perm));
162 }
163
164 Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
165 KMemoryPermission perm) {
166 R_RETURN(m_page_table.MapPages(address, num_pages, state, perm));
167 }
168
169 Result UnmapPages(KProcessAddress addr, size_t num_pages, KMemoryState state) {
170 R_RETURN(m_page_table.UnmapPages(addr, num_pages, state));
171 }
172
173 Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages,
174 KMemoryState state_mask, KMemoryState state,
175 KMemoryPermission perm_mask, KMemoryPermission perm,
176 KMemoryAttribute attr_mask, KMemoryAttribute attr) {
177 R_RETURN(m_page_table.MakeAndOpenPageGroup(out, address, num_pages, state_mask, state,
178 perm_mask, perm, attr_mask, attr));
179 }
180
181 Result InvalidateProcessDataCache(KProcessAddress address, size_t size) {
182 R_RETURN(m_page_table.InvalidateProcessDataCache(address, size));
183 }
184
185 Result ReadDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
186 R_RETURN(m_page_table.ReadDebugMemory(dst_address, src_address, size));
187 }
188
189 Result ReadDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size,
190 KMemoryState state) {
191 R_RETURN(m_page_table.ReadDebugIoMemory(dst_address, src_address, size, state));
192 }
193
194 Result WriteDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
195 R_RETURN(m_page_table.WriteDebugMemory(dst_address, src_address, size));
196 }
197
198 Result WriteDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size,
199 KMemoryState state) {
200 R_RETURN(m_page_table.WriteDebugIoMemory(dst_address, src_address, size, state));
201 }
202
203 Result LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address, size_t size,
204 KMemoryPermission perm, bool is_aligned, bool check_heap) {
205 R_RETURN(m_page_table.LockForMapDeviceAddressSpace(out_is_io, address, size, perm,
206 is_aligned, check_heap));
207 }
208
209 Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size, bool check_heap) {
210 R_RETURN(m_page_table.LockForUnmapDeviceAddressSpace(address, size, check_heap));
211 }
212
213 Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size) {
214 R_RETURN(m_page_table.UnlockForDeviceAddressSpace(address, size));
215 }
216
217 Result UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size) {
218 R_RETURN(m_page_table.UnlockForDeviceAddressSpacePartialMap(address, size));
219 }
220
221 Result OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange* out,
222 KProcessAddress address, size_t size,
223 KMemoryPermission perm, bool is_aligned) {
224 R_RETURN(m_page_table.OpenMemoryRangeForMapDeviceAddressSpace(out, address, size, perm,
225 is_aligned));
226 }
227
228 Result OpenMemoryRangeForUnmapDeviceAddressSpace(KPageTableBase::MemoryRange* out,
229 KProcessAddress address, size_t size) {
230 R_RETURN(m_page_table.OpenMemoryRangeForUnmapDeviceAddressSpace(out, address, size));
231 }
232
233 Result LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address, size_t size) {
234 R_RETURN(m_page_table.LockForIpcUserBuffer(out, address, size));
235 }
236
237 Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size) {
238 R_RETURN(m_page_table.UnlockForIpcUserBuffer(address, size));
239 }
240
241 Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size,
242 KMemoryPermission perm) {
243 R_RETURN(m_page_table.LockForTransferMemory(out, address, size, perm));
244 }
245
246 Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg) {
247 R_RETURN(m_page_table.UnlockForTransferMemory(address, size, pg));
248 }
249
250 Result LockForCodeMemory(KPageGroup* out, KProcessAddress address, size_t size) {
251 R_RETURN(m_page_table.LockForCodeMemory(out, address, size));
252 }
253
254 Result UnlockForCodeMemory(KProcessAddress address, size_t size, const KPageGroup& pg) {
255 R_RETURN(m_page_table.UnlockForCodeMemory(address, size, pg));
256 }
257
258 Result OpenMemoryRangeForProcessCacheOperation(KPageTableBase::MemoryRange* out,
259 KProcessAddress address, size_t size) {
260 R_RETURN(m_page_table.OpenMemoryRangeForProcessCacheOperation(out, address, size));
261 }
262
263 Result CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size,
264 KProcessAddress src_addr, KMemoryState src_state_mask,
265 KMemoryState src_state, KMemoryPermission src_test_perm,
266 KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) {
267 R_RETURN(m_page_table.CopyMemoryFromLinearToUser(dst_addr, size, src_addr, src_state_mask,
268 src_state, src_test_perm, src_attr_mask,
269 src_attr));
270 }
271
272 Result CopyMemoryFromLinearToKernel(void* dst_addr, size_t size, KProcessAddress src_addr,
273 KMemoryState src_state_mask, KMemoryState src_state,
274 KMemoryPermission src_test_perm,
275 KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) {
276 R_RETURN(m_page_table.CopyMemoryFromLinearToKernel(dst_addr, size, src_addr, src_state_mask,
277 src_state, src_test_perm, src_attr_mask,
278 src_attr));
279 }
280
281 Result CopyMemoryFromUserToLinear(KProcessAddress dst_addr, size_t size,
282 KMemoryState dst_state_mask, KMemoryState dst_state,
283 KMemoryPermission dst_test_perm,
284 KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
285 KProcessAddress src_addr) {
286 R_RETURN(m_page_table.CopyMemoryFromUserToLinear(dst_addr, size, dst_state_mask, dst_state,
287 dst_test_perm, dst_attr_mask, dst_attr,
288 src_addr));
289 }
290
291 Result CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, size_t size,
292 KMemoryState dst_state_mask, KMemoryState dst_state,
293 KMemoryPermission dst_test_perm,
294 KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
295 void* src_addr) {
296 R_RETURN(m_page_table.CopyMemoryFromKernelToLinear(dst_addr, size, dst_state_mask,
297 dst_state, dst_test_perm, dst_attr_mask,
298 dst_attr, src_addr));
299 }
300
301 Result CopyMemoryFromHeapToHeap(KProcessPageTable& dst_page_table, KProcessAddress dst_addr,
302 size_t size, KMemoryState dst_state_mask,
303 KMemoryState dst_state, KMemoryPermission dst_test_perm,
304 KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr,
305 KProcessAddress src_addr, KMemoryState src_state_mask,
306 KMemoryState src_state, KMemoryPermission src_test_perm,
307 KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) {
308 R_RETURN(m_page_table.CopyMemoryFromHeapToHeap(
309 dst_page_table.m_page_table, dst_addr, size, dst_state_mask, dst_state, dst_test_perm,
310 dst_attr_mask, dst_attr, src_addr, src_state_mask, src_state, src_test_perm,
311 src_attr_mask, src_attr));
312 }
313
314 Result CopyMemoryFromHeapToHeapWithoutCheckDestination(
315 KProcessPageTable& dst_page_table, KProcessAddress dst_addr, size_t size,
316 KMemoryState dst_state_mask, KMemoryState dst_state, KMemoryPermission dst_test_perm,
317 KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, KProcessAddress src_addr,
318 KMemoryState src_state_mask, KMemoryState src_state, KMemoryPermission src_test_perm,
319 KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) {
320 R_RETURN(m_page_table.CopyMemoryFromHeapToHeapWithoutCheckDestination(
321 dst_page_table.m_page_table, dst_addr, size, dst_state_mask, dst_state, dst_test_perm,
322 dst_attr_mask, dst_attr, src_addr, src_state_mask, src_state, src_test_perm,
323 src_attr_mask, src_attr));
324 }
325
326 Result SetupForIpc(KProcessAddress* out_dst_addr, size_t size, KProcessAddress src_addr,
327 KProcessPageTable& src_page_table, KMemoryPermission test_perm,
328 KMemoryState dst_state, bool send) {
329 R_RETURN(m_page_table.SetupForIpc(out_dst_addr, size, src_addr, src_page_table.m_page_table,
330 test_perm, dst_state, send));
331 }
332
333 Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state) {
334 R_RETURN(m_page_table.CleanupForIpcServer(address, size, dst_state));
335 }
336
337 Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state) {
338 R_RETURN(m_page_table.CleanupForIpcClient(address, size, dst_state));
339 }
340
341 Result MapPhysicalMemory(KProcessAddress address, size_t size) {
342 R_RETURN(m_page_table.MapPhysicalMemory(address, size));
343 }
344
345 Result UnmapPhysicalMemory(KProcessAddress address, size_t size) {
346 R_RETURN(m_page_table.UnmapPhysicalMemory(address, size));
347 }
348
349 Result MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size) {
350 R_RETURN(m_page_table.MapPhysicalMemoryUnsafe(address, size));
351 }
352
353 Result UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size) {
354 R_RETURN(m_page_table.UnmapPhysicalMemoryUnsafe(address, size));
355 }
356
357 Result UnmapProcessMemory(KProcessAddress dst_address, size_t size,
358 KProcessPageTable& src_page_table, KProcessAddress src_address) {
359 R_RETURN(m_page_table.UnmapProcessMemory(dst_address, size, src_page_table.m_page_table,
360 src_address));
361 }
362
363 bool GetPhysicalAddress(KPhysicalAddress* out, KProcessAddress address) {
364 return m_page_table.GetPhysicalAddress(out, address);
365 }
366
367 bool Contains(KProcessAddress addr, size_t size) const {
368 return m_page_table.Contains(addr, size);
369 }
370
371 bool IsInAliasRegion(KProcessAddress addr, size_t size) const {
372 return m_page_table.IsInAliasRegion(addr, size);
373 }
374 bool IsInHeapRegion(KProcessAddress addr, size_t size) const {
375 return m_page_table.IsInHeapRegion(addr, size);
376 }
377 bool IsInUnsafeAliasRegion(KProcessAddress addr, size_t size) const {
378 return m_page_table.IsInUnsafeAliasRegion(addr, size);
379 }
380
381 bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const {
382 return m_page_table.CanContain(addr, size, state);
383 }
384
385 KProcessAddress GetAddressSpaceStart() const {
386 return m_page_table.GetAddressSpaceStart();
387 }
388 KProcessAddress GetHeapRegionStart() const {
389 return m_page_table.GetHeapRegionStart();
390 }
391 KProcessAddress GetAliasRegionStart() const {
392 return m_page_table.GetAliasRegionStart();
393 }
394 KProcessAddress GetStackRegionStart() const {
395 return m_page_table.GetStackRegionStart();
396 }
397 KProcessAddress GetKernelMapRegionStart() const {
398 return m_page_table.GetKernelMapRegionStart();
399 }
400 KProcessAddress GetCodeRegionStart() const {
401 return m_page_table.GetCodeRegionStart();
402 }
403 KProcessAddress GetAliasCodeRegionStart() const {
404 return m_page_table.GetAliasCodeRegionStart();
405 }
406
407 size_t GetAddressSpaceSize() const {
408 return m_page_table.GetAddressSpaceSize();
409 }
410 size_t GetHeapRegionSize() const {
411 return m_page_table.GetHeapRegionSize();
412 }
413 size_t GetAliasRegionSize() const {
414 return m_page_table.GetAliasRegionSize();
415 }
416 size_t GetStackRegionSize() const {
417 return m_page_table.GetStackRegionSize();
418 }
419 size_t GetKernelMapRegionSize() const {
420 return m_page_table.GetKernelMapRegionSize();
421 }
422 size_t GetCodeRegionSize() const {
423 return m_page_table.GetCodeRegionSize();
424 }
425 size_t GetAliasCodeRegionSize() const {
426 return m_page_table.GetAliasCodeRegionSize();
427 }
428
429 size_t GetNormalMemorySize() const {
430 return m_page_table.GetNormalMemorySize();
431 }
432
433 size_t GetCodeSize() const {
434 return m_page_table.GetCodeSize();
435 }
436 size_t GetCodeDataSize() const {
437 return m_page_table.GetCodeDataSize();
438 }
439
440 size_t GetAliasCodeSize() const {
441 return m_page_table.GetAliasCodeSize();
442 }
443 size_t GetAliasCodeDataSize() const {
444 return m_page_table.GetAliasCodeDataSize();
445 }
446
447 u32 GetAllocateOption() const {
448 return m_page_table.GetAllocateOption();
449 }
450
451 u32 GetAddressSpaceWidth() const {
452 return m_page_table.GetAddressSpaceWidth();
453 }
454
455 KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress address) {
456 return m_page_table.GetHeapPhysicalAddress(address);
457 }
458
459 u8* GetHeapVirtualPointer(KPhysicalAddress address) {
460 return m_page_table.GetHeapVirtualPointer(address);
461 }
462
463 KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress address) {
464 return m_page_table.GetHeapVirtualAddress(address);
465 }
466
467 KBlockInfoManager* GetBlockInfoManager() {
468 return m_page_table.GetBlockInfoManager();
469 }
470
471 KPageTable& GetBasePageTable() {
472 return m_page_table;
473 }
474
475 const KPageTable& GetBasePageTable() const {
476 return m_page_table;
477 }
478};
479
480} // namespace Kernel
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index c64ceb530..3ea653163 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -383,7 +383,7 @@ Result KServerSession::SendReply(bool is_hle) {
383 if (event != nullptr) { 383 if (event != nullptr) {
384 // // Get the client process/page table. 384 // // Get the client process/page table.
385 // KProcess *client_process = client_thread->GetOwnerProcess(); 385 // KProcess *client_process = client_thread->GetOwnerProcess();
386 // KPageTable *client_page_table = std::addressof(client_process->PageTable()); 386 // KProcessPageTable *client_page_table = std::addressof(client_process->PageTable());
387 387
388 // // If we need to, reply with an async error. 388 // // If we need to, reply with an async error.
389 // if (R_FAILED(client_result)) { 389 // if (R_FAILED(client_result)) {
diff --git a/src/core/hle/kernel/k_system_resource.cpp b/src/core/hle/kernel/k_system_resource.cpp
index 07e92aa80..b51941faf 100644
--- a/src/core/hle/kernel/k_system_resource.cpp
+++ b/src/core/hle/kernel/k_system_resource.cpp
@@ -40,7 +40,7 @@ Result KSecureSystemResource::Initialize(size_t size, KResourceLimit* resource_l
40 40
41 // Get resource pointer. 41 // Get resource pointer.
42 KPhysicalAddress resource_paddr = 42 KPhysicalAddress resource_paddr =
43 KPageTable::GetHeapPhysicalAddress(m_kernel.MemoryLayout(), m_resource_address); 43 KPageTable::GetHeapPhysicalAddress(m_kernel, m_resource_address);
44 auto* resource = 44 auto* resource =
45 m_kernel.System().DeviceMemory().GetPointer<KPageTableManager::RefCount>(resource_paddr); 45 m_kernel.System().DeviceMemory().GetPointer<KPageTableManager::RefCount>(resource_paddr);
46 46
diff --git a/src/core/hle/kernel/k_thread_local_page.cpp b/src/core/hle/kernel/k_thread_local_page.cpp
index 2c45b4232..a632d1634 100644
--- a/src/core/hle/kernel/k_thread_local_page.cpp
+++ b/src/core/hle/kernel/k_thread_local_page.cpp
@@ -37,8 +37,8 @@ Result KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) {
37 37
38Result KThreadLocalPage::Finalize() { 38Result KThreadLocalPage::Finalize() {
39 // Get the physical address of the page. 39 // Get the physical address of the page.
40 const KPhysicalAddress phys_addr = m_owner->GetPageTable().GetPhysicalAddr(m_virt_addr); 40 KPhysicalAddress phys_addr{};
41 ASSERT(phys_addr); 41 ASSERT(m_owner->GetPageTable().GetPhysicalAddress(std::addressof(phys_addr), m_virt_addr));
42 42
43 // Unmap the page. 43 // Unmap the page.
44 R_TRY(m_owner->GetPageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal)); 44 R_TRY(m_owner->GetPageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal));
diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp
deleted file mode 100644
index 773319ad8..000000000
--- a/src/core/hle/kernel/process_capability.cpp
+++ /dev/null
@@ -1,389 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <bit>
5
6#include "common/bit_util.h"
7#include "common/logging/log.h"
8#include "core/hle/kernel/k_handle_table.h"
9#include "core/hle/kernel/k_page_table.h"
10#include "core/hle/kernel/process_capability.h"
11#include "core/hle/kernel/svc_results.h"
12
13namespace Kernel {
14namespace {
15
16// clang-format off
17
18// Shift offsets for kernel capability types.
19enum : u32 {
20 CapabilityOffset_PriorityAndCoreNum = 3,
21 CapabilityOffset_Syscall = 4,
22 CapabilityOffset_MapPhysical = 6,
23 CapabilityOffset_MapIO = 7,
24 CapabilityOffset_MapRegion = 10,
25 CapabilityOffset_Interrupt = 11,
26 CapabilityOffset_ProgramType = 13,
27 CapabilityOffset_KernelVersion = 14,
28 CapabilityOffset_HandleTableSize = 15,
29 CapabilityOffset_Debug = 16,
30};
31
32// Combined mask of all parameters that may be initialized only once.
33constexpr u32 InitializeOnceMask = (1U << CapabilityOffset_PriorityAndCoreNum) |
34 (1U << CapabilityOffset_ProgramType) |
35 (1U << CapabilityOffset_KernelVersion) |
36 (1U << CapabilityOffset_HandleTableSize) |
37 (1U << CapabilityOffset_Debug);
38
39// Packed kernel version indicating 10.4.0
40constexpr u32 PackedKernelVersion = 0x520000;
41
42// Indicates possible types of capabilities that can be specified.
43enum class CapabilityType : u32 {
44 Unset = 0U,
45 PriorityAndCoreNum = (1U << CapabilityOffset_PriorityAndCoreNum) - 1,
46 Syscall = (1U << CapabilityOffset_Syscall) - 1,
47 MapPhysical = (1U << CapabilityOffset_MapPhysical) - 1,
48 MapIO = (1U << CapabilityOffset_MapIO) - 1,
49 MapRegion = (1U << CapabilityOffset_MapRegion) - 1,
50 Interrupt = (1U << CapabilityOffset_Interrupt) - 1,
51 ProgramType = (1U << CapabilityOffset_ProgramType) - 1,
52 KernelVersion = (1U << CapabilityOffset_KernelVersion) - 1,
53 HandleTableSize = (1U << CapabilityOffset_HandleTableSize) - 1,
54 Debug = (1U << CapabilityOffset_Debug) - 1,
55 Ignorable = 0xFFFFFFFFU,
56};
57
58// clang-format on
59
60constexpr CapabilityType GetCapabilityType(u32 value) {
61 return static_cast<CapabilityType>((~value & (value + 1)) - 1);
62}
63
64u32 GetFlagBitOffset(CapabilityType type) {
65 const auto value = static_cast<u32>(type);
66 return static_cast<u32>(Common::BitSize<u32>() - static_cast<u32>(std::countl_zero(value)));
67}
68
69} // Anonymous namespace
70
71Result ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities,
72 std::size_t num_capabilities,
73 KPageTable& page_table) {
74 Clear();
75
76 // Allow all cores and priorities.
77 core_mask = 0xF;
78 priority_mask = 0xFFFFFFFFFFFFFFFF;
79 kernel_version = PackedKernelVersion;
80
81 return ParseCapabilities(capabilities, num_capabilities, page_table);
82}
83
84Result ProcessCapabilities::InitializeForUserProcess(const u32* capabilities,
85 std::size_t num_capabilities,
86 KPageTable& page_table) {
87 Clear();
88
89 return ParseCapabilities(capabilities, num_capabilities, page_table);
90}
91
92void ProcessCapabilities::InitializeForMetadatalessProcess() {
93 // Allow all cores and priorities
94 core_mask = 0xF;
95 priority_mask = 0xFFFFFFFFFFFFFFFF;
96 kernel_version = PackedKernelVersion;
97
98 // Allow all system calls and interrupts.
99 svc_capabilities.set();
100 interrupt_capabilities.set();
101
102 // Allow using the maximum possible amount of handles
103 handle_table_size = static_cast<s32>(KHandleTable::MaxTableSize);
104
105 // Allow all debugging capabilities.
106 is_debuggable = true;
107 can_force_debug = true;
108}
109
110Result ProcessCapabilities::ParseCapabilities(const u32* capabilities, std::size_t num_capabilities,
111 KPageTable& page_table) {
112 u32 set_flags = 0;
113 u32 set_svc_bits = 0;
114
115 for (std::size_t i = 0; i < num_capabilities; ++i) {
116 const u32 descriptor = capabilities[i];
117 const auto type = GetCapabilityType(descriptor);
118
119 if (type == CapabilityType::MapPhysical) {
120 i++;
121
122 // The MapPhysical type uses two descriptor flags for its parameters.
123 // If there's only one, then there's a problem.
124 if (i >= num_capabilities) {
125 LOG_ERROR(Kernel, "Invalid combination! i={}", i);
126 return ResultInvalidCombination;
127 }
128
129 const auto size_flags = capabilities[i];
130 if (GetCapabilityType(size_flags) != CapabilityType::MapPhysical) {
131 LOG_ERROR(Kernel, "Invalid capability type! size_flags={}", size_flags);
132 return ResultInvalidCombination;
133 }
134
135 const auto result = HandleMapPhysicalFlags(descriptor, size_flags, page_table);
136 if (result.IsError()) {
137 LOG_ERROR(Kernel, "Failed to map physical flags! descriptor={}, size_flags={}",
138 descriptor, size_flags);
139 return result;
140 }
141 } else {
142 const auto result =
143 ParseSingleFlagCapability(set_flags, set_svc_bits, descriptor, page_table);
144 if (result.IsError()) {
145 LOG_ERROR(
146 Kernel,
147 "Failed to parse capability flag! set_flags={}, set_svc_bits={}, descriptor={}",
148 set_flags, set_svc_bits, descriptor);
149 return result;
150 }
151 }
152 }
153
154 return ResultSuccess;
155}
156
157Result ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag,
158 KPageTable& page_table) {
159 const auto type = GetCapabilityType(flag);
160
161 if (type == CapabilityType::Unset) {
162 return ResultInvalidArgument;
163 }
164
165 // Bail early on ignorable entries, as one would expect,
166 // ignorable descriptors can be ignored.
167 if (type == CapabilityType::Ignorable) {
168 return ResultSuccess;
169 }
170
171 // Ensure that the give flag hasn't already been initialized before.
172 // If it has been, then bail.
173 const u32 flag_length = GetFlagBitOffset(type);
174 const u32 set_flag = 1U << flag_length;
175 if ((set_flag & set_flags & InitializeOnceMask) != 0) {
176 LOG_ERROR(Kernel,
177 "Attempted to initialize flags that may only be initialized once. set_flags={}",
178 set_flags);
179 return ResultInvalidCombination;
180 }
181 set_flags |= set_flag;
182
183 switch (type) {
184 case CapabilityType::PriorityAndCoreNum:
185 return HandlePriorityCoreNumFlags(flag);
186 case CapabilityType::Syscall:
187 return HandleSyscallFlags(set_svc_bits, flag);
188 case CapabilityType::MapIO:
189 return HandleMapIOFlags(flag, page_table);
190 case CapabilityType::MapRegion:
191 return HandleMapRegionFlags(flag, page_table);
192 case CapabilityType::Interrupt:
193 return HandleInterruptFlags(flag);
194 case CapabilityType::ProgramType:
195 return HandleProgramTypeFlags(flag);
196 case CapabilityType::KernelVersion:
197 return HandleKernelVersionFlags(flag);
198 case CapabilityType::HandleTableSize:
199 return HandleHandleTableFlags(flag);
200 case CapabilityType::Debug:
201 return HandleDebugFlags(flag);
202 default:
203 break;
204 }
205
206 LOG_ERROR(Kernel, "Invalid capability type! type={}", type);
207 return ResultInvalidArgument;
208}
209
210void ProcessCapabilities::Clear() {
211 svc_capabilities.reset();
212 interrupt_capabilities.reset();
213
214 core_mask = 0;
215 priority_mask = 0;
216
217 handle_table_size = 0;
218 kernel_version = 0;
219
220 program_type = ProgramType::SysModule;
221
222 is_debuggable = false;
223 can_force_debug = false;
224}
225
226Result ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
227 if (priority_mask != 0 || core_mask != 0) {
228 LOG_ERROR(Kernel, "Core or priority mask are not zero! priority_mask={}, core_mask={}",
229 priority_mask, core_mask);
230 return ResultInvalidArgument;
231 }
232
233 const u32 core_num_min = (flags >> 16) & 0xFF;
234 const u32 core_num_max = (flags >> 24) & 0xFF;
235 if (core_num_min > core_num_max) {
236 LOG_ERROR(Kernel, "Core min is greater than core max! core_num_min={}, core_num_max={}",
237 core_num_min, core_num_max);
238 return ResultInvalidCombination;
239 }
240
241 const u32 priority_min = (flags >> 10) & 0x3F;
242 const u32 priority_max = (flags >> 4) & 0x3F;
243 if (priority_min > priority_max) {
244 LOG_ERROR(Kernel,
245 "Priority min is greater than priority max! priority_min={}, priority_max={}",
246 core_num_min, priority_max);
247 return ResultInvalidCombination;
248 }
249
250 // The switch only has 4 usable cores.
251 if (core_num_max >= 4) {
252 LOG_ERROR(Kernel, "Invalid max cores specified! core_num_max={}", core_num_max);
253 return ResultInvalidCoreId;
254 }
255
256 const auto make_mask = [](u64 min, u64 max) {
257 const u64 range = max - min + 1;
258 const u64 mask = (1ULL << range) - 1;
259
260 return mask << min;
261 };
262
263 core_mask = make_mask(core_num_min, core_num_max);
264 priority_mask = make_mask(priority_min, priority_max);
265 return ResultSuccess;
266}
267
268Result ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) {
269 const u32 index = flags >> 29;
270 const u32 svc_bit = 1U << index;
271
272 // If we've already set this svc before, bail.
273 if ((set_svc_bits & svc_bit) != 0) {
274 return ResultInvalidCombination;
275 }
276 set_svc_bits |= svc_bit;
277
278 const u32 svc_mask = (flags >> 5) & 0xFFFFFF;
279 for (u32 i = 0; i < 24; ++i) {
280 const u32 svc_number = index * 24 + i;
281
282 if ((svc_mask & (1U << i)) == 0) {
283 continue;
284 }
285
286 svc_capabilities[svc_number] = true;
287 }
288
289 return ResultSuccess;
290}
291
292Result ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags,
293 KPageTable& page_table) {
294 // TODO(Lioncache): Implement once the memory manager can handle this.
295 return ResultSuccess;
296}
297
298Result ProcessCapabilities::HandleMapIOFlags(u32 flags, KPageTable& page_table) {
299 // TODO(Lioncache): Implement once the memory manager can handle this.
300 return ResultSuccess;
301}
302
303Result ProcessCapabilities::HandleMapRegionFlags(u32 flags, KPageTable& page_table) {
304 // TODO(Lioncache): Implement once the memory manager can handle this.
305 return ResultSuccess;
306}
307
308Result ProcessCapabilities::HandleInterruptFlags(u32 flags) {
309 constexpr u32 interrupt_ignore_value = 0x3FF;
310 const u32 interrupt0 = (flags >> 12) & 0x3FF;
311 const u32 interrupt1 = (flags >> 22) & 0x3FF;
312
313 for (u32 interrupt : {interrupt0, interrupt1}) {
314 if (interrupt == interrupt_ignore_value) {
315 continue;
316 }
317
318 // NOTE:
319 // This should be checking a generic interrupt controller value
320 // as part of the calculation, however, given we don't currently
321 // emulate that, it's sufficient to mark every interrupt as defined.
322
323 if (interrupt >= interrupt_capabilities.size()) {
324 LOG_ERROR(Kernel, "Process interrupt capability is out of range! svc_number={}",
325 interrupt);
326 return ResultOutOfRange;
327 }
328
329 interrupt_capabilities[interrupt] = true;
330 }
331
332 return ResultSuccess;
333}
334
335Result ProcessCapabilities::HandleProgramTypeFlags(u32 flags) {
336 const u32 reserved = flags >> 17;
337 if (reserved != 0) {
338 LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
339 return ResultReservedUsed;
340 }
341
342 program_type = static_cast<ProgramType>((flags >> 14) & 0b111);
343 return ResultSuccess;
344}
345
346Result ProcessCapabilities::HandleKernelVersionFlags(u32 flags) {
347 // Yes, the internal member variable is checked in the actual kernel here.
348 // This might look odd for options that are only allowed to be initialized
349 // just once, however the kernel has a separate initialization function for
350 // kernel processes and userland processes. The kernel variant sets this
351 // member variable ahead of time.
352
353 const u32 major_version = kernel_version >> 19;
354
355 if (major_version != 0 || flags < 0x80000) {
356 LOG_ERROR(Kernel,
357 "Kernel version is non zero or flags are too small! major_version={}, flags={}",
358 major_version, flags);
359 return ResultInvalidArgument;
360 }
361
362 kernel_version = flags;
363 return ResultSuccess;
364}
365
366Result ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
367 const u32 reserved = flags >> 26;
368 if (reserved != 0) {
369 LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
370 return ResultReservedUsed;
371 }
372
373 handle_table_size = static_cast<s32>((flags >> 16) & 0x3FF);
374 return ResultSuccess;
375}
376
377Result ProcessCapabilities::HandleDebugFlags(u32 flags) {
378 const u32 reserved = flags >> 19;
379 if (reserved != 0) {
380 LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
381 return ResultReservedUsed;
382 }
383
384 is_debuggable = (flags & 0x20000) != 0;
385 can_force_debug = (flags & 0x40000) != 0;
386 return ResultSuccess;
387}
388
389} // namespace Kernel
diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h
deleted file mode 100644
index ff05dc5ff..000000000
--- a/src/core/hle/kernel/process_capability.h
+++ /dev/null
@@ -1,266 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <bitset>
7
8#include "common/common_types.h"
9
10union Result;
11
12namespace Kernel {
13
14class KPageTable;
15
16/// The possible types of programs that may be indicated
17/// by the program type capability descriptor.
18enum class ProgramType {
19 SysModule,
20 Application,
21 Applet,
22};
23
24/// Handles kernel capability descriptors that are provided by
25/// application metadata. These descriptors provide information
26/// that alters certain parameters for kernel process instance
27/// that will run said application (or applet).
28///
29/// Capabilities are a sequence of flag descriptors, that indicate various
30/// configurations and constraints for a particular process.
31///
32/// Flag types are indicated by a sequence of set low bits. E.g. the
33/// types are indicated with the low bits as follows (where x indicates "don't care"):
34///
35/// - Priority and core mask : 0bxxxxxxxxxxxx0111
36/// - Allowed service call mask: 0bxxxxxxxxxxx01111
37/// - Map physical memory : 0bxxxxxxxxx0111111
38/// - Map IO memory : 0bxxxxxxxx01111111
39/// - Interrupts : 0bxxxx011111111111
40/// - Application type : 0bxx01111111111111
41/// - Kernel version : 0bx011111111111111
42/// - Handle table size : 0b0111111111111111
43/// - Debugger flags : 0b1111111111111111
44///
45/// These are essentially a bit offset subtracted by 1 to create a mask.
46/// e.g. The first entry in the above list is simply bit 3 (value 8 -> 0b1000)
47/// subtracted by one (7 -> 0b0111)
48///
49/// An example of a bit layout (using the map physical layout):
50/// <example>
51/// The MapPhysical type indicates a sequence entry pair of:
52///
53/// [initial, memory_flags], where:
54///
55/// initial:
56/// bits:
57/// 7-24: Starting page to map memory at.
58/// 25 : Indicates if the memory should be mapped as read only.
59///
60/// memory_flags:
61/// bits:
62/// 7-20 : Number of pages to map
63/// 21-25: Seems to be reserved (still checked against though)
64/// 26 : Whether or not the memory being mapped is IO memory, or physical memory
65/// </example>
66///
67class ProcessCapabilities {
68public:
69 using InterruptCapabilities = std::bitset<1024>;
70 using SyscallCapabilities = std::bitset<192>;
71
72 ProcessCapabilities() = default;
73 ProcessCapabilities(const ProcessCapabilities&) = delete;
74 ProcessCapabilities(ProcessCapabilities&&) = default;
75
76 ProcessCapabilities& operator=(const ProcessCapabilities&) = delete;
77 ProcessCapabilities& operator=(ProcessCapabilities&&) = default;
78
79 /// Initializes this process capabilities instance for a kernel process.
80 ///
81 /// @param capabilities The capabilities to parse
82 /// @param num_capabilities The number of capabilities to parse.
83 /// @param page_table The memory manager to use for handling any mapping-related
84 /// operations (such as mapping IO memory, etc).
85 ///
86 /// @returns ResultSuccess if this capabilities instance was able to be initialized,
87 /// otherwise, an error code upon failure.
88 ///
89 Result InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities,
90 KPageTable& page_table);
91
92 /// Initializes this process capabilities instance for a userland process.
93 ///
94 /// @param capabilities The capabilities to parse.
95 /// @param num_capabilities The total number of capabilities to parse.
96 /// @param page_table The memory manager to use for handling any mapping-related
97 /// operations (such as mapping IO memory, etc).
98 ///
99 /// @returns ResultSuccess if this capabilities instance was able to be initialized,
100 /// otherwise, an error code upon failure.
101 ///
102 Result InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities,
103 KPageTable& page_table);
104
105 /// Initializes this process capabilities instance for a process that does not
106 /// have any metadata to parse.
107 ///
108 /// This is necessary, as we allow running raw executables, and the internal
109 /// kernel process capabilities also determine what CPU cores the process is
110 /// allowed to run on, and what priorities are allowed for threads. It also
111 /// determines the max handle table size, what the program type is, whether or
112 /// not the process can be debugged, or whether it's possible for a process to
113 /// forcibly debug another process.
114 ///
115 /// Given the above, this essentially enables all capabilities across the board
116 /// for the process. It allows the process to:
117 ///
118 /// - Run on any core
119 /// - Use any thread priority
120 /// - Use the maximum amount of handles a process is allowed to.
121 /// - Be debuggable
122 /// - Forcibly debug other processes.
123 ///
124 /// Note that this is not a behavior that the kernel allows a process to do via
125 /// a single function like this. This is yuzu-specific behavior to handle
126 /// executables with no capability descriptors whatsoever to derive behavior from.
127 /// It being yuzu-specific is why this is also not the default behavior and not
128 /// done by default in the constructor.
129 ///
130 void InitializeForMetadatalessProcess();
131
132 /// Gets the allowable core mask
133 u64 GetCoreMask() const {
134 return core_mask;
135 }
136
137 /// Gets the allowable priority mask
138 u64 GetPriorityMask() const {
139 return priority_mask;
140 }
141
142 /// Gets the SVC access permission bits
143 const SyscallCapabilities& GetServiceCapabilities() const {
144 return svc_capabilities;
145 }
146
147 /// Gets the valid interrupt bits.
148 const InterruptCapabilities& GetInterruptCapabilities() const {
149 return interrupt_capabilities;
150 }
151
152 /// Gets the program type for this process.
153 ProgramType GetProgramType() const {
154 return program_type;
155 }
156
157 /// Gets the number of total allowable handles for the process' handle table.
158 s32 GetHandleTableSize() const {
159 return handle_table_size;
160 }
161
162 /// Gets the kernel version value.
163 u32 GetKernelVersion() const {
164 return kernel_version;
165 }
166
167 /// Whether or not this process can be debugged.
168 bool IsDebuggable() const {
169 return is_debuggable;
170 }
171
172 /// Whether or not this process can forcibly debug another
173 /// process, even if that process is not considered debuggable.
174 bool CanForceDebug() const {
175 return can_force_debug;
176 }
177
178private:
179 /// Attempts to parse a given sequence of capability descriptors.
180 ///
181 /// @param capabilities The sequence of capability descriptors to parse.
182 /// @param num_capabilities The number of descriptors within the given sequence.
183 /// @param page_table The memory manager that will perform any memory
184 /// mapping if necessary.
185 ///
186 /// @return ResultSuccess if no errors occur, otherwise an error code.
187 ///
188 Result ParseCapabilities(const u32* capabilities, std::size_t num_capabilities,
189 KPageTable& page_table);
190
191 /// Attempts to parse a capability descriptor that is only represented by a
192 /// single flag set.
193 ///
194 /// @param set_flags Running set of flags that are used to catch
195 /// flags being initialized more than once when they shouldn't be.
196 /// @param set_svc_bits Running set of bits representing the allowed supervisor calls mask.
197 /// @param flag The flag to attempt to parse.
198 /// @param page_table The memory manager that will perform any memory
199 /// mapping if necessary.
200 ///
201 /// @return ResultSuccess if no errors occurred, otherwise an error code.
202 ///
203 Result ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag,
204 KPageTable& page_table);
205
206 /// Clears the internal state of this process capability instance. Necessary,
207 /// to have a sane starting point due to us allowing running executables without
208 /// configuration metadata. We assume a process is not going to have metadata,
209 /// and if it turns out that the process does, in fact, have metadata, then
210 /// we attempt to parse it. Thus, we need this to reset data members back to
211 /// a good state.
212 ///
213 /// DO NOT ever make this a public member function. This isn't an invariant
214 /// anything external should depend upon (and if anything comes to rely on it,
215 /// you should immediately be questioning the design of that thing, not this
216 /// class. If the kernel itself can run without depending on behavior like that,
217 /// then so can yuzu).
218 ///
219 void Clear();
220
221 /// Handles flags related to the priority and core number capability flags.
222 Result HandlePriorityCoreNumFlags(u32 flags);
223
224 /// Handles flags related to determining the allowable SVC mask.
225 Result HandleSyscallFlags(u32& set_svc_bits, u32 flags);
226
227 /// Handles flags related to mapping physical memory pages.
228 Result HandleMapPhysicalFlags(u32 flags, u32 size_flags, KPageTable& page_table);
229
230 /// Handles flags related to mapping IO pages.
231 Result HandleMapIOFlags(u32 flags, KPageTable& page_table);
232
233 /// Handles flags related to mapping physical memory regions.
234 Result HandleMapRegionFlags(u32 flags, KPageTable& page_table);
235
236 /// Handles flags related to the interrupt capability flags.
237 Result HandleInterruptFlags(u32 flags);
238
239 /// Handles flags related to the program type.
240 Result HandleProgramTypeFlags(u32 flags);
241
242 /// Handles flags related to the handle table size.
243 Result HandleHandleTableFlags(u32 flags);
244
245 /// Handles flags related to the kernel version capability flags.
246 Result HandleKernelVersionFlags(u32 flags);
247
248 /// Handles flags related to debug-specific capabilities.
249 Result HandleDebugFlags(u32 flags);
250
251 SyscallCapabilities svc_capabilities;
252 InterruptCapabilities interrupt_capabilities;
253
254 u64 core_mask = 0;
255 u64 priority_mask = 0;
256
257 s32 handle_table_size = 0;
258 u32 kernel_version = 0;
259
260 ProgramType program_type = ProgramType::SysModule;
261
262 bool is_debuggable = false;
263 bool can_force_debug = false;
264};
265
266} // namespace Kernel
diff --git a/src/core/hle/kernel/svc/svc_memory.cpp b/src/core/hle/kernel/svc/svc_memory.cpp
index 97f1210de..4ca62860d 100644
--- a/src/core/hle/kernel/svc/svc_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_memory.cpp
@@ -29,7 +29,8 @@ constexpr bool IsValidAddressRange(u64 address, u64 size) {
29// Helper function that performs the common sanity checks for svcMapMemory 29// Helper function that performs the common sanity checks for svcMapMemory
30// and svcUnmapMemory. This is doable, as both functions perform their sanitizing 30// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
31// in the same order. 31// in the same order.
32Result MapUnmapMemorySanityChecks(const KPageTable& manager, u64 dst_addr, u64 src_addr, u64 size) { 32Result MapUnmapMemorySanityChecks(const KProcessPageTable& manager, u64 dst_addr, u64 src_addr,
33 u64 size) {
33 if (!Common::Is4KBAligned(dst_addr)) { 34 if (!Common::Is4KBAligned(dst_addr)) {
34 LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr); 35 LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
35 R_THROW(ResultInvalidAddress); 36 R_THROW(ResultInvalidAddress);
@@ -123,7 +124,8 @@ Result SetMemoryAttribute(Core::System& system, u64 address, u64 size, u32 mask,
123 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); 124 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
124 125
125 // Set the memory attribute. 126 // Set the memory attribute.
126 R_RETURN(page_table.SetMemoryAttribute(address, size, mask, attr)); 127 R_RETURN(page_table.SetMemoryAttribute(address, size, static_cast<KMemoryAttribute>(mask),
128 static_cast<KMemoryAttribute>(attr)));
127} 129}
128 130
129/// Maps a memory range into a different range. 131/// Maps a memory range into a different range.
diff --git a/src/core/hle/kernel/svc/svc_physical_memory.cpp b/src/core/hle/kernel/svc/svc_physical_memory.cpp
index 99330d02a..793e9f8d0 100644
--- a/src/core/hle/kernel/svc/svc_physical_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_physical_memory.cpp
@@ -16,7 +16,14 @@ Result SetHeapSize(Core::System& system, u64* out_address, u64 size) {
16 R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize); 16 R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize);
17 17
18 // Set the heap size. 18 // Set the heap size.
19 R_RETURN(GetCurrentProcess(system.Kernel()).GetPageTable().SetHeapSize(out_address, size)); 19 KProcessAddress address{};
20 R_TRY(GetCurrentProcess(system.Kernel())
21 .GetPageTable()
22 .SetHeapSize(std::addressof(address), size));
23
24 // We succeeded.
25 *out_address = GetInteger(address);
26 R_SUCCEED();
20} 27}
21 28
22/// Maps memory at a desired address 29/// Maps memory at a desired address
diff --git a/src/core/hle/kernel/svc/svc_process_memory.cpp b/src/core/hle/kernel/svc/svc_process_memory.cpp
index 07cd48175..e1427947b 100644
--- a/src/core/hle/kernel/svc/svc_process_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_process_memory.cpp
@@ -247,8 +247,7 @@ Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 d
247 R_THROW(ResultInvalidCurrentMemory); 247 R_THROW(ResultInvalidCurrentMemory);
248 } 248 }
249 249
250 R_RETURN(page_table.UnmapCodeMemory(dst_address, src_address, size, 250 R_RETURN(page_table.UnmapCodeMemory(dst_address, src_address, size));
251 KPageTable::ICacheInvalidationStrategy::InvalidateAll));
252} 251}
253 252
254Result SetProcessMemoryPermission64(Core::System& system, Handle process_handle, uint64_t address, 253Result SetProcessMemoryPermission64(Core::System& system, Handle process_handle, uint64_t address,
diff --git a/src/core/hle/kernel/svc/svc_query_memory.cpp b/src/core/hle/kernel/svc/svc_query_memory.cpp
index 51af06e97..816dcb8d0 100644
--- a/src/core/hle/kernel/svc/svc_query_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_query_memory.cpp
@@ -31,12 +31,12 @@ Result QueryProcessMemory(Core::System& system, uint64_t out_memory_info, PageIn
31 } 31 }
32 32
33 auto& current_memory{GetCurrentMemory(system.Kernel())}; 33 auto& current_memory{GetCurrentMemory(system.Kernel())};
34 const auto memory_info{process->GetPageTable().QueryInfo(address).GetSvcMemoryInfo()};
35 34
36 current_memory.WriteBlock(out_memory_info, std::addressof(memory_info), sizeof(memory_info)); 35 KMemoryInfo mem_info;
36 R_TRY(process->GetPageTable().QueryInfo(std::addressof(mem_info), out_page_info, address));
37 37
38 //! This is supposed to be part of the QueryInfo call. 38 const auto svc_mem_info = mem_info.GetSvcMemoryInfo();
39 *out_page_info = {}; 39 current_memory.WriteBlock(out_memory_info, std::addressof(svc_mem_info), sizeof(svc_mem_info));
40 40
41 R_SUCCEED(); 41 R_SUCCEED();
42} 42}
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index dd0b27f47..749f51f69 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -407,3 +407,34 @@ constexpr inline Result __TmpCurrentResultReference = ResultSuccess;
407 407
408/// Evaluates a boolean expression, and succeeds if that expression is true. 408/// Evaluates a boolean expression, and succeeds if that expression is true.
409#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), ResultSuccess) 409#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), ResultSuccess)
410
411#define R_TRY_CATCH(res_expr) \
412 { \
413 const auto R_CURRENT_RESULT = (res_expr); \
414 if (R_FAILED(R_CURRENT_RESULT)) { \
415 if (false)
416
417#define R_END_TRY_CATCH \
418 else if (R_FAILED(R_CURRENT_RESULT)) { \
419 R_THROW(R_CURRENT_RESULT); \
420 } \
421 } \
422 }
423
424#define R_CATCH_ALL() \
425 } \
426 else if (R_FAILED(R_CURRENT_RESULT)) { \
427 if (true)
428
429#define R_CATCH(res_expr) \
430 } \
431 else if ((res_expr) == (R_CURRENT_RESULT)) { \
432 if (true)
433
434#define R_CONVERT(catch_type, convert_type) \
435 R_CATCH(catch_type) { R_THROW(static_cast<Result>(convert_type)); }
436
437#define R_CONVERT_ALL(convert_type) \
438 R_CATCH_ALL() { R_THROW(static_cast<Result>(convert_type)); }
439
440#define R_ASSERT(res_expr) ASSERT(R_SUCCEEDED(res_expr))
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 1b1c8190e..f21553644 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -3,11 +3,13 @@
3 3
4#include <algorithm> 4#include <algorithm>
5#include <array> 5#include <array>
6
6#include "common/common_types.h" 7#include "common/common_types.h"
7#include "common/fs/file.h" 8#include "common/fs/file.h"
8#include "common/fs/path_util.h" 9#include "common/fs/path_util.h"
9#include "common/logging/log.h" 10#include "common/logging/log.h"
10#include "common/polyfill_ranges.h" 11#include "common/polyfill_ranges.h"
12#include "common/stb.h"
11#include "common/string_util.h" 13#include "common/string_util.h"
12#include "common/swap.h" 14#include "common/swap.h"
13#include "core/constants.h" 15#include "core/constants.h"
@@ -38,9 +40,36 @@ static std::filesystem::path GetImagePath(const Common::UUID& uuid) {
38 fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString()); 40 fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString());
39} 41}
40 42
41static constexpr u32 SanitizeJPEGSize(std::size_t size) { 43static void JPGToMemory(void* context, void* data, int len) {
44 std::vector<u8>* jpg_image = static_cast<std::vector<u8>*>(context);
45 unsigned char* jpg = static_cast<unsigned char*>(data);
46 jpg_image->insert(jpg_image->end(), jpg, jpg + len);
47}
48
49static void SanitizeJPEGImageSize(std::vector<u8>& image) {
42 constexpr std::size_t max_jpeg_image_size = 0x20000; 50 constexpr std::size_t max_jpeg_image_size = 0x20000;
43 return static_cast<u32>(std::min(size, max_jpeg_image_size)); 51 constexpr int profile_dimensions = 256;
52 int original_width, original_height, color_channels;
53
54 const auto plain_image =
55 stbi_load_from_memory(image.data(), static_cast<int>(image.size()), &original_width,
56 &original_height, &color_channels, STBI_rgb);
57
58 // Resize image to match 256*256
59 if (original_width != profile_dimensions || original_height != profile_dimensions) {
60 // Use vector instead of array to avoid overflowing the stack
61 std::vector<u8> out_image(profile_dimensions * profile_dimensions * STBI_rgb);
62 stbir_resize_uint8_srgb(plain_image, original_width, original_height, 0, out_image.data(),
63 profile_dimensions, profile_dimensions, 0, STBI_rgb, 0,
64 STBIR_FILTER_BOX);
65 image.clear();
66 if (!stbi_write_jpg_to_func(JPGToMemory, &image, profile_dimensions, profile_dimensions,
67 STBI_rgb, out_image.data(), 0)) {
68 LOG_ERROR(Service_ACC, "Failed to resize the user provided image.");
69 }
70 }
71
72 image.resize(std::min(image.size(), max_jpeg_image_size));
44} 73}
45 74
46class IManagerForSystemService final : public ServiceFramework<IManagerForSystemService> { 75class IManagerForSystemService final : public ServiceFramework<IManagerForSystemService> {
@@ -339,19 +368,20 @@ protected:
339 LOG_WARNING(Service_ACC, 368 LOG_WARNING(Service_ACC,
340 "Failed to load user provided image! Falling back to built-in backup..."); 369 "Failed to load user provided image! Falling back to built-in backup...");
341 ctx.WriteBuffer(Core::Constants::ACCOUNT_BACKUP_JPEG); 370 ctx.WriteBuffer(Core::Constants::ACCOUNT_BACKUP_JPEG);
342 rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); 371 rb.Push(static_cast<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size()));
343 return; 372 return;
344 } 373 }
345 374
346 const u32 size = SanitizeJPEGSize(image.GetSize()); 375 std::vector<u8> buffer(image.GetSize());
347 std::vector<u8> buffer(size);
348 376
349 if (image.Read(buffer) != buffer.size()) { 377 if (image.Read(buffer) != buffer.size()) {
350 LOG_ERROR(Service_ACC, "Failed to read all the bytes in the user provided image."); 378 LOG_ERROR(Service_ACC, "Failed to read all the bytes in the user provided image.");
351 } 379 }
352 380
381 SanitizeJPEGImageSize(buffer);
382
353 ctx.WriteBuffer(buffer); 383 ctx.WriteBuffer(buffer);
354 rb.Push<u32>(size); 384 rb.Push(static_cast<u32>(buffer.size()));
355 } 385 }
356 386
357 void GetImageSize(HLERequestContext& ctx) { 387 void GetImageSize(HLERequestContext& ctx) {
@@ -365,10 +395,18 @@ protected:
365 if (!image.IsOpen()) { 395 if (!image.IsOpen()) {
366 LOG_WARNING(Service_ACC, 396 LOG_WARNING(Service_ACC,
367 "Failed to load user provided image! Falling back to built-in backup..."); 397 "Failed to load user provided image! Falling back to built-in backup...");
368 rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); 398 rb.Push(static_cast<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size()));
369 } else { 399 return;
370 rb.Push(SanitizeJPEGSize(image.GetSize()));
371 } 400 }
401
402 std::vector<u8> buffer(image.GetSize());
403
404 if (image.Read(buffer) != buffer.size()) {
405 LOG_ERROR(Service_ACC, "Failed to read all the bytes in the user provided image.");
406 }
407
408 SanitizeJPEGImageSize(buffer);
409 rb.Push(static_cast<u32>(buffer.size()));
372 } 410 }
373 411
374 void Store(HLERequestContext& ctx) { 412 void Store(HLERequestContext& ctx) {
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index cc643ea09..a266d7c21 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -13,6 +13,7 @@
13#include "core/file_sys/patch_manager.h" 13#include "core/file_sys/patch_manager.h"
14#include "core/file_sys/registered_cache.h" 14#include "core/file_sys/registered_cache.h"
15#include "core/file_sys/savedata_factory.h" 15#include "core/file_sys/savedata_factory.h"
16#include "core/hid/hid_types.h"
16#include "core/hle/kernel/k_event.h" 17#include "core/hle/kernel/k_event.h"
17#include "core/hle/kernel/k_transfer_memory.h" 18#include "core/hle/kernel/k_transfer_memory.h"
18#include "core/hle/result.h" 19#include "core/hle/result.h"
@@ -21,6 +22,7 @@
21#include "core/hle/service/am/applet_ae.h" 22#include "core/hle/service/am/applet_ae.h"
22#include "core/hle/service/am/applet_oe.h" 23#include "core/hle/service/am/applet_oe.h"
23#include "core/hle/service/am/applets/applet_cabinet.h" 24#include "core/hle/service/am/applets/applet_cabinet.h"
25#include "core/hle/service/am/applets/applet_controller.h"
24#include "core/hle/service/am/applets/applet_mii_edit_types.h" 26#include "core/hle/service/am/applets/applet_mii_edit_types.h"
25#include "core/hle/service/am/applets/applet_profile_select.h" 27#include "core/hle/service/am/applets/applet_profile_select.h"
26#include "core/hle/service/am/applets/applet_software_keyboard_types.h" 28#include "core/hle/service/am/applets/applet_software_keyboard_types.h"
@@ -35,6 +37,7 @@
35#include "core/hle/service/caps/caps_su.h" 37#include "core/hle/service/caps/caps_su.h"
36#include "core/hle/service/caps/caps_types.h" 38#include "core/hle/service/caps/caps_types.h"
37#include "core/hle/service/filesystem/filesystem.h" 39#include "core/hle/service/filesystem/filesystem.h"
40#include "core/hle/service/hid/controllers/npad.h"
38#include "core/hle/service/ipc_helpers.h" 41#include "core/hle/service/ipc_helpers.h"
39#include "core/hle/service/ns/ns.h" 42#include "core/hle/service/ns/ns.h"
40#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" 43#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
@@ -73,7 +76,7 @@ IWindowController::IWindowController(Core::System& system_)
73 static const FunctionInfo functions[] = { 76 static const FunctionInfo functions[] = {
74 {0, nullptr, "CreateWindow"}, 77 {0, nullptr, "CreateWindow"},
75 {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, 78 {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"},
76 {2, nullptr, "GetAppletResourceUserIdOfCallerApplet"}, 79 {2, &IWindowController::GetAppletResourceUserIdOfCallerApplet, "GetAppletResourceUserIdOfCallerApplet"},
77 {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, 80 {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"},
78 {11, nullptr, "ReleaseForegroundRights"}, 81 {11, nullptr, "ReleaseForegroundRights"},
79 {12, nullptr, "RejectToChangeIntoBackground"}, 82 {12, nullptr, "RejectToChangeIntoBackground"},
@@ -97,6 +100,16 @@ void IWindowController::GetAppletResourceUserId(HLERequestContext& ctx) {
97 rb.Push<u64>(process_id); 100 rb.Push<u64>(process_id);
98} 101}
99 102
103void IWindowController::GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx) {
104 const u64 process_id = 0;
105
106 LOG_WARNING(Service_AM, "(STUBBED) called");
107
108 IPC::ResponseBuilder rb{ctx, 4};
109 rb.Push(ResultSuccess);
110 rb.Push<u64>(process_id);
111}
112
100void IWindowController::AcquireForegroundRights(HLERequestContext& ctx) { 113void IWindowController::AcquireForegroundRights(HLERequestContext& ctx) {
101 LOG_WARNING(Service_AM, "(STUBBED) called"); 114 LOG_WARNING(Service_AM, "(STUBBED) called");
102 IPC::ResponseBuilder rb{ctx, 2}; 115 IPC::ResponseBuilder rb{ctx, 2};
@@ -1565,7 +1578,7 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_)
1565 {6, nullptr, "GetPopInteractiveInDataEvent"}, 1578 {6, nullptr, "GetPopInteractiveInDataEvent"},
1566 {10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"}, 1579 {10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"},
1567 {11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"}, 1580 {11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"},
1568 {12, nullptr, "GetMainAppletIdentityInfo"}, 1581 {12, &ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo, "GetMainAppletIdentityInfo"},
1569 {13, nullptr, "CanUseApplicationCore"}, 1582 {13, nullptr, "CanUseApplicationCore"},
1570 {14, &ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo, "GetCallerAppletIdentityInfo"}, 1583 {14, &ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo, "GetCallerAppletIdentityInfo"},
1571 {15, nullptr, "GetMainAppletApplicationControlProperty"}, 1584 {15, nullptr, "GetMainAppletApplicationControlProperty"},
@@ -1609,6 +1622,9 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_)
1609 case Applets::AppletId::SoftwareKeyboard: 1622 case Applets::AppletId::SoftwareKeyboard:
1610 PushInShowSoftwareKeyboard(); 1623 PushInShowSoftwareKeyboard();
1611 break; 1624 break;
1625 case Applets::AppletId::Controller:
1626 PushInShowController();
1627 break;
1612 default: 1628 default:
1613 break; 1629 break;
1614 } 1630 }
@@ -1666,13 +1682,33 @@ void ILibraryAppletSelfAccessor::GetLibraryAppletInfo(HLERequestContext& ctx) {
1666 rb.PushRaw(applet_info); 1682 rb.PushRaw(applet_info);
1667} 1683}
1668 1684
1669void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) { 1685void ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo(HLERequestContext& ctx) {
1670 struct AppletIdentityInfo { 1686 struct AppletIdentityInfo {
1671 Applets::AppletId applet_id; 1687 Applets::AppletId applet_id;
1672 INSERT_PADDING_BYTES(0x4); 1688 INSERT_PADDING_BYTES(0x4);
1673 u64 application_id; 1689 u64 application_id;
1674 }; 1690 };
1691 static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size.");
1692
1693 LOG_WARNING(Service_AM, "(STUBBED) called");
1694
1695 const AppletIdentityInfo applet_info{
1696 .applet_id = Applets::AppletId::QLaunch,
1697 .application_id = 0x0100000000001000ull,
1698 };
1699
1700 IPC::ResponseBuilder rb{ctx, 6};
1701 rb.Push(ResultSuccess);
1702 rb.PushRaw(applet_info);
1703}
1675 1704
1705void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) {
1706 struct AppletIdentityInfo {
1707 Applets::AppletId applet_id;
1708 INSERT_PADDING_BYTES(0x4);
1709 u64 application_id;
1710 };
1711 static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size.");
1676 LOG_WARNING(Service_AM, "(STUBBED) called"); 1712 LOG_WARNING(Service_AM, "(STUBBED) called");
1677 1713
1678 const AppletIdentityInfo applet_info{ 1714 const AppletIdentityInfo applet_info{
@@ -1737,6 +1773,55 @@ void ILibraryAppletSelfAccessor::PushInShowAlbum() {
1737 queue_data.emplace_back(std::move(settings_data)); 1773 queue_data.emplace_back(std::move(settings_data));
1738} 1774}
1739 1775
1776void ILibraryAppletSelfAccessor::PushInShowController() {
1777 const Applets::CommonArguments common_args = {
1778 .arguments_version = Applets::CommonArgumentVersion::Version3,
1779 .size = Applets::CommonArgumentSize::Version3,
1780 .library_version = static_cast<u32>(Applets::ControllerAppletVersion::Version8),
1781 .theme_color = Applets::ThemeColor::BasicBlack,
1782 .play_startup_sound = true,
1783 .system_tick = system.CoreTiming().GetClockTicks(),
1784 };
1785
1786 Applets::ControllerSupportArgNew user_args = {
1787 .header = {.player_count_min = 1,
1788 .player_count_max = 4,
1789 .enable_take_over_connection = true,
1790 .enable_left_justify = false,
1791 .enable_permit_joy_dual = true,
1792 .enable_single_mode = false,
1793 .enable_identification_color = false},
1794 .identification_colors = {},
1795 .enable_explain_text = false,
1796 .explain_text = {},
1797 };
1798
1799 Applets::ControllerSupportArgPrivate private_args = {
1800 .arg_private_size = sizeof(Applets::ControllerSupportArgPrivate),
1801 .arg_size = sizeof(Applets::ControllerSupportArgNew),
1802 .is_home_menu = true,
1803 .flag_1 = true,
1804 .mode = Applets::ControllerSupportMode::ShowControllerSupport,
1805 .caller = Applets::ControllerSupportCaller::
1806 Application, // switchbrew: Always zero except with
1807 // ShowControllerFirmwareUpdateForSystem/ShowControllerKeyRemappingForSystem,
1808 // which sets this to the input param
1809 .style_set = Core::HID::NpadStyleSet::None,
1810 .joy_hold_type = 0,
1811 };
1812 std::vector<u8> common_args_data(sizeof(common_args));
1813 std::vector<u8> private_args_data(sizeof(private_args));
1814 std::vector<u8> user_args_data(sizeof(user_args));
1815
1816 std::memcpy(common_args_data.data(), &common_args, sizeof(common_args));
1817 std::memcpy(private_args_data.data(), &private_args, sizeof(private_args));
1818 std::memcpy(user_args_data.data(), &user_args, sizeof(user_args));
1819
1820 queue_data.emplace_back(std::move(common_args_data));
1821 queue_data.emplace_back(std::move(private_args_data));
1822 queue_data.emplace_back(std::move(user_args_data));
1823}
1824
1740void ILibraryAppletSelfAccessor::PushInShowCabinetData() { 1825void ILibraryAppletSelfAccessor::PushInShowCabinetData() {
1741 const Applets::CommonArguments arguments{ 1826 const Applets::CommonArguments arguments{
1742 .arguments_version = Applets::CommonArgumentVersion::Version3, 1827 .arguments_version = Applets::CommonArgumentVersion::Version3,
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 8f8cb8a9e..905a71b9f 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -87,6 +87,7 @@ public:
87 87
88private: 88private:
89 void GetAppletResourceUserId(HLERequestContext& ctx); 89 void GetAppletResourceUserId(HLERequestContext& ctx);
90 void GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx);
90 void AcquireForegroundRights(HLERequestContext& ctx); 91 void AcquireForegroundRights(HLERequestContext& ctx);
91}; 92};
92 93
@@ -345,6 +346,7 @@ private:
345 void PopInData(HLERequestContext& ctx); 346 void PopInData(HLERequestContext& ctx);
346 void PushOutData(HLERequestContext& ctx); 347 void PushOutData(HLERequestContext& ctx);
347 void GetLibraryAppletInfo(HLERequestContext& ctx); 348 void GetLibraryAppletInfo(HLERequestContext& ctx);
349 void GetMainAppletIdentityInfo(HLERequestContext& ctx);
348 void ExitProcessAndReturn(HLERequestContext& ctx); 350 void ExitProcessAndReturn(HLERequestContext& ctx);
349 void GetCallerAppletIdentityInfo(HLERequestContext& ctx); 351 void GetCallerAppletIdentityInfo(HLERequestContext& ctx);
350 void GetDesirableKeyboardLayout(HLERequestContext& ctx); 352 void GetDesirableKeyboardLayout(HLERequestContext& ctx);
@@ -355,6 +357,7 @@ private:
355 void PushInShowCabinetData(); 357 void PushInShowCabinetData();
356 void PushInShowMiiEditData(); 358 void PushInShowMiiEditData();
357 void PushInShowSoftwareKeyboard(); 359 void PushInShowSoftwareKeyboard();
360 void PushInShowController();
358 361
359 std::deque<std::vector<u8>> queue_data; 362 std::deque<std::vector<u8>> queue_data;
360}; 363};
diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp
index b379dadeb..9d1960cb7 100644
--- a/src/core/hle/service/am/applets/applet_cabinet.cpp
+++ b/src/core/hle/service/am/applets/applet_cabinet.cpp
@@ -122,7 +122,8 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name)
122 Service::NFP::RegisterInfoPrivate register_info{}; 122 Service::NFP::RegisterInfoPrivate register_info{};
123 std::memcpy(register_info.amiibo_name.data(), amiibo_name.data(), 123 std::memcpy(register_info.amiibo_name.data(), amiibo_name.data(),
124 std::min(amiibo_name.size(), register_info.amiibo_name.size() - 1)); 124 std::min(amiibo_name.size(), register_info.amiibo_name.size() - 1));
125 125 register_info.mii_store_data.BuildRandom(Mii::Age::All, Mii::Gender::All, Mii::Race::All);
126 register_info.mii_store_data.SetNickname({u'y', u'u', u'z', u'u'});
126 nfp_device->SetRegisterInfoPrivate(register_info); 127 nfp_device->SetRegisterInfoPrivate(register_info);
127 break; 128 break;
128 } 129 }
diff --git a/src/core/hle/service/am/applets/applet_controller.h b/src/core/hle/service/am/applets/applet_controller.h
index f6c64f633..9f839f3d7 100644
--- a/src/core/hle/service/am/applets/applet_controller.h
+++ b/src/core/hle/service/am/applets/applet_controller.h
@@ -56,7 +56,7 @@ enum class ControllerSupportResult : u32 {
56struct ControllerSupportArgPrivate { 56struct ControllerSupportArgPrivate {
57 u32 arg_private_size{}; 57 u32 arg_private_size{};
58 u32 arg_size{}; 58 u32 arg_size{};
59 bool flag_0{}; 59 bool is_home_menu{};
60 bool flag_1{}; 60 bool flag_1{};
61 ControllerSupportMode mode{}; 61 ControllerSupportMode mode{};
62 ControllerSupportCaller caller{}; 62 ControllerSupportCaller caller{};
diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp
index 1c9a1dc29..b0ea2b381 100644
--- a/src/core/hle/service/am/applets/applet_web_browser.cpp
+++ b/src/core/hle/service/am/applets/applet_web_browser.cpp
@@ -330,8 +330,7 @@ void WebBrowser::ExtractOfflineRomFS() {
330 LOG_DEBUG(Service_AM, "Extracting RomFS to {}", 330 LOG_DEBUG(Service_AM, "Extracting RomFS to {}",
331 Common::FS::PathToUTF8String(offline_cache_dir)); 331 Common::FS::PathToUTF8String(offline_cache_dir));
332 332
333 const auto extracted_romfs_dir = 333 const auto extracted_romfs_dir = FileSys::ExtractRomFS(offline_romfs);
334 FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard);
335 334
336 const auto temp_dir = system.GetFilesystem()->CreateDirectory( 335 const auto temp_dir = system.GetFilesystem()->CreateDirectory(
337 Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite); 336 Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite);
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index f02bbc450..0bf2598b7 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -69,6 +69,30 @@ enum class AppletId : u32 {
69 MyPage = 0x1A, 69 MyPage = 0x1A,
70}; 70};
71 71
72enum class AppletProgramId : u64 {
73 QLaunch = 0x0100000000001000ull,
74 Auth = 0x0100000000001001ull,
75 Cabinet = 0x0100000000001002ull,
76 Controller = 0x0100000000001003ull,
77 DataErase = 0x0100000000001004ull,
78 Error = 0x0100000000001005ull,
79 NetConnect = 0x0100000000001006ull,
80 ProfileSelect = 0x0100000000001007ull,
81 SoftwareKeyboard = 0x0100000000001008ull,
82 MiiEdit = 0x0100000000001009ull,
83 Web = 0x010000000000100Aull,
84 Shop = 0x010000000000100Bull,
85 OverlayDisplay = 0x010000000000100Cull,
86 PhotoViewer = 0x010000000000100Dull,
87 Settings = 0x010000000000100Eull,
88 OfflineWeb = 0x010000000000100Full,
89 LoginShare = 0x0100000000001010ull,
90 WebAuth = 0x0100000000001011ull,
91 Starter = 0x0100000000001012ull,
92 MyPage = 0x0100000000001013ull,
93 MaxProgramId = 0x0100000000001FFFull,
94};
95
72enum class LibraryAppletMode : u32 { 96enum class LibraryAppletMode : u32 {
73 AllForeground = 0, 97 AllForeground = 0,
74 Background = 1, 98 Background = 1,
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index 8069f75b7..c65e32489 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -127,7 +127,7 @@ public:
127 127
128private: 128private:
129 void GetCore(HLERequestContext& ctx) { 129 void GetCore(HLERequestContext& ctx) {
130 LOG_DEBUG(Service_BTM, "called"); 130 LOG_WARNING(Service_BTM, "called");
131 131
132 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 132 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
133 rb.Push(ResultSuccess); 133 rb.Push(ResultSuccess);
@@ -263,13 +263,13 @@ public:
263 explicit IBtmSystemCore(Core::System& system_) : ServiceFramework{system_, "IBtmSystemCore"} { 263 explicit IBtmSystemCore(Core::System& system_) : ServiceFramework{system_, "IBtmSystemCore"} {
264 // clang-format off 264 // clang-format off
265 static const FunctionInfo functions[] = { 265 static const FunctionInfo functions[] = {
266 {0, nullptr, "StartGamepadPairing"}, 266 {0, &IBtmSystemCore::StartGamepadPairing, "StartGamepadPairing"},
267 {1, nullptr, "CancelGamepadPairing"}, 267 {1, &IBtmSystemCore::CancelGamepadPairing, "CancelGamepadPairing"},
268 {2, nullptr, "ClearGamepadPairingDatabase"}, 268 {2, nullptr, "ClearGamepadPairingDatabase"},
269 {3, nullptr, "GetPairedGamepadCount"}, 269 {3, nullptr, "GetPairedGamepadCount"},
270 {4, nullptr, "EnableRadio"}, 270 {4, nullptr, "EnableRadio"},
271 {5, nullptr, "DisableRadio"}, 271 {5, nullptr, "DisableRadio"},
272 {6, nullptr, "GetRadioOnOff"}, 272 {6, &IBtmSystemCore::IsRadioEnabled, "IsRadioEnabled"},
273 {7, nullptr, "AcquireRadioEvent"}, 273 {7, nullptr, "AcquireRadioEvent"},
274 {8, nullptr, "AcquireGamepadPairingEvent"}, 274 {8, nullptr, "AcquireGamepadPairingEvent"},
275 {9, nullptr, "IsGamepadPairingStarted"}, 275 {9, nullptr, "IsGamepadPairingStarted"},
@@ -280,18 +280,58 @@ public:
280 {14, nullptr, "AcquireAudioDeviceConnectionEvent"}, 280 {14, nullptr, "AcquireAudioDeviceConnectionEvent"},
281 {15, nullptr, "ConnectAudioDevice"}, 281 {15, nullptr, "ConnectAudioDevice"},
282 {16, nullptr, "IsConnectingAudioDevice"}, 282 {16, nullptr, "IsConnectingAudioDevice"},
283 {17, nullptr, "GetConnectedAudioDevices"}, 283 {17, &IBtmSystemCore::GetConnectedAudioDevices, "GetConnectedAudioDevices"},
284 {18, nullptr, "DisconnectAudioDevice"}, 284 {18, nullptr, "DisconnectAudioDevice"},
285 {19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"}, 285 {19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"},
286 {20, nullptr, "GetPairedAudioDevices"}, 286 {20, nullptr, "GetPairedAudioDevices"},
287 {21, nullptr, "RemoveAudioDevicePairing"}, 287 {21, nullptr, "RemoveAudioDevicePairing"},
288 {22, nullptr, "RequestAudioDeviceConnectionRejection"}, 288 {22, &IBtmSystemCore::RequestAudioDeviceConnectionRejection, "RequestAudioDeviceConnectionRejection"},
289 {23, nullptr, "CancelAudioDeviceConnectionRejection"} 289 {23, &IBtmSystemCore::CancelAudioDeviceConnectionRejection, "CancelAudioDeviceConnectionRejection"}
290 }; 290 };
291 // clang-format on 291 // clang-format on
292 292
293 RegisterHandlers(functions); 293 RegisterHandlers(functions);
294 } 294 }
295
296private:
297 void IsRadioEnabled(HLERequestContext& ctx) {
298 LOG_DEBUG(Service_BTM, "(STUBBED) called"); // Spams a lot when controller applet is running
299
300 IPC::ResponseBuilder rb{ctx, 3};
301 rb.Push(ResultSuccess);
302 rb.Push(true);
303 }
304
305 void StartGamepadPairing(HLERequestContext& ctx) {
306 LOG_WARNING(Service_BTM, "(STUBBED) called");
307 IPC::ResponseBuilder rb{ctx, 2};
308 rb.Push(ResultSuccess);
309 }
310
311 void CancelGamepadPairing(HLERequestContext& ctx) {
312 LOG_WARNING(Service_BTM, "(STUBBED) called");
313 IPC::ResponseBuilder rb{ctx, 2};
314 rb.Push(ResultSuccess);
315 }
316
317 void CancelAudioDeviceConnectionRejection(HLERequestContext& ctx) {
318 LOG_WARNING(Service_BTM, "(STUBBED) called");
319 IPC::ResponseBuilder rb{ctx, 2};
320 rb.Push(ResultSuccess);
321 }
322
323 void GetConnectedAudioDevices(HLERequestContext& ctx) {
324 LOG_WARNING(Service_BTM, "(STUBBED) called");
325 IPC::ResponseBuilder rb{ctx, 3};
326 rb.Push(ResultSuccess);
327 rb.Push<u32>(0);
328 }
329
330 void RequestAudioDeviceConnectionRejection(HLERequestContext& ctx) {
331 LOG_WARNING(Service_BTM, "(STUBBED) called");
332 IPC::ResponseBuilder rb{ctx, 2};
333 rb.Push(ResultSuccess);
334 }
295}; 335};
296 336
297class BTM_SYS final : public ServiceFramework<BTM_SYS> { 337class BTM_SYS final : public ServiceFramework<BTM_SYS> {
@@ -308,7 +348,7 @@ public:
308 348
309private: 349private:
310 void GetCore(HLERequestContext& ctx) { 350 void GetCore(HLERequestContext& ctx) {
311 LOG_DEBUG(Service_BTM, "called"); 351 LOG_WARNING(Service_BTM, "called");
312 352
313 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 353 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
314 rb.Push(ResultSuccess); 354 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index 9d05f9801..0507b14e7 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -32,7 +32,7 @@ public:
32 {10200, nullptr, "SendFriendRequestForApplication"}, 32 {10200, nullptr, "SendFriendRequestForApplication"},
33 {10211, nullptr, "AddFacedFriendRequestForApplication"}, 33 {10211, nullptr, "AddFacedFriendRequestForApplication"},
34 {10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"}, 34 {10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"},
35 {10420, nullptr, "IsBlockedUserListCacheAvailable"}, 35 {10420, &IFriendService::CheckBlockedUserListAvailability, "CheckBlockedUserListAvailability"},
36 {10421, nullptr, "EnsureBlockedUserListAvailable"}, 36 {10421, nullptr, "EnsureBlockedUserListAvailable"},
37 {10500, nullptr, "GetProfileList"}, 37 {10500, nullptr, "GetProfileList"},
38 {10600, nullptr, "DeclareOpenOnlinePlaySession"}, 38 {10600, nullptr, "DeclareOpenOnlinePlaySession"},
@@ -206,6 +206,17 @@ private:
206 rb.Push(true); 206 rb.Push(true);
207 } 207 }
208 208
209 void CheckBlockedUserListAvailability(HLERequestContext& ctx) {
210 IPC::RequestParser rp{ctx};
211 const auto uuid{rp.PopRaw<Common::UUID>()};
212
213 LOG_WARNING(Service_Friend, "(STUBBED) called, uuid=0x{}", uuid.RawString());
214
215 IPC::ResponseBuilder rb{ctx, 3};
216 rb.Push(ResultSuccess);
217 rb.Push(true);
218 }
219
209 KernelHelpers::ServiceContext service_context; 220 KernelHelpers::ServiceContext service_context;
210 221
211 Kernel::KEvent* completion_event; 222 Kernel::KEvent* completion_event;
diff --git a/src/core/hle/service/hid/controllers/console_six_axis.cpp b/src/core/hle/service/hid/controllers/console_six_axis.cpp
new file mode 100644
index 000000000..b2bf1d78d
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/console_six_axis.cpp
@@ -0,0 +1,42 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hid/emulated_console.h"
7#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/console_six_axis.h"
9#include "core/memory.h"
10
11namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200;
13
14ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
15 : ControllerBase{hid_core_} {
16 console = hid_core.GetEmulatedConsole();
17 static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size,
18 "ConsoleSharedMemory is bigger than the shared memory");
19 shared_memory = std::construct_at(
20 reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
21}
22
23ConsoleSixAxis::~ConsoleSixAxis() = default;
24
25void ConsoleSixAxis::OnInit() {}
26
27void ConsoleSixAxis::OnRelease() {}
28
29void ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
30 if (!IsControllerActivated()) {
31 return;
32 }
33
34 const auto motion_status = console->GetMotion();
35
36 shared_memory->sampling_number++;
37 shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
38 shared_memory->verticalization_error = motion_status.verticalization_error;
39 shared_memory->gyro_bias = motion_status.gyro_bias;
40}
41
42} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/console_six_axis.h b/src/core/hle/service/hid/controllers/console_six_axis.h
new file mode 100644
index 000000000..5b7c6a29a
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/console_six_axis.h
@@ -0,0 +1,43 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/vector_math.h"
7#include "core/hle/service/hid/controllers/controller_base.h"
8
9namespace Core::HID {
10class EmulatedConsole;
11} // namespace Core::HID
12
13namespace Service::HID {
14class ConsoleSixAxis final : public ControllerBase {
15public:
16 explicit ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
17 ~ConsoleSixAxis() override;
18
19 // Called when the controller is initialized
20 void OnInit() override;
21
22 // When the controller is released
23 void OnRelease() override;
24
25 // When the controller is requesting an update for the shared memory
26 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
27
28private:
29 // This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
30 struct ConsoleSharedMemory {
31 u64 sampling_number{};
32 bool is_seven_six_axis_sensor_at_rest{};
33 INSERT_PADDING_BYTES(3); // padding
34 f32 verticalization_error{};
35 Common::Vec3f gyro_bias{};
36 INSERT_PADDING_BYTES(4); // padding
37 };
38 static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
39
40 ConsoleSharedMemory* shared_memory = nullptr;
41 Core::HID::EmulatedConsole* console = nullptr;
42};
43} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp
index c58d67d7d..0bcd87062 100644
--- a/src/core/hle/service/hid/controllers/controller_base.cpp
+++ b/src/core/hle/service/hid/controllers/controller_base.cpp
@@ -8,12 +8,17 @@ namespace Service::HID {
8ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {} 8ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {}
9ControllerBase::~ControllerBase() = default; 9ControllerBase::~ControllerBase() = default;
10 10
11void ControllerBase::ActivateController() { 11Result ControllerBase::Activate() {
12 if (is_activated) { 12 if (is_activated) {
13 return; 13 return ResultSuccess;
14 } 14 }
15 is_activated = true; 15 is_activated = true;
16 OnInit(); 16 OnInit();
17 return ResultSuccess;
18}
19
20Result ControllerBase::Activate(u64 aruid) {
21 return Activate();
17} 22}
18 23
19void ControllerBase::DeactivateController() { 24void ControllerBase::DeactivateController() {
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index d6f7a5073..9a44ee41e 100644
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/hle/result.h"
7 8
8namespace Core::Timing { 9namespace Core::Timing {
9class CoreTiming; 10class CoreTiming;
@@ -31,7 +32,8 @@ public:
31 // When the controller is requesting a motion update for the shared memory 32 // When the controller is requesting a motion update for the shared memory
32 virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {} 33 virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {}
33 34
34 void ActivateController(); 35 Result Activate();
36 Result Activate(u64 aruid);
35 37
36 void DeactivateController(); 38 void DeactivateController();
37 39
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index 8ec9f4a95..9de19ebfc 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -13,7 +13,7 @@
13namespace Service::HID { 13namespace Service::HID {
14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000; 14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000;
15 15
16Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) 16DebugPad::DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
17 : ControllerBase{hid_core_} { 17 : ControllerBase{hid_core_} {
18 static_assert(SHARED_MEMORY_OFFSET + sizeof(DebugPadSharedMemory) < shared_memory_size, 18 static_assert(SHARED_MEMORY_OFFSET + sizeof(DebugPadSharedMemory) < shared_memory_size,
19 "DebugPadSharedMemory is bigger than the shared memory"); 19 "DebugPadSharedMemory is bigger than the shared memory");
@@ -22,13 +22,13 @@ Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_
22 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); 22 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
23} 23}
24 24
25Controller_DebugPad::~Controller_DebugPad() = default; 25DebugPad::~DebugPad() = default;
26 26
27void Controller_DebugPad::OnInit() {} 27void DebugPad::OnInit() {}
28 28
29void Controller_DebugPad::OnRelease() {} 29void DebugPad::OnRelease() {}
30 30
31void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 31void DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
32 if (!IsControllerActivated()) { 32 if (!IsControllerActivated()) {
33 shared_memory->debug_pad_lifo.buffer_count = 0; 33 shared_memory->debug_pad_lifo.buffer_count = 0;
34 shared_memory->debug_pad_lifo.buffer_tail = 0; 34 shared_memory->debug_pad_lifo.buffer_tail = 0;
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index 68ff0ea79..5566dba77 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -15,10 +15,10 @@ struct AnalogStickState;
15} // namespace Core::HID 15} // namespace Core::HID
16 16
17namespace Service::HID { 17namespace Service::HID {
18class Controller_DebugPad final : public ControllerBase { 18class DebugPad final : public ControllerBase {
19public: 19public:
20 explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 20 explicit DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
21 ~Controller_DebugPad() override; 21 ~DebugPad() override;
22 22
23 // Called when the controller is initialized 23 // Called when the controller is initialized
24 void OnInit() override; 24 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index 63eecd42b..59b2ec73c 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -23,7 +23,7 @@ constexpr f32 Square(s32 num) {
23 return static_cast<f32>(num * num); 23 return static_cast<f32>(num * num);
24} 24}
25 25
26Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) 26Gesture::Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
27 : ControllerBase(hid_core_) { 27 : ControllerBase(hid_core_) {
28 static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size, 28 static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size,
29 "GestureSharedMemory is bigger than the shared memory"); 29 "GestureSharedMemory is bigger than the shared memory");
@@ -31,17 +31,17 @@ Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_sh
31 reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); 31 reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
32 console = hid_core.GetEmulatedConsole(); 32 console = hid_core.GetEmulatedConsole();
33} 33}
34Controller_Gesture::~Controller_Gesture() = default; 34Gesture::~Gesture() = default;
35 35
36void Controller_Gesture::OnInit() { 36void Gesture::OnInit() {
37 shared_memory->gesture_lifo.buffer_count = 0; 37 shared_memory->gesture_lifo.buffer_count = 0;
38 shared_memory->gesture_lifo.buffer_tail = 0; 38 shared_memory->gesture_lifo.buffer_tail = 0;
39 force_update = true; 39 force_update = true;
40} 40}
41 41
42void Controller_Gesture::OnRelease() {} 42void Gesture::OnRelease() {}
43 43
44void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 44void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
45 if (!IsControllerActivated()) { 45 if (!IsControllerActivated()) {
46 shared_memory->gesture_lifo.buffer_count = 0; 46 shared_memory->gesture_lifo.buffer_count = 0;
47 shared_memory->gesture_lifo.buffer_tail = 0; 47 shared_memory->gesture_lifo.buffer_tail = 0;
@@ -64,7 +64,7 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
64 UpdateGestureSharedMemory(gesture, time_difference); 64 UpdateGestureSharedMemory(gesture, time_difference);
65} 65}
66 66
67void Controller_Gesture::ReadTouchInput() { 67void Gesture::ReadTouchInput() {
68 if (!Settings::values.touchscreen.enabled) { 68 if (!Settings::values.touchscreen.enabled) {
69 fingers = {}; 69 fingers = {};
70 return; 70 return;
@@ -76,8 +76,7 @@ void Controller_Gesture::ReadTouchInput() {
76 } 76 }
77} 77}
78 78
79bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture, 79bool Gesture::ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference) {
80 f32 time_difference) {
81 const auto& last_entry = GetLastGestureEntry(); 80 const auto& last_entry = GetLastGestureEntry();
82 if (force_update) { 81 if (force_update) {
83 force_update = false; 82 force_update = false;
@@ -100,8 +99,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
100 return false; 99 return false;
101} 100}
102 101
103void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, 102void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference) {
104 f32 time_difference) {
105 GestureType type = GestureType::Idle; 103 GestureType type = GestureType::Idle;
106 GestureAttribute attributes{}; 104 GestureAttribute attributes{};
107 105
@@ -138,8 +136,8 @@ void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture,
138 shared_memory->gesture_lifo.WriteNextEntry(next_state); 136 shared_memory->gesture_lifo.WriteNextEntry(next_state);
139} 137}
140 138
141void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type, 139void Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
142 GestureAttribute& attributes) { 140 GestureAttribute& attributes) {
143 const auto& last_entry = GetLastGestureEntry(); 141 const auto& last_entry = GetLastGestureEntry();
144 142
145 gesture.detection_count++; 143 gesture.detection_count++;
@@ -152,8 +150,8 @@ void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& typ
152 } 150 }
153} 151}
154 152
155void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type, 153void Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type,
156 f32 time_difference) { 154 f32 time_difference) {
157 const auto& last_entry = GetLastGestureEntry(); 155 const auto& last_entry = GetLastGestureEntry();
158 156
159 // Promote to pan type if touch moved 157 // Promote to pan type if touch moved
@@ -186,9 +184,8 @@ void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, Gestu
186 } 184 }
187} 185}
188 186
189void Controller_Gesture::EndGesture(GestureProperties& gesture, 187void Gesture::EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props,
190 GestureProperties& last_gesture_props, GestureType& type, 188 GestureType& type, GestureAttribute& attributes, f32 time_difference) {
191 GestureAttribute& attributes, f32 time_difference) {
192 const auto& last_entry = GetLastGestureEntry(); 189 const auto& last_entry = GetLastGestureEntry();
193 190
194 if (last_gesture_props.active_points != 0) { 191 if (last_gesture_props.active_points != 0) {
@@ -222,9 +219,8 @@ void Controller_Gesture::EndGesture(GestureProperties& gesture,
222 } 219 }
223} 220}
224 221
225void Controller_Gesture::SetTapEvent(GestureProperties& gesture, 222void Gesture::SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
226 GestureProperties& last_gesture_props, GestureType& type, 223 GestureType& type, GestureAttribute& attributes) {
227 GestureAttribute& attributes) {
228 type = GestureType::Tap; 224 type = GestureType::Tap;
229 gesture = last_gesture_props; 225 gesture = last_gesture_props;
230 force_update = true; 226 force_update = true;
@@ -236,9 +232,8 @@ void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
236 } 232 }
237} 233}
238 234
239void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture, 235void Gesture::UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
240 GestureProperties& last_gesture_props, GestureType& type, 236 GestureType& type, f32 time_difference) {
241 f32 time_difference) {
242 const auto& last_entry = GetLastGestureEntry(); 237 const auto& last_entry = GetLastGestureEntry();
243 238
244 next_state.delta = gesture.mid_point - last_entry.pos; 239 next_state.delta = gesture.mid_point - last_entry.pos;
@@ -263,9 +258,8 @@ void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture,
263 } 258 }
264} 259}
265 260
266void Controller_Gesture::EndPanEvent(GestureProperties& gesture, 261void Gesture::EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
267 GestureProperties& last_gesture_props, GestureType& type, 262 GestureType& type, f32 time_difference) {
268 f32 time_difference) {
269 const auto& last_entry = GetLastGestureEntry(); 263 const auto& last_entry = GetLastGestureEntry();
270 next_state.vel_x = 264 next_state.vel_x =
271 static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference); 265 static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference);
@@ -287,8 +281,8 @@ void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
287 force_update = true; 281 force_update = true;
288} 282}
289 283
290void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture, 284void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
291 GestureProperties& last_gesture_props, GestureType& type) { 285 GestureType& type) {
292 const auto& last_entry = GetLastGestureEntry(); 286 const auto& last_entry = GetLastGestureEntry();
293 287
294 type = GestureType::Swipe; 288 type = GestureType::Swipe;
@@ -311,11 +305,11 @@ void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture,
311 next_state.direction = GestureDirection::Up; 305 next_state.direction = GestureDirection::Up;
312} 306}
313 307
314const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const { 308const Gesture::GestureState& Gesture::GetLastGestureEntry() const {
315 return shared_memory->gesture_lifo.ReadCurrentEntry().state; 309 return shared_memory->gesture_lifo.ReadCurrentEntry().state;
316} 310}
317 311
318Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() { 312Gesture::GestureProperties Gesture::GetGestureProperties() {
319 GestureProperties gesture; 313 GestureProperties gesture;
320 std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers; 314 std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers;
321 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), 315 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index 0d6099ea0..4c6f8ee07 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -12,10 +12,10 @@
12#include "core/hle/service/hid/ring_lifo.h" 12#include "core/hle/service/hid/ring_lifo.h"
13 13
14namespace Service::HID { 14namespace Service::HID {
15class Controller_Gesture final : public ControllerBase { 15class Gesture final : public ControllerBase {
16public: 16public:
17 explicit Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 17 explicit Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
18 ~Controller_Gesture() override; 18 ~Gesture() override;
19 19
20 // Called when the controller is initialized 20 // Called when the controller is initialized
21 void OnInit() override; 21 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index 117d87433..ddb1b0ba4 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -12,7 +12,7 @@
12namespace Service::HID { 12namespace Service::HID {
13constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; 13constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
14 14
15Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) 15Keyboard::Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
16 : ControllerBase{hid_core_} { 16 : ControllerBase{hid_core_} {
17 static_assert(SHARED_MEMORY_OFFSET + sizeof(KeyboardSharedMemory) < shared_memory_size, 17 static_assert(SHARED_MEMORY_OFFSET + sizeof(KeyboardSharedMemory) < shared_memory_size,
18 "KeyboardSharedMemory is bigger than the shared memory"); 18 "KeyboardSharedMemory is bigger than the shared memory");
@@ -21,13 +21,13 @@ Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_
21 emulated_devices = hid_core.GetEmulatedDevices(); 21 emulated_devices = hid_core.GetEmulatedDevices();
22} 22}
23 23
24Controller_Keyboard::~Controller_Keyboard() = default; 24Keyboard::~Keyboard() = default;
25 25
26void Controller_Keyboard::OnInit() {} 26void Keyboard::OnInit() {}
27 27
28void Controller_Keyboard::OnRelease() {} 28void Keyboard::OnRelease() {}
29 29
30void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 30void Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
31 if (!IsControllerActivated()) { 31 if (!IsControllerActivated()) {
32 shared_memory->keyboard_lifo.buffer_count = 0; 32 shared_memory->keyboard_lifo.buffer_count = 0;
33 shared_memory->keyboard_lifo.buffer_tail = 0; 33 shared_memory->keyboard_lifo.buffer_tail = 0;
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index 7532f53c6..172ec1309 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -14,10 +14,10 @@ struct KeyboardKey;
14} // namespace Core::HID 14} // namespace Core::HID
15 15
16namespace Service::HID { 16namespace Service::HID {
17class Controller_Keyboard final : public ControllerBase { 17class Keyboard final : public ControllerBase {
18public: 18public:
19 explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 19 explicit Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
20 ~Controller_Keyboard() override; 20 ~Keyboard() override;
21 21
22 // Called when the controller is initialized 22 // Called when the controller is initialized
23 void OnInit() override; 23 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 0afc66681..6e5a04e34 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -12,8 +12,7 @@
12namespace Service::HID { 12namespace Service::HID {
13constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; 13constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
14 14
15Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) 15Mouse::Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} {
16 : ControllerBase{hid_core_} {
17 static_assert(SHARED_MEMORY_OFFSET + sizeof(MouseSharedMemory) < shared_memory_size, 16 static_assert(SHARED_MEMORY_OFFSET + sizeof(MouseSharedMemory) < shared_memory_size,
18 "MouseSharedMemory is bigger than the shared memory"); 17 "MouseSharedMemory is bigger than the shared memory");
19 shared_memory = std::construct_at( 18 shared_memory = std::construct_at(
@@ -21,12 +20,12 @@ Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared
21 emulated_devices = hid_core.GetEmulatedDevices(); 20 emulated_devices = hid_core.GetEmulatedDevices();
22} 21}
23 22
24Controller_Mouse::~Controller_Mouse() = default; 23Mouse::~Mouse() = default;
25 24
26void Controller_Mouse::OnInit() {} 25void Mouse::OnInit() {}
27void Controller_Mouse::OnRelease() {} 26void Mouse::OnRelease() {}
28 27
29void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 28void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
30 if (!IsControllerActivated()) { 29 if (!IsControllerActivated()) {
31 shared_memory->mouse_lifo.buffer_count = 0; 30 shared_memory->mouse_lifo.buffer_count = 0;
32 shared_memory->mouse_lifo.buffer_tail = 0; 31 shared_memory->mouse_lifo.buffer_tail = 0;
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index 733d00577..a80f3823f 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -14,10 +14,10 @@ struct AnalogStickState;
14} // namespace Core::HID 14} // namespace Core::HID
15 15
16namespace Service::HID { 16namespace Service::HID {
17class Controller_Mouse final : public ControllerBase { 17class Mouse final : public ControllerBase {
18public: 18public:
19 explicit Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 19 explicit Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
20 ~Controller_Mouse() override; 20 ~Mouse() override;
21 21
22 // Called when the controller is initialized 22 // Called when the controller is initialized
23 void OnInit() override; 23 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index bc822f19e..08ee9de9c 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -18,6 +18,7 @@
18#include "core/hle/kernel/k_readable_event.h" 18#include "core/hle/kernel/k_readable_event.h"
19#include "core/hle/service/hid/controllers/npad.h" 19#include "core/hle/service/hid/controllers/npad.h"
20#include "core/hle/service/hid/errors.h" 20#include "core/hle/service/hid/errors.h"
21#include "core/hle/service/hid/hid_util.h"
21#include "core/hle/service/kernel_helpers.h" 22#include "core/hle/service/kernel_helpers.h"
22 23
23namespace Service::HID { 24namespace Service::HID {
@@ -29,60 +30,8 @@ constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{
29 Core::HID::NpadIdType::Handheld, 30 Core::HID::NpadIdType::Handheld,
30}; 31};
31 32
32bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) { 33NPad::NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
33 switch (npad_id) { 34 KernelHelpers::ServiceContext& service_context_)
34 case Core::HID::NpadIdType::Player1:
35 case Core::HID::NpadIdType::Player2:
36 case Core::HID::NpadIdType::Player3:
37 case Core::HID::NpadIdType::Player4:
38 case Core::HID::NpadIdType::Player5:
39 case Core::HID::NpadIdType::Player6:
40 case Core::HID::NpadIdType::Player7:
41 case Core::HID::NpadIdType::Player8:
42 case Core::HID::NpadIdType::Other:
43 case Core::HID::NpadIdType::Handheld:
44 return true;
45 default:
46 LOG_ERROR(Service_HID, "Invalid npad id {}", npad_id);
47 return false;
48 }
49}
50
51Result Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) {
52 const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
53 const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
54 const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
55
56 if (!npad_type) {
57 return VibrationInvalidStyleIndex;
58 }
59 if (!npad_id) {
60 return VibrationInvalidNpadId;
61 }
62 if (!device_index) {
63 return VibrationDeviceIndexOutOfRange;
64 }
65
66 return ResultSuccess;
67}
68
69Result Controller_NPad::VerifyValidSixAxisSensorHandle(
70 const Core::HID::SixAxisSensorHandle& device_handle) {
71 const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
72 const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
73
74 if (!npad_id) {
75 return InvalidNpadId;
76 }
77 if (!device_index) {
78 return NpadDeviceIndexOutOfRange;
79 }
80
81 return ResultSuccess;
82}
83
84Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
85 KernelHelpers::ServiceContext& service_context_)
86 : ControllerBase{hid_core_}, service_context{service_context_} { 35 : ControllerBase{hid_core_}, service_context{service_context_} {
87 static_assert(NPAD_OFFSET + (NPAD_COUNT * sizeof(NpadInternalState)) < shared_memory_size); 36 static_assert(NPAD_OFFSET + (NPAD_COUNT * sizeof(NpadInternalState)) < shared_memory_size);
88 for (std::size_t i = 0; i < controller_data.size(); ++i) { 37 for (std::size_t i = 0; i < controller_data.size(); ++i) {
@@ -103,7 +52,7 @@ Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_m
103 } 52 }
104} 53}
105 54
106Controller_NPad::~Controller_NPad() { 55NPad::~NPad() {
107 for (std::size_t i = 0; i < controller_data.size(); ++i) { 56 for (std::size_t i = 0; i < controller_data.size(); ++i) {
108 auto& controller = controller_data[i]; 57 auto& controller = controller_data[i];
109 controller.device->DeleteCallback(controller.callback_key); 58 controller.device->DeleteCallback(controller.callback_key);
@@ -111,8 +60,7 @@ Controller_NPad::~Controller_NPad() {
111 OnRelease(); 60 OnRelease();
112} 61}
113 62
114void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, 63void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx) {
115 std::size_t controller_idx) {
116 if (type == Core::HID::ControllerTriggerType::All) { 64 if (type == Core::HID::ControllerTriggerType::All) {
117 ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx); 65 ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx);
118 ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx); 66 ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx);
@@ -150,7 +98,7 @@ void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type,
150 } 98 }
151} 99}
152 100
153void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { 101void NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
154 auto& controller = GetControllerFromNpadIdType(npad_id); 102 auto& controller = GetControllerFromNpadIdType(npad_id);
155 if (!IsControllerSupported(controller.device->GetNpadStyleIndex())) { 103 if (!IsControllerSupported(controller.device->GetNpadStyleIndex())) {
156 return; 104 return;
@@ -344,12 +292,13 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
344 controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices, 292 controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices,
345 Common::Input::PollingMode::Active); 293 Common::Input::PollingMode::Active);
346 } 294 }
295
347 SignalStyleSetChangedEvent(npad_id); 296 SignalStyleSetChangedEvent(npad_id);
348 WriteEmptyEntry(controller.shared_memory); 297 WriteEmptyEntry(controller.shared_memory);
349 hid_core.SetLastActiveController(npad_id); 298 hid_core.SetLastActiveController(npad_id);
350} 299}
351 300
352void Controller_NPad::OnInit() { 301void NPad::OnInit() {
353 if (!IsControllerActivated()) { 302 if (!IsControllerActivated()) {
354 return; 303 return;
355 } 304 }
@@ -383,7 +332,7 @@ void Controller_NPad::OnInit() {
383 } 332 }
384} 333}
385 334
386void Controller_NPad::WriteEmptyEntry(NpadInternalState* npad) { 335void NPad::WriteEmptyEntry(NpadInternalState* npad) {
387 NPadGenericState dummy_pad_state{}; 336 NPadGenericState dummy_pad_state{};
388 NpadGcTriggerState dummy_gc_state{}; 337 NpadGcTriggerState dummy_gc_state{};
389 dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().sampling_number + 1; 338 dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().sampling_number + 1;
@@ -404,7 +353,7 @@ void Controller_NPad::WriteEmptyEntry(NpadInternalState* npad) {
404 npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state); 353 npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state);
405} 354}
406 355
407void Controller_NPad::OnRelease() { 356void NPad::OnRelease() {
408 is_controller_initialized = false; 357 is_controller_initialized = false;
409 for (std::size_t i = 0; i < controller_data.size(); ++i) { 358 for (std::size_t i = 0; i < controller_data.size(); ++i) {
410 auto& controller = controller_data[i]; 359 auto& controller = controller_data[i];
@@ -415,7 +364,7 @@ void Controller_NPad::OnRelease() {
415 } 364 }
416} 365}
417 366
418void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { 367void NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
419 std::scoped_lock lock{mutex}; 368 std::scoped_lock lock{mutex};
420 auto& controller = GetControllerFromNpadIdType(npad_id); 369 auto& controller = GetControllerFromNpadIdType(npad_id);
421 const auto controller_type = controller.device->GetNpadStyleIndex(); 370 const auto controller_type = controller.device->GetNpadStyleIndex();
@@ -457,12 +406,14 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
457 pad_entry.l_stick = stick_state.left; 406 pad_entry.l_stick = stick_state.left;
458 } 407 }
459 408
460 if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft) { 409 if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft ||
410 controller_type == Core::HID::NpadStyleIndex::JoyconDual) {
461 pad_entry.npad_buttons.left_sl.Assign(button_state.left_sl); 411 pad_entry.npad_buttons.left_sl.Assign(button_state.left_sl);
462 pad_entry.npad_buttons.left_sr.Assign(button_state.left_sr); 412 pad_entry.npad_buttons.left_sr.Assign(button_state.left_sr);
463 } 413 }
464 414
465 if (controller_type == Core::HID::NpadStyleIndex::JoyconRight) { 415 if (controller_type == Core::HID::NpadStyleIndex::JoyconRight ||
416 controller_type == Core::HID::NpadStyleIndex::JoyconDual) {
466 pad_entry.npad_buttons.right_sl.Assign(button_state.right_sl); 417 pad_entry.npad_buttons.right_sl.Assign(button_state.right_sl);
467 pad_entry.npad_buttons.right_sr.Assign(button_state.right_sr); 418 pad_entry.npad_buttons.right_sr.Assign(button_state.right_sr);
468 } 419 }
@@ -482,7 +433,7 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
482 } 433 }
483} 434}
484 435
485void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 436void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
486 if (!IsControllerActivated()) { 437 if (!IsControllerActivated()) {
487 return; 438 return;
488 } 439 }
@@ -612,134 +563,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
612 } 563 }
613} 564}
614 565
615void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) { 566void NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
616 if (!IsControllerActivated()) {
617 return;
618 }
619
620 for (std::size_t i = 0; i < controller_data.size(); ++i) {
621 auto& controller = controller_data[i];
622
623 const auto& controller_type = controller.device->GetNpadStyleIndex();
624
625 if (controller_type == Core::HID::NpadStyleIndex::None ||
626 !controller.device->IsConnected()) {
627 continue;
628 }
629
630 auto* npad = controller.shared_memory;
631 const auto& motion_state = controller.device->GetMotions();
632 auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
633 auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
634 auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
635 auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
636 auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
637 auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
638
639 // Clear previous state
640 sixaxis_fullkey_state = {};
641 sixaxis_handheld_state = {};
642 sixaxis_dual_left_state = {};
643 sixaxis_dual_right_state = {};
644 sixaxis_left_lifo_state = {};
645 sixaxis_right_lifo_state = {};
646
647 if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
648 controller.sixaxis_at_rest = true;
649 for (std::size_t e = 0; e < motion_state.size(); ++e) {
650 controller.sixaxis_at_rest =
651 controller.sixaxis_at_rest && motion_state[e].is_at_rest;
652 }
653 }
654
655 const auto set_motion_state = [&](SixAxisSensorState& state,
656 const Core::HID::ControllerMotion& hid_state) {
657 using namespace std::literals::chrono_literals;
658 static constexpr SixAxisSensorState default_motion_state = {
659 .delta_time = std::chrono::nanoseconds(5ms).count(),
660 .accel = {0, 0, -1.0f},
661 .orientation =
662 {
663 Common::Vec3f{1.0f, 0, 0},
664 Common::Vec3f{0, 1.0f, 0},
665 Common::Vec3f{0, 0, 1.0f},
666 },
667 .attribute = {1},
668 };
669 if (!controller.sixaxis_sensor_enabled) {
670 state = default_motion_state;
671 return;
672 }
673 if (!Settings::values.motion_enabled.GetValue()) {
674 state = default_motion_state;
675 return;
676 }
677 state.attribute.is_connected.Assign(1);
678 state.delta_time = std::chrono::nanoseconds(5ms).count();
679 state.accel = hid_state.accel;
680 state.gyro = hid_state.gyro;
681 state.rotation = hid_state.rotation;
682 state.orientation = hid_state.orientation;
683 };
684
685 switch (controller_type) {
686 case Core::HID::NpadStyleIndex::None:
687 ASSERT(false);
688 break;
689 case Core::HID::NpadStyleIndex::ProController:
690 set_motion_state(sixaxis_fullkey_state, motion_state[0]);
691 break;
692 case Core::HID::NpadStyleIndex::Handheld:
693 set_motion_state(sixaxis_handheld_state, motion_state[0]);
694 break;
695 case Core::HID::NpadStyleIndex::JoyconDual:
696 set_motion_state(sixaxis_dual_left_state, motion_state[0]);
697 set_motion_state(sixaxis_dual_right_state, motion_state[1]);
698 break;
699 case Core::HID::NpadStyleIndex::JoyconLeft:
700 set_motion_state(sixaxis_left_lifo_state, motion_state[0]);
701 break;
702 case Core::HID::NpadStyleIndex::JoyconRight:
703 set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
704 break;
705 case Core::HID::NpadStyleIndex::Pokeball:
706 using namespace std::literals::chrono_literals;
707 set_motion_state(sixaxis_fullkey_state, motion_state[0]);
708 sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count();
709 break;
710 default:
711 break;
712 }
713
714 sixaxis_fullkey_state.sampling_number =
715 npad->sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
716 sixaxis_handheld_state.sampling_number =
717 npad->sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
718 sixaxis_dual_left_state.sampling_number =
719 npad->sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
720 sixaxis_dual_right_state.sampling_number =
721 npad->sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
722 sixaxis_left_lifo_state.sampling_number =
723 npad->sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
724 sixaxis_right_lifo_state.sampling_number =
725 npad->sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
726
727 if (Core::HID::IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
728 // This buffer only is updated on handheld on HW
729 npad->sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state);
730 } else {
731 // Handheld doesn't update this buffer on HW
732 npad->sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state);
733 }
734
735 npad->sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state);
736 npad->sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state);
737 npad->sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state);
738 npad->sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state);
739 }
740}
741
742void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
743 hid_core.SetSupportedStyleTag(style_set); 567 hid_core.SetSupportedStyleTag(style_set);
744 568
745 if (is_controller_initialized) { 569 if (is_controller_initialized) {
@@ -750,14 +574,14 @@ void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
750 is_controller_initialized = true; 574 is_controller_initialized = true;
751} 575}
752 576
753Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const { 577Core::HID::NpadStyleTag NPad::GetSupportedStyleSet() const {
754 if (!is_controller_initialized) { 578 if (!is_controller_initialized) {
755 return {Core::HID::NpadStyleSet::None}; 579 return {Core::HID::NpadStyleSet::None};
756 } 580 }
757 return hid_core.GetSupportedStyleTag(); 581 return hid_core.GetSupportedStyleTag();
758} 582}
759 583
760Result Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) { 584Result NPad::SetSupportedNpadIdTypes(std::span<const u8> data) {
761 constexpr std::size_t max_number_npad_ids = 0xa; 585 constexpr std::size_t max_number_npad_ids = 0xa;
762 const auto length = data.size(); 586 const auto length = data.size();
763 ASSERT(length > 0 && (length % sizeof(u32)) == 0); 587 ASSERT(length > 0 && (length % sizeof(u32)) == 0);
@@ -773,17 +597,17 @@ Result Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) {
773 return ResultSuccess; 597 return ResultSuccess;
774} 598}
775 599
776void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { 600void NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
777 const auto copy_amount = supported_npad_id_types.size() * sizeof(u32); 601 const auto copy_amount = supported_npad_id_types.size() * sizeof(u32);
778 ASSERT(max_length <= copy_amount); 602 ASSERT(max_length <= copy_amount);
779 std::memcpy(data, supported_npad_id_types.data(), copy_amount); 603 std::memcpy(data, supported_npad_id_types.data(), copy_amount);
780} 604}
781 605
782std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const { 606std::size_t NPad::GetSupportedNpadIdTypesSize() const {
783 return supported_npad_id_types.size(); 607 return supported_npad_id_types.size();
784} 608}
785 609
786void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) { 610void NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
787 if (joy_hold_type != NpadJoyHoldType::Horizontal && 611 if (joy_hold_type != NpadJoyHoldType::Horizontal &&
788 joy_hold_type != NpadJoyHoldType::Vertical) { 612 joy_hold_type != NpadJoyHoldType::Vertical) {
789 LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}", 613 LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}",
@@ -793,11 +617,11 @@ void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
793 hold_type = joy_hold_type; 617 hold_type = joy_hold_type;
794} 618}
795 619
796Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const { 620NPad::NpadJoyHoldType NPad::GetHoldType() const {
797 return hold_type; 621 return hold_type;
798} 622}
799 623
800void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { 624void NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) {
801 if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) { 625 if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) {
802 ASSERT_MSG(false, "Activation mode should be always None, Single or Dual"); 626 ASSERT_MSG(false, "Activation mode should be always None, Single or Dual");
803 return; 627 return;
@@ -806,21 +630,20 @@ void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode a
806 handheld_activation_mode = activation_mode; 630 handheld_activation_mode = activation_mode;
807} 631}
808 632
809Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActivationMode() const { 633NPad::NpadHandheldActivationMode NPad::GetNpadHandheldActivationMode() const {
810 return handheld_activation_mode; 634 return handheld_activation_mode;
811} 635}
812 636
813void Controller_NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) { 637void NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) {
814 communication_mode = communication_mode_; 638 communication_mode = communication_mode_;
815} 639}
816 640
817Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode() const { 641NPad::NpadCommunicationMode NPad::GetNpadCommunicationMode() const {
818 return communication_mode; 642 return communication_mode;
819} 643}
820 644
821bool Controller_NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id, 645bool NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id,
822 NpadJoyDeviceType npad_device_type, 646 NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode) {
823 NpadJoyAssignmentMode assignment_mode) {
824 if (!IsNpadIdValid(npad_id)) { 647 if (!IsNpadIdValid(npad_id)) {
825 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 648 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
826 return false; 649 return false;
@@ -889,9 +712,8 @@ bool Controller_NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID:
889 return true; 712 return true;
890} 713}
891 714
892bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, 715bool NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
893 std::size_t device_index, 716 const Core::HID::VibrationValue& vibration_value) {
894 const Core::HID::VibrationValue& vibration_value) {
895 auto& controller = GetControllerFromNpadIdType(npad_id); 717 auto& controller = GetControllerFromNpadIdType(npad_id);
896 if (!controller.device->IsConnected()) { 718 if (!controller.device->IsConnected()) {
897 return false; 719 return false;
@@ -935,10 +757,9 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
935 return controller.device->SetVibration(device_index, vibration); 757 return controller.device->SetVibration(device_index, vibration);
936} 758}
937 759
938void Controller_NPad::VibrateController( 760void NPad::VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle,
939 const Core::HID::VibrationDeviceHandle& vibration_device_handle, 761 const Core::HID::VibrationValue& vibration_value) {
940 const Core::HID::VibrationValue& vibration_value) { 762 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
941 if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
942 return; 763 return;
943 } 764 }
944 765
@@ -982,7 +803,7 @@ void Controller_NPad::VibrateController(
982 } 803 }
983} 804}
984 805
985void Controller_NPad::VibrateControllers( 806void NPad::VibrateControllers(
986 std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles, 807 std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles,
987 std::span<const Core::HID::VibrationValue> vibration_values) { 808 std::span<const Core::HID::VibrationValue> vibration_values) {
988 if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { 809 if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
@@ -999,9 +820,9 @@ void Controller_NPad::VibrateControllers(
999 } 820 }
1000} 821}
1001 822
1002Core::HID::VibrationValue Controller_NPad::GetLastVibration( 823Core::HID::VibrationValue NPad::GetLastVibration(
1003 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { 824 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
1004 if (IsDeviceHandleValid(vibration_device_handle).IsError()) { 825 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
1005 return {}; 826 return {};
1006 } 827 }
1007 828
@@ -1010,9 +831,9 @@ Core::HID::VibrationValue Controller_NPad::GetLastVibration(
1010 return controller.vibration[device_index].latest_vibration_value; 831 return controller.vibration[device_index].latest_vibration_value;
1011} 832}
1012 833
1013void Controller_NPad::InitializeVibrationDevice( 834void NPad::InitializeVibrationDevice(
1014 const Core::HID::VibrationDeviceHandle& vibration_device_handle) { 835 const Core::HID::VibrationDeviceHandle& vibration_device_handle) {
1015 if (IsDeviceHandleValid(vibration_device_handle).IsError()) { 836 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
1016 return; 837 return;
1017 } 838 }
1018 839
@@ -1021,8 +842,8 @@ void Controller_NPad::InitializeVibrationDevice(
1021 InitializeVibrationDeviceAtIndex(npad_index, device_index); 842 InitializeVibrationDeviceAtIndex(npad_index, device_index);
1022} 843}
1023 844
1024void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, 845void NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id,
1025 std::size_t device_index) { 846 std::size_t device_index) {
1026 auto& controller = GetControllerFromNpadIdType(npad_id); 847 auto& controller = GetControllerFromNpadIdType(npad_id);
1027 if (!Settings::values.vibration_enabled.GetValue()) { 848 if (!Settings::values.vibration_enabled.GetValue()) {
1028 controller.vibration[device_index].device_mounted = false; 849 controller.vibration[device_index].device_mounted = false;
@@ -1033,13 +854,13 @@ void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npa
1033 controller.device->IsVibrationEnabled(device_index); 854 controller.device->IsVibrationEnabled(device_index);
1034} 855}
1035 856
1036void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { 857void NPad::SetPermitVibrationSession(bool permit_vibration_session) {
1037 permit_vibration_session_enabled = permit_vibration_session; 858 permit_vibration_session_enabled = permit_vibration_session;
1038} 859}
1039 860
1040bool Controller_NPad::IsVibrationDeviceMounted( 861bool NPad::IsVibrationDeviceMounted(
1041 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { 862 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
1042 if (IsDeviceHandleValid(vibration_device_handle).IsError()) { 863 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
1043 return false; 864 return false;
1044 } 865 }
1045 866
@@ -1048,7 +869,7 @@ bool Controller_NPad::IsVibrationDeviceMounted(
1048 return controller.vibration[device_index].device_mounted; 869 return controller.vibration[device_index].device_mounted;
1049} 870}
1050 871
1051Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) { 872Kernel::KReadableEvent& NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) {
1052 if (!IsNpadIdValid(npad_id)) { 873 if (!IsNpadIdValid(npad_id)) {
1053 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 874 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1054 // Fallback to player 1 875 // Fallback to player 1
@@ -1060,18 +881,17 @@ Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::Npad
1060 return controller.styleset_changed_event->GetReadableEvent(); 881 return controller.styleset_changed_event->GetReadableEvent();
1061} 882}
1062 883
1063void Controller_NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const { 884void NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const {
1064 const auto& controller = GetControllerFromNpadIdType(npad_id); 885 const auto& controller = GetControllerFromNpadIdType(npad_id);
1065 controller.styleset_changed_event->Signal(); 886 controller.styleset_changed_event->Signal();
1066} 887}
1067 888
1068void Controller_NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, 889void NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id) {
1069 Core::HID::NpadIdType npad_id) {
1070 UpdateControllerAt(controller, npad_id, true); 890 UpdateControllerAt(controller, npad_id, true);
1071} 891}
1072 892
1073void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, 893void NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, Core::HID::NpadIdType npad_id,
1074 Core::HID::NpadIdType npad_id, bool connected) { 894 bool connected) {
1075 auto& controller = GetControllerFromNpadIdType(npad_id); 895 auto& controller = GetControllerFromNpadIdType(npad_id);
1076 if (!connected) { 896 if (!connected) {
1077 DisconnectNpad(npad_id); 897 DisconnectNpad(npad_id);
@@ -1082,7 +902,7 @@ void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type,
1082 InitNewlyAddedController(npad_id); 902 InitNewlyAddedController(npad_id);
1083} 903}
1084 904
1085Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { 905Result NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
1086 if (!IsNpadIdValid(npad_id)) { 906 if (!IsNpadIdValid(npad_id)) {
1087 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 907 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1088 return InvalidNpadId; 908 return InvalidNpadId;
@@ -1108,9 +928,9 @@ Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
1108 shared_memory->sixaxis_dual_right_properties.raw = 0; 928 shared_memory->sixaxis_dual_right_properties.raw = 0;
1109 shared_memory->sixaxis_left_properties.raw = 0; 929 shared_memory->sixaxis_left_properties.raw = 0;
1110 shared_memory->sixaxis_right_properties.raw = 0; 930 shared_memory->sixaxis_right_properties.raw = 0;
1111 shared_memory->battery_level_dual = 0; 931 shared_memory->battery_level_dual = Core::HID::NpadBatteryLevel::Empty;
1112 shared_memory->battery_level_left = 0; 932 shared_memory->battery_level_left = Core::HID::NpadBatteryLevel::Empty;
1113 shared_memory->battery_level_right = 0; 933 shared_memory->battery_level_right = Core::HID::NpadBatteryLevel::Empty;
1114 shared_memory->fullkey_color = { 934 shared_memory->fullkey_color = {
1115 .attribute = ColorAttribute::NoController, 935 .attribute = ColorAttribute::NoController,
1116 .fullkey = {}, 936 .fullkey = {},
@@ -1131,54 +951,9 @@ Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
1131 return ResultSuccess; 951 return ResultSuccess;
1132} 952}
1133 953
1134Result Controller_NPad::SetGyroscopeZeroDriftMode( 954Result NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
1135 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1136 Core::HID::GyroscopeZeroDriftMode drift_mode) {
1137 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1138 if (is_valid.IsError()) {
1139 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1140 return is_valid;
1141 }
1142
1143 auto& sixaxis = GetSixaxisState(sixaxis_handle);
1144 auto& controller = GetControllerFromHandle(sixaxis_handle);
1145 sixaxis.gyroscope_zero_drift_mode = drift_mode;
1146 controller.device->SetGyroscopeZeroDriftMode(drift_mode);
1147
1148 return ResultSuccess;
1149}
1150
1151Result Controller_NPad::GetGyroscopeZeroDriftMode(
1152 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1153 Core::HID::GyroscopeZeroDriftMode& drift_mode) const {
1154 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1155 if (is_valid.IsError()) {
1156 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1157 return is_valid;
1158 }
1159
1160 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1161 drift_mode = sixaxis.gyroscope_zero_drift_mode;
1162
1163 return ResultSuccess;
1164}
1165
1166Result Controller_NPad::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1167 bool& is_at_rest) const {
1168 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1169 if (is_valid.IsError()) {
1170 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1171 return is_valid;
1172 }
1173
1174 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1175 is_at_rest = controller.sixaxis_at_rest;
1176 return ResultSuccess;
1177}
1178
1179Result Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
1180 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const { 955 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const {
1181 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); 956 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
1182 if (is_valid.IsError()) { 957 if (is_valid.IsError()) {
1183 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); 958 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1184 return is_valid; 959 return is_valid;
@@ -1189,65 +964,9 @@ Result Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
1189 return ResultSuccess; 964 return ResultSuccess;
1190} 965}
1191 966
1192Result Controller_NPad::EnableSixAxisSensorUnalteredPassthrough( 967Result NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
1193 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) {
1194 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1195 if (is_valid.IsError()) {
1196 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1197 return is_valid;
1198 }
1199
1200 auto& sixaxis = GetSixaxisState(sixaxis_handle);
1201 sixaxis.unaltered_passtrough = is_enabled;
1202 return ResultSuccess;
1203}
1204
1205Result Controller_NPad::IsSixAxisSensorUnalteredPassthroughEnabled(
1206 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const {
1207 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1208 if (is_valid.IsError()) {
1209 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1210 return is_valid;
1211 }
1212
1213 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1214 is_enabled = sixaxis.unaltered_passtrough;
1215 return ResultSuccess;
1216}
1217
1218Result Controller_NPad::LoadSixAxisSensorCalibrationParameter(
1219 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1220 Core::HID::SixAxisSensorCalibrationParameter& calibration) const {
1221 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1222 if (is_valid.IsError()) {
1223 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1224 return is_valid;
1225 }
1226
1227 // TODO: Request this data to the controller. On error return 0xd8ca
1228 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1229 calibration = sixaxis.calibration;
1230 return ResultSuccess;
1231}
1232
1233Result Controller_NPad::GetSixAxisSensorIcInformation(
1234 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1235 Core::HID::SixAxisSensorIcInformation& ic_information) const {
1236 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1237 if (is_valid.IsError()) {
1238 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1239 return is_valid;
1240 }
1241
1242 // TODO: Request this data to the controller. On error return 0xd8ca
1243 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1244 ic_information = sixaxis.ic_information;
1245 return ResultSuccess;
1246}
1247
1248Result Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
1249 const Core::HID::SixAxisSensorHandle& sixaxis_handle) { 968 const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
1250 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); 969 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
1251 if (is_valid.IsError()) { 970 if (is_valid.IsError()) {
1252 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); 971 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1253 return is_valid; 972 return is_valid;
@@ -1259,83 +978,32 @@ Result Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
1259 return ResultSuccess; 978 return ResultSuccess;
1260} 979}
1261 980
1262Result Controller_NPad::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, 981NPad::SixAxisLifo& NPad::GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id) {
1263 bool sixaxis_status) { 982 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_fullkey_lifo;
1264 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1265 if (is_valid.IsError()) {
1266 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1267 return is_valid;
1268 }
1269
1270 auto& controller = GetControllerFromHandle(sixaxis_handle);
1271 controller.sixaxis_sensor_enabled = sixaxis_status;
1272 return ResultSuccess;
1273} 983}
1274 984
1275Result Controller_NPad::IsSixAxisSensorFusionEnabled( 985NPad::SixAxisLifo& NPad::GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id) {
1276 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_fusion_enabled) const { 986 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_handheld_lifo;
1277 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1278 if (is_valid.IsError()) {
1279 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1280 return is_valid;
1281 }
1282
1283 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1284 is_fusion_enabled = sixaxis.is_fusion_enabled;
1285
1286 return ResultSuccess;
1287} 987}
1288Result Controller_NPad::SetSixAxisFusionEnabled(
1289 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_fusion_enabled) {
1290 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1291 if (is_valid.IsError()) {
1292 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1293 return is_valid;
1294 }
1295 988
1296 auto& sixaxis = GetSixaxisState(sixaxis_handle); 989NPad::SixAxisLifo& NPad::GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id) {
1297 sixaxis.is_fusion_enabled = is_fusion_enabled; 990 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_left_lifo;
1298
1299 return ResultSuccess;
1300} 991}
1301 992
1302Result Controller_NPad::SetSixAxisFusionParameters( 993NPad::SixAxisLifo& NPad::GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id) {
1303 const Core::HID::SixAxisSensorHandle& sixaxis_handle, 994 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_right_lifo;
1304 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
1305 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1306 if (is_valid.IsError()) {
1307 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1308 return is_valid;
1309 }
1310
1311 const auto param1 = sixaxis_fusion_parameters.parameter1;
1312 if (param1 < 0.0f || param1 > 1.0f) {
1313 return InvalidSixAxisFusionRange;
1314 }
1315
1316 auto& sixaxis = GetSixaxisState(sixaxis_handle);
1317 sixaxis.fusion = sixaxis_fusion_parameters;
1318
1319 return ResultSuccess;
1320} 995}
1321 996
1322Result Controller_NPad::GetSixAxisFusionParameters( 997NPad::SixAxisLifo& NPad::GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id) {
1323 const Core::HID::SixAxisSensorHandle& sixaxis_handle, 998 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_left_lifo;
1324 Core::HID::SixAxisSensorFusionParameters& parameters) const { 999}
1325 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1326 if (is_valid.IsError()) {
1327 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1328 return is_valid;
1329 }
1330
1331 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1332 parameters = sixaxis.fusion;
1333 1000
1334 return ResultSuccess; 1001NPad::SixAxisLifo& NPad::GetSixAxisRightLifo(Core::HID::NpadIdType npad_id) {
1002 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_right_lifo;
1335} 1003}
1336 1004
1337Result Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, 1005Result NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
1338 Core::HID::NpadIdType npad_id_2) { 1006 Core::HID::NpadIdType npad_id_2) {
1339 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { 1007 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
1340 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, 1008 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
1341 npad_id_2); 1009 npad_id_2);
@@ -1397,18 +1065,17 @@ Result Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
1397 return ResultSuccess; 1065 return ResultSuccess;
1398} 1066}
1399 1067
1400void Controller_NPad::StartLRAssignmentMode() { 1068void NPad::StartLRAssignmentMode() {
1401 // Nothing internally is used for lr assignment mode. Since we have the ability to set the 1069 // Nothing internally is used for lr assignment mode. Since we have the ability to set the
1402 // controller types from boot, it doesn't really matter about showing a selection screen 1070 // controller types from boot, it doesn't really matter about showing a selection screen
1403 is_in_lr_assignment_mode = true; 1071 is_in_lr_assignment_mode = true;
1404} 1072}
1405 1073
1406void Controller_NPad::StopLRAssignmentMode() { 1074void NPad::StopLRAssignmentMode() {
1407 is_in_lr_assignment_mode = false; 1075 is_in_lr_assignment_mode = false;
1408} 1076}
1409 1077
1410Result Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, 1078Result NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2) {
1411 Core::HID::NpadIdType npad_id_2) {
1412 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { 1079 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
1413 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, 1080 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
1414 npad_id_2); 1081 npad_id_2);
@@ -1439,8 +1106,7 @@ Result Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
1439 return ResultSuccess; 1106 return ResultSuccess;
1440} 1107}
1441 1108
1442Result Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id, 1109Result NPad::GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const {
1443 Core::HID::LedPattern& pattern) const {
1444 if (!IsNpadIdValid(npad_id)) { 1110 if (!IsNpadIdValid(npad_id)) {
1445 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1111 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1446 return InvalidNpadId; 1112 return InvalidNpadId;
@@ -1450,8 +1116,8 @@ Result Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id,
1450 return ResultSuccess; 1116 return ResultSuccess;
1451} 1117}
1452 1118
1453Result Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, 1119Result NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
1454 bool& is_valid) const { 1120 bool& is_valid) const {
1455 if (!IsNpadIdValid(npad_id)) { 1121 if (!IsNpadIdValid(npad_id)) {
1456 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1122 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1457 return InvalidNpadId; 1123 return InvalidNpadId;
@@ -1461,8 +1127,8 @@ Result Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::
1461 return ResultSuccess; 1127 return ResultSuccess;
1462} 1128}
1463 1129
1464Result Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled( 1130Result NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
1465 bool is_protection_enabled, Core::HID::NpadIdType npad_id) { 1131 Core::HID::NpadIdType npad_id) {
1466 if (!IsNpadIdValid(npad_id)) { 1132 if (!IsNpadIdValid(npad_id)) {
1467 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1133 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1468 return InvalidNpadId; 1134 return InvalidNpadId;
@@ -1472,11 +1138,11 @@ Result Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(
1472 return ResultSuccess; 1138 return ResultSuccess;
1473} 1139}
1474 1140
1475void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { 1141void NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
1476 analog_stick_use_center_clamp = use_center_clamp; 1142 analog_stick_use_center_clamp = use_center_clamp;
1477} 1143}
1478 1144
1479void Controller_NPad::ClearAllConnectedControllers() { 1145void NPad::ClearAllConnectedControllers() {
1480 for (auto& controller : controller_data) { 1146 for (auto& controller : controller_data) {
1481 if (controller.device->IsConnected() && 1147 if (controller.device->IsConnected() &&
1482 controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) { 1148 controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) {
@@ -1486,13 +1152,13 @@ void Controller_NPad::ClearAllConnectedControllers() {
1486 } 1152 }
1487} 1153}
1488 1154
1489void Controller_NPad::DisconnectAllConnectedControllers() { 1155void NPad::DisconnectAllConnectedControllers() {
1490 for (auto& controller : controller_data) { 1156 for (auto& controller : controller_data) {
1491 controller.device->Disconnect(); 1157 controller.device->Disconnect();
1492 } 1158 }
1493} 1159}
1494 1160
1495void Controller_NPad::ConnectAllDisconnectedControllers() { 1161void NPad::ConnectAllDisconnectedControllers() {
1496 for (auto& controller : controller_data) { 1162 for (auto& controller : controller_data) {
1497 if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None && 1163 if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None &&
1498 !controller.device->IsConnected()) { 1164 !controller.device->IsConnected()) {
@@ -1501,18 +1167,18 @@ void Controller_NPad::ConnectAllDisconnectedControllers() {
1501 } 1167 }
1502} 1168}
1503 1169
1504void Controller_NPad::ClearAllControllers() { 1170void NPad::ClearAllControllers() {
1505 for (auto& controller : controller_data) { 1171 for (auto& controller : controller_data) {
1506 controller.device->Disconnect(); 1172 controller.device->Disconnect();
1507 controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None); 1173 controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
1508 } 1174 }
1509} 1175}
1510 1176
1511Core::HID::NpadButton Controller_NPad::GetAndResetPressState() { 1177Core::HID::NpadButton NPad::GetAndResetPressState() {
1512 return static_cast<Core::HID::NpadButton>(press_state.exchange(0)); 1178 return static_cast<Core::HID::NpadButton>(press_state.exchange(0));
1513} 1179}
1514 1180
1515void Controller_NPad::ApplyNpadSystemCommonPolicy() { 1181void NPad::ApplyNpadSystemCommonPolicy() {
1516 Core::HID::NpadStyleTag styletag{}; 1182 Core::HID::NpadStyleTag styletag{};
1517 styletag.fullkey.Assign(1); 1183 styletag.fullkey.Assign(1);
1518 styletag.handheld.Assign(1); 1184 styletag.handheld.Assign(1);
@@ -1537,7 +1203,7 @@ void Controller_NPad::ApplyNpadSystemCommonPolicy() {
1537 supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld; 1203 supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld;
1538} 1204}
1539 1205
1540bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const { 1206bool NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const {
1541 if (controller == Core::HID::NpadStyleIndex::Handheld) { 1207 if (controller == Core::HID::NpadStyleIndex::Handheld) {
1542 const bool support_handheld = 1208 const bool support_handheld =
1543 std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), 1209 std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
@@ -1588,51 +1254,50 @@ bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller
1588 return false; 1254 return false;
1589} 1255}
1590 1256
1591Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( 1257NPad::NpadControllerData& NPad::GetControllerFromHandle(
1592 const Core::HID::SixAxisSensorHandle& device_handle) { 1258 const Core::HID::VibrationDeviceHandle& device_handle) {
1593 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); 1259 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1594 return GetControllerFromNpadIdType(npad_id); 1260 return GetControllerFromNpadIdType(npad_id);
1595} 1261}
1596 1262
1597const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( 1263const NPad::NpadControllerData& NPad::GetControllerFromHandle(
1598 const Core::HID::SixAxisSensorHandle& device_handle) const { 1264 const Core::HID::VibrationDeviceHandle& device_handle) const {
1599 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); 1265 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1600 return GetControllerFromNpadIdType(npad_id); 1266 return GetControllerFromNpadIdType(npad_id);
1601} 1267}
1602 1268
1603Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( 1269NPad::NpadControllerData& NPad::GetControllerFromHandle(
1604 const Core::HID::VibrationDeviceHandle& device_handle) { 1270 const Core::HID::SixAxisSensorHandle& device_handle) {
1605 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); 1271 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1606 return GetControllerFromNpadIdType(npad_id); 1272 return GetControllerFromNpadIdType(npad_id);
1607} 1273}
1608 1274
1609const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( 1275const NPad::NpadControllerData& NPad::GetControllerFromHandle(
1610 const Core::HID::VibrationDeviceHandle& device_handle) const { 1276 const Core::HID::SixAxisSensorHandle& device_handle) const {
1611 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); 1277 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1612 return GetControllerFromNpadIdType(npad_id); 1278 return GetControllerFromNpadIdType(npad_id);
1613} 1279}
1614 1280
1615Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType( 1281NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) {
1616 Core::HID::NpadIdType npad_id) {
1617 if (!IsNpadIdValid(npad_id)) { 1282 if (!IsNpadIdValid(npad_id)) {
1618 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1283 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1619 npad_id = Core::HID::NpadIdType::Player1; 1284 npad_id = Core::HID::NpadIdType::Player1;
1620 } 1285 }
1621 const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id); 1286 const auto npad_index = NpadIdTypeToIndex(npad_id);
1622 return controller_data[npad_index]; 1287 return controller_data[npad_index];
1623} 1288}
1624 1289
1625const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType( 1290const NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(
1626 Core::HID::NpadIdType npad_id) const { 1291 Core::HID::NpadIdType npad_id) const {
1627 if (!IsNpadIdValid(npad_id)) { 1292 if (!IsNpadIdValid(npad_id)) {
1628 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1293 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1629 npad_id = Core::HID::NpadIdType::Player1; 1294 npad_id = Core::HID::NpadIdType::Player1;
1630 } 1295 }
1631 const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id); 1296 const auto npad_index = NpadIdTypeToIndex(npad_id);
1632 return controller_data[npad_index]; 1297 return controller_data[npad_index];
1633} 1298}
1634 1299
1635Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( 1300Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
1636 const Core::HID::SixAxisSensorHandle& sixaxis_handle) { 1301 const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
1637 auto& controller = GetControllerFromHandle(sixaxis_handle); 1302 auto& controller = GetControllerFromHandle(sixaxis_handle);
1638 switch (sixaxis_handle.npad_type) { 1303 switch (sixaxis_handle.npad_type) {
@@ -1655,7 +1320,7 @@ Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
1655 } 1320 }
1656} 1321}
1657 1322
1658const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( 1323const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
1659 const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { 1324 const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
1660 const auto& controller = GetControllerFromHandle(sixaxis_handle); 1325 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1661 switch (sixaxis_handle.npad_type) { 1326 switch (sixaxis_handle.npad_type) {
@@ -1678,50 +1343,13 @@ const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
1678 } 1343 }
1679} 1344}
1680 1345
1681Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState( 1346NPad::AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) {
1682 const Core::HID::SixAxisSensorHandle& sixaxis_handle) { 1347 const auto& shared_memory = GetControllerFromNpadIdType(npad_id).shared_memory;
1683 auto& controller = GetControllerFromHandle(sixaxis_handle);
1684 switch (sixaxis_handle.npad_type) {
1685 case Core::HID::NpadStyleIndex::ProController:
1686 case Core::HID::NpadStyleIndex::Pokeball:
1687 return controller.sixaxis_fullkey;
1688 case Core::HID::NpadStyleIndex::Handheld:
1689 return controller.sixaxis_handheld;
1690 case Core::HID::NpadStyleIndex::JoyconDual:
1691 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1692 return controller.sixaxis_dual_left;
1693 }
1694 return controller.sixaxis_dual_right;
1695 case Core::HID::NpadStyleIndex::JoyconLeft:
1696 return controller.sixaxis_left;
1697 case Core::HID::NpadStyleIndex::JoyconRight:
1698 return controller.sixaxis_right;
1699 default:
1700 return controller.sixaxis_unknown;
1701 }
1702}
1703 1348
1704const Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState( 1349 return {
1705 const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { 1350 .ui_variant = 0,
1706 const auto& controller = GetControllerFromHandle(sixaxis_handle); 1351 .footer = shared_memory->applet_footer_type,
1707 switch (sixaxis_handle.npad_type) { 1352 };
1708 case Core::HID::NpadStyleIndex::ProController:
1709 case Core::HID::NpadStyleIndex::Pokeball:
1710 return controller.sixaxis_fullkey;
1711 case Core::HID::NpadStyleIndex::Handheld:
1712 return controller.sixaxis_handheld;
1713 case Core::HID::NpadStyleIndex::JoyconDual:
1714 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1715 return controller.sixaxis_dual_left;
1716 }
1717 return controller.sixaxis_dual_right;
1718 case Core::HID::NpadStyleIndex::JoyconLeft:
1719 return controller.sixaxis_left;
1720 case Core::HID::NpadStyleIndex::JoyconRight:
1721 return controller.sixaxis_right;
1722 default:
1723 return controller.sixaxis_unknown;
1724 }
1725} 1353}
1726 1354
1727} // namespace Service::HID 1355} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 949e58a4c..9167c93f0 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -10,7 +10,6 @@
10 10
11#include "common/bit_field.h" 11#include "common/bit_field.h"
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/vector_math.h"
14 13
15#include "core/hid/hid_types.h" 14#include "core/hid/hid_types.h"
16#include "core/hle/service/hid/controllers/controller_base.h" 15#include "core/hle/service/hid/controllers/controller_base.h"
@@ -34,11 +33,11 @@ union Result;
34 33
35namespace Service::HID { 34namespace Service::HID {
36 35
37class Controller_NPad final : public ControllerBase { 36class NPad final : public ControllerBase {
38public: 37public:
39 explicit Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, 38 explicit NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
40 KernelHelpers::ServiceContext& service_context_); 39 KernelHelpers::ServiceContext& service_context_);
41 ~Controller_NPad() override; 40 ~NPad() override;
42 41
43 // Called when the controller is initialized 42 // Called when the controller is initialized
44 void OnInit() override; 43 void OnInit() override;
@@ -49,9 +48,6 @@ public:
49 // When the controller is requesting an update for the shared memory 48 // When the controller is requesting an update for the shared memory
50 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; 49 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
51 50
52 // When the controller is requesting a motion update for the shared memory
53 void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) override;
54
55 // This is nn::hid::NpadJoyHoldType 51 // This is nn::hid::NpadJoyHoldType
56 enum class NpadJoyHoldType : u64 { 52 enum class NpadJoyHoldType : u64 {
57 Vertical = 0, 53 Vertical = 0,
@@ -78,6 +74,46 @@ public:
78 MaxActivationMode = 3, 74 MaxActivationMode = 3,
79 }; 75 };
80 76
77 // This is nn::hid::system::AppletFooterUiAttributesSet
78 struct AppletFooterUiAttributes {
79 INSERT_PADDING_BYTES(0x4);
80 };
81
82 // This is nn::hid::system::AppletFooterUiType
83 enum class AppletFooterUiType : u8 {
84 None = 0,
85 HandheldNone = 1,
86 HandheldJoyConLeftOnly = 2,
87 HandheldJoyConRightOnly = 3,
88 HandheldJoyConLeftJoyConRight = 4,
89 JoyDual = 5,
90 JoyDualLeftOnly = 6,
91 JoyDualRightOnly = 7,
92 JoyLeftHorizontal = 8,
93 JoyLeftVertical = 9,
94 JoyRightHorizontal = 10,
95 JoyRightVertical = 11,
96 SwitchProController = 12,
97 CompatibleProController = 13,
98 CompatibleJoyCon = 14,
99 LarkHvc1 = 15,
100 LarkHvc2 = 16,
101 LarkNesLeft = 17,
102 LarkNesRight = 18,
103 Lucia = 19,
104 Verification = 20,
105 Lagon = 21,
106 };
107
108 using AppletFooterUiVariant = u8;
109
110 // This is "nn::hid::system::AppletDetailedUiType".
111 struct AppletDetailedUiType {
112 AppletFooterUiVariant ui_variant;
113 INSERT_PADDING_BYTES(0x2);
114 AppletFooterUiType footer;
115 };
116 static_assert(sizeof(AppletDetailedUiType) == 0x4, "AppletDetailedUiType is an invalid size");
81 // This is nn::hid::NpadCommunicationMode 117 // This is nn::hid::NpadCommunicationMode
82 enum class NpadCommunicationMode : u64 { 118 enum class NpadCommunicationMode : u64 {
83 Mode_5ms = 0, 119 Mode_5ms = 0,
@@ -86,6 +122,15 @@ public:
86 Default = 3, 122 Default = 3,
87 }; 123 };
88 124
125 enum class NpadRevision : u32 {
126 Revision0 = 0,
127 Revision1 = 1,
128 Revision2 = 2,
129 Revision3 = 3,
130 };
131
132 using SixAxisLifo = Lifo<Core::HID::SixAxisSensorState, hid_entry_count>;
133
89 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set); 134 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
90 Core::HID::NpadStyleTag GetSupportedStyleSet() const; 135 Core::HID::NpadStyleTag GetSupportedStyleSet() const;
91 136
@@ -138,37 +183,18 @@ public:
138 183
139 Result DisconnectNpad(Core::HID::NpadIdType npad_id); 184 Result DisconnectNpad(Core::HID::NpadIdType npad_id);
140 185
141 Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
142 Core::HID::GyroscopeZeroDriftMode drift_mode);
143 Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
144 Core::HID::GyroscopeZeroDriftMode& drift_mode) const;
145 Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
146 bool& is_at_rest) const;
147 Result IsFirmwareUpdateAvailableForSixAxisSensor( 186 Result IsFirmwareUpdateAvailableForSixAxisSensor(
148 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const; 187 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const;
149 Result EnableSixAxisSensorUnalteredPassthrough(
150 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled);
151 Result IsSixAxisSensorUnalteredPassthroughEnabled(
152 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const;
153 Result LoadSixAxisSensorCalibrationParameter(
154 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
155 Core::HID::SixAxisSensorCalibrationParameter& calibration) const;
156 Result GetSixAxisSensorIcInformation(
157 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
158 Core::HID::SixAxisSensorIcInformation& ic_information) const;
159 Result ResetIsSixAxisSensorDeviceNewlyAssigned( 188 Result ResetIsSixAxisSensorDeviceNewlyAssigned(
160 const Core::HID::SixAxisSensorHandle& sixaxis_handle); 189 const Core::HID::SixAxisSensorHandle& sixaxis_handle);
161 Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, 190
162 bool sixaxis_status); 191 SixAxisLifo& GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id);
163 Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, 192 SixAxisLifo& GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id);
164 bool& is_fusion_enabled) const; 193 SixAxisLifo& GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id);
165 Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, 194 SixAxisLifo& GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id);
166 bool is_fusion_enabled); 195 SixAxisLifo& GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id);
167 Result SetSixAxisFusionParameters( 196 SixAxisLifo& GetSixAxisRightLifo(Core::HID::NpadIdType npad_id);
168 const Core::HID::SixAxisSensorHandle& sixaxis_handle, 197
169 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
170 Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
171 Core::HID::SixAxisSensorFusionParameters& parameters) const;
172 Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const; 198 Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const;
173 Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, 199 Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
174 bool& is_enabled) const; 200 bool& is_enabled) const;
@@ -192,10 +218,7 @@ public:
192 218
193 void ApplyNpadSystemCommonPolicy(); 219 void ApplyNpadSystemCommonPolicy();
194 220
195 static bool IsNpadIdValid(Core::HID::NpadIdType npad_id); 221 AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id);
196 static Result IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
197 static Result VerifyValidSixAxisSensorHandle(
198 const Core::HID::SixAxisSensorHandle& device_handle);
199 222
200private: 223private:
201 static constexpr std::size_t NPAD_COUNT = 10; 224 static constexpr std::size_t NPAD_COUNT = 10;
@@ -254,29 +277,6 @@ private:
254 }; 277 };
255 static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); 278 static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
256 279
257 // This is nn::hid::SixAxisSensorAttribute
258 struct SixAxisSensorAttribute {
259 union {
260 u32 raw{};
261 BitField<0, 1, u32> is_connected;
262 BitField<1, 1, u32> is_interpolated;
263 };
264 };
265 static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size");
266
267 // This is nn::hid::SixAxisSensorState
268 struct SixAxisSensorState {
269 s64 delta_time{};
270 s64 sampling_number{};
271 Common::Vec3f accel{};
272 Common::Vec3f gyro{};
273 Common::Vec3f rotation{};
274 std::array<Common::Vec3f, 3> orientation{};
275 SixAxisSensorAttribute attribute{};
276 INSERT_PADDING_BYTES(4); // Reserved
277 };
278 static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
279
280 // This is nn::hid::server::NpadGcTriggerState 280 // This is nn::hid::server::NpadGcTriggerState
281 struct NpadGcTriggerState { 281 struct NpadGcTriggerState {
282 s64 sampling_number{}; 282 s64 sampling_number{};
@@ -353,37 +353,6 @@ private:
353 static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, 353 static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
354 "NfcXcdDeviceHandleStateImpl is an invalid size"); 354 "NfcXcdDeviceHandleStateImpl is an invalid size");
355 355
356 // This is nn::hid::system::AppletFooterUiAttributesSet
357 struct AppletFooterUiAttributes {
358 INSERT_PADDING_BYTES(0x4);
359 };
360
361 // This is nn::hid::system::AppletFooterUiType
362 enum class AppletFooterUiType : u8 {
363 None = 0,
364 HandheldNone = 1,
365 HandheldJoyConLeftOnly = 2,
366 HandheldJoyConRightOnly = 3,
367 HandheldJoyConLeftJoyConRight = 4,
368 JoyDual = 5,
369 JoyDualLeftOnly = 6,
370 JoyDualRightOnly = 7,
371 JoyLeftHorizontal = 8,
372 JoyLeftVertical = 9,
373 JoyRightHorizontal = 10,
374 JoyRightVertical = 11,
375 SwitchProController = 12,
376 CompatibleProController = 13,
377 CompatibleJoyCon = 14,
378 LarkHvc1 = 15,
379 LarkHvc2 = 16,
380 LarkNesLeft = 17,
381 LarkNesRight = 18,
382 Lucia = 19,
383 Verification = 20,
384 Lagon = 21,
385 };
386
387 // This is nn::hid::NpadLarkType 356 // This is nn::hid::NpadLarkType
388 enum class NpadLarkType : u32 { 357 enum class NpadLarkType : u32 {
389 Invalid, 358 Invalid,
@@ -427,12 +396,12 @@ private:
427 Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{}; 396 Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{};
428 Lifo<NPadGenericState, hid_entry_count> palma_lifo{}; 397 Lifo<NPadGenericState, hid_entry_count> palma_lifo{};
429 Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{}; 398 Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{};
430 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{}; 399 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{};
431 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{}; 400 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{};
432 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{}; 401 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{};
433 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{}; 402 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{};
434 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{}; 403 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{};
435 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{}; 404 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{};
436 DeviceType device_type{}; 405 DeviceType device_type{};
437 INSERT_PADDING_BYTES(0x4); // Reserved 406 INSERT_PADDING_BYTES(0x4); // Reserved
438 NPadSystemProperties system_properties{}; 407 NPadSystemProperties system_properties{};
@@ -466,16 +435,6 @@ private:
466 std::chrono::steady_clock::time_point last_vibration_timepoint{}; 435 std::chrono::steady_clock::time_point last_vibration_timepoint{};
467 }; 436 };
468 437
469 struct SixaxisParameters {
470 bool is_fusion_enabled{true};
471 bool unaltered_passtrough{false};
472 Core::HID::SixAxisSensorFusionParameters fusion{};
473 Core::HID::SixAxisSensorCalibrationParameter calibration{};
474 Core::HID::SixAxisSensorIcInformation ic_information{};
475 Core::HID::GyroscopeZeroDriftMode gyroscope_zero_drift_mode{
476 Core::HID::GyroscopeZeroDriftMode::Standard};
477 };
478
479 struct NpadControllerData { 438 struct NpadControllerData {
480 Kernel::KEvent* styleset_changed_event{}; 439 Kernel::KEvent* styleset_changed_event{};
481 NpadInternalState* shared_memory = nullptr; 440 NpadInternalState* shared_memory = nullptr;
@@ -489,27 +448,10 @@ private:
489 bool is_dual_left_connected{true}; 448 bool is_dual_left_connected{true};
490 bool is_dual_right_connected{true}; 449 bool is_dual_right_connected{true};
491 450
492 // Motion parameters
493 bool sixaxis_at_rest{true};
494 bool sixaxis_sensor_enabled{true};
495 SixaxisParameters sixaxis_fullkey{};
496 SixaxisParameters sixaxis_handheld{};
497 SixaxisParameters sixaxis_dual_left{};
498 SixaxisParameters sixaxis_dual_right{};
499 SixaxisParameters sixaxis_left{};
500 SixaxisParameters sixaxis_right{};
501 SixaxisParameters sixaxis_unknown{};
502
503 // Current pad state 451 // Current pad state
504 NPadGenericState npad_pad_state{}; 452 NPadGenericState npad_pad_state{};
505 NPadGenericState npad_libnx_state{}; 453 NPadGenericState npad_libnx_state{};
506 NpadGcTriggerState npad_trigger_state{}; 454 NpadGcTriggerState npad_trigger_state{};
507 SixAxisSensorState sixaxis_fullkey_state{};
508 SixAxisSensorState sixaxis_handheld_state{};
509 SixAxisSensorState sixaxis_dual_left_state{};
510 SixAxisSensorState sixaxis_dual_right_state{};
511 SixAxisSensorState sixaxis_left_lifo_state{};
512 SixAxisSensorState sixaxis_right_lifo_state{};
513 int callback_key{}; 455 int callback_key{};
514 }; 456 };
515 457
@@ -520,13 +462,13 @@ private:
520 void WriteEmptyEntry(NpadInternalState* npad); 462 void WriteEmptyEntry(NpadInternalState* npad);
521 463
522 NpadControllerData& GetControllerFromHandle( 464 NpadControllerData& GetControllerFromHandle(
523 const Core::HID::SixAxisSensorHandle& device_handle);
524 const NpadControllerData& GetControllerFromHandle(
525 const Core::HID::SixAxisSensorHandle& device_handle) const;
526 NpadControllerData& GetControllerFromHandle(
527 const Core::HID::VibrationDeviceHandle& device_handle); 465 const Core::HID::VibrationDeviceHandle& device_handle);
528 const NpadControllerData& GetControllerFromHandle( 466 const NpadControllerData& GetControllerFromHandle(
529 const Core::HID::VibrationDeviceHandle& device_handle) const; 467 const Core::HID::VibrationDeviceHandle& device_handle) const;
468 NpadControllerData& GetControllerFromHandle(
469 const Core::HID::SixAxisSensorHandle& device_handle);
470 const NpadControllerData& GetControllerFromHandle(
471 const Core::HID::SixAxisSensorHandle& device_handle) const;
530 NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id); 472 NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
531 const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const; 473 const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
532 474
@@ -534,9 +476,6 @@ private:
534 const Core::HID::SixAxisSensorHandle& device_handle); 476 const Core::HID::SixAxisSensorHandle& device_handle);
535 const Core::HID::SixAxisSensorProperties& GetSixaxisProperties( 477 const Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
536 const Core::HID::SixAxisSensorHandle& device_handle) const; 478 const Core::HID::SixAxisSensorHandle& device_handle) const;
537 SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle);
538 const SixaxisParameters& GetSixaxisState(
539 const Core::HID::SixAxisSensorHandle& device_handle) const;
540 479
541 std::atomic<u64> press_state{}; 480 std::atomic<u64> press_state{};
542 481
diff --git a/src/core/hle/service/hid/controllers/palma.cpp b/src/core/hle/service/hid/controllers/palma.cpp
index 73a2a2b91..588ff9d62 100644
--- a/src/core/hle/service/hid/controllers/palma.cpp
+++ b/src/core/hle/service/hid/controllers/palma.cpp
@@ -12,43 +12,43 @@
12 12
13namespace Service::HID { 13namespace Service::HID {
14 14
15Controller_Palma::Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, 15Palma::Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
16 KernelHelpers::ServiceContext& service_context_) 16 KernelHelpers::ServiceContext& service_context_)
17 : ControllerBase{hid_core_}, service_context{service_context_} { 17 : ControllerBase{hid_core_}, service_context{service_context_} {
18 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); 18 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
19 operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent"); 19 operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent");
20} 20}
21 21
22Controller_Palma::~Controller_Palma() { 22Palma::~Palma() {
23 service_context.CloseEvent(operation_complete_event); 23 service_context.CloseEvent(operation_complete_event);
24}; 24};
25 25
26void Controller_Palma::OnInit() {} 26void Palma::OnInit() {}
27 27
28void Controller_Palma::OnRelease() {} 28void Palma::OnRelease() {}
29 29
30void Controller_Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 30void Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
31 if (!IsControllerActivated()) { 31 if (!IsControllerActivated()) {
32 return; 32 return;
33 } 33 }
34} 34}
35 35
36Result Controller_Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, 36Result Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id,
37 PalmaConnectionHandle& handle) { 37 PalmaConnectionHandle& handle) {
38 active_handle.npad_id = npad_id; 38 active_handle.npad_id = npad_id;
39 handle = active_handle; 39 handle = active_handle;
40 return ResultSuccess; 40 return ResultSuccess;
41} 41}
42 42
43Result Controller_Palma::InitializePalma(const PalmaConnectionHandle& handle) { 43Result Palma::InitializePalma(const PalmaConnectionHandle& handle) {
44 if (handle.npad_id != active_handle.npad_id) { 44 if (handle.npad_id != active_handle.npad_id) {
45 return InvalidPalmaHandle; 45 return InvalidPalmaHandle;
46 } 46 }
47 ActivateController(); 47 Activate();
48 return ResultSuccess; 48 return ResultSuccess;
49} 49}
50 50
51Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent( 51Kernel::KReadableEvent& Palma::AcquirePalmaOperationCompleteEvent(
52 const PalmaConnectionHandle& handle) const { 52 const PalmaConnectionHandle& handle) const {
53 if (handle.npad_id != active_handle.npad_id) { 53 if (handle.npad_id != active_handle.npad_id) {
54 LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id); 54 LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id);
@@ -56,9 +56,9 @@ Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent(
56 return operation_complete_event->GetReadableEvent(); 56 return operation_complete_event->GetReadableEvent();
57} 57}
58 58
59Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle, 59Result Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle,
60 PalmaOperationType& operation_type, 60 PalmaOperationType& operation_type,
61 PalmaOperationData& data) const { 61 PalmaOperationData& data) const {
62 if (handle.npad_id != active_handle.npad_id) { 62 if (handle.npad_id != active_handle.npad_id) {
63 return InvalidPalmaHandle; 63 return InvalidPalmaHandle;
64 } 64 }
@@ -67,8 +67,7 @@ Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& hand
67 return ResultSuccess; 67 return ResultSuccess;
68} 68}
69 69
70Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, 70Result Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity) {
71 u64 palma_activity) {
72 if (handle.npad_id != active_handle.npad_id) { 71 if (handle.npad_id != active_handle.npad_id) {
73 return InvalidPalmaHandle; 72 return InvalidPalmaHandle;
74 } 73 }
@@ -79,8 +78,7 @@ Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle,
79 return ResultSuccess; 78 return ResultSuccess;
80} 79}
81 80
82Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, 81Result Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_) {
83 PalmaFrModeType fr_mode_) {
84 if (handle.npad_id != active_handle.npad_id) { 82 if (handle.npad_id != active_handle.npad_id) {
85 return InvalidPalmaHandle; 83 return InvalidPalmaHandle;
86 } 84 }
@@ -88,7 +86,7 @@ Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle,
88 return ResultSuccess; 86 return ResultSuccess;
89} 87}
90 88
91Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) { 89Result Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) {
92 if (handle.npad_id != active_handle.npad_id) { 90 if (handle.npad_id != active_handle.npad_id) {
93 return InvalidPalmaHandle; 91 return InvalidPalmaHandle;
94 } 92 }
@@ -99,25 +97,25 @@ Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) {
99 return ResultSuccess; 97 return ResultSuccess;
100} 98}
101 99
102Result Controller_Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) { 100Result Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) {
103 if (handle.npad_id != active_handle.npad_id) { 101 if (handle.npad_id != active_handle.npad_id) {
104 return InvalidPalmaHandle; 102 return InvalidPalmaHandle;
105 } 103 }
106 return ResultSuccess; 104 return ResultSuccess;
107} 105}
108 106
109Result Controller_Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) { 107Result Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) {
110 if (handle.npad_id != active_handle.npad_id) { 108 if (handle.npad_id != active_handle.npad_id) {
111 return InvalidPalmaHandle; 109 return InvalidPalmaHandle;
112 } 110 }
113 return ResultSuccess; 111 return ResultSuccess;
114} 112}
115 113
116void Controller_Palma::ReadPalmaApplicationSection() {} 114void Palma::ReadPalmaApplicationSection() {}
117 115
118void Controller_Palma::WritePalmaApplicationSection() {} 116void Palma::WritePalmaApplicationSection() {}
119 117
120Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) { 118Result Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) {
121 if (handle.npad_id != active_handle.npad_id) { 119 if (handle.npad_id != active_handle.npad_id) {
122 return InvalidPalmaHandle; 120 return InvalidPalmaHandle;
123 } 121 }
@@ -128,7 +126,7 @@ Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle
128 return ResultSuccess; 126 return ResultSuccess;
129} 127}
130 128
131Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) { 129Result Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) {
132 if (handle.npad_id != active_handle.npad_id) { 130 if (handle.npad_id != active_handle.npad_id) {
133 return InvalidPalmaHandle; 131 return InvalidPalmaHandle;
134 } 132 }
@@ -139,10 +137,9 @@ Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle&
139 return ResultSuccess; 137 return ResultSuccess;
140} 138}
141 139
142void Controller_Palma::WritePalmaActivityEntry() {} 140void Palma::WritePalmaActivityEntry() {}
143 141
144Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, 142Result Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown) {
145 u64 unknown) {
146 if (handle.npad_id != active_handle.npad_id) { 143 if (handle.npad_id != active_handle.npad_id) {
147 return InvalidPalmaHandle; 144 return InvalidPalmaHandle;
148 } 145 }
@@ -153,8 +150,8 @@ Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandl
153 return ResultSuccess; 150 return ResultSuccess;
154} 151}
155 152
156Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, 153Result Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave,
157 Common::ProcessAddress t_mem, u64 size) { 154 Common::ProcessAddress t_mem, u64 size) {
158 if (handle.npad_id != active_handle.npad_id) { 155 if (handle.npad_id != active_handle.npad_id) {
159 return InvalidPalmaHandle; 156 return InvalidPalmaHandle;
160 } 157 }
@@ -165,8 +162,8 @@ Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle
165 return ResultSuccess; 162 return ResultSuccess;
166} 163}
167 164
168Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle, 165Result Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle,
169 s32 database_id_version_) { 166 s32 database_id_version_) {
170 if (handle.npad_id != active_handle.npad_id) { 167 if (handle.npad_id != active_handle.npad_id) {
171 return InvalidPalmaHandle; 168 return InvalidPalmaHandle;
172 } 169 }
@@ -178,8 +175,7 @@ Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnec
178 return ResultSuccess; 175 return ResultSuccess;
179} 176}
180 177
181Result Controller_Palma::GetPalmaDataBaseIdentificationVersion( 178Result Palma::GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle) {
182 const PalmaConnectionHandle& handle) {
183 if (handle.npad_id != active_handle.npad_id) { 179 if (handle.npad_id != active_handle.npad_id) {
184 return InvalidPalmaHandle; 180 return InvalidPalmaHandle;
185 } 181 }
@@ -191,26 +187,26 @@ Result Controller_Palma::GetPalmaDataBaseIdentificationVersion(
191 return ResultSuccess; 187 return ResultSuccess;
192} 188}
193 189
194void Controller_Palma::SuspendPalmaFeature() {} 190void Palma::SuspendPalmaFeature() {}
195 191
196Result Controller_Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const { 192Result Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const {
197 if (handle.npad_id != active_handle.npad_id) { 193 if (handle.npad_id != active_handle.npad_id) {
198 return InvalidPalmaHandle; 194 return InvalidPalmaHandle;
199 } 195 }
200 return operation.result; 196 return operation.result;
201} 197}
202void Controller_Palma::ReadPalmaPlayLog() {} 198void Palma::ReadPalmaPlayLog() {}
203 199
204void Controller_Palma::ResetPalmaPlayLog() {} 200void Palma::ResetPalmaPlayLog() {}
205 201
206void Controller_Palma::SetIsPalmaAllConnectable(bool is_all_connectable) { 202void Palma::SetIsPalmaAllConnectable(bool is_all_connectable) {
207 // If true controllers are able to be paired 203 // If true controllers are able to be paired
208 is_connectable = is_all_connectable; 204 is_connectable = is_all_connectable;
209} 205}
210 206
211void Controller_Palma::SetIsPalmaPairedConnectable() {} 207void Palma::SetIsPalmaPairedConnectable() {}
212 208
213Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) { 209Result Palma::PairPalma(const PalmaConnectionHandle& handle) {
214 if (handle.npad_id != active_handle.npad_id) { 210 if (handle.npad_id != active_handle.npad_id) {
215 return InvalidPalmaHandle; 211 return InvalidPalmaHandle;
216 } 212 }
@@ -218,14 +214,14 @@ Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) {
218 return ResultSuccess; 214 return ResultSuccess;
219} 215}
220 216
221void Controller_Palma::SetPalmaBoostMode(bool boost_mode) {} 217void Palma::SetPalmaBoostMode(bool boost_mode) {}
222 218
223void Controller_Palma::CancelWritePalmaWaveEntry() {} 219void Palma::CancelWritePalmaWaveEntry() {}
224 220
225void Controller_Palma::EnablePalmaBoostMode() {} 221void Palma::EnablePalmaBoostMode() {}
226 222
227void Controller_Palma::GetPalmaBluetoothAddress() {} 223void Palma::GetPalmaBluetoothAddress() {}
228 224
229void Controller_Palma::SetDisallowedPalmaConnection() {} 225void Palma::SetDisallowedPalmaConnection() {}
230 226
231} // namespace Service::HID 227} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/palma.h b/src/core/hle/service/hid/controllers/palma.h
index a0491a819..a6047f36a 100644
--- a/src/core/hle/service/hid/controllers/palma.h
+++ b/src/core/hle/service/hid/controllers/palma.h
@@ -23,7 +23,7 @@ class EmulatedController;
23} // namespace Core::HID 23} // namespace Core::HID
24 24
25namespace Service::HID { 25namespace Service::HID {
26class Controller_Palma final : public ControllerBase { 26class Palma final : public ControllerBase {
27public: 27public:
28 using PalmaOperationData = std::array<u8, 0x140>; 28 using PalmaOperationData = std::array<u8, 0x140>;
29 29
@@ -97,9 +97,9 @@ public:
97 static_assert(sizeof(PalmaConnectionHandle) == 0x8, 97 static_assert(sizeof(PalmaConnectionHandle) == 0x8,
98 "PalmaConnectionHandle has incorrect size."); 98 "PalmaConnectionHandle has incorrect size.");
99 99
100 explicit Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, 100 explicit Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
101 KernelHelpers::ServiceContext& service_context_); 101 KernelHelpers::ServiceContext& service_context_);
102 ~Controller_Palma() override; 102 ~Palma() override;
103 103
104 // Called when the controller is initialized 104 // Called when the controller is initialized
105 void OnInit() override; 105 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.cpp b/src/core/hle/service/hid/controllers/seven_six_axis.cpp
index bcb272eaf..495568484 100644
--- a/src/core/hle/service/hid/controllers/console_sixaxis.cpp
+++ b/src/core/hle/service/hid/controllers/seven_six_axis.cpp
@@ -1,32 +1,29 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include <cstring>
5#include "common/common_types.h"
4#include "core/core.h" 6#include "core/core.h"
5#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/frontend/emu_window.h"
6#include "core/hid/emulated_console.h" 9#include "core/hid/emulated_console.h"
10#include "core/hid/emulated_devices.h"
7#include "core/hid/hid_core.h" 11#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/console_sixaxis.h" 12#include "core/hle/service/hid/controllers/seven_six_axis.h"
9#include "core/memory.h" 13#include "core/memory.h"
10 14
11namespace Service::HID { 15namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200; 16SevenSixAxis::SevenSixAxis(Core::System& system_)
13
14Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::System& system_, u8* raw_shared_memory_)
15 : ControllerBase{system_.HIDCore()}, system{system_} { 17 : ControllerBase{system_.HIDCore()}, system{system_} {
16 console = hid_core.GetEmulatedConsole(); 18 console = hid_core.GetEmulatedConsole();
17 static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size,
18 "ConsoleSharedMemory is bigger than the shared memory");
19 shared_memory = std::construct_at(
20 reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
21} 19}
22 20
23Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default; 21SevenSixAxis::~SevenSixAxis() = default;
24
25void Controller_ConsoleSixAxis::OnInit() {}
26 22
27void Controller_ConsoleSixAxis::OnRelease() {} 23void SevenSixAxis::OnInit() {}
24void SevenSixAxis::OnRelease() {}
28 25
29void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 26void SevenSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
30 if (!IsControllerActivated() || transfer_memory == 0) { 27 if (!IsControllerActivated() || transfer_memory == 0) {
31 seven_sixaxis_lifo.buffer_count = 0; 28 seven_sixaxis_lifo.buffer_count = 0;
32 seven_sixaxis_lifo.buffer_tail = 0; 29 seven_sixaxis_lifo.buffer_tail = 0;
@@ -53,22 +50,17 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti
53 -motion_status.quaternion.xyz.z, 50 -motion_status.quaternion.xyz.z,
54 }; 51 };
55 52
56 shared_memory->sampling_number++;
57 shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
58 shared_memory->verticalization_error = motion_status.verticalization_error;
59 shared_memory->gyro_bias = motion_status.gyro_bias;
60
61 // Update seven six axis transfer memory
62 seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state); 53 seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state);
63 system.ApplicationMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo, 54 system.ApplicationMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo,
64 sizeof(seven_sixaxis_lifo)); 55 sizeof(seven_sixaxis_lifo));
65} 56}
66 57
67void Controller_ConsoleSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem) { 58void SevenSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem) {
68 transfer_memory = t_mem; 59 transfer_memory = t_mem;
69} 60}
70 61
71void Controller_ConsoleSixAxis::ResetTimestamp() { 62void SevenSixAxis::ResetTimestamp() {
72 last_saved_timestamp = last_global_timestamp; 63 last_saved_timestamp = last_global_timestamp;
73} 64}
65
74} // namespace Service::HID 66} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.h b/src/core/hle/service/hid/controllers/seven_six_axis.h
index 7015d924c..40e3f5d12 100644
--- a/src/core/hle/service/hid/controllers/console_sixaxis.h
+++ b/src/core/hle/service/hid/controllers/seven_six_axis.h
@@ -1,10 +1,9 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#pragma once 4#pragma once
5 5
6#include <array> 6#include "common/common_types.h"
7
8#include "common/quaternion.h" 7#include "common/quaternion.h"
9#include "common/typed_address.h" 8#include "common/typed_address.h"
10#include "core/hle/service/hid/controllers/controller_base.h" 9#include "core/hle/service/hid/controllers/controller_base.h"
@@ -19,10 +18,10 @@ class EmulatedConsole;
19} // namespace Core::HID 18} // namespace Core::HID
20 19
21namespace Service::HID { 20namespace Service::HID {
22class Controller_ConsoleSixAxis final : public ControllerBase { 21class SevenSixAxis final : public ControllerBase {
23public: 22public:
24 explicit Controller_ConsoleSixAxis(Core::System& system_, u8* raw_shared_memory_); 23 explicit SevenSixAxis(Core::System& system_);
25 ~Controller_ConsoleSixAxis() override; 24 ~SevenSixAxis() override;
26 25
27 // Called when the controller is initialized 26 // Called when the controller is initialized
28 void OnInit() override; 27 void OnInit() override;
@@ -51,28 +50,16 @@ private:
51 }; 50 };
52 static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size"); 51 static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size");
53 52
54 // This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
55 struct ConsoleSharedMemory {
56 u64 sampling_number{};
57 bool is_seven_six_axis_sensor_at_rest{};
58 INSERT_PADDING_BYTES(3); // padding
59 f32 verticalization_error{};
60 Common::Vec3f gyro_bias{};
61 INSERT_PADDING_BYTES(4); // padding
62 };
63 static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
64
65 Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{}; 53 Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{};
66 static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size"); 54 static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size");
67 55
56 u64 last_saved_timestamp{};
57 u64 last_global_timestamp{};
58
68 SevenSixAxisState next_seven_sixaxis_state{}; 59 SevenSixAxisState next_seven_sixaxis_state{};
69 Common::ProcessAddress transfer_memory{}; 60 Common::ProcessAddress transfer_memory{};
70 ConsoleSharedMemory* shared_memory = nullptr;
71 Core::HID::EmulatedConsole* console = nullptr; 61 Core::HID::EmulatedConsole* console = nullptr;
72 62
73 u64 last_saved_timestamp{};
74 u64 last_global_timestamp{};
75
76 Core::System& system; 63 Core::System& system;
77}; 64};
78} // namespace Service::HID 65} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/six_axis.cpp b/src/core/hle/service/hid/controllers/six_axis.cpp
new file mode 100644
index 000000000..3d24a5c04
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/six_axis.cpp
@@ -0,0 +1,413 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "common/common_types.h"
5#include "core/core_timing.h"
6#include "core/hid/emulated_controller.h"
7#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/npad.h"
9#include "core/hle/service/hid/controllers/six_axis.h"
10#include "core/hle/service/hid/errors.h"
11#include "core/hle/service/hid/hid_util.h"
12
13namespace Service::HID {
14
15SixAxis::SixAxis(Core::HID::HIDCore& hid_core_, std::shared_ptr<NPad> npad_)
16 : ControllerBase{hid_core_}, npad{npad_} {
17 for (std::size_t i = 0; i < controller_data.size(); ++i) {
18 auto& controller = controller_data[i];
19 controller.device = hid_core.GetEmulatedControllerByIndex(i);
20 }
21}
22
23SixAxis::~SixAxis() = default;
24
25void SixAxis::OnInit() {}
26void SixAxis::OnRelease() {}
27
28void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
29 if (!IsControllerActivated()) {
30 return;
31 }
32
33 for (std::size_t i = 0; i < controller_data.size(); ++i) {
34 auto& controller = controller_data[i];
35
36 const auto npad_id = IndexToNpadIdType(i);
37 const auto& controller_type = controller.device->GetNpadStyleIndex();
38
39 if (controller_type == Core::HID::NpadStyleIndex::None ||
40 !controller.device->IsConnected()) {
41 continue;
42 }
43
44 const auto& motion_state = controller.device->GetMotions();
45 auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
46 auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
47 auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
48 auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
49 auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
50 auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
51
52 auto& sixaxis_fullkey_lifo = npad->GetSixAxisFullkeyLifo(npad_id);
53 auto& sixaxis_handheld_lifo = npad->GetSixAxisHandheldLifo(npad_id);
54 auto& sixaxis_dual_left_lifo = npad->GetSixAxisDualLeftLifo(npad_id);
55 auto& sixaxis_dual_right_lifo = npad->GetSixAxisDualRightLifo(npad_id);
56 auto& sixaxis_left_lifo = npad->GetSixAxisLeftLifo(npad_id);
57 auto& sixaxis_right_lifo = npad->GetSixAxisRightLifo(npad_id);
58
59 // Clear previous state
60 sixaxis_fullkey_state = {};
61 sixaxis_handheld_state = {};
62 sixaxis_dual_left_state = {};
63 sixaxis_dual_right_state = {};
64 sixaxis_left_lifo_state = {};
65 sixaxis_right_lifo_state = {};
66
67 if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
68 controller.sixaxis_at_rest = true;
69 for (std::size_t e = 0; e < motion_state.size(); ++e) {
70 controller.sixaxis_at_rest =
71 controller.sixaxis_at_rest && motion_state[e].is_at_rest;
72 }
73 }
74
75 const auto set_motion_state = [&](Core::HID::SixAxisSensorState& state,
76 const Core::HID::ControllerMotion& hid_state) {
77 using namespace std::literals::chrono_literals;
78 static constexpr Core::HID::SixAxisSensorState default_motion_state = {
79 .delta_time = std::chrono::nanoseconds(5ms).count(),
80 .accel = {0, 0, -1.0f},
81 .orientation =
82 {
83 Common::Vec3f{1.0f, 0, 0},
84 Common::Vec3f{0, 1.0f, 0},
85 Common::Vec3f{0, 0, 1.0f},
86 },
87 .attribute = {1},
88 };
89 if (!controller.sixaxis_sensor_enabled) {
90 state = default_motion_state;
91 return;
92 }
93 if (!Settings::values.motion_enabled.GetValue()) {
94 state = default_motion_state;
95 return;
96 }
97 state.attribute.is_connected.Assign(1);
98 state.delta_time = std::chrono::nanoseconds(5ms).count();
99 state.accel = hid_state.accel;
100 state.gyro = hid_state.gyro;
101 state.rotation = hid_state.rotation;
102 state.orientation = hid_state.orientation;
103 };
104
105 switch (controller_type) {
106 case Core::HID::NpadStyleIndex::None:
107 ASSERT(false);
108 break;
109 case Core::HID::NpadStyleIndex::ProController:
110 set_motion_state(sixaxis_fullkey_state, motion_state[0]);
111 break;
112 case Core::HID::NpadStyleIndex::Handheld:
113 set_motion_state(sixaxis_handheld_state, motion_state[0]);
114 break;
115 case Core::HID::NpadStyleIndex::JoyconDual:
116 set_motion_state(sixaxis_dual_left_state, motion_state[0]);
117 set_motion_state(sixaxis_dual_right_state, motion_state[1]);
118 break;
119 case Core::HID::NpadStyleIndex::JoyconLeft:
120 set_motion_state(sixaxis_left_lifo_state, motion_state[0]);
121 break;
122 case Core::HID::NpadStyleIndex::JoyconRight:
123 set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
124 break;
125 case Core::HID::NpadStyleIndex::Pokeball:
126 using namespace std::literals::chrono_literals;
127 set_motion_state(sixaxis_fullkey_state, motion_state[0]);
128 sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count();
129 break;
130 default:
131 break;
132 }
133
134 sixaxis_fullkey_state.sampling_number =
135 sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
136 sixaxis_handheld_state.sampling_number =
137 sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
138 sixaxis_dual_left_state.sampling_number =
139 sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
140 sixaxis_dual_right_state.sampling_number =
141 sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
142 sixaxis_left_lifo_state.sampling_number =
143 sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
144 sixaxis_right_lifo_state.sampling_number =
145 sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
146
147 if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
148 // This buffer only is updated on handheld on HW
149 sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state);
150 } else {
151 // Handheld doesn't update this buffer on HW
152 sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state);
153 }
154
155 sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state);
156 sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state);
157 sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state);
158 sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state);
159 }
160}
161
162Result SixAxis::SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
163 Core::HID::GyroscopeZeroDriftMode drift_mode) {
164 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
165 if (is_valid.IsError()) {
166 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
167 return is_valid;
168 }
169
170 auto& sixaxis = GetSixaxisState(sixaxis_handle);
171 auto& controller = GetControllerFromHandle(sixaxis_handle);
172 sixaxis.gyroscope_zero_drift_mode = drift_mode;
173 controller.device->SetGyroscopeZeroDriftMode(drift_mode);
174
175 return ResultSuccess;
176}
177
178Result SixAxis::GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
179 Core::HID::GyroscopeZeroDriftMode& drift_mode) const {
180 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
181 if (is_valid.IsError()) {
182 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
183 return is_valid;
184 }
185
186 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
187 drift_mode = sixaxis.gyroscope_zero_drift_mode;
188
189 return ResultSuccess;
190}
191
192Result SixAxis::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
193 bool& is_at_rest) const {
194 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
195 if (is_valid.IsError()) {
196 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
197 return is_valid;
198 }
199
200 const auto& controller = GetControllerFromHandle(sixaxis_handle);
201 is_at_rest = controller.sixaxis_at_rest;
202 return ResultSuccess;
203}
204
205Result SixAxis::LoadSixAxisSensorCalibrationParameter(
206 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
207 Core::HID::SixAxisSensorCalibrationParameter& calibration) const {
208 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
209 if (is_valid.IsError()) {
210 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
211 return is_valid;
212 }
213
214 // TODO: Request this data to the controller. On error return 0xd8ca
215 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
216 calibration = sixaxis.calibration;
217 return ResultSuccess;
218}
219
220Result SixAxis::GetSixAxisSensorIcInformation(
221 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
222 Core::HID::SixAxisSensorIcInformation& ic_information) const {
223 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
224 if (is_valid.IsError()) {
225 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
226 return is_valid;
227 }
228
229 // TODO: Request this data to the controller. On error return 0xd8ca
230 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
231 ic_information = sixaxis.ic_information;
232 return ResultSuccess;
233}
234
235Result SixAxis::EnableSixAxisSensorUnalteredPassthrough(
236 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) {
237 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
238 if (is_valid.IsError()) {
239 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
240 return is_valid;
241 }
242
243 auto& sixaxis = GetSixaxisState(sixaxis_handle);
244 sixaxis.unaltered_passtrough = is_enabled;
245 return ResultSuccess;
246}
247
248Result SixAxis::IsSixAxisSensorUnalteredPassthroughEnabled(
249 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const {
250 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
251 if (is_valid.IsError()) {
252 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
253 return is_valid;
254 }
255
256 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
257 is_enabled = sixaxis.unaltered_passtrough;
258 return ResultSuccess;
259}
260
261Result SixAxis::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
262 bool sixaxis_status) {
263 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
264 if (is_valid.IsError()) {
265 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
266 return is_valid;
267 }
268
269 auto& controller = GetControllerFromHandle(sixaxis_handle);
270 controller.sixaxis_sensor_enabled = sixaxis_status;
271 return ResultSuccess;
272}
273
274Result SixAxis::IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
275 bool& is_fusion_enabled) const {
276 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
277 if (is_valid.IsError()) {
278 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
279 return is_valid;
280 }
281
282 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
283 is_fusion_enabled = sixaxis.is_fusion_enabled;
284
285 return ResultSuccess;
286}
287Result SixAxis::SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
288 bool is_fusion_enabled) {
289 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
290 if (is_valid.IsError()) {
291 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
292 return is_valid;
293 }
294
295 auto& sixaxis = GetSixaxisState(sixaxis_handle);
296 sixaxis.is_fusion_enabled = is_fusion_enabled;
297
298 return ResultSuccess;
299}
300
301Result SixAxis::SetSixAxisFusionParameters(
302 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
303 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
304 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
305 if (is_valid.IsError()) {
306 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
307 return is_valid;
308 }
309
310 const auto param1 = sixaxis_fusion_parameters.parameter1;
311 if (param1 < 0.0f || param1 > 1.0f) {
312 return InvalidSixAxisFusionRange;
313 }
314
315 auto& sixaxis = GetSixaxisState(sixaxis_handle);
316 sixaxis.fusion = sixaxis_fusion_parameters;
317
318 return ResultSuccess;
319}
320
321Result SixAxis::GetSixAxisFusionParameters(
322 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
323 Core::HID::SixAxisSensorFusionParameters& parameters) const {
324 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
325 if (is_valid.IsError()) {
326 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
327 return is_valid;
328 }
329
330 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
331 parameters = sixaxis.fusion;
332
333 return ResultSuccess;
334}
335
336SixAxis::SixaxisParameters& SixAxis::GetSixaxisState(
337 const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
338 auto& controller = GetControllerFromHandle(sixaxis_handle);
339 switch (sixaxis_handle.npad_type) {
340 case Core::HID::NpadStyleIndex::ProController:
341 case Core::HID::NpadStyleIndex::Pokeball:
342 return controller.sixaxis_fullkey;
343 case Core::HID::NpadStyleIndex::Handheld:
344 return controller.sixaxis_handheld;
345 case Core::HID::NpadStyleIndex::JoyconDual:
346 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
347 return controller.sixaxis_dual_left;
348 }
349 return controller.sixaxis_dual_right;
350 case Core::HID::NpadStyleIndex::JoyconLeft:
351 return controller.sixaxis_left;
352 case Core::HID::NpadStyleIndex::JoyconRight:
353 return controller.sixaxis_right;
354 default:
355 return controller.sixaxis_unknown;
356 }
357}
358
359const SixAxis::SixaxisParameters& SixAxis::GetSixaxisState(
360 const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
361 const auto& controller = GetControllerFromHandle(sixaxis_handle);
362 switch (sixaxis_handle.npad_type) {
363 case Core::HID::NpadStyleIndex::ProController:
364 case Core::HID::NpadStyleIndex::Pokeball:
365 return controller.sixaxis_fullkey;
366 case Core::HID::NpadStyleIndex::Handheld:
367 return controller.sixaxis_handheld;
368 case Core::HID::NpadStyleIndex::JoyconDual:
369 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
370 return controller.sixaxis_dual_left;
371 }
372 return controller.sixaxis_dual_right;
373 case Core::HID::NpadStyleIndex::JoyconLeft:
374 return controller.sixaxis_left;
375 case Core::HID::NpadStyleIndex::JoyconRight:
376 return controller.sixaxis_right;
377 default:
378 return controller.sixaxis_unknown;
379 }
380}
381
382SixAxis::NpadControllerData& SixAxis::GetControllerFromHandle(
383 const Core::HID::SixAxisSensorHandle& device_handle) {
384 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
385 return GetControllerFromNpadIdType(npad_id);
386}
387
388const SixAxis::NpadControllerData& SixAxis::GetControllerFromHandle(
389 const Core::HID::SixAxisSensorHandle& device_handle) const {
390 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
391 return GetControllerFromNpadIdType(npad_id);
392}
393
394SixAxis::NpadControllerData& SixAxis::GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) {
395 if (!IsNpadIdValid(npad_id)) {
396 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
397 npad_id = Core::HID::NpadIdType::Player1;
398 }
399 const auto npad_index = NpadIdTypeToIndex(npad_id);
400 return controller_data[npad_index];
401}
402
403const SixAxis::NpadControllerData& SixAxis::GetControllerFromNpadIdType(
404 Core::HID::NpadIdType npad_id) const {
405 if (!IsNpadIdValid(npad_id)) {
406 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
407 npad_id = Core::HID::NpadIdType::Player1;
408 }
409 const auto npad_index = NpadIdTypeToIndex(npad_id);
410 return controller_data[npad_index];
411}
412
413} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/six_axis.h b/src/core/hle/service/hid/controllers/six_axis.h
new file mode 100644
index 000000000..4c4f5dc7b
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/six_axis.h
@@ -0,0 +1,111 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hid/hid_types.h"
8#include "core/hle/service/hid/controllers/controller_base.h"
9#include "core/hle/service/hid/ring_lifo.h"
10
11namespace Core::HID {
12class EmulatedController;
13} // namespace Core::HID
14
15namespace Service::HID {
16class NPad;
17
18class SixAxis final : public ControllerBase {
19public:
20 explicit SixAxis(Core::HID::HIDCore& hid_core_, std::shared_ptr<NPad> npad_);
21 ~SixAxis() override;
22
23 // Called when the controller is initialized
24 void OnInit() override;
25
26 // When the controller is released
27 void OnRelease() override;
28
29 // When the controller is requesting an update for the shared memory
30 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
31
32 Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
33 Core::HID::GyroscopeZeroDriftMode drift_mode);
34 Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
35 Core::HID::GyroscopeZeroDriftMode& drift_mode) const;
36 Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
37 bool& is_at_rest) const;
38 Result EnableSixAxisSensorUnalteredPassthrough(
39 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled);
40 Result IsSixAxisSensorUnalteredPassthroughEnabled(
41 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const;
42 Result LoadSixAxisSensorCalibrationParameter(
43 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
44 Core::HID::SixAxisSensorCalibrationParameter& calibration) const;
45 Result GetSixAxisSensorIcInformation(
46 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
47 Core::HID::SixAxisSensorIcInformation& ic_information) const;
48 Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
49 bool sixaxis_status);
50 Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
51 bool& is_fusion_enabled) const;
52 Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
53 bool is_fusion_enabled);
54 Result SetSixAxisFusionParameters(
55 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
56 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
57 Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
58 Core::HID::SixAxisSensorFusionParameters& parameters) const;
59
60private:
61 static constexpr std::size_t NPAD_COUNT = 10;
62
63 struct SixaxisParameters {
64 bool is_fusion_enabled{true};
65 bool unaltered_passtrough{false};
66 Core::HID::SixAxisSensorFusionParameters fusion{};
67 Core::HID::SixAxisSensorCalibrationParameter calibration{};
68 Core::HID::SixAxisSensorIcInformation ic_information{};
69 Core::HID::GyroscopeZeroDriftMode gyroscope_zero_drift_mode{
70 Core::HID::GyroscopeZeroDriftMode::Standard};
71 };
72
73 struct NpadControllerData {
74 Core::HID::EmulatedController* device = nullptr;
75
76 // Motion parameters
77 bool sixaxis_at_rest{true};
78 bool sixaxis_sensor_enabled{true};
79 SixaxisParameters sixaxis_fullkey{};
80 SixaxisParameters sixaxis_handheld{};
81 SixaxisParameters sixaxis_dual_left{};
82 SixaxisParameters sixaxis_dual_right{};
83 SixaxisParameters sixaxis_left{};
84 SixaxisParameters sixaxis_right{};
85 SixaxisParameters sixaxis_unknown{};
86
87 // Current pad state
88 Core::HID::SixAxisSensorState sixaxis_fullkey_state{};
89 Core::HID::SixAxisSensorState sixaxis_handheld_state{};
90 Core::HID::SixAxisSensorState sixaxis_dual_left_state{};
91 Core::HID::SixAxisSensorState sixaxis_dual_right_state{};
92 Core::HID::SixAxisSensorState sixaxis_left_lifo_state{};
93 Core::HID::SixAxisSensorState sixaxis_right_lifo_state{};
94 int callback_key{};
95 };
96
97 SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle);
98 const SixaxisParameters& GetSixaxisState(
99 const Core::HID::SixAxisSensorHandle& device_handle) const;
100
101 NpadControllerData& GetControllerFromHandle(
102 const Core::HID::SixAxisSensorHandle& device_handle);
103 const NpadControllerData& GetControllerFromHandle(
104 const Core::HID::SixAxisSensorHandle& device_handle) const;
105 NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
106 const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
107
108 std::shared_ptr<NPad> npad;
109 std::array<NpadControllerData, NPAD_COUNT> controller_data{};
110};
111} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 3ef91df4b..3bcf0ee9f 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -15,8 +15,7 @@
15namespace Service::HID { 15namespace Service::HID {
16constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; 16constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
17 17
18Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_, 18TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
19 u8* raw_shared_memory_)
20 : ControllerBase{hid_core_} { 19 : ControllerBase{hid_core_} {
21 static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size, 20 static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size,
22 "TouchSharedMemory is bigger than the shared memory"); 21 "TouchSharedMemory is bigger than the shared memory");
@@ -25,13 +24,13 @@ Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_,
25 console = hid_core.GetEmulatedConsole(); 24 console = hid_core.GetEmulatedConsole();
26} 25}
27 26
28Controller_Touchscreen::~Controller_Touchscreen() = default; 27TouchScreen::~TouchScreen() = default;
29 28
30void Controller_Touchscreen::OnInit() {} 29void TouchScreen::OnInit() {}
31 30
32void Controller_Touchscreen::OnRelease() {} 31void TouchScreen::OnRelease() {}
33 32
34void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 33void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
35 shared_memory->touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count(); 34 shared_memory->touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count();
36 35
37 if (!IsControllerActivated()) { 36 if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index dd00921fd..cd342ce91 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -14,10 +14,10 @@ class EmulatedConsole;
14} // namespace Core::HID 14} // namespace Core::HID
15 15
16namespace Service::HID { 16namespace Service::HID {
17class Controller_Touchscreen final : public ControllerBase { 17class TouchScreen final : public ControllerBase {
18public: 18public:
19 explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 19 explicit TouchScreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
20 ~Controller_Touchscreen() override; 20 ~TouchScreen() override;
21 21
22 // Called when the controller is initialized 22 // Called when the controller is initialized
23 void OnInit() override; 23 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
index 62119e2c5..0aaed1fa7 100644
--- a/src/core/hle/service/hid/controllers/xpad.cpp
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -10,20 +10,19 @@
10namespace Service::HID { 10namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; 11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
12 12
13Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) 13XPad::XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} {
14 : ControllerBase{hid_core_} {
15 static_assert(SHARED_MEMORY_OFFSET + sizeof(XpadSharedMemory) < shared_memory_size, 14 static_assert(SHARED_MEMORY_OFFSET + sizeof(XpadSharedMemory) < shared_memory_size,
16 "XpadSharedMemory is bigger than the shared memory"); 15 "XpadSharedMemory is bigger than the shared memory");
17 shared_memory = std::construct_at( 16 shared_memory = std::construct_at(
18 reinterpret_cast<XpadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); 17 reinterpret_cast<XpadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
19} 18}
20Controller_XPad::~Controller_XPad() = default; 19XPad::~XPad() = default;
21 20
22void Controller_XPad::OnInit() {} 21void XPad::OnInit() {}
23 22
24void Controller_XPad::OnRelease() {} 23void XPad::OnRelease() {}
25 24
26void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 25void XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
27 if (!IsControllerActivated()) { 26 if (!IsControllerActivated()) {
28 shared_memory->basic_xpad_lifo.buffer_count = 0; 27 shared_memory->basic_xpad_lifo.buffer_count = 0;
29 shared_memory->basic_xpad_lifo.buffer_tail = 0; 28 shared_memory->basic_xpad_lifo.buffer_tail = 0;
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
index d01dee5fc..9e63a317a 100644
--- a/src/core/hle/service/hid/controllers/xpad.h
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -10,10 +10,10 @@
10#include "core/hle/service/hid/ring_lifo.h" 10#include "core/hle/service/hid/ring_lifo.h"
11 11
12namespace Service::HID { 12namespace Service::HID {
13class Controller_XPad final : public ControllerBase { 13class XPad final : public ControllerBase {
14public: 14public:
15 explicit Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 15 explicit XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
16 ~Controller_XPad() override; 16 ~XPad() override;
17 17
18 // Called when the controller is initialized 18 // Called when the controller is initialized
19 void OnInit() override; 19 void OnInit() override;
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 929dd5f03..1b7381d8d 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -1,2864 +1,39 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <array>
5#include "common/common_types.h"
6#include "common/logging/log.h"
7#include "common/settings.h"
8#include "core/core.h"
9#include "core/core_timing.h"
10#include "core/hid/hid_core.h"
11#include "core/hle/kernel/k_readable_event.h"
12#include "core/hle/kernel/k_shared_memory.h"
13#include "core/hle/kernel/k_transfer_memory.h"
14#include "core/hle/kernel/kernel.h"
15#include "core/hle/service/hid/errors.h"
16#include "core/hle/service/hid/hid.h" 4#include "core/hle/service/hid/hid.h"
5#include "core/hle/service/hid/hid_debug_server.h"
6#include "core/hle/service/hid/hid_firmware_settings.h"
7#include "core/hle/service/hid/hid_server.h"
8#include "core/hle/service/hid/hid_system_server.h"
17#include "core/hle/service/hid/hidbus.h" 9#include "core/hle/service/hid/hidbus.h"
18#include "core/hle/service/hid/irs.h" 10#include "core/hle/service/hid/irs.h"
11#include "core/hle/service/hid/resource_manager.h"
19#include "core/hle/service/hid/xcd.h" 12#include "core/hle/service/hid/xcd.h"
20#include "core/hle/service/ipc_helpers.h"
21#include "core/hle/service/server_manager.h" 13#include "core/hle/service/server_manager.h"
22#include "core/memory.h"
23
24#include "core/hle/service/hid/controllers/console_sixaxis.h"
25#include "core/hle/service/hid/controllers/controller_base.h"
26#include "core/hle/service/hid/controllers/debug_pad.h"
27#include "core/hle/service/hid/controllers/gesture.h"
28#include "core/hle/service/hid/controllers/keyboard.h"
29#include "core/hle/service/hid/controllers/mouse.h"
30#include "core/hle/service/hid/controllers/npad.h"
31#include "core/hle/service/hid/controllers/palma.h"
32#include "core/hle/service/hid/controllers/stubbed.h"
33#include "core/hle/service/hid/controllers/touchscreen.h"
34#include "core/hle/service/hid/controllers/xpad.h"
35 14
36namespace Service::HID { 15namespace Service::HID {
37 16
38// Updating period for each HID device.
39// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
40// Correct npad_update_ns is 4ms this is overclocked to lower input lag
41constexpr auto npad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz)
42constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz)
43constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
44constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
45
46IAppletResource::IAppletResource(Core::System& system_,
47 KernelHelpers::ServiceContext& service_context_)
48 : ServiceFramework{system_, "IAppletResource"}, service_context{service_context_} {
49 static const FunctionInfo functions[] = {
50 {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
51 };
52 RegisterHandlers(functions);
53 u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer();
54 MakeController<Controller_DebugPad>(HidController::DebugPad, shared_memory);
55 MakeController<Controller_Touchscreen>(HidController::Touchscreen, shared_memory);
56 MakeController<Controller_Mouse>(HidController::Mouse, shared_memory);
57 MakeController<Controller_Keyboard>(HidController::Keyboard, shared_memory);
58 MakeController<Controller_XPad>(HidController::XPad, shared_memory);
59 MakeController<Controller_Stubbed>(HidController::HomeButton, shared_memory);
60 MakeController<Controller_Stubbed>(HidController::SleepButton, shared_memory);
61 MakeController<Controller_Stubbed>(HidController::CaptureButton, shared_memory);
62 MakeController<Controller_Stubbed>(HidController::InputDetector, shared_memory);
63 MakeController<Controller_Stubbed>(HidController::UniquePad, shared_memory);
64 MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad, shared_memory);
65 MakeController<Controller_Gesture>(HidController::Gesture, shared_memory);
66 MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor, shared_memory);
67 MakeController<Controller_Stubbed>(HidController::DebugMouse, shared_memory);
68 MakeControllerWithServiceContext<Controller_Palma>(HidController::Palma, shared_memory);
69
70 // Homebrew doesn't try to activate some controllers, so we activate them by default
71 GetController<Controller_NPad>(HidController::NPad).ActivateController();
72 GetController<Controller_Touchscreen>(HidController::Touchscreen).ActivateController();
73
74 GetController<Controller_Stubbed>(HidController::HomeButton).SetCommonHeaderOffset(0x4C00);
75 GetController<Controller_Stubbed>(HidController::SleepButton).SetCommonHeaderOffset(0x4E00);
76 GetController<Controller_Stubbed>(HidController::CaptureButton).SetCommonHeaderOffset(0x5000);
77 GetController<Controller_Stubbed>(HidController::InputDetector).SetCommonHeaderOffset(0x5200);
78 GetController<Controller_Stubbed>(HidController::UniquePad).SetCommonHeaderOffset(0x5A00);
79 GetController<Controller_Stubbed>(HidController::DebugMouse).SetCommonHeaderOffset(0x3DC00);
80
81 // Register update callbacks
82 npad_update_event = Core::Timing::CreateEvent(
83 "HID::UpdatePadCallback",
84 [this](std::uintptr_t user_data, s64 time,
85 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
86 const auto guard = LockService();
87 UpdateNpad(user_data, ns_late);
88 return std::nullopt;
89 });
90 default_update_event = Core::Timing::CreateEvent(
91 "HID::UpdateDefaultCallback",
92 [this](std::uintptr_t user_data, s64 time,
93 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
94 const auto guard = LockService();
95 UpdateControllers(user_data, ns_late);
96 return std::nullopt;
97 });
98 mouse_keyboard_update_event = Core::Timing::CreateEvent(
99 "HID::UpdateMouseKeyboardCallback",
100 [this](std::uintptr_t user_data, s64 time,
101 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
102 const auto guard = LockService();
103 UpdateMouseKeyboard(user_data, ns_late);
104 return std::nullopt;
105 });
106 motion_update_event = Core::Timing::CreateEvent(
107 "HID::UpdateMotionCallback",
108 [this](std::uintptr_t user_data, s64 time,
109 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
110 const auto guard = LockService();
111 UpdateMotion(user_data, ns_late);
112 return std::nullopt;
113 });
114
115 system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event);
116 system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns,
117 default_update_event);
118 system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns,
119 mouse_keyboard_update_event);
120 system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns,
121 motion_update_event);
122
123 system.HIDCore().ReloadInputDevices();
124}
125
126void IAppletResource::ActivateController(HidController controller) {
127 controllers[static_cast<size_t>(controller)]->ActivateController();
128}
129
130void IAppletResource::DeactivateController(HidController controller) {
131 controllers[static_cast<size_t>(controller)]->DeactivateController();
132}
133
134IAppletResource::~IAppletResource() {
135 system.CoreTiming().UnscheduleEvent(npad_update_event, 0);
136 system.CoreTiming().UnscheduleEvent(default_update_event, 0);
137 system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
138 system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
139}
140
141void IAppletResource::GetSharedMemoryHandle(HLERequestContext& ctx) {
142 LOG_DEBUG(Service_HID, "called");
143
144 IPC::ResponseBuilder rb{ctx, 2, 1};
145 rb.Push(ResultSuccess);
146 rb.PushCopyObjects(&system.Kernel().GetHidSharedMem());
147}
148
149void IAppletResource::UpdateControllers(std::uintptr_t user_data,
150 std::chrono::nanoseconds ns_late) {
151 auto& core_timing = system.CoreTiming();
152
153 for (const auto& controller : controllers) {
154 // Keyboard has it's own update event
155 if (controller == controllers[static_cast<size_t>(HidController::Keyboard)]) {
156 continue;
157 }
158 // Mouse has it's own update event
159 if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) {
160 continue;
161 }
162 // Npad has it's own update event
163 if (controller == controllers[static_cast<size_t>(HidController::NPad)]) {
164 continue;
165 }
166 controller->OnUpdate(core_timing);
167 }
168}
169
170void IAppletResource::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
171 auto& core_timing = system.CoreTiming();
172
173 controllers[static_cast<size_t>(HidController::NPad)]->OnUpdate(core_timing);
174}
175
176void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,
177 std::chrono::nanoseconds ns_late) {
178 auto& core_timing = system.CoreTiming();
179
180 controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing);
181 controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing);
182}
183
184void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
185 auto& core_timing = system.CoreTiming();
186
187 controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing);
188}
189
190class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
191public:
192 explicit IActiveVibrationDeviceList(Core::System& system_,
193 std::shared_ptr<IAppletResource> applet_resource_)
194 : ServiceFramework{system_, "IActiveVibrationDeviceList"},
195 applet_resource(applet_resource_) {
196 // clang-format off
197 static const FunctionInfo functions[] = {
198 {0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"},
199 };
200 // clang-format on
201
202 RegisterHandlers(functions);
203 }
204
205private:
206 void InitializeVibrationDevice(HLERequestContext& ctx) {
207 IPC::RequestParser rp{ctx};
208 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
209
210 if (applet_resource != nullptr) {
211 applet_resource->GetController<Controller_NPad>(HidController::NPad)
212 .InitializeVibrationDevice(vibration_device_handle);
213 }
214
215 LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}",
216 vibration_device_handle.npad_type, vibration_device_handle.npad_id,
217 vibration_device_handle.device_index);
218
219 IPC::ResponseBuilder rb{ctx, 2};
220 rb.Push(ResultSuccess);
221 }
222
223 std::shared_ptr<IAppletResource> applet_resource;
224};
225
226std::shared_ptr<IAppletResource> Hid::GetAppletResource() {
227 if (applet_resource == nullptr) {
228 applet_resource = std::make_shared<IAppletResource>(system, service_context);
229 }
230
231 return applet_resource;
232}
233
234Hid::Hid(Core::System& system_, std::shared_ptr<IAppletResource> applet_resource_)
235 : ServiceFramework{system_, "hid"}, applet_resource{applet_resource_}, service_context{
236 system_,
237 service_name} {
238 // clang-format off
239 static const FunctionInfo functions[] = {
240 {0, &Hid::CreateAppletResource, "CreateAppletResource"},
241 {1, &Hid::ActivateDebugPad, "ActivateDebugPad"},
242 {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"},
243 {21, &Hid::ActivateMouse, "ActivateMouse"},
244 {26, nullptr, "ActivateDebugMouse"},
245 {31, &Hid::ActivateKeyboard, "ActivateKeyboard"},
246 {32, &Hid::SendKeyboardLockKeyEvent, "SendKeyboardLockKeyEvent"},
247 {40, nullptr, "AcquireXpadIdEventHandle"},
248 {41, nullptr, "ReleaseXpadIdEventHandle"},
249 {51, &Hid::ActivateXpad, "ActivateXpad"},
250 {55, &Hid::GetXpadIDs, "GetXpadIds"},
251 {56, nullptr, "ActivateJoyXpad"},
252 {58, nullptr, "GetJoyXpadLifoHandle"},
253 {59, nullptr, "GetJoyXpadIds"},
254 {60, &Hid::ActivateSixAxisSensor, "ActivateSixAxisSensor"},
255 {61, &Hid::DeactivateSixAxisSensor, "DeactivateSixAxisSensor"},
256 {62, nullptr, "GetSixAxisSensorLifoHandle"},
257 {63, nullptr, "ActivateJoySixAxisSensor"},
258 {64, nullptr, "DeactivateJoySixAxisSensor"},
259 {65, nullptr, "GetJoySixAxisSensorLifoHandle"},
260 {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"},
261 {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"},
262 {68, &Hid::IsSixAxisSensorFusionEnabled, "IsSixAxisSensorFusionEnabled"},
263 {69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"},
264 {70, &Hid::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"},
265 {71, &Hid::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"},
266 {72, &Hid::ResetSixAxisSensorFusionParameters, "ResetSixAxisSensorFusionParameters"},
267 {73, nullptr, "SetAccelerometerParameters"},
268 {74, nullptr, "GetAccelerometerParameters"},
269 {75, nullptr, "ResetAccelerometerParameters"},
270 {76, nullptr, "SetAccelerometerPlayMode"},
271 {77, nullptr, "GetAccelerometerPlayMode"},
272 {78, nullptr, "ResetAccelerometerPlayMode"},
273 {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"},
274 {80, &Hid::GetGyroscopeZeroDriftMode, "GetGyroscopeZeroDriftMode"},
275 {81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"},
276 {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
277 {83, &Hid::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"},
278 {84, &Hid::EnableSixAxisSensorUnalteredPassthrough, "EnableSixAxisSensorUnalteredPassthrough"},
279 {85, &Hid::IsSixAxisSensorUnalteredPassthroughEnabled, "IsSixAxisSensorUnalteredPassthroughEnabled"},
280 {86, nullptr, "StoreSixAxisSensorCalibrationParameter"},
281 {87, &Hid::LoadSixAxisSensorCalibrationParameter, "LoadSixAxisSensorCalibrationParameter"},
282 {88, &Hid::GetSixAxisSensorIcInformation, "GetSixAxisSensorIcInformation"},
283 {89, &Hid::ResetIsSixAxisSensorDeviceNewlyAssigned, "ResetIsSixAxisSensorDeviceNewlyAssigned"},
284 {91, &Hid::ActivateGesture, "ActivateGesture"},
285 {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
286 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
287 {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"},
288 {103, &Hid::ActivateNpad, "ActivateNpad"},
289 {104, &Hid::DeactivateNpad, "DeactivateNpad"},
290 {106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"},
291 {107, &Hid::DisconnectNpad, "DisconnectNpad"},
292 {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"},
293 {109, &Hid::ActivateNpadWithRevision, "ActivateNpadWithRevision"},
294 {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
295 {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
296 {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"},
297 {123, &Hid::SetNpadJoyAssignmentModeSingle, "SetNpadJoyAssignmentModeSingle"},
298 {124, &Hid::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"},
299 {125, &Hid::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"},
300 {126, &Hid::StartLrAssignmentMode, "StartLrAssignmentMode"},
301 {127, &Hid::StopLrAssignmentMode, "StopLrAssignmentMode"},
302 {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"},
303 {129, &Hid::GetNpadHandheldActivationMode, "GetNpadHandheldActivationMode"},
304 {130, &Hid::SwapNpadAssignment, "SwapNpadAssignment"},
305 {131, &Hid::IsUnintendedHomeButtonInputProtectionEnabled, "IsUnintendedHomeButtonInputProtectionEnabled"},
306 {132, &Hid::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"},
307 {133, &Hid::SetNpadJoyAssignmentModeSingleWithDestination, "SetNpadJoyAssignmentModeSingleWithDestination"},
308 {134, &Hid::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"},
309 {135, &Hid::SetNpadCaptureButtonAssignment, "SetNpadCaptureButtonAssignment"},
310 {136, &Hid::ClearNpadCaptureButtonAssignment, "ClearNpadCaptureButtonAssignment"},
311 {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
312 {201, &Hid::SendVibrationValue, "SendVibrationValue"},
313 {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"},
314 {203, &Hid::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"},
315 {204, &Hid::PermitVibration, "PermitVibration"},
316 {205, &Hid::IsVibrationPermitted, "IsVibrationPermitted"},
317 {206, &Hid::SendVibrationValues, "SendVibrationValues"},
318 {207, &Hid::SendVibrationGcErmCommand, "SendVibrationGcErmCommand"},
319 {208, &Hid::GetActualVibrationGcErmCommand, "GetActualVibrationGcErmCommand"},
320 {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
321 {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"},
322 {211, &Hid::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"},
323 {212, nullptr, "SendVibrationValueInBool"},
324 {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
325 {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
326 {302, &Hid::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"},
327 {303, &Hid::ActivateSevenSixAxisSensor, "ActivateSevenSixAxisSensor"},
328 {304, &Hid::StartSevenSixAxisSensor, "StartSevenSixAxisSensor"},
329 {305, &Hid::StopSevenSixAxisSensor, "StopSevenSixAxisSensor"},
330 {306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"},
331 {307, &Hid::FinalizeSevenSixAxisSensor, "FinalizeSevenSixAxisSensor"},
332 {308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
333 {309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
334 {310, &Hid::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"},
335 {400, &Hid::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
336 {401, nullptr, "EnableUsbFullKeyController"},
337 {402, nullptr, "IsUsbFullKeyControllerConnected"},
338 {403, nullptr, "HasBattery"},
339 {404, nullptr, "HasLeftRightBattery"},
340 {405, nullptr, "GetNpadInterfaceType"},
341 {406, nullptr, "GetNpadLeftRightInterfaceType"},
342 {407, nullptr, "GetNpadOfHighestBatteryLevel"},
343 {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"},
344 {500, &Hid::GetPalmaConnectionHandle, "GetPalmaConnectionHandle"},
345 {501, &Hid::InitializePalma, "InitializePalma"},
346 {502, &Hid::AcquirePalmaOperationCompleteEvent, "AcquirePalmaOperationCompleteEvent"},
347 {503, &Hid::GetPalmaOperationInfo, "GetPalmaOperationInfo"},
348 {504, &Hid::PlayPalmaActivity, "PlayPalmaActivity"},
349 {505, &Hid::SetPalmaFrModeType, "SetPalmaFrModeType"},
350 {506, &Hid::ReadPalmaStep, "ReadPalmaStep"},
351 {507, &Hid::EnablePalmaStep, "EnablePalmaStep"},
352 {508, &Hid::ResetPalmaStep, "ResetPalmaStep"},
353 {509, &Hid::ReadPalmaApplicationSection, "ReadPalmaApplicationSection"},
354 {510, &Hid::WritePalmaApplicationSection, "WritePalmaApplicationSection"},
355 {511, &Hid::ReadPalmaUniqueCode, "ReadPalmaUniqueCode"},
356 {512, &Hid::SetPalmaUniqueCodeInvalid, "SetPalmaUniqueCodeInvalid"},
357 {513, &Hid::WritePalmaActivityEntry, "WritePalmaActivityEntry"},
358 {514, &Hid::WritePalmaRgbLedPatternEntry, "WritePalmaRgbLedPatternEntry"},
359 {515, &Hid::WritePalmaWaveEntry, "WritePalmaWaveEntry"},
360 {516, &Hid::SetPalmaDataBaseIdentificationVersion, "SetPalmaDataBaseIdentificationVersion"},
361 {517, &Hid::GetPalmaDataBaseIdentificationVersion, "GetPalmaDataBaseIdentificationVersion"},
362 {518, &Hid::SuspendPalmaFeature, "SuspendPalmaFeature"},
363 {519, &Hid::GetPalmaOperationResult, "GetPalmaOperationResult"},
364 {520, &Hid::ReadPalmaPlayLog, "ReadPalmaPlayLog"},
365 {521, &Hid::ResetPalmaPlayLog, "ResetPalmaPlayLog"},
366 {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"},
367 {523, &Hid::SetIsPalmaPairedConnectable, "SetIsPalmaPairedConnectable"},
368 {524, &Hid::PairPalma, "PairPalma"},
369 {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"},
370 {526, &Hid::CancelWritePalmaWaveEntry, "CancelWritePalmaWaveEntry"},
371 {527, &Hid::EnablePalmaBoostMode, "EnablePalmaBoostMode"},
372 {528, &Hid::GetPalmaBluetoothAddress, "GetPalmaBluetoothAddress"},
373 {529, &Hid::SetDisallowedPalmaConnection, "SetDisallowedPalmaConnection"},
374 {1000, &Hid::SetNpadCommunicationMode, "SetNpadCommunicationMode"},
375 {1001, &Hid::GetNpadCommunicationMode, "GetNpadCommunicationMode"},
376 {1002, &Hid::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"},
377 {1003, &Hid::IsFirmwareUpdateNeededForNotification, "IsFirmwareUpdateNeededForNotification"},
378 {2000, nullptr, "ActivateDigitizer"},
379 };
380 // clang-format on
381
382 RegisterHandlers(functions);
383}
384
385Hid::~Hid() = default;
386
387void Hid::CreateAppletResource(HLERequestContext& ctx) {
388 IPC::RequestParser rp{ctx};
389 const auto applet_resource_user_id{rp.Pop<u64>()};
390
391 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
392
393 if (applet_resource == nullptr) {
394 applet_resource = std::make_shared<IAppletResource>(system, service_context);
395 }
396
397 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
398 rb.Push(ResultSuccess);
399 rb.PushIpcInterface<IAppletResource>(applet_resource);
400}
401
402void Hid::ActivateDebugPad(HLERequestContext& ctx) {
403 IPC::RequestParser rp{ctx};
404 const auto applet_resource_user_id{rp.Pop<u64>()};
405
406 applet_resource->ActivateController(HidController::DebugPad);
407
408 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
409
410 IPC::ResponseBuilder rb{ctx, 2};
411 rb.Push(ResultSuccess);
412}
413
414void Hid::ActivateTouchScreen(HLERequestContext& ctx) {
415 IPC::RequestParser rp{ctx};
416 const auto applet_resource_user_id{rp.Pop<u64>()};
417
418 applet_resource->ActivateController(HidController::Touchscreen);
419
420 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
421
422 IPC::ResponseBuilder rb{ctx, 2};
423 rb.Push(ResultSuccess);
424}
425
426void Hid::ActivateMouse(HLERequestContext& ctx) {
427 IPC::RequestParser rp{ctx};
428 const auto applet_resource_user_id{rp.Pop<u64>()};
429
430 applet_resource->ActivateController(HidController::Mouse);
431
432 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
433
434 IPC::ResponseBuilder rb{ctx, 2};
435 rb.Push(ResultSuccess);
436}
437
438void Hid::ActivateKeyboard(HLERequestContext& ctx) {
439 IPC::RequestParser rp{ctx};
440 const auto applet_resource_user_id{rp.Pop<u64>()};
441
442 applet_resource->ActivateController(HidController::Keyboard);
443
444 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
445
446 IPC::ResponseBuilder rb{ctx, 2};
447 rb.Push(ResultSuccess);
448}
449
450void Hid::SendKeyboardLockKeyEvent(HLERequestContext& ctx) {
451 IPC::RequestParser rp{ctx};
452 const auto flags{rp.Pop<u32>()};
453
454 LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags);
455
456 IPC::ResponseBuilder rb{ctx, 2};
457 rb.Push(ResultSuccess);
458}
459
460void Hid::ActivateXpad(HLERequestContext& ctx) {
461 IPC::RequestParser rp{ctx};
462 struct Parameters {
463 u32 basic_xpad_id;
464 INSERT_PADDING_WORDS_NOINIT(1);
465 u64 applet_resource_user_id;
466 };
467 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
468
469 const auto parameters{rp.PopRaw<Parameters>()};
470
471 applet_resource->ActivateController(HidController::XPad);
472
473 LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}",
474 parameters.basic_xpad_id, parameters.applet_resource_user_id);
475
476 IPC::ResponseBuilder rb{ctx, 2};
477 rb.Push(ResultSuccess);
478}
479
480void Hid::GetXpadIDs(HLERequestContext& ctx) {
481 IPC::RequestParser rp{ctx};
482 const auto applet_resource_user_id{rp.Pop<u64>()};
483
484 LOG_DEBUG(Service_HID, "(STUBBED) called, applet_resource_user_id={}", applet_resource_user_id);
485
486 IPC::ResponseBuilder rb{ctx, 3};
487 rb.Push(ResultSuccess);
488 rb.Push(0);
489}
490
491void Hid::ActivateSixAxisSensor(HLERequestContext& ctx) {
492 IPC::RequestParser rp{ctx};
493 struct Parameters {
494 u32 basic_xpad_id;
495 INSERT_PADDING_WORDS_NOINIT(1);
496 u64 applet_resource_user_id;
497 };
498 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
499
500 const auto parameters{rp.PopRaw<Parameters>()};
501
502 // This function does nothing on 10.0.0+
503
504 LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}",
505 parameters.basic_xpad_id, parameters.applet_resource_user_id);
506
507 IPC::ResponseBuilder rb{ctx, 2};
508 rb.Push(ResultSuccess);
509}
510
511void Hid::DeactivateSixAxisSensor(HLERequestContext& ctx) {
512 IPC::RequestParser rp{ctx};
513 struct Parameters {
514 u32 basic_xpad_id;
515 INSERT_PADDING_WORDS_NOINIT(1);
516 u64 applet_resource_user_id;
517 };
518 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
519
520 const auto parameters{rp.PopRaw<Parameters>()};
521
522 // This function does nothing on 10.0.0+
523
524 LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}",
525 parameters.basic_xpad_id, parameters.applet_resource_user_id);
526
527 IPC::ResponseBuilder rb{ctx, 2};
528 rb.Push(ResultSuccess);
529}
530
531void Hid::StartSixAxisSensor(HLERequestContext& ctx) {
532 IPC::RequestParser rp{ctx};
533 struct Parameters {
534 Core::HID::SixAxisSensorHandle sixaxis_handle;
535 INSERT_PADDING_WORDS_NOINIT(1);
536 u64 applet_resource_user_id;
537 };
538 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
539
540 const auto parameters{rp.PopRaw<Parameters>()};
541
542 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
543 const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, true);
544
545 LOG_DEBUG(Service_HID,
546 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
547 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
548 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
549
550 IPC::ResponseBuilder rb{ctx, 2};
551 rb.Push(result);
552}
553
554void Hid::StopSixAxisSensor(HLERequestContext& ctx) {
555 IPC::RequestParser rp{ctx};
556 struct Parameters {
557 Core::HID::SixAxisSensorHandle sixaxis_handle;
558 INSERT_PADDING_WORDS_NOINIT(1);
559 u64 applet_resource_user_id;
560 };
561 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
562
563 const auto parameters{rp.PopRaw<Parameters>()};
564
565 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
566 const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, false);
567
568 LOG_DEBUG(Service_HID,
569 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
570 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
571 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
572
573 IPC::ResponseBuilder rb{ctx, 2};
574 rb.Push(result);
575}
576
577void Hid::IsSixAxisSensorFusionEnabled(HLERequestContext& ctx) {
578 IPC::RequestParser rp{ctx};
579 struct Parameters {
580 Core::HID::SixAxisSensorHandle sixaxis_handle;
581 INSERT_PADDING_WORDS_NOINIT(1);
582 u64 applet_resource_user_id;
583 };
584 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
585
586 const auto parameters{rp.PopRaw<Parameters>()};
587
588 bool is_enabled{};
589 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
590 const auto result =
591 controller.IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled);
592
593 LOG_DEBUG(Service_HID,
594 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
595 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
596 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
597
598 IPC::ResponseBuilder rb{ctx, 3};
599 rb.Push(result);
600 rb.Push(is_enabled);
601}
602
603void Hid::EnableSixAxisSensorFusion(HLERequestContext& ctx) {
604 IPC::RequestParser rp{ctx};
605 struct Parameters {
606 bool enable_sixaxis_sensor_fusion;
607 INSERT_PADDING_BYTES_NOINIT(3);
608 Core::HID::SixAxisSensorHandle sixaxis_handle;
609 u64 applet_resource_user_id;
610 };
611 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
612
613 const auto parameters{rp.PopRaw<Parameters>()};
614
615 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
616 const auto result = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle,
617 parameters.enable_sixaxis_sensor_fusion);
618
619 LOG_DEBUG(Service_HID,
620 "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, "
621 "device_index={}, applet_resource_user_id={}",
622 parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type,
623 parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
624 parameters.applet_resource_user_id);
625
626 IPC::ResponseBuilder rb{ctx, 2};
627 rb.Push(result);
628}
629
630void Hid::SetSixAxisSensorFusionParameters(HLERequestContext& ctx) {
631 IPC::RequestParser rp{ctx};
632 struct Parameters {
633 Core::HID::SixAxisSensorHandle sixaxis_handle;
634 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion;
635 INSERT_PADDING_WORDS_NOINIT(1);
636 u64 applet_resource_user_id;
637 };
638 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
639
640 const auto parameters{rp.PopRaw<Parameters>()};
641
642 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
643 const auto result =
644 controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion);
645
646 LOG_DEBUG(Service_HID,
647 "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, "
648 "parameter2={}, applet_resource_user_id={}",
649 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
650 parameters.sixaxis_handle.device_index, parameters.sixaxis_fusion.parameter1,
651 parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id);
652
653 IPC::ResponseBuilder rb{ctx, 2};
654 rb.Push(result);
655}
656
657void Hid::GetSixAxisSensorFusionParameters(HLERequestContext& ctx) {
658 IPC::RequestParser rp{ctx};
659 struct Parameters {
660 Core::HID::SixAxisSensorHandle sixaxis_handle;
661 INSERT_PADDING_WORDS_NOINIT(1);
662 u64 applet_resource_user_id;
663 };
664 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
665
666 const auto parameters{rp.PopRaw<Parameters>()};
667
668 Core::HID::SixAxisSensorFusionParameters fusion_parameters{};
669 const auto& controller =
670 GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
671 const auto result =
672 controller.GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
673
674 LOG_DEBUG(Service_HID,
675 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
676 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
677 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
678
679 IPC::ResponseBuilder rb{ctx, 4};
680 rb.Push(result);
681 rb.PushRaw(fusion_parameters);
682}
683
684void Hid::ResetSixAxisSensorFusionParameters(HLERequestContext& ctx) {
685 IPC::RequestParser rp{ctx};
686 struct Parameters {
687 Core::HID::SixAxisSensorHandle sixaxis_handle;
688 INSERT_PADDING_WORDS_NOINIT(1);
689 u64 applet_resource_user_id;
690 };
691 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
692
693 const auto parameters{rp.PopRaw<Parameters>()};
694
695 // Since these parameters are unknown just use what HW outputs
696 const Core::HID::SixAxisSensorFusionParameters fusion_parameters{
697 .parameter1 = 0.03f,
698 .parameter2 = 0.4f,
699 };
700 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
701 const auto result1 =
702 controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
703 const auto result2 = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, true);
704
705 LOG_DEBUG(Service_HID,
706 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
707 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
708 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
709
710 IPC::ResponseBuilder rb{ctx, 2};
711 if (result1.IsError()) {
712 rb.Push(result1);
713 return;
714 }
715 rb.Push(result2);
716}
717
718void Hid::SetGyroscopeZeroDriftMode(HLERequestContext& ctx) {
719 IPC::RequestParser rp{ctx};
720 const auto sixaxis_handle{rp.PopRaw<Core::HID::SixAxisSensorHandle>()};
721 const auto drift_mode{rp.PopEnum<Core::HID::GyroscopeZeroDriftMode>()};
722 const auto applet_resource_user_id{rp.Pop<u64>()};
723
724 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
725 const auto result = controller.SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode);
726
727 LOG_DEBUG(Service_HID,
728 "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, "
729 "applet_resource_user_id={}",
730 sixaxis_handle.npad_type, sixaxis_handle.npad_id, sixaxis_handle.device_index,
731 drift_mode, applet_resource_user_id);
732
733 IPC::ResponseBuilder rb{ctx, 2};
734 rb.Push(result);
735}
736
737void Hid::GetGyroscopeZeroDriftMode(HLERequestContext& ctx) {
738 IPC::RequestParser rp{ctx};
739 struct Parameters {
740 Core::HID::SixAxisSensorHandle sixaxis_handle;
741 INSERT_PADDING_WORDS_NOINIT(1);
742 u64 applet_resource_user_id;
743 };
744 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
745
746 const auto parameters{rp.PopRaw<Parameters>()};
747
748 auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard};
749 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
750 const auto result = controller.GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
751
752 LOG_DEBUG(Service_HID,
753 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
754 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
755 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
756
757 IPC::ResponseBuilder rb{ctx, 3};
758 rb.Push(result);
759 rb.PushEnum(drift_mode);
760}
761
762void Hid::ResetGyroscopeZeroDriftMode(HLERequestContext& ctx) {
763 IPC::RequestParser rp{ctx};
764 struct Parameters {
765 Core::HID::SixAxisSensorHandle sixaxis_handle;
766 INSERT_PADDING_WORDS_NOINIT(1);
767 u64 applet_resource_user_id;
768 };
769 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
770
771 const auto parameters{rp.PopRaw<Parameters>()};
772
773 const auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard};
774 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
775 const auto result = controller.SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
776
777 LOG_DEBUG(Service_HID,
778 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
779 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
780 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
781
782 IPC::ResponseBuilder rb{ctx, 2};
783 rb.Push(result);
784}
785
786void Hid::IsSixAxisSensorAtRest(HLERequestContext& ctx) {
787 IPC::RequestParser rp{ctx};
788 struct Parameters {
789 Core::HID::SixAxisSensorHandle sixaxis_handle;
790 INSERT_PADDING_WORDS_NOINIT(1);
791 u64 applet_resource_user_id;
792 };
793 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
794
795 const auto parameters{rp.PopRaw<Parameters>()};
796
797 bool is_at_rest{};
798 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
799 controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest);
800
801 LOG_DEBUG(Service_HID,
802 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
803 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
804 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
805
806 IPC::ResponseBuilder rb{ctx, 3};
807 rb.Push(ResultSuccess);
808 rb.Push(is_at_rest);
809}
810
811void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx) {
812 IPC::RequestParser rp{ctx};
813 struct Parameters {
814 Core::HID::SixAxisSensorHandle sixaxis_handle;
815 INSERT_PADDING_WORDS_NOINIT(1);
816 u64 applet_resource_user_id;
817 };
818 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
819
820 const auto parameters{rp.PopRaw<Parameters>()};
821
822 bool is_firmware_available{};
823 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
824 controller.IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle,
825 is_firmware_available);
826
827 LOG_WARNING(
828 Service_HID,
829 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
830 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
831 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
832
833 IPC::ResponseBuilder rb{ctx, 3};
834 rb.Push(ResultSuccess);
835 rb.Push(is_firmware_available);
836}
837
838void Hid::EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx) {
839 IPC::RequestParser rp{ctx};
840 struct Parameters {
841 bool enabled;
842 Core::HID::SixAxisSensorHandle sixaxis_handle;
843 u64 applet_resource_user_id;
844 };
845 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
846
847 const auto parameters{rp.PopRaw<Parameters>()};
848
849 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
850 const auto result = controller.EnableSixAxisSensorUnalteredPassthrough(
851 parameters.sixaxis_handle, parameters.enabled);
852
853 LOG_DEBUG(Service_HID,
854 "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, "
855 "applet_resource_user_id={}",
856 parameters.enabled, parameters.sixaxis_handle.npad_type,
857 parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
858 parameters.applet_resource_user_id);
859
860 IPC::ResponseBuilder rb{ctx, 2};
861 rb.Push(result);
862}
863
864void Hid::IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx) {
865 IPC::RequestParser rp{ctx};
866 struct Parameters {
867 Core::HID::SixAxisSensorHandle sixaxis_handle;
868 INSERT_PADDING_WORDS_NOINIT(1);
869 u64 applet_resource_user_id;
870 };
871 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
872
873 const auto parameters{rp.PopRaw<Parameters>()};
874
875 bool is_unaltered_sisxaxis_enabled{};
876 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
877 const auto result = controller.IsSixAxisSensorUnalteredPassthroughEnabled(
878 parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled);
879
880 LOG_DEBUG(
881 Service_HID,
882 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
883 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
884 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
885
886 IPC::ResponseBuilder rb{ctx, 3};
887 rb.Push(result);
888 rb.Push(is_unaltered_sisxaxis_enabled);
889}
890
891void Hid::LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx) {
892 IPC::RequestParser rp{ctx};
893 struct Parameters {
894 Core::HID::SixAxisSensorHandle sixaxis_handle;
895 INSERT_PADDING_WORDS_NOINIT(1);
896 u64 applet_resource_user_id;
897 };
898 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
899
900 const auto parameters{rp.PopRaw<Parameters>()};
901
902 Core::HID::SixAxisSensorCalibrationParameter calibration{};
903 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
904 const auto result =
905 controller.LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration);
906
907 LOG_WARNING(
908 Service_HID,
909 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
910 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
911 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
912
913 if (result.IsSuccess()) {
914 ctx.WriteBuffer(calibration);
915 }
916
917 IPC::ResponseBuilder rb{ctx, 2};
918 rb.Push(result);
919}
920
921void Hid::GetSixAxisSensorIcInformation(HLERequestContext& ctx) {
922 IPC::RequestParser rp{ctx};
923 struct Parameters {
924 Core::HID::SixAxisSensorHandle sixaxis_handle;
925 INSERT_PADDING_WORDS_NOINIT(1);
926 u64 applet_resource_user_id;
927 };
928 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
929
930 const auto parameters{rp.PopRaw<Parameters>()};
931
932 Core::HID::SixAxisSensorIcInformation ic_information{};
933 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
934 const auto result =
935 controller.GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information);
936
937 LOG_WARNING(
938 Service_HID,
939 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
940 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
941 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
942
943 if (result.IsSuccess()) {
944 ctx.WriteBuffer(ic_information);
945 }
946
947 IPC::ResponseBuilder rb{ctx, 2};
948 rb.Push(result);
949}
950
951void Hid::ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx) {
952 IPC::RequestParser rp{ctx};
953 struct Parameters {
954 Core::HID::SixAxisSensorHandle sixaxis_handle;
955 INSERT_PADDING_WORDS_NOINIT(1);
956 u64 applet_resource_user_id;
957 };
958 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
959
960 const auto parameters{rp.PopRaw<Parameters>()};
961
962 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
963 const auto result =
964 controller.ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle);
965
966 LOG_WARNING(
967 Service_HID,
968 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
969 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
970 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
971
972 IPC::ResponseBuilder rb{ctx, 2};
973 rb.Push(result);
974}
975
976void Hid::ActivateGesture(HLERequestContext& ctx) {
977 IPC::RequestParser rp{ctx};
978 struct Parameters {
979 u32 unknown;
980 INSERT_PADDING_WORDS_NOINIT(1);
981 u64 applet_resource_user_id;
982 };
983 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
984
985 const auto parameters{rp.PopRaw<Parameters>()};
986
987 applet_resource->ActivateController(HidController::Gesture);
988
989 LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}",
990 parameters.unknown, parameters.applet_resource_user_id);
991
992 IPC::ResponseBuilder rb{ctx, 2};
993 rb.Push(ResultSuccess);
994}
995
996void Hid::SetSupportedNpadStyleSet(HLERequestContext& ctx) {
997 IPC::RequestParser rp{ctx};
998 struct Parameters {
999 Core::HID::NpadStyleSet supported_styleset;
1000 INSERT_PADDING_WORDS_NOINIT(1);
1001 u64 applet_resource_user_id;
1002 };
1003 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1004
1005 const auto parameters{rp.PopRaw<Parameters>()};
1006
1007 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1008 .SetSupportedStyleSet({parameters.supported_styleset});
1009
1010 LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}",
1011 parameters.supported_styleset, parameters.applet_resource_user_id);
1012
1013 IPC::ResponseBuilder rb{ctx, 2};
1014 rb.Push(ResultSuccess);
1015}
1016
1017void Hid::GetSupportedNpadStyleSet(HLERequestContext& ctx) {
1018 IPC::RequestParser rp{ctx};
1019 const auto applet_resource_user_id{rp.Pop<u64>()};
1020
1021 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1022
1023 IPC::ResponseBuilder rb{ctx, 3};
1024 rb.Push(ResultSuccess);
1025 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
1026 .GetSupportedStyleSet()
1027 .raw);
1028}
1029
1030void Hid::SetSupportedNpadIdType(HLERequestContext& ctx) {
1031 IPC::RequestParser rp{ctx};
1032 const auto applet_resource_user_id{rp.Pop<u64>()};
1033
1034 const auto result = applet_resource->GetController<Controller_NPad>(HidController::NPad)
1035 .SetSupportedNpadIdTypes(ctx.ReadBuffer());
1036
1037 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1038
1039 IPC::ResponseBuilder rb{ctx, 2};
1040 rb.Push(result);
1041}
1042
1043void Hid::ActivateNpad(HLERequestContext& ctx) {
1044 IPC::RequestParser rp{ctx};
1045 const auto applet_resource_user_id{rp.Pop<u64>()};
1046
1047 applet_resource->ActivateController(HidController::NPad);
1048
1049 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1050
1051 IPC::ResponseBuilder rb{ctx, 2};
1052 rb.Push(ResultSuccess);
1053}
1054
1055void Hid::DeactivateNpad(HLERequestContext& ctx) {
1056 IPC::RequestParser rp{ctx};
1057 const auto applet_resource_user_id{rp.Pop<u64>()};
1058
1059 applet_resource->DeactivateController(HidController::NPad);
1060
1061 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1062
1063 IPC::ResponseBuilder rb{ctx, 2};
1064 rb.Push(ResultSuccess);
1065}
1066
1067void Hid::AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx) {
1068 IPC::RequestParser rp{ctx};
1069 struct Parameters {
1070 Core::HID::NpadIdType npad_id;
1071 INSERT_PADDING_WORDS_NOINIT(1);
1072 u64 applet_resource_user_id;
1073 u64 unknown;
1074 };
1075 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1076
1077 const auto parameters{rp.PopRaw<Parameters>()};
1078
1079 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}",
1080 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown);
1081
1082 // Games expect this event to be signaled after calling this function
1083 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1084 .SignalStyleSetChangedEvent(parameters.npad_id);
1085
1086 IPC::ResponseBuilder rb{ctx, 2, 1};
1087 rb.Push(ResultSuccess);
1088 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
1089 .GetStyleSetChangedEvent(parameters.npad_id));
1090}
1091
1092void Hid::DisconnectNpad(HLERequestContext& ctx) {
1093 IPC::RequestParser rp{ctx};
1094 struct Parameters {
1095 Core::HID::NpadIdType npad_id;
1096 INSERT_PADDING_WORDS_NOINIT(1);
1097 u64 applet_resource_user_id;
1098 };
1099 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1100
1101 const auto parameters{rp.PopRaw<Parameters>()};
1102
1103 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1104 controller.DisconnectNpad(parameters.npad_id);
1105
1106 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1107 parameters.applet_resource_user_id);
1108
1109 IPC::ResponseBuilder rb{ctx, 2};
1110 rb.Push(ResultSuccess);
1111}
1112
1113void Hid::GetPlayerLedPattern(HLERequestContext& ctx) {
1114 IPC::RequestParser rp{ctx};
1115 const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
1116
1117 Core::HID::LedPattern pattern{0, 0, 0, 0};
1118 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1119 const auto result = controller.GetLedPattern(npad_id, pattern);
1120
1121 LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
1122
1123 IPC::ResponseBuilder rb{ctx, 4};
1124 rb.Push(result);
1125 rb.Push(pattern.raw);
1126}
1127
1128void Hid::ActivateNpadWithRevision(HLERequestContext& ctx) {
1129 // Should have no effect with how our npad sets up the data
1130 IPC::RequestParser rp{ctx};
1131 struct Parameters {
1132 s32 revision;
1133 INSERT_PADDING_WORDS_NOINIT(1);
1134 u64 applet_resource_user_id;
1135 };
1136 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1137
1138 const auto parameters{rp.PopRaw<Parameters>()};
1139
1140 applet_resource->ActivateController(HidController::NPad);
1141
1142 LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision,
1143 parameters.applet_resource_user_id);
1144
1145 IPC::ResponseBuilder rb{ctx, 2};
1146 rb.Push(ResultSuccess);
1147}
1148
1149void Hid::SetNpadJoyHoldType(HLERequestContext& ctx) {
1150 IPC::RequestParser rp{ctx};
1151 const auto applet_resource_user_id{rp.Pop<u64>()};
1152 const auto hold_type{rp.PopEnum<Controller_NPad::NpadJoyHoldType>()};
1153
1154 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetHoldType(hold_type);
1155
1156 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}",
1157 applet_resource_user_id, hold_type);
1158
1159 IPC::ResponseBuilder rb{ctx, 2};
1160 rb.Push(ResultSuccess);
1161}
1162
1163void Hid::GetNpadJoyHoldType(HLERequestContext& ctx) {
1164 IPC::RequestParser rp{ctx};
1165 const auto applet_resource_user_id{rp.Pop<u64>()};
1166
1167 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1168
1169 IPC::ResponseBuilder rb{ctx, 4};
1170 rb.Push(ResultSuccess);
1171 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad).GetHoldType());
1172}
1173
1174void Hid::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) {
1175 IPC::RequestParser rp{ctx};
1176 struct Parameters {
1177 Core::HID::NpadIdType npad_id;
1178 INSERT_PADDING_WORDS_NOINIT(1);
1179 u64 applet_resource_user_id;
1180 };
1181 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1182
1183 const auto parameters{rp.PopRaw<Parameters>()};
1184
1185 Core::HID::NpadIdType new_npad_id{};
1186 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1187 controller.SetNpadMode(new_npad_id, parameters.npad_id,
1188 Controller_NPad::NpadJoyDeviceType::Left,
1189 Controller_NPad::NpadJoyAssignmentMode::Single);
1190
1191 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1192 parameters.applet_resource_user_id);
1193
1194 IPC::ResponseBuilder rb{ctx, 2};
1195 rb.Push(ResultSuccess);
1196}
1197
1198void Hid::SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx) {
1199 IPC::RequestParser rp{ctx};
1200 struct Parameters {
1201 Core::HID::NpadIdType npad_id;
1202 INSERT_PADDING_WORDS_NOINIT(1);
1203 u64 applet_resource_user_id;
1204 Controller_NPad::NpadJoyDeviceType npad_joy_device_type;
1205 };
1206 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1207
1208 const auto parameters{rp.PopRaw<Parameters>()};
1209
1210 Core::HID::NpadIdType new_npad_id{};
1211 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1212 controller.SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type,
1213 Controller_NPad::NpadJoyAssignmentMode::Single);
1214
1215 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
1216 parameters.npad_id, parameters.applet_resource_user_id,
1217 parameters.npad_joy_device_type);
1218
1219 IPC::ResponseBuilder rb{ctx, 2};
1220 rb.Push(ResultSuccess);
1221}
1222
1223void Hid::SetNpadJoyAssignmentModeDual(HLERequestContext& ctx) {
1224 IPC::RequestParser rp{ctx};
1225 struct Parameters {
1226 Core::HID::NpadIdType npad_id;
1227 INSERT_PADDING_WORDS_NOINIT(1);
1228 u64 applet_resource_user_id;
1229 };
1230 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1231
1232 const auto parameters{rp.PopRaw<Parameters>()};
1233
1234 Core::HID::NpadIdType new_npad_id{};
1235 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1236 controller.SetNpadMode(new_npad_id, parameters.npad_id, {},
1237 Controller_NPad::NpadJoyAssignmentMode::Dual);
1238
1239 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1240 parameters.applet_resource_user_id);
1241
1242 IPC::ResponseBuilder rb{ctx, 2};
1243 rb.Push(ResultSuccess);
1244}
1245
1246void Hid::MergeSingleJoyAsDualJoy(HLERequestContext& ctx) {
1247 IPC::RequestParser rp{ctx};
1248 const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()};
1249 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
1250 const auto applet_resource_user_id{rp.Pop<u64>()};
1251
1252 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1253 const auto result = controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
1254
1255 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
1256 npad_id_1, npad_id_2, applet_resource_user_id);
1257
1258 IPC::ResponseBuilder rb{ctx, 2};
1259 rb.Push(result);
1260}
1261
1262void Hid::StartLrAssignmentMode(HLERequestContext& ctx) {
1263 IPC::RequestParser rp{ctx};
1264 const auto applet_resource_user_id{rp.Pop<u64>()};
1265
1266 applet_resource->GetController<Controller_NPad>(HidController::NPad).StartLRAssignmentMode();
1267
1268 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1269
1270 IPC::ResponseBuilder rb{ctx, 2};
1271 rb.Push(ResultSuccess);
1272}
1273
1274void Hid::StopLrAssignmentMode(HLERequestContext& ctx) {
1275 IPC::RequestParser rp{ctx};
1276 const auto applet_resource_user_id{rp.Pop<u64>()};
1277
1278 applet_resource->GetController<Controller_NPad>(HidController::NPad).StopLRAssignmentMode();
1279
1280 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1281
1282 IPC::ResponseBuilder rb{ctx, 2};
1283 rb.Push(ResultSuccess);
1284}
1285
1286void Hid::SetNpadHandheldActivationMode(HLERequestContext& ctx) {
1287 IPC::RequestParser rp{ctx};
1288 const auto applet_resource_user_id{rp.Pop<u64>()};
1289 const auto activation_mode{rp.PopEnum<Controller_NPad::NpadHandheldActivationMode>()};
1290
1291 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1292 .SetNpadHandheldActivationMode(activation_mode);
1293
1294 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}",
1295 applet_resource_user_id, activation_mode);
1296
1297 IPC::ResponseBuilder rb{ctx, 2};
1298 rb.Push(ResultSuccess);
1299}
1300
1301void Hid::GetNpadHandheldActivationMode(HLERequestContext& ctx) {
1302 IPC::RequestParser rp{ctx};
1303 const auto applet_resource_user_id{rp.Pop<u64>()};
1304
1305 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1306
1307 IPC::ResponseBuilder rb{ctx, 4};
1308 rb.Push(ResultSuccess);
1309 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
1310 .GetNpadHandheldActivationMode());
1311}
1312
1313void Hid::SwapNpadAssignment(HLERequestContext& ctx) {
1314 IPC::RequestParser rp{ctx};
1315 const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()};
1316 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
1317 const auto applet_resource_user_id{rp.Pop<u64>()};
1318
1319 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1320 const auto result = controller.SwapNpadAssignment(npad_id_1, npad_id_2);
1321
1322 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
1323 npad_id_1, npad_id_2, applet_resource_user_id);
1324
1325 IPC::ResponseBuilder rb{ctx, 2};
1326 rb.Push(result);
1327}
1328
1329void Hid::IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx) {
1330 IPC::RequestParser rp{ctx};
1331 struct Parameters {
1332 Core::HID::NpadIdType npad_id;
1333 INSERT_PADDING_WORDS_NOINIT(1);
1334 u64 applet_resource_user_id;
1335 };
1336 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1337
1338 const auto parameters{rp.PopRaw<Parameters>()};
1339
1340 bool is_enabled = false;
1341 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1342 const auto result =
1343 controller.IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled);
1344
1345 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
1346 parameters.npad_id, parameters.applet_resource_user_id);
1347
1348 IPC::ResponseBuilder rb{ctx, 3};
1349 rb.Push(result);
1350 rb.Push(is_enabled);
1351}
1352
1353void Hid::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx) {
1354 IPC::RequestParser rp{ctx};
1355 struct Parameters {
1356 bool unintended_home_button_input_protection;
1357 INSERT_PADDING_BYTES_NOINIT(3);
1358 Core::HID::NpadIdType npad_id;
1359 u64 applet_resource_user_id;
1360 };
1361 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1362
1363 const auto parameters{rp.PopRaw<Parameters>()};
1364
1365 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1366 const auto result = controller.SetUnintendedHomeButtonInputProtectionEnabled(
1367 parameters.unintended_home_button_input_protection, parameters.npad_id);
1368
1369 LOG_WARNING(Service_HID,
1370 "(STUBBED) called, unintended_home_button_input_protection={}, npad_id={},"
1371 "applet_resource_user_id={}",
1372 parameters.unintended_home_button_input_protection, parameters.npad_id,
1373 parameters.applet_resource_user_id);
1374
1375 IPC::ResponseBuilder rb{ctx, 2};
1376 rb.Push(result);
1377}
1378
1379void Hid::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx) {
1380 IPC::RequestParser rp{ctx};
1381 struct Parameters {
1382 Core::HID::NpadIdType npad_id;
1383 INSERT_PADDING_WORDS_NOINIT(1);
1384 u64 applet_resource_user_id;
1385 Controller_NPad::NpadJoyDeviceType npad_joy_device_type;
1386 };
1387 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1388
1389 const auto parameters{rp.PopRaw<Parameters>()};
1390
1391 Core::HID::NpadIdType new_npad_id{};
1392 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1393 const auto is_reassigned =
1394 controller.SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type,
1395 Controller_NPad::NpadJoyAssignmentMode::Single);
1396
1397 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
1398 parameters.npad_id, parameters.applet_resource_user_id,
1399 parameters.npad_joy_device_type);
1400
1401 IPC::ResponseBuilder rb{ctx, 4};
1402 rb.Push(ResultSuccess);
1403 rb.Push(is_reassigned);
1404 rb.PushEnum(new_npad_id);
1405}
1406
1407void Hid::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) {
1408 IPC::RequestParser rp{ctx};
1409 struct Parameters {
1410 bool analog_stick_use_center_clamp;
1411 INSERT_PADDING_BYTES_NOINIT(7);
1412 u64 applet_resource_user_id;
1413 };
1414 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1415
1416 const auto parameters{rp.PopRaw<Parameters>()};
1417
1418 GetAppletResource()
1419 ->GetController<Controller_NPad>(HidController::NPad)
1420 .SetAnalogStickUseCenterClamp(parameters.analog_stick_use_center_clamp);
1421
1422 LOG_WARNING(Service_HID,
1423 "(STUBBED) called, analog_stick_use_center_clamp={}, applet_resource_user_id={}",
1424 parameters.analog_stick_use_center_clamp, parameters.applet_resource_user_id);
1425
1426 IPC::ResponseBuilder rb{ctx, 2};
1427 rb.Push(ResultSuccess);
1428}
1429
1430void Hid::SetNpadCaptureButtonAssignment(HLERequestContext& ctx) {
1431 IPC::RequestParser rp{ctx};
1432 struct Parameters {
1433 Core::HID::NpadStyleSet npad_styleset;
1434 INSERT_PADDING_WORDS_NOINIT(1);
1435 u64 applet_resource_user_id;
1436 Core::HID::NpadButton button;
1437 };
1438 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1439
1440 const auto parameters{rp.PopRaw<Parameters>()};
1441
1442 LOG_WARNING(Service_HID,
1443 "(STUBBED) called, npad_styleset={}, applet_resource_user_id={}, button={}",
1444 parameters.npad_styleset, parameters.applet_resource_user_id, parameters.button);
1445
1446 IPC::ResponseBuilder rb{ctx, 2};
1447 rb.Push(ResultSuccess);
1448}
1449
1450void Hid::ClearNpadCaptureButtonAssignment(HLERequestContext& ctx) {
1451 IPC::RequestParser rp{ctx};
1452 const auto applet_resource_user_id{rp.Pop<u64>()};
1453
1454 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1455 applet_resource_user_id);
1456
1457 IPC::ResponseBuilder rb{ctx, 2};
1458 rb.Push(ResultSuccess);
1459}
1460
1461void Hid::GetVibrationDeviceInfo(HLERequestContext& ctx) {
1462 IPC::RequestParser rp{ctx};
1463 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
1464 const auto& controller =
1465 GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1466
1467 Core::HID::VibrationDeviceInfo vibration_device_info;
1468 bool check_device_index = false;
1469
1470 switch (vibration_device_handle.npad_type) {
1471 case Core::HID::NpadStyleIndex::ProController:
1472 case Core::HID::NpadStyleIndex::Handheld:
1473 case Core::HID::NpadStyleIndex::JoyconDual:
1474 case Core::HID::NpadStyleIndex::JoyconLeft:
1475 case Core::HID::NpadStyleIndex::JoyconRight:
1476 vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator;
1477 check_device_index = true;
1478 break;
1479 case Core::HID::NpadStyleIndex::GameCube:
1480 vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm;
1481 break;
1482 case Core::HID::NpadStyleIndex::N64:
1483 vibration_device_info.type = Core::HID::VibrationDeviceType::N64;
1484 break;
1485 default:
1486 vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown;
1487 break;
1488 }
1489
1490 vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
1491 if (check_device_index) {
1492 switch (vibration_device_handle.device_index) {
1493 case Core::HID::DeviceIndex::Left:
1494 vibration_device_info.position = Core::HID::VibrationDevicePosition::Left;
1495 break;
1496 case Core::HID::DeviceIndex::Right:
1497 vibration_device_info.position = Core::HID::VibrationDevicePosition::Right;
1498 break;
1499 case Core::HID::DeviceIndex::None:
1500 default:
1501 ASSERT_MSG(false, "DeviceIndex should never be None!");
1502 break;
1503 }
1504 }
1505
1506 LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}",
1507 vibration_device_info.type, vibration_device_info.position);
1508
1509 const auto result = controller.IsDeviceHandleValid(vibration_device_handle);
1510 if (result.IsError()) {
1511 IPC::ResponseBuilder rb{ctx, 2};
1512 rb.Push(result);
1513 return;
1514 }
1515
1516 IPC::ResponseBuilder rb{ctx, 4};
1517 rb.Push(ResultSuccess);
1518 rb.PushRaw(vibration_device_info);
1519}
1520
1521void Hid::SendVibrationValue(HLERequestContext& ctx) {
1522 IPC::RequestParser rp{ctx};
1523 struct Parameters {
1524 Core::HID::VibrationDeviceHandle vibration_device_handle;
1525 Core::HID::VibrationValue vibration_value;
1526 INSERT_PADDING_WORDS_NOINIT(1);
1527 u64 applet_resource_user_id;
1528 };
1529 static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
1530
1531 const auto parameters{rp.PopRaw<Parameters>()};
1532
1533 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1534 .VibrateController(parameters.vibration_device_handle, parameters.vibration_value);
1535
1536 LOG_DEBUG(Service_HID,
1537 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1538 parameters.vibration_device_handle.npad_type,
1539 parameters.vibration_device_handle.npad_id,
1540 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1541
1542 IPC::ResponseBuilder rb{ctx, 2};
1543 rb.Push(ResultSuccess);
1544}
1545
1546void Hid::GetActualVibrationValue(HLERequestContext& ctx) {
1547 IPC::RequestParser rp{ctx};
1548 struct Parameters {
1549 Core::HID::VibrationDeviceHandle vibration_device_handle;
1550 INSERT_PADDING_WORDS_NOINIT(1);
1551 u64 applet_resource_user_id;
1552 };
1553 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1554
1555 const auto parameters{rp.PopRaw<Parameters>()};
1556
1557 LOG_DEBUG(Service_HID,
1558 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1559 parameters.vibration_device_handle.npad_type,
1560 parameters.vibration_device_handle.npad_id,
1561 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1562
1563 IPC::ResponseBuilder rb{ctx, 6};
1564 rb.Push(ResultSuccess);
1565 rb.PushRaw(applet_resource->GetController<Controller_NPad>(HidController::NPad)
1566 .GetLastVibration(parameters.vibration_device_handle));
1567}
1568
1569void Hid::CreateActiveVibrationDeviceList(HLERequestContext& ctx) {
1570 LOG_DEBUG(Service_HID, "called");
1571
1572 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1573 rb.Push(ResultSuccess);
1574 rb.PushIpcInterface<IActiveVibrationDeviceList>(system, applet_resource);
1575}
1576
1577void Hid::PermitVibration(HLERequestContext& ctx) {
1578 IPC::RequestParser rp{ctx};
1579 const auto can_vibrate{rp.Pop<bool>()};
1580
1581 // nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value
1582 // by converting it to a bool
1583 Settings::values.vibration_enabled.SetValue(can_vibrate);
1584
1585 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
1586
1587 IPC::ResponseBuilder rb{ctx, 2};
1588 rb.Push(ResultSuccess);
1589}
1590
1591void Hid::IsVibrationPermitted(HLERequestContext& ctx) {
1592 LOG_DEBUG(Service_HID, "called");
1593
1594 // nnSDK checks if a float is greater than zero. We return the bool we stored earlier
1595 const auto is_enabled = Settings::values.vibration_enabled.GetValue();
1596
1597 IPC::ResponseBuilder rb{ctx, 3};
1598 rb.Push(ResultSuccess);
1599 rb.Push(is_enabled);
1600}
1601
1602void Hid::SendVibrationValues(HLERequestContext& ctx) {
1603 IPC::RequestParser rp{ctx};
1604 const auto applet_resource_user_id{rp.Pop<u64>()};
1605
1606 const auto handle_data = ctx.ReadBuffer(0);
1607 const auto handle_count = ctx.GetReadBufferNumElements<Core::HID::VibrationDeviceHandle>(0);
1608 const auto vibration_data = ctx.ReadBuffer(1);
1609 const auto vibration_count = ctx.GetReadBufferNumElements<Core::HID::VibrationValue>(1);
1610
1611 auto vibration_device_handles =
1612 std::span(reinterpret_cast<const Core::HID::VibrationDeviceHandle*>(handle_data.data()),
1613 handle_count);
1614 auto vibration_values = std::span(
1615 reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count);
1616
1617 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1618 .VibrateControllers(vibration_device_handles, vibration_values);
1619
1620 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1621
1622 IPC::ResponseBuilder rb{ctx, 2};
1623 rb.Push(ResultSuccess);
1624}
1625
1626void Hid::SendVibrationGcErmCommand(HLERequestContext& ctx) {
1627 IPC::RequestParser rp{ctx};
1628 struct Parameters {
1629 Core::HID::VibrationDeviceHandle vibration_device_handle;
1630 INSERT_PADDING_WORDS_NOINIT(1);
1631 u64 applet_resource_user_id;
1632 Core::HID::VibrationGcErmCommand gc_erm_command;
1633 };
1634 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1635
1636 const auto parameters{rp.PopRaw<Parameters>()};
1637
1638 /**
1639 * Note: This uses yuzu-specific behavior such that the StopHard command produces
1640 * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined below,
1641 * in order to differentiate between Stop and StopHard commands.
1642 * This is done to reuse the controller vibration functions made for regular controllers.
1643 */
1644 const auto vibration_value = [parameters] {
1645 switch (parameters.gc_erm_command) {
1646 case Core::HID::VibrationGcErmCommand::Stop:
1647 return Core::HID::VibrationValue{
1648 .low_amplitude = 0.0f,
1649 .low_frequency = 160.0f,
1650 .high_amplitude = 0.0f,
1651 .high_frequency = 320.0f,
1652 };
1653 case Core::HID::VibrationGcErmCommand::Start:
1654 return Core::HID::VibrationValue{
1655 .low_amplitude = 1.0f,
1656 .low_frequency = 160.0f,
1657 .high_amplitude = 1.0f,
1658 .high_frequency = 320.0f,
1659 };
1660 case Core::HID::VibrationGcErmCommand::StopHard:
1661 return Core::HID::VibrationValue{
1662 .low_amplitude = 0.0f,
1663 .low_frequency = 0.0f,
1664 .high_amplitude = 0.0f,
1665 .high_frequency = 0.0f,
1666 };
1667 default:
1668 return Core::HID::DEFAULT_VIBRATION_VALUE;
1669 }
1670 }();
1671
1672 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1673 .VibrateController(parameters.vibration_device_handle, vibration_value);
1674
1675 LOG_DEBUG(Service_HID,
1676 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, "
1677 "gc_erm_command={}",
1678 parameters.vibration_device_handle.npad_type,
1679 parameters.vibration_device_handle.npad_id,
1680 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id,
1681 parameters.gc_erm_command);
1682
1683 IPC::ResponseBuilder rb{ctx, 2};
1684 rb.Push(ResultSuccess);
1685}
1686
1687void Hid::GetActualVibrationGcErmCommand(HLERequestContext& ctx) {
1688 IPC::RequestParser rp{ctx};
1689 struct Parameters {
1690 Core::HID::VibrationDeviceHandle vibration_device_handle;
1691 INSERT_PADDING_WORDS_NOINIT(1);
1692 u64 applet_resource_user_id;
1693 };
1694
1695 const auto parameters{rp.PopRaw<Parameters>()};
1696
1697 const auto last_vibration = applet_resource->GetController<Controller_NPad>(HidController::NPad)
1698 .GetLastVibration(parameters.vibration_device_handle);
1699
1700 const auto gc_erm_command = [last_vibration] {
1701 if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) {
1702 return Core::HID::VibrationGcErmCommand::Start;
1703 }
1704
1705 /**
1706 * Note: This uses yuzu-specific behavior such that the StopHard command produces
1707 * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined in the HID function
1708 * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands.
1709 * This is done to reuse the controller vibration functions made for regular controllers.
1710 */
1711 if (last_vibration.low_frequency == 0.0f && last_vibration.high_frequency == 0.0f) {
1712 return Core::HID::VibrationGcErmCommand::StopHard;
1713 }
1714
1715 return Core::HID::VibrationGcErmCommand::Stop;
1716 }();
1717
1718 LOG_DEBUG(Service_HID,
1719 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1720 parameters.vibration_device_handle.npad_type,
1721 parameters.vibration_device_handle.npad_id,
1722 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1723
1724 IPC::ResponseBuilder rb{ctx, 4};
1725 rb.Push(ResultSuccess);
1726 rb.PushEnum(gc_erm_command);
1727}
1728
1729void Hid::BeginPermitVibrationSession(HLERequestContext& ctx) {
1730 IPC::RequestParser rp{ctx};
1731 const auto applet_resource_user_id{rp.Pop<u64>()};
1732
1733 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1734 .SetPermitVibrationSession(true);
1735
1736 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1737
1738 IPC::ResponseBuilder rb{ctx, 2};
1739 rb.Push(ResultSuccess);
1740}
1741
1742void Hid::EndPermitVibrationSession(HLERequestContext& ctx) {
1743 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1744 .SetPermitVibrationSession(false);
1745
1746 LOG_DEBUG(Service_HID, "called");
1747
1748 IPC::ResponseBuilder rb{ctx, 2};
1749 rb.Push(ResultSuccess);
1750}
1751
1752void Hid::IsVibrationDeviceMounted(HLERequestContext& ctx) {
1753 IPC::RequestParser rp{ctx};
1754 struct Parameters {
1755 Core::HID::VibrationDeviceHandle vibration_device_handle;
1756 INSERT_PADDING_WORDS_NOINIT(1);
1757 u64 applet_resource_user_id;
1758 };
1759 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1760
1761 const auto parameters{rp.PopRaw<Parameters>()};
1762
1763 LOG_DEBUG(Service_HID,
1764 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1765 parameters.vibration_device_handle.npad_type,
1766 parameters.vibration_device_handle.npad_id,
1767 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1768
1769 IPC::ResponseBuilder rb{ctx, 3};
1770 rb.Push(ResultSuccess);
1771 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
1772 .IsVibrationDeviceMounted(parameters.vibration_device_handle));
1773}
1774
1775void Hid::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) {
1776 IPC::RequestParser rp{ctx};
1777 const auto applet_resource_user_id{rp.Pop<u64>()};
1778
1779 applet_resource->ActivateController(HidController::ConsoleSixAxisSensor);
1780
1781 LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1782
1783 IPC::ResponseBuilder rb{ctx, 2};
1784 rb.Push(ResultSuccess);
1785}
1786
1787void Hid::StartConsoleSixAxisSensor(HLERequestContext& ctx) {
1788 IPC::RequestParser rp{ctx};
1789 struct Parameters {
1790 Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle;
1791 INSERT_PADDING_WORDS_NOINIT(1);
1792 u64 applet_resource_user_id;
1793 };
1794 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1795
1796 const auto parameters{rp.PopRaw<Parameters>()};
1797
1798 LOG_WARNING(Service_HID,
1799 "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
1800 parameters.console_sixaxis_handle.unknown_1,
1801 parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id);
1802
1803 IPC::ResponseBuilder rb{ctx, 2};
1804 rb.Push(ResultSuccess);
1805}
1806
1807void Hid::StopConsoleSixAxisSensor(HLERequestContext& ctx) {
1808 IPC::RequestParser rp{ctx};
1809 struct Parameters {
1810 Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle;
1811 INSERT_PADDING_WORDS_NOINIT(1);
1812 u64 applet_resource_user_id;
1813 };
1814 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1815
1816 const auto parameters{rp.PopRaw<Parameters>()};
1817
1818 LOG_WARNING(Service_HID,
1819 "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
1820 parameters.console_sixaxis_handle.unknown_1,
1821 parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id);
1822
1823 IPC::ResponseBuilder rb{ctx, 2};
1824 rb.Push(ResultSuccess);
1825}
1826
1827void Hid::ActivateSevenSixAxisSensor(HLERequestContext& ctx) {
1828 IPC::RequestParser rp{ctx};
1829 const auto applet_resource_user_id{rp.Pop<u64>()};
1830
1831 applet_resource->ActivateController(HidController::ConsoleSixAxisSensor);
1832
1833 LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1834
1835 IPC::ResponseBuilder rb{ctx, 2};
1836 rb.Push(ResultSuccess);
1837}
1838
1839void Hid::StartSevenSixAxisSensor(HLERequestContext& ctx) {
1840 IPC::RequestParser rp{ctx};
1841 const auto applet_resource_user_id{rp.Pop<u64>()};
1842
1843 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1844 applet_resource_user_id);
1845
1846 IPC::ResponseBuilder rb{ctx, 2};
1847 rb.Push(ResultSuccess);
1848}
1849
1850void Hid::StopSevenSixAxisSensor(HLERequestContext& ctx) {
1851 IPC::RequestParser rp{ctx};
1852 const auto applet_resource_user_id{rp.Pop<u64>()};
1853
1854 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1855 applet_resource_user_id);
1856
1857 IPC::ResponseBuilder rb{ctx, 2};
1858 rb.Push(ResultSuccess);
1859}
1860
1861void Hid::InitializeSevenSixAxisSensor(HLERequestContext& ctx) {
1862 IPC::RequestParser rp{ctx};
1863 const auto applet_resource_user_id{rp.Pop<u64>()};
1864 const auto t_mem_1_size{rp.Pop<u64>()};
1865 const auto t_mem_2_size{rp.Pop<u64>()};
1866 const auto t_mem_1_handle{ctx.GetCopyHandle(0)};
1867 const auto t_mem_2_handle{ctx.GetCopyHandle(1)};
1868
1869 ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes");
1870 ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes");
1871
1872 auto t_mem_1 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
1873 t_mem_1_handle);
1874
1875 if (t_mem_1.IsNull()) {
1876 LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle);
1877 IPC::ResponseBuilder rb{ctx, 2};
1878 rb.Push(ResultUnknown);
1879 return;
1880 }
1881
1882 auto t_mem_2 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
1883 t_mem_2_handle);
1884
1885 if (t_mem_2.IsNull()) {
1886 LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle);
1887 IPC::ResponseBuilder rb{ctx, 2};
1888 rb.Push(ResultUnknown);
1889 return;
1890 }
1891
1892 ASSERT_MSG(t_mem_1->GetSize() == 0x1000, "t_mem_1 has incorrect size");
1893 ASSERT_MSG(t_mem_2->GetSize() == 0x7F000, "t_mem_2 has incorrect size");
1894
1895 // Activate console six axis controller
1896 applet_resource->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor)
1897 .ActivateController();
1898
1899 applet_resource->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor)
1900 .SetTransferMemoryAddress(t_mem_1->GetSourceAddress());
1901
1902 LOG_WARNING(Service_HID,
1903 "called, t_mem_1_handle=0x{:08X}, t_mem_2_handle=0x{:08X}, "
1904 "applet_resource_user_id={}",
1905 t_mem_1_handle, t_mem_2_handle, applet_resource_user_id);
1906
1907 IPC::ResponseBuilder rb{ctx, 2};
1908 rb.Push(ResultSuccess);
1909}
1910
1911void Hid::FinalizeSevenSixAxisSensor(HLERequestContext& ctx) {
1912 IPC::RequestParser rp{ctx};
1913 const auto applet_resource_user_id{rp.Pop<u64>()};
1914
1915 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1916 applet_resource_user_id);
1917
1918 IPC::ResponseBuilder rb{ctx, 2};
1919 rb.Push(ResultSuccess);
1920}
1921
1922void Hid::ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx) {
1923 IPC::RequestParser rp{ctx};
1924 const auto applet_resource_user_id{rp.Pop<u64>()};
1925
1926 applet_resource->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor)
1927 .ResetTimestamp();
1928
1929 LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1930
1931 IPC::ResponseBuilder rb{ctx, 2};
1932 rb.Push(ResultSuccess);
1933}
1934
1935void Hid::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
1936 IPC::RequestParser rp{ctx};
1937
1938 LOG_WARNING(Service_HID, "(STUBBED) called");
1939
1940 IPC::ResponseBuilder rb{ctx, 3};
1941 rb.Push(ResultSuccess);
1942 rb.Push(false);
1943}
1944
1945void Hid::GetPalmaConnectionHandle(HLERequestContext& ctx) {
1946 IPC::RequestParser rp{ctx};
1947 struct Parameters {
1948 Core::HID::NpadIdType npad_id;
1949 INSERT_PADDING_WORDS_NOINIT(1);
1950 u64 applet_resource_user_id;
1951 };
1952 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1953
1954 const auto parameters{rp.PopRaw<Parameters>()};
1955
1956 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
1957 parameters.npad_id, parameters.applet_resource_user_id);
1958
1959 Controller_Palma::PalmaConnectionHandle handle;
1960 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
1961 const auto result = controller.GetPalmaConnectionHandle(parameters.npad_id, handle);
1962
1963 IPC::ResponseBuilder rb{ctx, 4};
1964 rb.Push(result);
1965 rb.PushRaw(handle);
1966}
1967
1968void Hid::InitializePalma(HLERequestContext& ctx) {
1969 IPC::RequestParser rp{ctx};
1970 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
1971
1972 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1973
1974 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
1975 const auto result = controller.InitializePalma(connection_handle);
1976
1977 IPC::ResponseBuilder rb{ctx, 2};
1978 rb.Push(result);
1979}
1980
1981void Hid::AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx) {
1982 IPC::RequestParser rp{ctx};
1983 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
1984
1985 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1986
1987 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
1988
1989 IPC::ResponseBuilder rb{ctx, 2, 1};
1990 rb.Push(ResultSuccess);
1991 rb.PushCopyObjects(controller.AcquirePalmaOperationCompleteEvent(connection_handle));
1992}
1993
1994void Hid::GetPalmaOperationInfo(HLERequestContext& ctx) {
1995 IPC::RequestParser rp{ctx};
1996 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
1997
1998 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1999
2000 Controller_Palma::PalmaOperationType operation_type;
2001 Controller_Palma::PalmaOperationData data;
2002 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
2003 const auto result = controller.GetPalmaOperationInfo(connection_handle, operation_type, data);
2004
2005 if (result.IsError()) {
2006 IPC::ResponseBuilder rb{ctx, 2};
2007 rb.Push(result);
2008 }
2009
2010 ctx.WriteBuffer(data);
2011 IPC::ResponseBuilder rb{ctx, 4};
2012 rb.Push(result);
2013 rb.Push(static_cast<u64>(operation_type));
2014}
2015
2016void Hid::PlayPalmaActivity(HLERequestContext& ctx) {
2017 IPC::RequestParser rp{ctx};
2018 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2019 const auto palma_activity{rp.Pop<u64>()};
2020
2021 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}",
2022 connection_handle.npad_id, palma_activity);
2023
2024 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
2025 const auto result = controller.PlayPalmaActivity(connection_handle, palma_activity);
2026
2027 IPC::ResponseBuilder rb{ctx, 2};
2028 rb.Push(result);
2029}
2030
2031void Hid::SetPalmaFrModeType(HLERequestContext& ctx) {
2032 IPC::RequestParser rp{ctx};
2033 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2034 const auto fr_mode{rp.PopEnum<Controller_Palma::PalmaFrModeType>()};
2035
2036 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}",
2037 connection_handle.npad_id, fr_mode);
2038
2039 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
2040 const auto result = controller.SetPalmaFrModeType(connection_handle, fr_mode);
2041
2042 IPC::ResponseBuilder rb{ctx, 2};
2043 rb.Push(result);
2044}
2045
2046void Hid::ReadPalmaStep(HLERequestContext& ctx) {
2047 IPC::RequestParser rp{ctx};
2048 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2049
2050 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2051
2052 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
2053 const auto result = controller.ReadPalmaStep(connection_handle);
2054
2055 IPC::ResponseBuilder rb{ctx, 2};
2056 rb.Push(result);
2057}
2058
2059void Hid::EnablePalmaStep(HLERequestContext& ctx) {
2060 IPC::RequestParser rp{ctx};
2061 struct Parameters {
2062 bool is_enabled;
2063 INSERT_PADDING_WORDS_NOINIT(1);
2064 Controller_Palma::PalmaConnectionHandle connection_handle;
2065 };
2066 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2067
2068 const auto parameters{rp.PopRaw<Parameters>()};
2069
2070 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}",
2071 parameters.connection_handle.npad_id, parameters.is_enabled);
2072
2073 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
2074 const auto result =
2075 controller.EnablePalmaStep(parameters.connection_handle, parameters.is_enabled);
2076
2077 IPC::ResponseBuilder rb{ctx, 2};
2078 rb.Push(result);
2079}
2080
2081void Hid::ResetPalmaStep(HLERequestContext& ctx) {
2082 IPC::RequestParser rp{ctx};
2083 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2084
2085 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2086
2087 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
2088 const auto result = controller.ResetPalmaStep(connection_handle);
2089
2090 IPC::ResponseBuilder rb{ctx, 2};
2091 rb.Push(result);
2092}
2093
2094void Hid::ReadPalmaApplicationSection(HLERequestContext& ctx) {
2095 LOG_WARNING(Service_HID, "(STUBBED) called");
2096
2097 IPC::ResponseBuilder rb{ctx, 2};
2098 rb.Push(ResultSuccess);
2099}
2100
2101void Hid::WritePalmaApplicationSection(HLERequestContext& ctx) {
2102 LOG_WARNING(Service_HID, "(STUBBED) called");
2103
2104 IPC::ResponseBuilder rb{ctx, 2};
2105 rb.Push(ResultSuccess);
2106}
2107
2108void Hid::ReadPalmaUniqueCode(HLERequestContext& ctx) {
2109 IPC::RequestParser rp{ctx};
2110 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2111
2112 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2113
2114 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2115 .ReadPalmaUniqueCode(connection_handle);
2116
2117 IPC::ResponseBuilder rb{ctx, 2};
2118 rb.Push(ResultSuccess);
2119}
2120
2121void Hid::SetPalmaUniqueCodeInvalid(HLERequestContext& ctx) {
2122 IPC::RequestParser rp{ctx};
2123 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2124
2125 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2126
2127 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2128 .SetPalmaUniqueCodeInvalid(connection_handle);
2129
2130 IPC::ResponseBuilder rb{ctx, 2};
2131 rb.Push(ResultSuccess);
2132}
2133
2134void Hid::WritePalmaActivityEntry(HLERequestContext& ctx) {
2135 LOG_CRITICAL(Service_HID, "(STUBBED) called");
2136
2137 IPC::ResponseBuilder rb{ctx, 2};
2138 rb.Push(ResultSuccess);
2139}
2140
2141void Hid::WritePalmaRgbLedPatternEntry(HLERequestContext& ctx) {
2142 IPC::RequestParser rp{ctx};
2143 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2144 const auto unknown{rp.Pop<u64>()};
2145
2146 [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
2147
2148 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}",
2149 connection_handle.npad_id, unknown);
2150
2151 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2152 .WritePalmaRgbLedPatternEntry(connection_handle, unknown);
2153
2154 IPC::ResponseBuilder rb{ctx, 2};
2155 rb.Push(ResultSuccess);
2156}
2157
2158void Hid::WritePalmaWaveEntry(HLERequestContext& ctx) {
2159 IPC::RequestParser rp{ctx};
2160 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2161 const auto wave_set{rp.PopEnum<Controller_Palma::PalmaWaveSet>()};
2162 const auto unknown{rp.Pop<u64>()};
2163 const auto t_mem_size{rp.Pop<u64>()};
2164 const auto t_mem_handle{ctx.GetCopyHandle(0)};
2165 const auto size{rp.Pop<u64>()};
2166
2167 ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes");
2168
2169 auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
2170 t_mem_handle);
2171
2172 if (t_mem.IsNull()) {
2173 LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
2174 IPC::ResponseBuilder rb{ctx, 2};
2175 rb.Push(ResultUnknown);
2176 return;
2177 }
2178
2179 ASSERT_MSG(t_mem->GetSize() == 0x3000, "t_mem has incorrect size");
2180
2181 LOG_WARNING(Service_HID,
2182 "(STUBBED) called, connection_handle={}, wave_set={}, unknown={}, "
2183 "t_mem_handle=0x{:08X}, t_mem_size={}, size={}",
2184 connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size);
2185
2186 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2187 .WritePalmaWaveEntry(connection_handle, wave_set, t_mem->GetSourceAddress(), t_mem_size);
2188
2189 IPC::ResponseBuilder rb{ctx, 2};
2190 rb.Push(ResultSuccess);
2191}
2192
2193void Hid::SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) {
2194 IPC::RequestParser rp{ctx};
2195 struct Parameters {
2196 s32 database_id_version;
2197 INSERT_PADDING_WORDS_NOINIT(1);
2198 Controller_Palma::PalmaConnectionHandle connection_handle;
2199 };
2200 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2201
2202 const auto parameters{rp.PopRaw<Parameters>()};
2203
2204 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}",
2205 parameters.connection_handle.npad_id, parameters.database_id_version);
2206
2207 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2208 .SetPalmaDataBaseIdentificationVersion(parameters.connection_handle,
2209 parameters.database_id_version);
2210
2211 IPC::ResponseBuilder rb{ctx, 2};
2212 rb.Push(ResultSuccess);
2213}
2214
2215void Hid::GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) {
2216 IPC::RequestParser rp{ctx};
2217 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2218
2219 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2220
2221 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2222 .GetPalmaDataBaseIdentificationVersion(connection_handle);
2223
2224 IPC::ResponseBuilder rb{ctx, 2};
2225 rb.Push(ResultSuccess);
2226}
2227
2228void Hid::SuspendPalmaFeature(HLERequestContext& ctx) {
2229 LOG_WARNING(Service_HID, "(STUBBED) called");
2230
2231 IPC::ResponseBuilder rb{ctx, 2};
2232 rb.Push(ResultSuccess);
2233}
2234
2235void Hid::GetPalmaOperationResult(HLERequestContext& ctx) {
2236 IPC::RequestParser rp{ctx};
2237 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2238
2239 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2240
2241 const auto result = applet_resource->GetController<Controller_Palma>(HidController::Palma)
2242 .GetPalmaOperationResult(connection_handle);
2243
2244 IPC::ResponseBuilder rb{ctx, 2};
2245 rb.Push(result);
2246}
2247
2248void Hid::ReadPalmaPlayLog(HLERequestContext& ctx) {
2249 LOG_WARNING(Service_HID, "(STUBBED) called");
2250
2251 IPC::ResponseBuilder rb{ctx, 2};
2252 rb.Push(ResultSuccess);
2253}
2254
2255void Hid::ResetPalmaPlayLog(HLERequestContext& ctx) {
2256 LOG_WARNING(Service_HID, "(STUBBED) called");
2257
2258 IPC::ResponseBuilder rb{ctx, 2};
2259 rb.Push(ResultSuccess);
2260}
2261
2262void Hid::SetIsPalmaAllConnectable(HLERequestContext& ctx) {
2263 IPC::RequestParser rp{ctx};
2264 struct Parameters {
2265 bool is_palma_all_connectable;
2266 INSERT_PADDING_BYTES_NOINIT(7);
2267 u64 applet_resource_user_id;
2268 };
2269 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2270
2271 const auto parameters{rp.PopRaw<Parameters>()};
2272
2273 LOG_WARNING(Service_HID,
2274 "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}",
2275 parameters.is_palma_all_connectable, parameters.applet_resource_user_id);
2276
2277 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2278 .SetIsPalmaAllConnectable(parameters.is_palma_all_connectable);
2279
2280 IPC::ResponseBuilder rb{ctx, 2};
2281 rb.Push(ResultSuccess);
2282}
2283
2284void Hid::SetIsPalmaPairedConnectable(HLERequestContext& ctx) {
2285 LOG_WARNING(Service_HID, "(STUBBED) called");
2286
2287 IPC::ResponseBuilder rb{ctx, 2};
2288 rb.Push(ResultSuccess);
2289}
2290
2291void Hid::PairPalma(HLERequestContext& ctx) {
2292 IPC::RequestParser rp{ctx};
2293 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2294
2295 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2296
2297 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2298 .PairPalma(connection_handle);
2299
2300 IPC::ResponseBuilder rb{ctx, 2};
2301 rb.Push(ResultSuccess);
2302}
2303
2304void Hid::SetPalmaBoostMode(HLERequestContext& ctx) {
2305 IPC::RequestParser rp{ctx};
2306 const auto palma_boost_mode{rp.Pop<bool>()};
2307
2308 LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode);
2309
2310 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2311 .SetPalmaBoostMode(palma_boost_mode);
2312
2313 IPC::ResponseBuilder rb{ctx, 2};
2314 rb.Push(ResultSuccess);
2315}
2316
2317void Hid::CancelWritePalmaWaveEntry(HLERequestContext& ctx) {
2318 LOG_WARNING(Service_HID, "(STUBBED) called");
2319
2320 IPC::ResponseBuilder rb{ctx, 2};
2321 rb.Push(ResultSuccess);
2322}
2323
2324void Hid::EnablePalmaBoostMode(HLERequestContext& ctx) {
2325 LOG_WARNING(Service_HID, "(STUBBED) called");
2326
2327 IPC::ResponseBuilder rb{ctx, 2};
2328 rb.Push(ResultSuccess);
2329}
2330
2331void Hid::GetPalmaBluetoothAddress(HLERequestContext& ctx) {
2332 LOG_WARNING(Service_HID, "(STUBBED) called");
2333
2334 IPC::ResponseBuilder rb{ctx, 2};
2335 rb.Push(ResultSuccess);
2336}
2337
2338void Hid::SetDisallowedPalmaConnection(HLERequestContext& ctx) {
2339 LOG_WARNING(Service_HID, "(STUBBED) called");
2340
2341 IPC::ResponseBuilder rb{ctx, 2};
2342 rb.Push(ResultSuccess);
2343}
2344
2345void Hid::SetNpadCommunicationMode(HLERequestContext& ctx) {
2346 IPC::RequestParser rp{ctx};
2347 const auto applet_resource_user_id{rp.Pop<u64>()};
2348 const auto communication_mode{rp.PopEnum<Controller_NPad::NpadCommunicationMode>()};
2349
2350 applet_resource->GetController<Controller_NPad>(HidController::NPad)
2351 .SetNpadCommunicationMode(communication_mode);
2352
2353 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, communication_mode={}",
2354 applet_resource_user_id, communication_mode);
2355
2356 IPC::ResponseBuilder rb{ctx, 2};
2357 rb.Push(ResultSuccess);
2358}
2359
2360void Hid::GetNpadCommunicationMode(HLERequestContext& ctx) {
2361 IPC::RequestParser rp{ctx};
2362
2363 LOG_WARNING(Service_HID, "(STUBBED) called");
2364
2365 IPC::ResponseBuilder rb{ctx, 4};
2366 rb.Push(ResultSuccess);
2367 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
2368 .GetNpadCommunicationMode());
2369}
2370
2371void Hid::SetTouchScreenConfiguration(HLERequestContext& ctx) {
2372 IPC::RequestParser rp{ctx};
2373 const auto touchscreen_mode{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()};
2374 const auto applet_resource_user_id{rp.Pop<u64>()};
2375
2376 LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}",
2377 touchscreen_mode.mode, applet_resource_user_id);
2378
2379 IPC::ResponseBuilder rb{ctx, 2};
2380 rb.Push(ResultSuccess);
2381}
2382
2383void Hid::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) {
2384 IPC::RequestParser rp{ctx};
2385 struct Parameters {
2386 s32 unknown;
2387 INSERT_PADDING_WORDS_NOINIT(1);
2388 u64 applet_resource_user_id;
2389 };
2390 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2391
2392 const auto parameters{rp.PopRaw<Parameters>()};
2393
2394 LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}",
2395 parameters.unknown, parameters.applet_resource_user_id);
2396
2397 IPC::ResponseBuilder rb{ctx, 3};
2398 rb.Push(ResultSuccess);
2399 rb.Push(false);
2400}
2401
2402class HidDbg final : public ServiceFramework<HidDbg> {
2403public:
2404 explicit HidDbg(Core::System& system_) : ServiceFramework{system_, "hid:dbg"} {
2405 // clang-format off
2406 static const FunctionInfo functions[] = {
2407 {0, nullptr, "DeactivateDebugPad"},
2408 {1, nullptr, "SetDebugPadAutoPilotState"},
2409 {2, nullptr, "UnsetDebugPadAutoPilotState"},
2410 {10, nullptr, "DeactivateTouchScreen"},
2411 {11, nullptr, "SetTouchScreenAutoPilotState"},
2412 {12, nullptr, "UnsetTouchScreenAutoPilotState"},
2413 {13, nullptr, "GetTouchScreenConfiguration"},
2414 {14, nullptr, "ProcessTouchScreenAutoTune"},
2415 {15, nullptr, "ForceStopTouchScreenManagement"},
2416 {16, nullptr, "ForceRestartTouchScreenManagement"},
2417 {17, nullptr, "IsTouchScreenManaged"},
2418 {20, nullptr, "DeactivateMouse"},
2419 {21, nullptr, "SetMouseAutoPilotState"},
2420 {22, nullptr, "UnsetMouseAutoPilotState"},
2421 {25, nullptr, "SetDebugMouseAutoPilotState"},
2422 {26, nullptr, "UnsetDebugMouseAutoPilotState"},
2423 {30, nullptr, "DeactivateKeyboard"},
2424 {31, nullptr, "SetKeyboardAutoPilotState"},
2425 {32, nullptr, "UnsetKeyboardAutoPilotState"},
2426 {50, nullptr, "DeactivateXpad"},
2427 {51, nullptr, "SetXpadAutoPilotState"},
2428 {52, nullptr, "UnsetXpadAutoPilotState"},
2429 {53, nullptr, "DeactivateJoyXpad"},
2430 {60, nullptr, "ClearNpadSystemCommonPolicy"},
2431 {61, nullptr, "DeactivateNpad"},
2432 {62, nullptr, "ForceDisconnectNpad"},
2433 {91, nullptr, "DeactivateGesture"},
2434 {110, nullptr, "DeactivateHomeButton"},
2435 {111, nullptr, "SetHomeButtonAutoPilotState"},
2436 {112, nullptr, "UnsetHomeButtonAutoPilotState"},
2437 {120, nullptr, "DeactivateSleepButton"},
2438 {121, nullptr, "SetSleepButtonAutoPilotState"},
2439 {122, nullptr, "UnsetSleepButtonAutoPilotState"},
2440 {123, nullptr, "DeactivateInputDetector"},
2441 {130, nullptr, "DeactivateCaptureButton"},
2442 {131, nullptr, "SetCaptureButtonAutoPilotState"},
2443 {132, nullptr, "UnsetCaptureButtonAutoPilotState"},
2444 {133, nullptr, "SetShiftAccelerometerCalibrationValue"},
2445 {134, nullptr, "GetShiftAccelerometerCalibrationValue"},
2446 {135, nullptr, "SetShiftGyroscopeCalibrationValue"},
2447 {136, nullptr, "GetShiftGyroscopeCalibrationValue"},
2448 {140, nullptr, "DeactivateConsoleSixAxisSensor"},
2449 {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"},
2450 {142, nullptr, "DeactivateSevenSixAxisSensor"},
2451 {143, nullptr, "GetConsoleSixAxisSensorCountStates"},
2452 {144, nullptr, "GetAccelerometerFsr"},
2453 {145, nullptr, "SetAccelerometerFsr"},
2454 {146, nullptr, "GetAccelerometerOdr"},
2455 {147, nullptr, "SetAccelerometerOdr"},
2456 {148, nullptr, "GetGyroscopeFsr"},
2457 {149, nullptr, "SetGyroscopeFsr"},
2458 {150, nullptr, "GetGyroscopeOdr"},
2459 {151, nullptr, "SetGyroscopeOdr"},
2460 {152, nullptr, "GetWhoAmI"},
2461 {201, nullptr, "ActivateFirmwareUpdate"},
2462 {202, nullptr, "DeactivateFirmwareUpdate"},
2463 {203, nullptr, "StartFirmwareUpdate"},
2464 {204, nullptr, "GetFirmwareUpdateStage"},
2465 {205, nullptr, "GetFirmwareVersion"},
2466 {206, nullptr, "GetDestinationFirmwareVersion"},
2467 {207, nullptr, "DiscardFirmwareInfoCacheForRevert"},
2468 {208, nullptr, "StartFirmwareUpdateForRevert"},
2469 {209, nullptr, "GetAvailableFirmwareVersionForRevert"},
2470 {210, nullptr, "IsFirmwareUpdatingDevice"},
2471 {211, nullptr, "StartFirmwareUpdateIndividual"},
2472 {215, nullptr, "SetUsbFirmwareForceUpdateEnabled"},
2473 {216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"},
2474 {221, nullptr, "UpdateControllerColor"},
2475 {222, nullptr, "ConnectUsbPadsAsync"},
2476 {223, nullptr, "DisconnectUsbPadsAsync"},
2477 {224, nullptr, "UpdateDesignInfo"},
2478 {225, nullptr, "GetUniquePadDriverState"},
2479 {226, nullptr, "GetSixAxisSensorDriverStates"},
2480 {227, nullptr, "GetRxPacketHistory"},
2481 {228, nullptr, "AcquireOperationEventHandle"},
2482 {229, nullptr, "ReadSerialFlash"},
2483 {230, nullptr, "WriteSerialFlash"},
2484 {231, nullptr, "GetOperationResult"},
2485 {232, nullptr, "EnableShipmentMode"},
2486 {233, nullptr, "ClearPairingInfo"},
2487 {234, nullptr, "GetUniquePadDeviceTypeSetInternal"},
2488 {235, nullptr, "EnableAnalogStickPower"},
2489 {236, nullptr, "RequestKuinaUartClockCal"},
2490 {237, nullptr, "GetKuinaUartClockCal"},
2491 {238, nullptr, "SetKuinaUartClockTrim"},
2492 {239, nullptr, "KuinaLoopbackTest"},
2493 {240, nullptr, "RequestBatteryVoltage"},
2494 {241, nullptr, "GetBatteryVoltage"},
2495 {242, nullptr, "GetUniquePadPowerInfo"},
2496 {243, nullptr, "RebootUniquePad"},
2497 {244, nullptr, "RequestKuinaFirmwareVersion"},
2498 {245, nullptr, "GetKuinaFirmwareVersion"},
2499 {246, nullptr, "GetVidPid"},
2500 {247, nullptr, "GetAnalogStickCalibrationValue"},
2501 {248, nullptr, "GetUniquePadIdsFull"},
2502 {249, nullptr, "ConnectUniquePad"},
2503 {250, nullptr, "IsVirtual"},
2504 {251, nullptr, "GetAnalogStickModuleParam"},
2505 {301, nullptr, "GetAbstractedPadHandles"},
2506 {302, nullptr, "GetAbstractedPadState"},
2507 {303, nullptr, "GetAbstractedPadsState"},
2508 {321, nullptr, "SetAutoPilotVirtualPadState"},
2509 {322, nullptr, "UnsetAutoPilotVirtualPadState"},
2510 {323, nullptr, "UnsetAllAutoPilotVirtualPadState"},
2511 {324, nullptr, "AttachHdlsWorkBuffer"},
2512 {325, nullptr, "ReleaseHdlsWorkBuffer"},
2513 {326, nullptr, "DumpHdlsNpadAssignmentState"},
2514 {327, nullptr, "DumpHdlsStates"},
2515 {328, nullptr, "ApplyHdlsNpadAssignmentState"},
2516 {329, nullptr, "ApplyHdlsStateList"},
2517 {330, nullptr, "AttachHdlsVirtualDevice"},
2518 {331, nullptr, "DetachHdlsVirtualDevice"},
2519 {332, nullptr, "SetHdlsState"},
2520 {350, nullptr, "AddRegisteredDevice"},
2521 {400, nullptr, "DisableExternalMcuOnNxDevice"},
2522 {401, nullptr, "DisableRailDeviceFiltering"},
2523 {402, nullptr, "EnableWiredPairing"},
2524 {403, nullptr, "EnableShipmentModeAutoClear"},
2525 {404, nullptr, "SetRailEnabled"},
2526 {500, nullptr, "SetFactoryInt"},
2527 {501, nullptr, "IsFactoryBootEnabled"},
2528 {550, nullptr, "SetAnalogStickModelDataTemporarily"},
2529 {551, nullptr, "GetAnalogStickModelData"},
2530 {552, nullptr, "ResetAnalogStickModelData"},
2531 {600, nullptr, "ConvertPadState"},
2532 {650, nullptr, "AddButtonPlayData"},
2533 {651, nullptr, "StartButtonPlayData"},
2534 {652, nullptr, "StopButtonPlayData"},
2535 {2000, nullptr, "DeactivateDigitizer"},
2536 {2001, nullptr, "SetDigitizerAutoPilotState"},
2537 {2002, nullptr, "UnsetDigitizerAutoPilotState"},
2538 {2002, nullptr, "ReloadFirmwareDebugSettings"},
2539 };
2540 // clang-format on
2541
2542 RegisterHandlers(functions);
2543 }
2544};
2545
2546class HidSys final : public ServiceFramework<HidSys> {
2547public:
2548 explicit HidSys(Core::System& system_, std::shared_ptr<IAppletResource> applet_resource_)
2549 : ServiceFramework{system_, "hid:sys"}, service_context{system_, "hid:sys"},
2550 applet_resource{applet_resource_} {
2551 // clang-format off
2552 static const FunctionInfo functions[] = {
2553 {31, nullptr, "SendKeyboardLockKeyEvent"},
2554 {101, nullptr, "AcquireHomeButtonEventHandle"},
2555 {111, nullptr, "ActivateHomeButton"},
2556 {121, nullptr, "AcquireSleepButtonEventHandle"},
2557 {131, nullptr, "ActivateSleepButton"},
2558 {141, nullptr, "AcquireCaptureButtonEventHandle"},
2559 {151, nullptr, "ActivateCaptureButton"},
2560 {161, nullptr, "GetPlatformConfig"},
2561 {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"},
2562 {211, nullptr, "GetNpadsWithNfc"},
2563 {212, nullptr, "AcquireNfcActivateEventHandle"},
2564 {213, nullptr, "ActivateNfc"},
2565 {214, nullptr, "GetXcdHandleForNpadWithNfc"},
2566 {215, nullptr, "IsNfcActivated"},
2567 {230, nullptr, "AcquireIrSensorEventHandle"},
2568 {231, nullptr, "ActivateIrSensor"},
2569 {232, nullptr, "GetIrSensorState"},
2570 {233, nullptr, "GetXcdHandleForNpadWithIrSensor"},
2571 {301, nullptr, "ActivateNpadSystem"},
2572 {303, &HidSys::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"},
2573 {304, nullptr, "EnableAssigningSingleOnSlSrPress"},
2574 {305, nullptr, "DisableAssigningSingleOnSlSrPress"},
2575 {306, &HidSys::GetLastActiveNpad, "GetLastActiveNpad"},
2576 {307, nullptr, "GetNpadSystemExtStyle"},
2577 {308, nullptr, "ApplyNpadSystemCommonPolicyFull"},
2578 {309, nullptr, "GetNpadFullKeyGripColor"},
2579 {310, nullptr, "GetMaskedSupportedNpadStyleSet"},
2580 {311, nullptr, "SetNpadPlayerLedBlinkingDevice"},
2581 {312, nullptr, "SetSupportedNpadStyleSetAll"},
2582 {313, nullptr, "GetNpadCaptureButtonAssignment"},
2583 {314, nullptr, "GetAppletFooterUiType"},
2584 {315, nullptr, "GetAppletDetailedUiType"},
2585 {316, nullptr, "GetNpadInterfaceType"},
2586 {317, nullptr, "GetNpadLeftRightInterfaceType"},
2587 {318, nullptr, "HasBattery"},
2588 {319, nullptr, "HasLeftRightBattery"},
2589 {321, &HidSys::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"},
2590 {322, nullptr, "GetIrSensorState"},
2591 {323, nullptr, "GetXcdHandleForNpadWithIrSensor"},
2592 {324, nullptr, "GetUniquePadButtonSet"},
2593 {325, nullptr, "GetUniquePadColor"},
2594 {326, nullptr, "GetUniquePadAppletDetailedUiType"},
2595 {327, nullptr, "GetAbstractedPadIdDataFromNpad"},
2596 {328, nullptr, "AttachAbstractedPadToNpad"},
2597 {329, nullptr, "DetachAbstractedPadAll"},
2598 {330, nullptr, "CheckAbstractedPadConnection"},
2599 {500, nullptr, "SetAppletResourceUserId"},
2600 {501, nullptr, "RegisterAppletResourceUserId"},
2601 {502, nullptr, "UnregisterAppletResourceUserId"},
2602 {503, nullptr, "EnableAppletToGetInput"},
2603 {504, nullptr, "SetAruidValidForVibration"},
2604 {505, nullptr, "EnableAppletToGetSixAxisSensor"},
2605 {506, nullptr, "EnableAppletToGetPadInput"},
2606 {507, nullptr, "EnableAppletToGetTouchScreen"},
2607 {510, nullptr, "SetVibrationMasterVolume"},
2608 {511, nullptr, "GetVibrationMasterVolume"},
2609 {512, nullptr, "BeginPermitVibrationSession"},
2610 {513, nullptr, "EndPermitVibrationSession"},
2611 {514, nullptr, "Unknown514"},
2612 {520, nullptr, "EnableHandheldHids"},
2613 {521, nullptr, "DisableHandheldHids"},
2614 {522, nullptr, "SetJoyConRailEnabled"},
2615 {523, nullptr, "IsJoyConRailEnabled"},
2616 {524, nullptr, "IsHandheldHidsEnabled"},
2617 {525, nullptr, "IsJoyConAttachedOnAllRail"},
2618 {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"},
2619 {541, nullptr, "GetPlayReportControllerUsages"},
2620 {542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"},
2621 {543, nullptr, "GetRegisteredDevicesOld"},
2622 {544, nullptr, "AcquireConnectionTriggerTimeoutEvent"},
2623 {545, nullptr, "SendConnectionTrigger"},
2624 {546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"},
2625 {547, nullptr, "GetAllowedBluetoothLinksCount"},
2626 {548, nullptr, "GetRegisteredDevices"},
2627 {549, nullptr, "GetConnectableRegisteredDevices"},
2628 {700, nullptr, "ActivateUniquePad"},
2629 {702, nullptr, "AcquireUniquePadConnectionEventHandle"},
2630 {703, nullptr, "GetUniquePadIds"},
2631 {751, &HidSys::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"},
2632 {800, nullptr, "ListSixAxisSensorHandles"},
2633 {801, nullptr, "IsSixAxisSensorUserCalibrationSupported"},
2634 {802, nullptr, "ResetSixAxisSensorCalibrationValues"},
2635 {803, nullptr, "StartSixAxisSensorUserCalibration"},
2636 {804, nullptr, "CancelSixAxisSensorUserCalibration"},
2637 {805, nullptr, "GetUniquePadBluetoothAddress"},
2638 {806, nullptr, "DisconnectUniquePad"},
2639 {807, nullptr, "GetUniquePadType"},
2640 {808, nullptr, "GetUniquePadInterface"},
2641 {809, nullptr, "GetUniquePadSerialNumber"},
2642 {810, nullptr, "GetUniquePadControllerNumber"},
2643 {811, nullptr, "GetSixAxisSensorUserCalibrationStage"},
2644 {812, nullptr, "GetConsoleUniqueSixAxisSensorHandle"},
2645 {821, nullptr, "StartAnalogStickManualCalibration"},
2646 {822, nullptr, "RetryCurrentAnalogStickManualCalibrationStage"},
2647 {823, nullptr, "CancelAnalogStickManualCalibration"},
2648 {824, nullptr, "ResetAnalogStickManualCalibration"},
2649 {825, nullptr, "GetAnalogStickState"},
2650 {826, nullptr, "GetAnalogStickManualCalibrationStage"},
2651 {827, nullptr, "IsAnalogStickButtonPressed"},
2652 {828, nullptr, "IsAnalogStickInReleasePosition"},
2653 {829, nullptr, "IsAnalogStickInCircumference"},
2654 {830, nullptr, "SetNotificationLedPattern"},
2655 {831, nullptr, "SetNotificationLedPatternWithTimeout"},
2656 {832, nullptr, "PrepareHidsForNotificationWake"},
2657 {850, &HidSys::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
2658 {851, nullptr, "EnableUsbFullKeyController"},
2659 {852, nullptr, "IsUsbConnected"},
2660 {870, nullptr, "IsHandheldButtonPressedOnConsoleMode"},
2661 {900, nullptr, "ActivateInputDetector"},
2662 {901, nullptr, "NotifyInputDetector"},
2663 {1000, nullptr, "InitializeFirmwareUpdate"},
2664 {1001, nullptr, "GetFirmwareVersion"},
2665 {1002, nullptr, "GetAvailableFirmwareVersion"},
2666 {1003, nullptr, "IsFirmwareUpdateAvailable"},
2667 {1004, nullptr, "CheckFirmwareUpdateRequired"},
2668 {1005, nullptr, "StartFirmwareUpdate"},
2669 {1006, nullptr, "AbortFirmwareUpdate"},
2670 {1007, nullptr, "GetFirmwareUpdateState"},
2671 {1008, nullptr, "ActivateAudioControl"},
2672 {1009, nullptr, "AcquireAudioControlEventHandle"},
2673 {1010, nullptr, "GetAudioControlStates"},
2674 {1011, nullptr, "DeactivateAudioControl"},
2675 {1050, nullptr, "IsSixAxisSensorAccurateUserCalibrationSupported"},
2676 {1051, nullptr, "StartSixAxisSensorAccurateUserCalibration"},
2677 {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"},
2678 {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"},
2679 {1100, nullptr, "GetHidbusSystemServiceObject"},
2680 {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"},
2681 {1130, nullptr, "InitializeUsbFirmwareUpdate"},
2682 {1131, nullptr, "FinalizeUsbFirmwareUpdate"},
2683 {1132, nullptr, "CheckUsbFirmwareUpdateRequired"},
2684 {1133, nullptr, "StartUsbFirmwareUpdate"},
2685 {1134, nullptr, "GetUsbFirmwareUpdateState"},
2686 {1150, nullptr, "SetTouchScreenMagnification"},
2687 {1151, nullptr, "GetTouchScreenFirmwareVersion"},
2688 {1152, nullptr, "SetTouchScreenDefaultConfiguration"},
2689 {1153, &HidSys::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
2690 {1154, nullptr, "IsFirmwareAvailableForNotification"},
2691 {1155, nullptr, "SetForceHandheldStyleVibration"},
2692 {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"},
2693 {1157, nullptr, "CancelConnectionTrigger"},
2694 {1200, nullptr, "IsButtonConfigSupported"},
2695 {1201, nullptr, "IsButtonConfigEmbeddedSupported"},
2696 {1202, nullptr, "DeleteButtonConfig"},
2697 {1203, nullptr, "DeleteButtonConfigEmbedded"},
2698 {1204, nullptr, "SetButtonConfigEnabled"},
2699 {1205, nullptr, "SetButtonConfigEmbeddedEnabled"},
2700 {1206, nullptr, "IsButtonConfigEnabled"},
2701 {1207, nullptr, "IsButtonConfigEmbeddedEnabled"},
2702 {1208, nullptr, "SetButtonConfigEmbedded"},
2703 {1209, nullptr, "SetButtonConfigFull"},
2704 {1210, nullptr, "SetButtonConfigLeft"},
2705 {1211, nullptr, "SetButtonConfigRight"},
2706 {1212, nullptr, "GetButtonConfigEmbedded"},
2707 {1213, nullptr, "GetButtonConfigFull"},
2708 {1214, nullptr, "GetButtonConfigLeft"},
2709 {1215, nullptr, "GetButtonConfigRight"},
2710 {1250, nullptr, "IsCustomButtonConfigSupported"},
2711 {1251, nullptr, "IsDefaultButtonConfigEmbedded"},
2712 {1252, nullptr, "IsDefaultButtonConfigFull"},
2713 {1253, nullptr, "IsDefaultButtonConfigLeft"},
2714 {1254, nullptr, "IsDefaultButtonConfigRight"},
2715 {1255, nullptr, "IsButtonConfigStorageEmbeddedEmpty"},
2716 {1256, nullptr, "IsButtonConfigStorageFullEmpty"},
2717 {1257, nullptr, "IsButtonConfigStorageLeftEmpty"},
2718 {1258, nullptr, "IsButtonConfigStorageRightEmpty"},
2719 {1259, nullptr, "GetButtonConfigStorageEmbeddedDeprecated"},
2720 {1260, nullptr, "GetButtonConfigStorageFullDeprecated"},
2721 {1261, nullptr, "GetButtonConfigStorageLeftDeprecated"},
2722 {1262, nullptr, "GetButtonConfigStorageRightDeprecated"},
2723 {1263, nullptr, "SetButtonConfigStorageEmbeddedDeprecated"},
2724 {1264, nullptr, "SetButtonConfigStorageFullDeprecated"},
2725 {1265, nullptr, "SetButtonConfigStorageLeftDeprecated"},
2726 {1266, nullptr, "SetButtonConfigStorageRightDeprecated"},
2727 {1267, nullptr, "DeleteButtonConfigStorageEmbedded"},
2728 {1268, nullptr, "DeleteButtonConfigStorageFull"},
2729 {1269, nullptr, "DeleteButtonConfigStorageLeft"},
2730 {1270, nullptr, "DeleteButtonConfigStorageRight"},
2731 {1271, nullptr, "IsUsingCustomButtonConfig"},
2732 {1272, nullptr, "IsAnyCustomButtonConfigEnabled"},
2733 {1273, nullptr, "SetAllCustomButtonConfigEnabled"},
2734 {1274, nullptr, "SetDefaultButtonConfig"},
2735 {1275, nullptr, "SetAllDefaultButtonConfig"},
2736 {1276, nullptr, "SetHidButtonConfigEmbedded"},
2737 {1277, nullptr, "SetHidButtonConfigFull"},
2738 {1278, nullptr, "SetHidButtonConfigLeft"},
2739 {1279, nullptr, "SetHidButtonConfigRight"},
2740 {1280, nullptr, "GetHidButtonConfigEmbedded"},
2741 {1281, nullptr, "GetHidButtonConfigFull"},
2742 {1282, nullptr, "GetHidButtonConfigLeft"},
2743 {1283, nullptr, "GetHidButtonConfigRight"},
2744 {1284, nullptr, "GetButtonConfigStorageEmbedded"},
2745 {1285, nullptr, "GetButtonConfigStorageFull"},
2746 {1286, nullptr, "GetButtonConfigStorageLeft"},
2747 {1287, nullptr, "GetButtonConfigStorageRight"},
2748 {1288, nullptr, "SetButtonConfigStorageEmbedded"},
2749 {1289, nullptr, "SetButtonConfigStorageFull"},
2750 {1290, nullptr, "DeleteButtonConfigStorageRight"},
2751 {1291, nullptr, "DeleteButtonConfigStorageRight"},
2752 };
2753 // clang-format on
2754
2755 RegisterHandlers(functions);
2756
2757 joy_detach_event = service_context.CreateEvent("HidSys::JoyDetachEvent");
2758 }
2759
2760 ~HidSys() {
2761 service_context.CloseEvent(joy_detach_event);
2762 };
2763
2764private:
2765 void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) {
2766 LOG_WARNING(Service_HID, "called");
2767
2768 GetAppletResource()
2769 ->GetController<Controller_NPad>(HidController::NPad)
2770 .ApplyNpadSystemCommonPolicy();
2771
2772 IPC::ResponseBuilder rb{ctx, 2};
2773 rb.Push(ResultSuccess);
2774 }
2775
2776 void GetLastActiveNpad(HLERequestContext& ctx) {
2777 LOG_DEBUG(Service_HID, "(STUBBED) called");
2778
2779 IPC::ResponseBuilder rb{ctx, 3};
2780 rb.Push(ResultSuccess);
2781 rb.PushEnum(system.HIDCore().GetLastActiveController());
2782 }
2783
2784 void GetUniquePadsFromNpad(HLERequestContext& ctx) {
2785 IPC::RequestParser rp{ctx};
2786 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
2787
2788 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type);
2789
2790 const std::vector<Core::HID::UniquePadId> unique_pads{};
2791
2792 ctx.WriteBuffer(unique_pads);
2793
2794 IPC::ResponseBuilder rb{ctx, 3};
2795 rb.Push(ResultSuccess);
2796 rb.Push(static_cast<u32>(unique_pads.size()));
2797 }
2798
2799 void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) {
2800 LOG_INFO(Service_AM, "called");
2801
2802 IPC::ResponseBuilder rb{ctx, 2, 1};
2803 rb.Push(ResultSuccess);
2804 rb.PushCopyObjects(joy_detach_event->GetReadableEvent());
2805 }
2806
2807 void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
2808 const bool is_enabled = false;
2809
2810 LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled);
2811
2812 IPC::ResponseBuilder rb{ctx, 3};
2813 rb.Push(ResultSuccess);
2814 rb.Push(is_enabled);
2815 }
2816
2817 void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
2818 LOG_WARNING(Service_HID, "(STUBBED) called");
2819
2820 Core::HID::TouchScreenConfigurationForNx touchscreen_config{
2821 .mode = Core::HID::TouchScreenModeForNx::Finger,
2822 };
2823
2824 if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
2825 touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
2826 touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
2827 }
2828
2829 IPC::ResponseBuilder rb{ctx, 6};
2830 rb.Push(ResultSuccess);
2831 rb.PushRaw(touchscreen_config);
2832 }
2833
2834 std::shared_ptr<IAppletResource> GetAppletResource() {
2835 if (applet_resource == nullptr) {
2836 applet_resource = std::make_shared<IAppletResource>(system, service_context);
2837 }
2838
2839 return applet_resource;
2840 }
2841
2842 Kernel::KEvent* joy_detach_event;
2843 KernelHelpers::ServiceContext service_context;
2844 std::shared_ptr<IAppletResource> applet_resource;
2845};
2846
2847void LoopProcess(Core::System& system) { 17void LoopProcess(Core::System& system) {
2848 auto server_manager = std::make_unique<ServerManager>(system); 18 auto server_manager = std::make_unique<ServerManager>(system);
2849 std::shared_ptr<IAppletResource> applet_resource; 19 std::shared_ptr<ResourceManager> resouce_manager = std::make_shared<ResourceManager>(system);
20 std::shared_ptr<HidFirmwareSettings> firmware_settings =
21 std::make_shared<HidFirmwareSettings>();
22
23 server_manager->RegisterNamedService(
24 "hid", std::make_shared<IHidServer>(system, resouce_manager, firmware_settings));
25 server_manager->RegisterNamedService(
26 "hid:dbg", std::make_shared<IHidDebugServer>(system, resouce_manager));
27 server_manager->RegisterNamedService(
28 "hid:sys", std::make_shared<IHidSystemServer>(system, resouce_manager));
2850 29
2851 server_manager->RegisterNamedService("hid", std::make_shared<Hid>(system, applet_resource));
2852 server_manager->RegisterNamedService("hidbus", std::make_shared<HidBus>(system)); 30 server_manager->RegisterNamedService("hidbus", std::make_shared<HidBus>(system));
2853 server_manager->RegisterNamedService("hid:dbg", std::make_shared<HidDbg>(system));
2854 server_manager->RegisterNamedService("hid:sys",
2855 std::make_shared<HidSys>(system, applet_resource));
2856 31
2857 server_manager->RegisterNamedService("irs", std::make_shared<Service::IRS::IRS>(system)); 32 server_manager->RegisterNamedService("irs", std::make_shared<IRS::IRS>(system));
2858 server_manager->RegisterNamedService("irs:sys", 33 server_manager->RegisterNamedService("irs:sys", std::make_shared<IRS::IRS_SYS>(system));
2859 std::make_shared<Service::IRS::IRS_SYS>(system));
2860 34
2861 server_manager->RegisterNamedService("xcd:sys", std::make_shared<XCD_SYS>(system)); 35 server_manager->RegisterNamedService("xcd:sys", std::make_shared<XCD_SYS>(system));
36
2862 system.RunServer(std::move(server_manager)); 37 system.RunServer(std::move(server_manager));
2863} 38}
2864 39
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 0ca43de93..ec5463f4e 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -3,220 +3,12 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <chrono> 6namespace Core {
7 7class System;
8#include "core/hle/service/hid/controllers/controller_base.h"
9#include "core/hle/service/kernel_helpers.h"
10#include "core/hle/service/service.h"
11
12namespace Core::Timing {
13struct EventType;
14}
15
16namespace Service::SM {
17class ServiceManager;
18} 8}
19 9
20namespace Service::HID { 10namespace Service::HID {
21 11
22enum class HidController : std::size_t {
23 DebugPad,
24 Touchscreen,
25 Mouse,
26 Keyboard,
27 XPad,
28 HomeButton,
29 SleepButton,
30 CaptureButton,
31 InputDetector,
32 UniquePad,
33 NPad,
34 Gesture,
35 ConsoleSixAxisSensor,
36 DebugMouse,
37 Palma,
38
39 MaxControllers,
40};
41
42class IAppletResource final : public ServiceFramework<IAppletResource> {
43public:
44 explicit IAppletResource(Core::System& system_,
45 KernelHelpers::ServiceContext& service_context_);
46 ~IAppletResource() override;
47
48 void ActivateController(HidController controller);
49 void DeactivateController(HidController controller);
50
51 template <typename T>
52 T& GetController(HidController controller) {
53 return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
54 }
55
56 template <typename T>
57 const T& GetController(HidController controller) const {
58 return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
59 }
60
61private:
62 template <typename T>
63 void MakeController(HidController controller, u8* shared_memory) {
64 if constexpr (std::is_constructible_v<T, Core::System&, u8*>) {
65 controllers[static_cast<std::size_t>(controller)] =
66 std::make_unique<T>(system, shared_memory);
67 } else {
68 controllers[static_cast<std::size_t>(controller)] =
69 std::make_unique<T>(system.HIDCore(), shared_memory);
70 }
71 }
72
73 template <typename T>
74 void MakeControllerWithServiceContext(HidController controller, u8* shared_memory) {
75 controllers[static_cast<std::size_t>(controller)] =
76 std::make_unique<T>(system.HIDCore(), shared_memory, service_context);
77 }
78
79 void GetSharedMemoryHandle(HLERequestContext& ctx);
80 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
81 void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
82 void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
83 void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
84
85 KernelHelpers::ServiceContext& service_context;
86
87 std::shared_ptr<Core::Timing::EventType> npad_update_event;
88 std::shared_ptr<Core::Timing::EventType> default_update_event;
89 std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
90 std::shared_ptr<Core::Timing::EventType> motion_update_event;
91
92 std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
93 controllers{};
94};
95
96class Hid final : public ServiceFramework<Hid> {
97public:
98 explicit Hid(Core::System& system_, std::shared_ptr<IAppletResource> applet_resource_);
99 ~Hid() override;
100
101 std::shared_ptr<IAppletResource> GetAppletResource();
102
103private:
104 void CreateAppletResource(HLERequestContext& ctx);
105 void ActivateDebugPad(HLERequestContext& ctx);
106 void ActivateTouchScreen(HLERequestContext& ctx);
107 void ActivateMouse(HLERequestContext& ctx);
108 void ActivateKeyboard(HLERequestContext& ctx);
109 void SendKeyboardLockKeyEvent(HLERequestContext& ctx);
110 void ActivateXpad(HLERequestContext& ctx);
111 void GetXpadIDs(HLERequestContext& ctx);
112 void ActivateSixAxisSensor(HLERequestContext& ctx);
113 void DeactivateSixAxisSensor(HLERequestContext& ctx);
114 void StartSixAxisSensor(HLERequestContext& ctx);
115 void StopSixAxisSensor(HLERequestContext& ctx);
116 void IsSixAxisSensorFusionEnabled(HLERequestContext& ctx);
117 void EnableSixAxisSensorFusion(HLERequestContext& ctx);
118 void SetSixAxisSensorFusionParameters(HLERequestContext& ctx);
119 void GetSixAxisSensorFusionParameters(HLERequestContext& ctx);
120 void ResetSixAxisSensorFusionParameters(HLERequestContext& ctx);
121 void SetGyroscopeZeroDriftMode(HLERequestContext& ctx);
122 void GetGyroscopeZeroDriftMode(HLERequestContext& ctx);
123 void ResetGyroscopeZeroDriftMode(HLERequestContext& ctx);
124 void IsSixAxisSensorAtRest(HLERequestContext& ctx);
125 void IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx);
126 void EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx);
127 void IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx);
128 void LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx);
129 void GetSixAxisSensorIcInformation(HLERequestContext& ctx);
130 void ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx);
131 void ActivateGesture(HLERequestContext& ctx);
132 void SetSupportedNpadStyleSet(HLERequestContext& ctx);
133 void GetSupportedNpadStyleSet(HLERequestContext& ctx);
134 void SetSupportedNpadIdType(HLERequestContext& ctx);
135 void ActivateNpad(HLERequestContext& ctx);
136 void DeactivateNpad(HLERequestContext& ctx);
137 void AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx);
138 void DisconnectNpad(HLERequestContext& ctx);
139 void GetPlayerLedPattern(HLERequestContext& ctx);
140 void ActivateNpadWithRevision(HLERequestContext& ctx);
141 void SetNpadJoyHoldType(HLERequestContext& ctx);
142 void GetNpadJoyHoldType(HLERequestContext& ctx);
143 void SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx);
144 void SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx);
145 void SetNpadJoyAssignmentModeDual(HLERequestContext& ctx);
146 void MergeSingleJoyAsDualJoy(HLERequestContext& ctx);
147 void StartLrAssignmentMode(HLERequestContext& ctx);
148 void StopLrAssignmentMode(HLERequestContext& ctx);
149 void SetNpadHandheldActivationMode(HLERequestContext& ctx);
150 void GetNpadHandheldActivationMode(HLERequestContext& ctx);
151 void SwapNpadAssignment(HLERequestContext& ctx);
152 void IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx);
153 void EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx);
154 void SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx);
155 void SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx);
156 void SetNpadCaptureButtonAssignment(HLERequestContext& ctx);
157 void ClearNpadCaptureButtonAssignment(HLERequestContext& ctx);
158 void GetVibrationDeviceInfo(HLERequestContext& ctx);
159 void SendVibrationValue(HLERequestContext& ctx);
160 void GetActualVibrationValue(HLERequestContext& ctx);
161 void CreateActiveVibrationDeviceList(HLERequestContext& ctx);
162 void PermitVibration(HLERequestContext& ctx);
163 void IsVibrationPermitted(HLERequestContext& ctx);
164 void SendVibrationValues(HLERequestContext& ctx);
165 void SendVibrationGcErmCommand(HLERequestContext& ctx);
166 void GetActualVibrationGcErmCommand(HLERequestContext& ctx);
167 void BeginPermitVibrationSession(HLERequestContext& ctx);
168 void EndPermitVibrationSession(HLERequestContext& ctx);
169 void IsVibrationDeviceMounted(HLERequestContext& ctx);
170 void ActivateConsoleSixAxisSensor(HLERequestContext& ctx);
171 void StartConsoleSixAxisSensor(HLERequestContext& ctx);
172 void StopConsoleSixAxisSensor(HLERequestContext& ctx);
173 void ActivateSevenSixAxisSensor(HLERequestContext& ctx);
174 void StartSevenSixAxisSensor(HLERequestContext& ctx);
175 void StopSevenSixAxisSensor(HLERequestContext& ctx);
176 void InitializeSevenSixAxisSensor(HLERequestContext& ctx);
177 void FinalizeSevenSixAxisSensor(HLERequestContext& ctx);
178 void ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx);
179 void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
180 void GetPalmaConnectionHandle(HLERequestContext& ctx);
181 void InitializePalma(HLERequestContext& ctx);
182 void AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx);
183 void GetPalmaOperationInfo(HLERequestContext& ctx);
184 void PlayPalmaActivity(HLERequestContext& ctx);
185 void SetPalmaFrModeType(HLERequestContext& ctx);
186 void ReadPalmaStep(HLERequestContext& ctx);
187 void EnablePalmaStep(HLERequestContext& ctx);
188 void ResetPalmaStep(HLERequestContext& ctx);
189 void ReadPalmaApplicationSection(HLERequestContext& ctx);
190 void WritePalmaApplicationSection(HLERequestContext& ctx);
191 void ReadPalmaUniqueCode(HLERequestContext& ctx);
192 void SetPalmaUniqueCodeInvalid(HLERequestContext& ctx);
193 void WritePalmaActivityEntry(HLERequestContext& ctx);
194 void WritePalmaRgbLedPatternEntry(HLERequestContext& ctx);
195 void WritePalmaWaveEntry(HLERequestContext& ctx);
196 void SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
197 void GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
198 void SuspendPalmaFeature(HLERequestContext& ctx);
199 void GetPalmaOperationResult(HLERequestContext& ctx);
200 void ReadPalmaPlayLog(HLERequestContext& ctx);
201 void ResetPalmaPlayLog(HLERequestContext& ctx);
202 void SetIsPalmaAllConnectable(HLERequestContext& ctx);
203 void SetIsPalmaPairedConnectable(HLERequestContext& ctx);
204 void PairPalma(HLERequestContext& ctx);
205 void SetPalmaBoostMode(HLERequestContext& ctx);
206 void CancelWritePalmaWaveEntry(HLERequestContext& ctx);
207 void EnablePalmaBoostMode(HLERequestContext& ctx);
208 void GetPalmaBluetoothAddress(HLERequestContext& ctx);
209 void SetDisallowedPalmaConnection(HLERequestContext& ctx);
210 void SetNpadCommunicationMode(HLERequestContext& ctx);
211 void GetNpadCommunicationMode(HLERequestContext& ctx);
212 void SetTouchScreenConfiguration(HLERequestContext& ctx);
213 void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx);
214
215 std::shared_ptr<IAppletResource> applet_resource;
216
217 KernelHelpers::ServiceContext service_context;
218};
219
220void LoopProcess(Core::System& system); 12void LoopProcess(Core::System& system);
221 13
222} // namespace Service::HID 14} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_debug_server.cpp b/src/core/hle/service/hid/hid_debug_server.cpp
new file mode 100644
index 000000000..6294f3dfb
--- /dev/null
+++ b/src/core/hle/service/hid/hid_debug_server.cpp
@@ -0,0 +1,159 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/hid/hid_debug_server.h"
5#include "core/hle/service/hid/resource_manager.h"
6#include "core/hle/service/ipc_helpers.h"
7
8namespace Service::HID {
9
10IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource)
11 : ServiceFramework{system_, "hid:dbg"}, resource_manager{resource} {
12 // clang-format off
13 static const FunctionInfo functions[] = {
14 {0, nullptr, "DeactivateDebugPad"},
15 {1, nullptr, "SetDebugPadAutoPilotState"},
16 {2, nullptr, "UnsetDebugPadAutoPilotState"},
17 {10, nullptr, "DeactivateTouchScreen"},
18 {11, nullptr, "SetTouchScreenAutoPilotState"},
19 {12, nullptr, "UnsetTouchScreenAutoPilotState"},
20 {13, nullptr, "GetTouchScreenConfiguration"},
21 {14, nullptr, "ProcessTouchScreenAutoTune"},
22 {15, nullptr, "ForceStopTouchScreenManagement"},
23 {16, nullptr, "ForceRestartTouchScreenManagement"},
24 {17, nullptr, "IsTouchScreenManaged"},
25 {20, nullptr, "DeactivateMouse"},
26 {21, nullptr, "SetMouseAutoPilotState"},
27 {22, nullptr, "UnsetMouseAutoPilotState"},
28 {25, nullptr, "SetDebugMouseAutoPilotState"},
29 {26, nullptr, "UnsetDebugMouseAutoPilotState"},
30 {30, nullptr, "DeactivateKeyboard"},
31 {31, nullptr, "SetKeyboardAutoPilotState"},
32 {32, nullptr, "UnsetKeyboardAutoPilotState"},
33 {50, nullptr, "DeactivateXpad"},
34 {51, nullptr, "SetXpadAutoPilotState"},
35 {52, nullptr, "UnsetXpadAutoPilotState"},
36 {53, nullptr, "DeactivateJoyXpad"},
37 {60, nullptr, "ClearNpadSystemCommonPolicy"},
38 {61, nullptr, "DeactivateNpad"},
39 {62, nullptr, "ForceDisconnectNpad"},
40 {91, nullptr, "DeactivateGesture"},
41 {110, nullptr, "DeactivateHomeButton"},
42 {111, nullptr, "SetHomeButtonAutoPilotState"},
43 {112, nullptr, "UnsetHomeButtonAutoPilotState"},
44 {120, nullptr, "DeactivateSleepButton"},
45 {121, nullptr, "SetSleepButtonAutoPilotState"},
46 {122, nullptr, "UnsetSleepButtonAutoPilotState"},
47 {123, nullptr, "DeactivateInputDetector"},
48 {130, nullptr, "DeactivateCaptureButton"},
49 {131, nullptr, "SetCaptureButtonAutoPilotState"},
50 {132, nullptr, "UnsetCaptureButtonAutoPilotState"},
51 {133, nullptr, "SetShiftAccelerometerCalibrationValue"},
52 {134, nullptr, "GetShiftAccelerometerCalibrationValue"},
53 {135, nullptr, "SetShiftGyroscopeCalibrationValue"},
54 {136, nullptr, "GetShiftGyroscopeCalibrationValue"},
55 {140, nullptr, "DeactivateConsoleSixAxisSensor"},
56 {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"},
57 {142, nullptr, "DeactivateSevenSixAxisSensor"},
58 {143, nullptr, "GetConsoleSixAxisSensorCountStates"},
59 {144, nullptr, "GetAccelerometerFsr"},
60 {145, nullptr, "SetAccelerometerFsr"},
61 {146, nullptr, "GetAccelerometerOdr"},
62 {147, nullptr, "SetAccelerometerOdr"},
63 {148, nullptr, "GetGyroscopeFsr"},
64 {149, nullptr, "SetGyroscopeFsr"},
65 {150, nullptr, "GetGyroscopeOdr"},
66 {151, nullptr, "SetGyroscopeOdr"},
67 {152, nullptr, "GetWhoAmI"},
68 {201, nullptr, "ActivateFirmwareUpdate"},
69 {202, nullptr, "DeactivateFirmwareUpdate"},
70 {203, nullptr, "StartFirmwareUpdate"},
71 {204, nullptr, "GetFirmwareUpdateStage"},
72 {205, nullptr, "GetFirmwareVersion"},
73 {206, nullptr, "GetDestinationFirmwareVersion"},
74 {207, nullptr, "DiscardFirmwareInfoCacheForRevert"},
75 {208, nullptr, "StartFirmwareUpdateForRevert"},
76 {209, nullptr, "GetAvailableFirmwareVersionForRevert"},
77 {210, nullptr, "IsFirmwareUpdatingDevice"},
78 {211, nullptr, "StartFirmwareUpdateIndividual"},
79 {215, nullptr, "SetUsbFirmwareForceUpdateEnabled"},
80 {216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"},
81 {221, nullptr, "UpdateControllerColor"},
82 {222, nullptr, "ConnectUsbPadsAsync"},
83 {223, nullptr, "DisconnectUsbPadsAsync"},
84 {224, nullptr, "UpdateDesignInfo"},
85 {225, nullptr, "GetUniquePadDriverState"},
86 {226, nullptr, "GetSixAxisSensorDriverStates"},
87 {227, nullptr, "GetRxPacketHistory"},
88 {228, nullptr, "AcquireOperationEventHandle"},
89 {229, nullptr, "ReadSerialFlash"},
90 {230, nullptr, "WriteSerialFlash"},
91 {231, nullptr, "GetOperationResult"},
92 {232, nullptr, "EnableShipmentMode"},
93 {233, nullptr, "ClearPairingInfo"},
94 {234, nullptr, "GetUniquePadDeviceTypeSetInternal"},
95 {235, nullptr, "EnableAnalogStickPower"},
96 {236, nullptr, "RequestKuinaUartClockCal"},
97 {237, nullptr, "GetKuinaUartClockCal"},
98 {238, nullptr, "SetKuinaUartClockTrim"},
99 {239, nullptr, "KuinaLoopbackTest"},
100 {240, nullptr, "RequestBatteryVoltage"},
101 {241, nullptr, "GetBatteryVoltage"},
102 {242, nullptr, "GetUniquePadPowerInfo"},
103 {243, nullptr, "RebootUniquePad"},
104 {244, nullptr, "RequestKuinaFirmwareVersion"},
105 {245, nullptr, "GetKuinaFirmwareVersion"},
106 {246, nullptr, "GetVidPid"},
107 {247, nullptr, "GetAnalogStickCalibrationValue"},
108 {248, nullptr, "GetUniquePadIdsFull"},
109 {249, nullptr, "ConnectUniquePad"},
110 {250, nullptr, "IsVirtual"},
111 {251, nullptr, "GetAnalogStickModuleParam"},
112 {301, nullptr, "GetAbstractedPadHandles"},
113 {302, nullptr, "GetAbstractedPadState"},
114 {303, nullptr, "GetAbstractedPadsState"},
115 {321, nullptr, "SetAutoPilotVirtualPadState"},
116 {322, nullptr, "UnsetAutoPilotVirtualPadState"},
117 {323, nullptr, "UnsetAllAutoPilotVirtualPadState"},
118 {324, nullptr, "AttachHdlsWorkBuffer"},
119 {325, nullptr, "ReleaseHdlsWorkBuffer"},
120 {326, nullptr, "DumpHdlsNpadAssignmentState"},
121 {327, nullptr, "DumpHdlsStates"},
122 {328, nullptr, "ApplyHdlsNpadAssignmentState"},
123 {329, nullptr, "ApplyHdlsStateList"},
124 {330, nullptr, "AttachHdlsVirtualDevice"},
125 {331, nullptr, "DetachHdlsVirtualDevice"},
126 {332, nullptr, "SetHdlsState"},
127 {350, nullptr, "AddRegisteredDevice"},
128 {400, nullptr, "DisableExternalMcuOnNxDevice"},
129 {401, nullptr, "DisableRailDeviceFiltering"},
130 {402, nullptr, "EnableWiredPairing"},
131 {403, nullptr, "EnableShipmentModeAutoClear"},
132 {404, nullptr, "SetRailEnabled"},
133 {500, nullptr, "SetFactoryInt"},
134 {501, nullptr, "IsFactoryBootEnabled"},
135 {550, nullptr, "SetAnalogStickModelDataTemporarily"},
136 {551, nullptr, "GetAnalogStickModelData"},
137 {552, nullptr, "ResetAnalogStickModelData"},
138 {600, nullptr, "ConvertPadState"},
139 {650, nullptr, "AddButtonPlayData"},
140 {651, nullptr, "StartButtonPlayData"},
141 {652, nullptr, "StopButtonPlayData"},
142 {2000, nullptr, "DeactivateDigitizer"},
143 {2001, nullptr, "SetDigitizerAutoPilotState"},
144 {2002, nullptr, "UnsetDigitizerAutoPilotState"},
145 {2002, nullptr, "ReloadFirmwareDebugSettings"},
146 };
147 // clang-format on
148
149 RegisterHandlers(functions);
150}
151
152IHidDebugServer::~IHidDebugServer() = default;
153
154std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() {
155 resource_manager->Initialize();
156 return resource_manager;
157}
158
159} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_debug_server.h b/src/core/hle/service/hid/hid_debug_server.h
new file mode 100644
index 000000000..406db2211
--- /dev/null
+++ b/src/core/hle/service/hid/hid_debug_server.h
@@ -0,0 +1,26 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Core {
9class System;
10}
11
12namespace Service::HID {
13class ResourceManager;
14
15class IHidDebugServer final : public ServiceFramework<IHidDebugServer> {
16public:
17 explicit IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource);
18 ~IHidDebugServer() override;
19
20private:
21 std::shared_ptr<ResourceManager> GetResourceManager();
22
23 std::shared_ptr<ResourceManager> resource_manager;
24};
25
26} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_firmware_settings.cpp b/src/core/hle/service/hid/hid_firmware_settings.cpp
new file mode 100644
index 000000000..59bd6825c
--- /dev/null
+++ b/src/core/hle/service/hid/hid_firmware_settings.cpp
@@ -0,0 +1,99 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/hid/hid_firmware_settings.h"
5
6namespace Service::HID {
7
8HidFirmwareSettings::HidFirmwareSettings() {
9 LoadSettings(true);
10}
11
12void HidFirmwareSettings::Reload() {
13 LoadSettings(true);
14}
15
16void HidFirmwareSettings::LoadSettings(bool reload_config) {
17 if (is_initalized && !reload_config) {
18 return;
19 }
20
21 // TODO: Use nn::settings::fwdbg::GetSettingsItemValue to load config values
22
23 is_debug_pad_enabled = true;
24 is_device_managed = true;
25 is_touch_i2c_managed = is_device_managed;
26 is_future_devices_emulated = false;
27 is_mcu_hardware_error_emulated = false;
28 is_rail_enabled = true;
29 is_firmware_update_failure_emulated = false;
30 is_firmware_update_failure = {};
31 is_ble_disabled = false;
32 is_dscale_disabled = false;
33 is_handheld_forced = true;
34 features_per_id_disabled = {};
35 is_touch_firmware_auto_update_disabled = false;
36 is_initalized = true;
37}
38
39bool HidFirmwareSettings::IsDebugPadEnabled() {
40 LoadSettings(false);
41 return is_debug_pad_enabled;
42}
43
44bool HidFirmwareSettings::IsDeviceManaged() {
45 LoadSettings(false);
46 return is_device_managed;
47}
48
49bool HidFirmwareSettings::IsEmulateFutureDevice() {
50 LoadSettings(false);
51 return is_future_devices_emulated;
52}
53
54bool HidFirmwareSettings::IsTouchI2cManaged() {
55 LoadSettings(false);
56 return is_touch_i2c_managed;
57}
58
59bool HidFirmwareSettings::IsHandheldForced() {
60 LoadSettings(false);
61 return is_handheld_forced;
62}
63
64bool HidFirmwareSettings::IsRailEnabled() {
65 LoadSettings(false);
66 return is_rail_enabled;
67}
68
69bool HidFirmwareSettings::IsHardwareErrorEmulated() {
70 LoadSettings(false);
71 return is_mcu_hardware_error_emulated;
72}
73
74bool HidFirmwareSettings::IsBleDisabled() {
75 LoadSettings(false);
76 return is_ble_disabled;
77}
78
79bool HidFirmwareSettings::IsDscaleDisabled() {
80 LoadSettings(false);
81 return is_dscale_disabled;
82}
83
84bool HidFirmwareSettings::IsTouchAutoUpdateDisabled() {
85 LoadSettings(false);
86 return is_touch_firmware_auto_update_disabled;
87}
88
89HidFirmwareSettings::FirmwareSetting HidFirmwareSettings::GetFirmwareUpdateFailure() {
90 LoadSettings(false);
91 return is_firmware_update_failure;
92}
93
94HidFirmwareSettings::FeaturesPerId HidFirmwareSettings::FeaturesDisabledPerId() {
95 LoadSettings(false);
96 return features_per_id_disabled;
97}
98
99} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_firmware_settings.h b/src/core/hle/service/hid/hid_firmware_settings.h
new file mode 100644
index 000000000..6c10c440b
--- /dev/null
+++ b/src/core/hle/service/hid/hid_firmware_settings.h
@@ -0,0 +1,54 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace Service::HID {
9
10/// Loads firmware config from nn::settings::fwdbg
11class HidFirmwareSettings {
12public:
13 using FirmwareSetting = std::array<u8, 4>;
14 using FeaturesPerId = std::array<bool, 0xA8>;
15
16 HidFirmwareSettings();
17
18 void Reload();
19 void LoadSettings(bool reload_config);
20
21 bool IsDebugPadEnabled();
22 bool IsDeviceManaged();
23 bool IsEmulateFutureDevice();
24 bool IsTouchI2cManaged();
25 bool IsHandheldForced();
26 bool IsRailEnabled();
27 bool IsHardwareErrorEmulated();
28 bool IsBleDisabled();
29 bool IsDscaleDisabled();
30 bool IsTouchAutoUpdateDisabled();
31
32 FirmwareSetting GetFirmwareUpdateFailure();
33 FeaturesPerId FeaturesDisabledPerId();
34
35private:
36 bool is_initalized{};
37
38 // Debug settings
39 bool is_debug_pad_enabled{};
40 bool is_device_managed{};
41 bool is_touch_i2c_managed{};
42 bool is_future_devices_emulated{};
43 bool is_mcu_hardware_error_emulated{};
44 bool is_rail_enabled{};
45 bool is_firmware_update_failure_emulated{};
46 bool is_ble_disabled{};
47 bool is_dscale_disabled{};
48 bool is_handheld_forced{};
49 bool is_touch_firmware_auto_update_disabled{};
50 FirmwareSetting is_firmware_update_failure{};
51 FeaturesPerId features_per_id_disabled{};
52};
53
54} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp
new file mode 100644
index 000000000..583142e35
--- /dev/null
+++ b/src/core/hle/service/hid/hid_server.cpp
@@ -0,0 +1,2371 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include <array>
5#include "common/common_types.h"
6#include "common/logging/log.h"
7#include "common/settings.h"
8#include "core/hid/hid_core.h"
9#include "core/hle/kernel/k_shared_memory.h"
10#include "core/hle/kernel/k_transfer_memory.h"
11#include "core/hle/kernel/kernel.h"
12#include "core/hle/service/hid/errors.h"
13#include "core/hle/service/hid/hid_firmware_settings.h"
14#include "core/hle/service/hid/hid_server.h"
15#include "core/hle/service/hid/hid_util.h"
16#include "core/hle/service/hid/resource_manager.h"
17#include "core/hle/service/ipc_helpers.h"
18#include "core/memory.h"
19
20#include "core/hle/service/hid/controllers/console_six_axis.h"
21#include "core/hle/service/hid/controllers/controller_base.h"
22#include "core/hle/service/hid/controllers/debug_pad.h"
23#include "core/hle/service/hid/controllers/gesture.h"
24#include "core/hle/service/hid/controllers/keyboard.h"
25#include "core/hle/service/hid/controllers/mouse.h"
26#include "core/hle/service/hid/controllers/npad.h"
27#include "core/hle/service/hid/controllers/palma.h"
28#include "core/hle/service/hid/controllers/seven_six_axis.h"
29#include "core/hle/service/hid/controllers/six_axis.h"
30#include "core/hle/service/hid/controllers/touchscreen.h"
31
32namespace Service::HID {
33
34class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
35public:
36 explicit IActiveVibrationDeviceList(Core::System& system_,
37 std::shared_ptr<ResourceManager> resource)
38 : ServiceFramework{system_, "IActiveVibrationDeviceList"}, resource_manager(resource) {
39 // clang-format off
40 static const FunctionInfo functions[] = {
41 {0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"},
42 };
43 // clang-format on
44
45 RegisterHandlers(functions);
46 }
47
48private:
49 void InitializeVibrationDevice(HLERequestContext& ctx) {
50 IPC::RequestParser rp{ctx};
51 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
52
53 if (resource_manager != nullptr) {
54 resource_manager->GetNpad()->InitializeVibrationDevice(vibration_device_handle);
55 }
56
57 LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}",
58 vibration_device_handle.npad_type, vibration_device_handle.npad_id,
59 vibration_device_handle.device_index);
60
61 IPC::ResponseBuilder rb{ctx, 2};
62 rb.Push(ResultSuccess);
63 }
64
65 std::shared_ptr<ResourceManager> resource_manager;
66};
67
68IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
69 std::shared_ptr<HidFirmwareSettings> settings)
70 : ServiceFramework{system_, "hid"}, resource_manager{resource}, firmware_settings{settings} {
71 // clang-format off
72 static const FunctionInfo functions[] = {
73 {0, &IHidServer::CreateAppletResource, "CreateAppletResource"},
74 {1, &IHidServer::ActivateDebugPad, "ActivateDebugPad"},
75 {11, &IHidServer::ActivateTouchScreen, "ActivateTouchScreen"},
76 {21, &IHidServer::ActivateMouse, "ActivateMouse"},
77 {26, nullptr, "ActivateDebugMouse"},
78 {31, &IHidServer::ActivateKeyboard, "ActivateKeyboard"},
79 {32, &IHidServer::SendKeyboardLockKeyEvent, "SendKeyboardLockKeyEvent"},
80 {40, &IHidServer::AcquireXpadIdEventHandle, "AcquireXpadIdEventHandle"},
81 {41, &IHidServer::ReleaseXpadIdEventHandle, "ReleaseXpadIdEventHandle"},
82 {51, &IHidServer::ActivateXpad, "ActivateXpad"},
83 {55, &IHidServer::GetXpadIds, "GetXpadIds"},
84 {56, &IHidServer::ActivateJoyXpad, "ActivateJoyXpad"},
85 {58, &IHidServer::GetJoyXpadLifoHandle, "GetJoyXpadLifoHandle"},
86 {59, &IHidServer::GetJoyXpadIds, "GetJoyXpadIds"},
87 {60, &IHidServer::ActivateSixAxisSensor, "ActivateSixAxisSensor"},
88 {61, &IHidServer::DeactivateSixAxisSensor, "DeactivateSixAxisSensor"},
89 {62, &IHidServer::GetSixAxisSensorLifoHandle, "GetSixAxisSensorLifoHandle"},
90 {63, &IHidServer::ActivateJoySixAxisSensor, "ActivateJoySixAxisSensor"},
91 {64, &IHidServer::DeactivateJoySixAxisSensor, "DeactivateJoySixAxisSensor"},
92 {65, &IHidServer::GetJoySixAxisSensorLifoHandle, "GetJoySixAxisSensorLifoHandle"},
93 {66, &IHidServer::StartSixAxisSensor, "StartSixAxisSensor"},
94 {67, &IHidServer::StopSixAxisSensor, "StopSixAxisSensor"},
95 {68, &IHidServer::IsSixAxisSensorFusionEnabled, "IsSixAxisSensorFusionEnabled"},
96 {69, &IHidServer::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"},
97 {70, &IHidServer::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"},
98 {71, &IHidServer::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"},
99 {72, &IHidServer::ResetSixAxisSensorFusionParameters, "ResetSixAxisSensorFusionParameters"},
100 {73, nullptr, "SetAccelerometerParameters"},
101 {74, nullptr, "GetAccelerometerParameters"},
102 {75, nullptr, "ResetAccelerometerParameters"},
103 {76, nullptr, "SetAccelerometerPlayMode"},
104 {77, nullptr, "GetAccelerometerPlayMode"},
105 {78, nullptr, "ResetAccelerometerPlayMode"},
106 {79, &IHidServer::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"},
107 {80, &IHidServer::GetGyroscopeZeroDriftMode, "GetGyroscopeZeroDriftMode"},
108 {81, &IHidServer::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"},
109 {82, &IHidServer::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
110 {83, &IHidServer::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"},
111 {84, &IHidServer::EnableSixAxisSensorUnalteredPassthrough, "EnableSixAxisSensorUnalteredPassthrough"},
112 {85, &IHidServer::IsSixAxisSensorUnalteredPassthroughEnabled, "IsSixAxisSensorUnalteredPassthroughEnabled"},
113 {86, nullptr, "StoreSixAxisSensorCalibrationParameter"},
114 {87, &IHidServer::LoadSixAxisSensorCalibrationParameter, "LoadSixAxisSensorCalibrationParameter"},
115 {88, &IHidServer::GetSixAxisSensorIcInformation, "GetSixAxisSensorIcInformation"},
116 {89, &IHidServer::ResetIsSixAxisSensorDeviceNewlyAssigned, "ResetIsSixAxisSensorDeviceNewlyAssigned"},
117 {91, &IHidServer::ActivateGesture, "ActivateGesture"},
118 {100, &IHidServer::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
119 {101, &IHidServer::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
120 {102, &IHidServer::SetSupportedNpadIdType, "SetSupportedNpadIdType"},
121 {103, &IHidServer::ActivateNpad, "ActivateNpad"},
122 {104, &IHidServer::DeactivateNpad, "DeactivateNpad"},
123 {106, &IHidServer::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"},
124 {107, &IHidServer::DisconnectNpad, "DisconnectNpad"},
125 {108, &IHidServer::GetPlayerLedPattern, "GetPlayerLedPattern"},
126 {109, &IHidServer::ActivateNpadWithRevision, "ActivateNpadWithRevision"},
127 {120, &IHidServer::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
128 {121, &IHidServer::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
129 {122, &IHidServer::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"},
130 {123, &IHidServer::SetNpadJoyAssignmentModeSingle, "SetNpadJoyAssignmentModeSingle"},
131 {124, &IHidServer::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"},
132 {125, &IHidServer::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"},
133 {126, &IHidServer::StartLrAssignmentMode, "StartLrAssignmentMode"},
134 {127, &IHidServer::StopLrAssignmentMode, "StopLrAssignmentMode"},
135 {128, &IHidServer::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"},
136 {129, &IHidServer::GetNpadHandheldActivationMode, "GetNpadHandheldActivationMode"},
137 {130, &IHidServer::SwapNpadAssignment, "SwapNpadAssignment"},
138 {131, &IHidServer::IsUnintendedHomeButtonInputProtectionEnabled, "IsUnintendedHomeButtonInputProtectionEnabled"},
139 {132, &IHidServer::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"},
140 {133, &IHidServer::SetNpadJoyAssignmentModeSingleWithDestination, "SetNpadJoyAssignmentModeSingleWithDestination"},
141 {134, &IHidServer::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"},
142 {135, &IHidServer::SetNpadCaptureButtonAssignment, "SetNpadCaptureButtonAssignment"},
143 {136, &IHidServer::ClearNpadCaptureButtonAssignment, "ClearNpadCaptureButtonAssignment"},
144 {200, &IHidServer::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
145 {201, &IHidServer::SendVibrationValue, "SendVibrationValue"},
146 {202, &IHidServer::GetActualVibrationValue, "GetActualVibrationValue"},
147 {203, &IHidServer::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"},
148 {204, &IHidServer::PermitVibration, "PermitVibration"},
149 {205, &IHidServer::IsVibrationPermitted, "IsVibrationPermitted"},
150 {206, &IHidServer::SendVibrationValues, "SendVibrationValues"},
151 {207, &IHidServer::SendVibrationGcErmCommand, "SendVibrationGcErmCommand"},
152 {208, &IHidServer::GetActualVibrationGcErmCommand, "GetActualVibrationGcErmCommand"},
153 {209, &IHidServer::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
154 {210, &IHidServer::EndPermitVibrationSession, "EndPermitVibrationSession"},
155 {211, &IHidServer::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"},
156 {212, nullptr, "SendVibrationValueInBool"},
157 {300, &IHidServer::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
158 {301, &IHidServer::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
159 {302, &IHidServer::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"},
160 {303, &IHidServer::ActivateSevenSixAxisSensor, "ActivateSevenSixAxisSensor"},
161 {304, &IHidServer::StartSevenSixAxisSensor, "StartSevenSixAxisSensor"},
162 {305, &IHidServer::StopSevenSixAxisSensor, "StopSevenSixAxisSensor"},
163 {306, &IHidServer::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"},
164 {307, &IHidServer::FinalizeSevenSixAxisSensor, "FinalizeSevenSixAxisSensor"},
165 {308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
166 {309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
167 {310, &IHidServer::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"},
168 {400, &IHidServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
169 {401, nullptr, "EnableUsbFullKeyController"},
170 {402, nullptr, "IsUsbFullKeyControllerConnected"},
171 {403, nullptr, "HasBattery"},
172 {404, nullptr, "HasLeftRightBattery"},
173 {405, nullptr, "GetNpadInterfaceType"},
174 {406, nullptr, "GetNpadLeftRightInterfaceType"},
175 {407, nullptr, "GetNpadOfHighestBatteryLevel"},
176 {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"},
177 {500, &IHidServer::GetPalmaConnectionHandle, "GetPalmaConnectionHandle"},
178 {501, &IHidServer::InitializePalma, "InitializePalma"},
179 {502, &IHidServer::AcquirePalmaOperationCompleteEvent, "AcquirePalmaOperationCompleteEvent"},
180 {503, &IHidServer::GetPalmaOperationInfo, "GetPalmaOperationInfo"},
181 {504, &IHidServer::PlayPalmaActivity, "PlayPalmaActivity"},
182 {505, &IHidServer::SetPalmaFrModeType, "SetPalmaFrModeType"},
183 {506, &IHidServer::ReadPalmaStep, "ReadPalmaStep"},
184 {507, &IHidServer::EnablePalmaStep, "EnablePalmaStep"},
185 {508, &IHidServer::ResetPalmaStep, "ResetPalmaStep"},
186 {509, &IHidServer::ReadPalmaApplicationSection, "ReadPalmaApplicationSection"},
187 {510, &IHidServer::WritePalmaApplicationSection, "WritePalmaApplicationSection"},
188 {511, &IHidServer::ReadPalmaUniqueCode, "ReadPalmaUniqueCode"},
189 {512, &IHidServer::SetPalmaUniqueCodeInvalid, "SetPalmaUniqueCodeInvalid"},
190 {513, &IHidServer::WritePalmaActivityEntry, "WritePalmaActivityEntry"},
191 {514, &IHidServer::WritePalmaRgbLedPatternEntry, "WritePalmaRgbLedPatternEntry"},
192 {515, &IHidServer::WritePalmaWaveEntry, "WritePalmaWaveEntry"},
193 {516, &IHidServer::SetPalmaDataBaseIdentificationVersion, "SetPalmaDataBaseIdentificationVersion"},
194 {517, &IHidServer::GetPalmaDataBaseIdentificationVersion, "GetPalmaDataBaseIdentificationVersion"},
195 {518, &IHidServer::SuspendPalmaFeature, "SuspendPalmaFeature"},
196 {519, &IHidServer::GetPalmaOperationResult, "GetPalmaOperationResult"},
197 {520, &IHidServer::ReadPalmaPlayLog, "ReadPalmaPlayLog"},
198 {521, &IHidServer::ResetPalmaPlayLog, "ResetPalmaPlayLog"},
199 {522, &IHidServer::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"},
200 {523, &IHidServer::SetIsPalmaPairedConnectable, "SetIsPalmaPairedConnectable"},
201 {524, &IHidServer::PairPalma, "PairPalma"},
202 {525, &IHidServer::SetPalmaBoostMode, "SetPalmaBoostMode"},
203 {526, &IHidServer::CancelWritePalmaWaveEntry, "CancelWritePalmaWaveEntry"},
204 {527, &IHidServer::EnablePalmaBoostMode, "EnablePalmaBoostMode"},
205 {528, &IHidServer::GetPalmaBluetoothAddress, "GetPalmaBluetoothAddress"},
206 {529, &IHidServer::SetDisallowedPalmaConnection, "SetDisallowedPalmaConnection"},
207 {1000, &IHidServer::SetNpadCommunicationMode, "SetNpadCommunicationMode"},
208 {1001, &IHidServer::GetNpadCommunicationMode, "GetNpadCommunicationMode"},
209 {1002, &IHidServer::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"},
210 {1003, &IHidServer::IsFirmwareUpdateNeededForNotification, "IsFirmwareUpdateNeededForNotification"},
211 {2000, nullptr, "ActivateDigitizer"},
212 };
213 // clang-format on
214
215 RegisterHandlers(functions);
216}
217
218IHidServer::~IHidServer() = default;
219
220void IHidServer::CreateAppletResource(HLERequestContext& ctx) {
221 IPC::RequestParser rp{ctx};
222 const auto applet_resource_user_id{rp.Pop<u64>()};
223
224 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
225
226 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
227 rb.Push(ResultSuccess);
228 rb.PushIpcInterface<IAppletResource>(system, resource_manager);
229}
230
231void IHidServer::ActivateDebugPad(HLERequestContext& ctx) {
232 IPC::RequestParser rp{ctx};
233 const auto applet_resource_user_id{rp.Pop<u64>()};
234
235 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
236
237 Result result = ResultSuccess;
238 auto debug_pad = GetResourceManager()->GetDebugPad();
239
240 if (!firmware_settings->IsDeviceManaged()) {
241 result = debug_pad->Activate();
242 }
243
244 if (result.IsSuccess()) {
245 result = debug_pad->Activate(applet_resource_user_id);
246 }
247
248 IPC::ResponseBuilder rb{ctx, 2};
249 rb.Push(result);
250}
251
252void IHidServer::ActivateTouchScreen(HLERequestContext& ctx) {
253 IPC::RequestParser rp{ctx};
254 const auto applet_resource_user_id{rp.Pop<u64>()};
255
256 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
257
258 Result result = ResultSuccess;
259 auto touch_screen = GetResourceManager()->GetTouchScreen();
260
261 if (!firmware_settings->IsDeviceManaged()) {
262 result = touch_screen->Activate();
263 }
264
265 if (result.IsSuccess()) {
266 result = touch_screen->Activate(applet_resource_user_id);
267 }
268
269 IPC::ResponseBuilder rb{ctx, 2};
270 rb.Push(result);
271}
272
273void IHidServer::ActivateMouse(HLERequestContext& ctx) {
274 IPC::RequestParser rp{ctx};
275 const auto applet_resource_user_id{rp.Pop<u64>()};
276
277 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
278
279 Result result = ResultSuccess;
280 auto mouse = GetResourceManager()->GetMouse();
281
282 if (!firmware_settings->IsDeviceManaged()) {
283 result = mouse->Activate();
284 }
285
286 if (result.IsSuccess()) {
287 result = mouse->Activate(applet_resource_user_id);
288 }
289
290 IPC::ResponseBuilder rb{ctx, 2};
291 rb.Push(result);
292}
293
294void IHidServer::ActivateKeyboard(HLERequestContext& ctx) {
295 IPC::RequestParser rp{ctx};
296 const auto applet_resource_user_id{rp.Pop<u64>()};
297
298 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
299
300 Result result = ResultSuccess;
301 auto keyboard = GetResourceManager()->GetKeyboard();
302
303 if (!firmware_settings->IsDeviceManaged()) {
304 result = keyboard->Activate();
305 }
306
307 if (result.IsSuccess()) {
308 result = keyboard->Activate(applet_resource_user_id);
309 }
310
311 IPC::ResponseBuilder rb{ctx, 2};
312 rb.Push(result);
313}
314
315void IHidServer::SendKeyboardLockKeyEvent(HLERequestContext& ctx) {
316 IPC::RequestParser rp{ctx};
317 const auto flags{rp.Pop<u32>()};
318
319 LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags);
320
321 IPC::ResponseBuilder rb{ctx, 2};
322 rb.Push(ResultSuccess);
323}
324
325void IHidServer::AcquireXpadIdEventHandle(HLERequestContext& ctx) {
326 IPC::RequestParser rp{ctx};
327 const auto applet_resource_user_id{rp.Pop<u64>()};
328
329 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
330
331 // This function has been stubbed since 10.0.0+
332
333 IPC::ResponseBuilder rb{ctx, 2, 1};
334 rb.Push(ResultSuccess);
335 // Handle returned is null here
336}
337
338void IHidServer::ReleaseXpadIdEventHandle(HLERequestContext& ctx) {
339 IPC::RequestParser rp{ctx};
340 const auto applet_resource_user_id{rp.Pop<u64>()};
341
342 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
343
344 // This function has been stubbed since 10.0.0+
345
346 IPC::ResponseBuilder rb{ctx, 2};
347 rb.Push(ResultSuccess);
348}
349
350void IHidServer::ActivateXpad(HLERequestContext& ctx) {
351 IPC::RequestParser rp{ctx};
352 struct Parameters {
353 u32 basic_xpad_id;
354 INSERT_PADDING_WORDS_NOINIT(1);
355 u64 applet_resource_user_id;
356 };
357 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
358
359 const auto parameters{rp.PopRaw<Parameters>()};
360
361 LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}",
362 parameters.basic_xpad_id, parameters.applet_resource_user_id);
363
364 // This function has been stubbed since 10.0.0+
365
366 IPC::ResponseBuilder rb{ctx, 2};
367 rb.Push(ResultSuccess);
368}
369
370void IHidServer::GetXpadIds(HLERequestContext& ctx) {
371 LOG_DEBUG(Service_HID, "called");
372
373 // This function has been hardcoded since 10.0.0+
374 const std::array<u32, 4> basic_xpad_id{0, 1, 2, 3};
375 ctx.WriteBuffer(basic_xpad_id);
376
377 IPC::ResponseBuilder rb{ctx, 4};
378 rb.Push(ResultSuccess);
379 rb.Push<s64>(basic_xpad_id.size());
380}
381
382void IHidServer::ActivateJoyXpad(HLERequestContext& ctx) {
383 IPC::RequestParser rp{ctx};
384 const auto joy_xpad_id{rp.Pop<u32>()};
385
386 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
387
388 // This function has been stubbed since 10.0.0+
389
390 IPC::ResponseBuilder rb{ctx, 2};
391 rb.Push(ResultSuccess);
392}
393
394void IHidServer::GetJoyXpadLifoHandle(HLERequestContext& ctx) {
395 IPC::RequestParser rp{ctx};
396 const auto joy_xpad_id{rp.Pop<u32>()};
397
398 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
399
400 // This function has been stubbed since 10.0.0+
401
402 IPC::ResponseBuilder rb{ctx, 2, 1};
403 rb.Push(ResultSuccess);
404 // Handle returned is null here
405}
406
407void IHidServer::GetJoyXpadIds(HLERequestContext& ctx) {
408 LOG_DEBUG(Service_HID, "called");
409
410 // This function has been hardcoded since 10.0.0+
411 const s64 basic_xpad_id_count{};
412
413 IPC::ResponseBuilder rb{ctx, 4};
414 rb.Push(ResultSuccess);
415 rb.Push(basic_xpad_id_count);
416}
417
418void IHidServer::ActivateSixAxisSensor(HLERequestContext& ctx) {
419 IPC::RequestParser rp{ctx};
420 const auto joy_xpad_id{rp.Pop<u32>()};
421
422 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
423
424 // This function has been stubbed since 10.0.0+
425
426 IPC::ResponseBuilder rb{ctx, 2};
427 rb.Push(ResultSuccess);
428}
429
430void IHidServer::DeactivateSixAxisSensor(HLERequestContext& ctx) {
431 IPC::RequestParser rp{ctx};
432 const auto joy_xpad_id{rp.Pop<u32>()};
433
434 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
435
436 // This function has been stubbed since 10.0.0+
437
438 IPC::ResponseBuilder rb{ctx, 2, 1};
439 rb.Push(ResultSuccess);
440}
441
442void IHidServer::GetSixAxisSensorLifoHandle(HLERequestContext& ctx) {
443 IPC::RequestParser rp{ctx};
444 const auto joy_xpad_id{rp.Pop<u32>()};
445
446 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
447
448 // This function has been stubbed since 10.0.0+
449
450 IPC::ResponseBuilder rb{ctx, 2};
451 rb.Push(ResultSuccess);
452}
453
454void IHidServer::ActivateJoySixAxisSensor(HLERequestContext& ctx) {
455 IPC::RequestParser rp{ctx};
456 const auto joy_xpad_id{rp.Pop<u32>()};
457
458 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
459
460 // This function has been stubbed since 10.0.0+
461
462 IPC::ResponseBuilder rb{ctx, 2};
463 rb.Push(ResultSuccess);
464}
465
466void IHidServer::DeactivateJoySixAxisSensor(HLERequestContext& ctx) {
467 IPC::RequestParser rp{ctx};
468 const auto joy_xpad_id{rp.Pop<u32>()};
469
470 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
471
472 // This function has been stubbed since 10.0.0+
473
474 IPC::ResponseBuilder rb{ctx, 2};
475 rb.Push(ResultSuccess);
476}
477
478void IHidServer::GetJoySixAxisSensorLifoHandle(HLERequestContext& ctx) {
479 IPC::RequestParser rp{ctx};
480 const auto joy_xpad_id{rp.Pop<u32>()};
481
482 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
483
484 // This function has been stubbed since 10.0.0+
485
486 IPC::ResponseBuilder rb{ctx, 2, 1};
487 rb.Push(ResultSuccess);
488 // Handle returned is null here
489}
490
491void IHidServer::StartSixAxisSensor(HLERequestContext& ctx) {
492 IPC::RequestParser rp{ctx};
493 struct Parameters {
494 Core::HID::SixAxisSensorHandle sixaxis_handle;
495 INSERT_PADDING_WORDS_NOINIT(1);
496 u64 applet_resource_user_id;
497 };
498 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
499
500 const auto parameters{rp.PopRaw<Parameters>()};
501
502 auto six_axis = GetResourceManager()->GetSixAxis();
503 const auto result = six_axis->SetSixAxisEnabled(parameters.sixaxis_handle, true);
504
505 LOG_DEBUG(Service_HID,
506 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
507 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
508 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
509
510 IPC::ResponseBuilder rb{ctx, 2};
511 rb.Push(result);
512}
513
514void IHidServer::StopSixAxisSensor(HLERequestContext& ctx) {
515 IPC::RequestParser rp{ctx};
516 struct Parameters {
517 Core::HID::SixAxisSensorHandle sixaxis_handle;
518 INSERT_PADDING_WORDS_NOINIT(1);
519 u64 applet_resource_user_id;
520 };
521 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
522
523 const auto parameters{rp.PopRaw<Parameters>()};
524
525 auto six_axis = GetResourceManager()->GetSixAxis();
526 const auto result = six_axis->SetSixAxisEnabled(parameters.sixaxis_handle, false);
527
528 LOG_DEBUG(Service_HID,
529 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
530 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
531 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
532
533 IPC::ResponseBuilder rb{ctx, 2};
534 rb.Push(result);
535}
536
537void IHidServer::IsSixAxisSensorFusionEnabled(HLERequestContext& ctx) {
538 IPC::RequestParser rp{ctx};
539 struct Parameters {
540 Core::HID::SixAxisSensorHandle sixaxis_handle;
541 INSERT_PADDING_WORDS_NOINIT(1);
542 u64 applet_resource_user_id;
543 };
544 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
545
546 const auto parameters{rp.PopRaw<Parameters>()};
547
548 bool is_enabled{};
549 auto six_axis = GetResourceManager()->GetSixAxis();
550 const auto result =
551 six_axis->IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled);
552
553 LOG_DEBUG(Service_HID,
554 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
555 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
556 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
557
558 IPC::ResponseBuilder rb{ctx, 3};
559 rb.Push(result);
560 rb.Push(is_enabled);
561}
562
563void IHidServer::EnableSixAxisSensorFusion(HLERequestContext& ctx) {
564 IPC::RequestParser rp{ctx};
565 struct Parameters {
566 bool enable_sixaxis_sensor_fusion;
567 INSERT_PADDING_BYTES_NOINIT(3);
568 Core::HID::SixAxisSensorHandle sixaxis_handle;
569 u64 applet_resource_user_id;
570 };
571 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
572
573 const auto parameters{rp.PopRaw<Parameters>()};
574
575 auto six_axis = GetResourceManager()->GetSixAxis();
576 const auto result = six_axis->SetSixAxisFusionEnabled(parameters.sixaxis_handle,
577 parameters.enable_sixaxis_sensor_fusion);
578
579 LOG_DEBUG(Service_HID,
580 "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, "
581 "device_index={}, applet_resource_user_id={}",
582 parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type,
583 parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
584 parameters.applet_resource_user_id);
585
586 IPC::ResponseBuilder rb{ctx, 2};
587 rb.Push(result);
588}
589
590void IHidServer::SetSixAxisSensorFusionParameters(HLERequestContext& ctx) {
591 IPC::RequestParser rp{ctx};
592 struct Parameters {
593 Core::HID::SixAxisSensorHandle sixaxis_handle;
594 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion;
595 INSERT_PADDING_WORDS_NOINIT(1);
596 u64 applet_resource_user_id;
597 };
598 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
599
600 const auto parameters{rp.PopRaw<Parameters>()};
601
602 auto six_axis = GetResourceManager()->GetSixAxis();
603 const auto result =
604 six_axis->SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion);
605
606 LOG_DEBUG(Service_HID,
607 "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, "
608 "parameter2={}, applet_resource_user_id={}",
609 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
610 parameters.sixaxis_handle.device_index, parameters.sixaxis_fusion.parameter1,
611 parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id);
612
613 IPC::ResponseBuilder rb{ctx, 2};
614 rb.Push(result);
615}
616
617void IHidServer::GetSixAxisSensorFusionParameters(HLERequestContext& ctx) {
618 IPC::RequestParser rp{ctx};
619 struct Parameters {
620 Core::HID::SixAxisSensorHandle sixaxis_handle;
621 INSERT_PADDING_WORDS_NOINIT(1);
622 u64 applet_resource_user_id;
623 };
624 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
625
626 const auto parameters{rp.PopRaw<Parameters>()};
627
628 Core::HID::SixAxisSensorFusionParameters fusion_parameters{};
629 auto six_axis = GetResourceManager()->GetSixAxis();
630 const auto result =
631 six_axis->GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
632
633 LOG_DEBUG(Service_HID,
634 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
635 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
636 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
637
638 IPC::ResponseBuilder rb{ctx, 4};
639 rb.Push(result);
640 rb.PushRaw(fusion_parameters);
641}
642
643void IHidServer::ResetSixAxisSensorFusionParameters(HLERequestContext& ctx) {
644 IPC::RequestParser rp{ctx};
645 struct Parameters {
646 Core::HID::SixAxisSensorHandle sixaxis_handle;
647 INSERT_PADDING_WORDS_NOINIT(1);
648 u64 applet_resource_user_id;
649 };
650 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
651
652 const auto parameters{rp.PopRaw<Parameters>()};
653
654 // Since these parameters are unknown just use what HW outputs
655 const Core::HID::SixAxisSensorFusionParameters fusion_parameters{
656 .parameter1 = 0.03f,
657 .parameter2 = 0.4f,
658 };
659 auto six_axis = GetResourceManager()->GetSixAxis();
660 const auto result1 =
661 six_axis->SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
662 const auto result2 = six_axis->SetSixAxisFusionEnabled(parameters.sixaxis_handle, true);
663
664 LOG_DEBUG(Service_HID,
665 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
666 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
667 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
668
669 IPC::ResponseBuilder rb{ctx, 2};
670 if (result1.IsError()) {
671 rb.Push(result1);
672 return;
673 }
674 rb.Push(result2);
675}
676
677void IHidServer::SetGyroscopeZeroDriftMode(HLERequestContext& ctx) {
678 IPC::RequestParser rp{ctx};
679 const auto sixaxis_handle{rp.PopRaw<Core::HID::SixAxisSensorHandle>()};
680 const auto drift_mode{rp.PopEnum<Core::HID::GyroscopeZeroDriftMode>()};
681 const auto applet_resource_user_id{rp.Pop<u64>()};
682
683 auto six_axis = GetResourceManager()->GetSixAxis();
684 const auto result = six_axis->SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode);
685
686 LOG_DEBUG(Service_HID,
687 "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, "
688 "applet_resource_user_id={}",
689 sixaxis_handle.npad_type, sixaxis_handle.npad_id, sixaxis_handle.device_index,
690 drift_mode, applet_resource_user_id);
691
692 IPC::ResponseBuilder rb{ctx, 2};
693 rb.Push(result);
694}
695
696void IHidServer::GetGyroscopeZeroDriftMode(HLERequestContext& ctx) {
697 IPC::RequestParser rp{ctx};
698 struct Parameters {
699 Core::HID::SixAxisSensorHandle sixaxis_handle;
700 INSERT_PADDING_WORDS_NOINIT(1);
701 u64 applet_resource_user_id;
702 };
703 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
704
705 const auto parameters{rp.PopRaw<Parameters>()};
706
707 auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard};
708 auto six_axis = GetResourceManager()->GetSixAxis();
709 const auto result = six_axis->GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
710
711 LOG_DEBUG(Service_HID,
712 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
713 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
714 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
715
716 IPC::ResponseBuilder rb{ctx, 3};
717 rb.Push(result);
718 rb.PushEnum(drift_mode);
719}
720
721void IHidServer::ResetGyroscopeZeroDriftMode(HLERequestContext& ctx) {
722 IPC::RequestParser rp{ctx};
723 struct Parameters {
724 Core::HID::SixAxisSensorHandle sixaxis_handle;
725 INSERT_PADDING_WORDS_NOINIT(1);
726 u64 applet_resource_user_id;
727 };
728 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
729
730 const auto parameters{rp.PopRaw<Parameters>()};
731
732 const auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard};
733 auto six_axis = GetResourceManager()->GetSixAxis();
734 const auto result = six_axis->SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
735
736 LOG_DEBUG(Service_HID,
737 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
738 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
739 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
740
741 IPC::ResponseBuilder rb{ctx, 2};
742 rb.Push(result);
743}
744
745void IHidServer::IsSixAxisSensorAtRest(HLERequestContext& ctx) {
746 IPC::RequestParser rp{ctx};
747 struct Parameters {
748 Core::HID::SixAxisSensorHandle sixaxis_handle;
749 INSERT_PADDING_WORDS_NOINIT(1);
750 u64 applet_resource_user_id;
751 };
752 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
753
754 const auto parameters{rp.PopRaw<Parameters>()};
755
756 bool is_at_rest{};
757 auto six_axis = GetResourceManager()->GetSixAxis();
758 six_axis->IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest);
759
760 LOG_DEBUG(Service_HID,
761 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
762 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
763 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
764
765 IPC::ResponseBuilder rb{ctx, 3};
766 rb.Push(ResultSuccess);
767 rb.Push(is_at_rest);
768}
769
770void IHidServer::IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx) {
771 IPC::RequestParser rp{ctx};
772 struct Parameters {
773 Core::HID::SixAxisSensorHandle sixaxis_handle;
774 INSERT_PADDING_WORDS_NOINIT(1);
775 u64 applet_resource_user_id;
776 };
777 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
778
779 const auto parameters{rp.PopRaw<Parameters>()};
780
781 bool is_firmware_available{};
782 auto controller = GetResourceManager()->GetNpad();
783 controller->IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle,
784 is_firmware_available);
785
786 LOG_WARNING(
787 Service_HID,
788 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
789 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
790 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
791
792 IPC::ResponseBuilder rb{ctx, 3};
793 rb.Push(ResultSuccess);
794 rb.Push(is_firmware_available);
795}
796
797void IHidServer::EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx) {
798 IPC::RequestParser rp{ctx};
799 struct Parameters {
800 bool enabled;
801 Core::HID::SixAxisSensorHandle sixaxis_handle;
802 u64 applet_resource_user_id;
803 };
804 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
805
806 const auto parameters{rp.PopRaw<Parameters>()};
807
808 auto six_axis = GetResourceManager()->GetSixAxis();
809 const auto result = six_axis->EnableSixAxisSensorUnalteredPassthrough(parameters.sixaxis_handle,
810 parameters.enabled);
811
812 LOG_DEBUG(Service_HID,
813 "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, "
814 "applet_resource_user_id={}",
815 parameters.enabled, parameters.sixaxis_handle.npad_type,
816 parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
817 parameters.applet_resource_user_id);
818
819 IPC::ResponseBuilder rb{ctx, 2};
820 rb.Push(result);
821}
822
823void IHidServer::IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx) {
824 IPC::RequestParser rp{ctx};
825 struct Parameters {
826 Core::HID::SixAxisSensorHandle sixaxis_handle;
827 INSERT_PADDING_WORDS_NOINIT(1);
828 u64 applet_resource_user_id;
829 };
830 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
831
832 const auto parameters{rp.PopRaw<Parameters>()};
833
834 bool is_unaltered_sisxaxis_enabled{};
835 auto six_axis = GetResourceManager()->GetSixAxis();
836 const auto result = six_axis->IsSixAxisSensorUnalteredPassthroughEnabled(
837 parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled);
838
839 LOG_DEBUG(
840 Service_HID,
841 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
842 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
843 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
844
845 IPC::ResponseBuilder rb{ctx, 3};
846 rb.Push(result);
847 rb.Push(is_unaltered_sisxaxis_enabled);
848}
849
850void IHidServer::LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx) {
851 IPC::RequestParser rp{ctx};
852 struct Parameters {
853 Core::HID::SixAxisSensorHandle sixaxis_handle;
854 INSERT_PADDING_WORDS_NOINIT(1);
855 u64 applet_resource_user_id;
856 };
857 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
858
859 const auto parameters{rp.PopRaw<Parameters>()};
860
861 Core::HID::SixAxisSensorCalibrationParameter calibration{};
862 auto six_axis = GetResourceManager()->GetSixAxis();
863 const auto result =
864 six_axis->LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration);
865
866 LOG_WARNING(
867 Service_HID,
868 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
869 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
870 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
871
872 if (result.IsSuccess()) {
873 ctx.WriteBuffer(calibration);
874 }
875
876 IPC::ResponseBuilder rb{ctx, 2};
877 rb.Push(result);
878}
879
880void IHidServer::GetSixAxisSensorIcInformation(HLERequestContext& ctx) {
881 IPC::RequestParser rp{ctx};
882 struct Parameters {
883 Core::HID::SixAxisSensorHandle sixaxis_handle;
884 INSERT_PADDING_WORDS_NOINIT(1);
885 u64 applet_resource_user_id;
886 };
887 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
888
889 const auto parameters{rp.PopRaw<Parameters>()};
890
891 Core::HID::SixAxisSensorIcInformation ic_information{};
892 auto six_axis = GetResourceManager()->GetSixAxis();
893 const auto result =
894 six_axis->GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information);
895
896 LOG_WARNING(
897 Service_HID,
898 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
899 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
900 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
901
902 if (result.IsSuccess()) {
903 ctx.WriteBuffer(ic_information);
904 }
905
906 IPC::ResponseBuilder rb{ctx, 2};
907 rb.Push(result);
908}
909
910void IHidServer::ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx) {
911 IPC::RequestParser rp{ctx};
912 struct Parameters {
913 Core::HID::SixAxisSensorHandle sixaxis_handle;
914 INSERT_PADDING_WORDS_NOINIT(1);
915 u64 applet_resource_user_id;
916 };
917 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
918
919 const auto parameters{rp.PopRaw<Parameters>()};
920
921 auto controller = GetResourceManager()->GetNpad();
922 const auto result =
923 controller->ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle);
924
925 LOG_WARNING(
926 Service_HID,
927 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
928 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
929 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
930
931 IPC::ResponseBuilder rb{ctx, 2};
932 rb.Push(result);
933}
934
935void IHidServer::ActivateGesture(HLERequestContext& ctx) {
936 IPC::RequestParser rp{ctx};
937 struct Parameters {
938 u32 basic_gesture_id;
939 INSERT_PADDING_WORDS_NOINIT(1);
940 u64 applet_resource_user_id;
941 };
942 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
943
944 const auto parameters{rp.PopRaw<Parameters>()};
945
946 LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}",
947 parameters.basic_gesture_id, parameters.applet_resource_user_id);
948
949 Result result = ResultSuccess;
950 auto gesture = GetResourceManager()->GetGesture();
951
952 if (!firmware_settings->IsDeviceManaged()) {
953 result = gesture->Activate();
954 }
955
956 if (result.IsSuccess()) {
957 // TODO: Use gesture id here
958 result = gesture->Activate(parameters.applet_resource_user_id);
959 }
960
961 IPC::ResponseBuilder rb{ctx, 2};
962 rb.Push(result);
963}
964
965void IHidServer::SetSupportedNpadStyleSet(HLERequestContext& ctx) {
966 IPC::RequestParser rp{ctx};
967 struct Parameters {
968 Core::HID::NpadStyleSet supported_styleset;
969 INSERT_PADDING_WORDS_NOINIT(1);
970 u64 applet_resource_user_id;
971 };
972 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
973
974 const auto parameters{rp.PopRaw<Parameters>()};
975
976 GetResourceManager()->GetNpad()->SetSupportedStyleSet({parameters.supported_styleset});
977
978 LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}",
979 parameters.supported_styleset, parameters.applet_resource_user_id);
980
981 IPC::ResponseBuilder rb{ctx, 2};
982 rb.Push(ResultSuccess);
983}
984
985void IHidServer::GetSupportedNpadStyleSet(HLERequestContext& ctx) {
986 IPC::RequestParser rp{ctx};
987 const auto applet_resource_user_id{rp.Pop<u64>()};
988
989 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
990
991 IPC::ResponseBuilder rb{ctx, 3};
992 rb.Push(ResultSuccess);
993 rb.PushEnum(GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw);
994}
995
996void IHidServer::SetSupportedNpadIdType(HLERequestContext& ctx) {
997 IPC::RequestParser rp{ctx};
998 const auto applet_resource_user_id{rp.Pop<u64>()};
999
1000 const auto result = GetResourceManager()->GetNpad()->SetSupportedNpadIdTypes(ctx.ReadBuffer());
1001
1002 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1003
1004 IPC::ResponseBuilder rb{ctx, 2};
1005 rb.Push(result);
1006}
1007
1008void IHidServer::ActivateNpad(HLERequestContext& ctx) {
1009 IPC::RequestParser rp{ctx};
1010 const auto applet_resource_user_id{rp.Pop<u64>()};
1011
1012 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1013
1014 auto npad = GetResourceManager()->GetNpad();
1015
1016 // TODO: npad->SetRevision(applet_resource_user_id, NpadRevision::Revision0);
1017 const Result result = npad->Activate(applet_resource_user_id);
1018
1019 IPC::ResponseBuilder rb{ctx, 2};
1020 rb.Push(result);
1021}
1022
1023void IHidServer::DeactivateNpad(HLERequestContext& ctx) {
1024 IPC::RequestParser rp{ctx};
1025 const auto applet_resource_user_id{rp.Pop<u64>()};
1026
1027 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1028
1029 // This function does nothing since 10.0.0+
1030
1031 IPC::ResponseBuilder rb{ctx, 2};
1032 rb.Push(ResultSuccess);
1033}
1034
1035void IHidServer::AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx) {
1036 IPC::RequestParser rp{ctx};
1037 struct Parameters {
1038 Core::HID::NpadIdType npad_id;
1039 INSERT_PADDING_WORDS_NOINIT(1);
1040 u64 applet_resource_user_id;
1041 u64 unknown;
1042 };
1043 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1044
1045 const auto parameters{rp.PopRaw<Parameters>()};
1046
1047 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}",
1048 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown);
1049
1050 // Games expect this event to be signaled after calling this function
1051 GetResourceManager()->GetNpad()->SignalStyleSetChangedEvent(parameters.npad_id);
1052
1053 IPC::ResponseBuilder rb{ctx, 2, 1};
1054 rb.Push(ResultSuccess);
1055 rb.PushCopyObjects(
1056 GetResourceManager()->GetNpad()->GetStyleSetChangedEvent(parameters.npad_id));
1057}
1058
1059void IHidServer::DisconnectNpad(HLERequestContext& ctx) {
1060 IPC::RequestParser rp{ctx};
1061 struct Parameters {
1062 Core::HID::NpadIdType npad_id;
1063 INSERT_PADDING_WORDS_NOINIT(1);
1064 u64 applet_resource_user_id;
1065 };
1066 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1067
1068 const auto parameters{rp.PopRaw<Parameters>()};
1069
1070 auto controller = GetResourceManager()->GetNpad();
1071 controller->DisconnectNpad(parameters.npad_id);
1072
1073 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1074 parameters.applet_resource_user_id);
1075
1076 IPC::ResponseBuilder rb{ctx, 2};
1077 rb.Push(ResultSuccess);
1078}
1079
1080void IHidServer::GetPlayerLedPattern(HLERequestContext& ctx) {
1081 IPC::RequestParser rp{ctx};
1082 const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
1083
1084 Core::HID::LedPattern pattern{0, 0, 0, 0};
1085 auto controller = GetResourceManager()->GetNpad();
1086 const auto result = controller->GetLedPattern(npad_id, pattern);
1087
1088 LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
1089
1090 IPC::ResponseBuilder rb{ctx, 4};
1091 rb.Push(result);
1092 rb.Push(pattern.raw);
1093}
1094
1095void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) {
1096 IPC::RequestParser rp{ctx};
1097 struct Parameters {
1098 NPad::NpadRevision revision;
1099 INSERT_PADDING_WORDS_NOINIT(1);
1100 u64 applet_resource_user_id;
1101 };
1102 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1103
1104 const auto parameters{rp.PopRaw<Parameters>()};
1105
1106 LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision,
1107 parameters.applet_resource_user_id);
1108
1109 auto npad = GetResourceManager()->GetNpad();
1110
1111 // TODO: npad->SetRevision(applet_resource_user_id, revision);
1112 const auto result = npad->Activate(parameters.applet_resource_user_id);
1113
1114 IPC::ResponseBuilder rb{ctx, 2};
1115 rb.Push(result);
1116}
1117
1118void IHidServer::SetNpadJoyHoldType(HLERequestContext& ctx) {
1119 IPC::RequestParser rp{ctx};
1120 const auto applet_resource_user_id{rp.Pop<u64>()};
1121 const auto hold_type{rp.PopEnum<NPad::NpadJoyHoldType>()};
1122
1123 GetResourceManager()->GetNpad()->SetHoldType(hold_type);
1124
1125 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}",
1126 applet_resource_user_id, hold_type);
1127
1128 IPC::ResponseBuilder rb{ctx, 2};
1129 rb.Push(ResultSuccess);
1130}
1131
1132void IHidServer::GetNpadJoyHoldType(HLERequestContext& ctx) {
1133 IPC::RequestParser rp{ctx};
1134 const auto applet_resource_user_id{rp.Pop<u64>()};
1135
1136 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1137
1138 IPC::ResponseBuilder rb{ctx, 4};
1139 rb.Push(ResultSuccess);
1140 rb.PushEnum(GetResourceManager()->GetNpad()->GetHoldType());
1141}
1142
1143void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) {
1144 IPC::RequestParser rp{ctx};
1145 struct Parameters {
1146 Core::HID::NpadIdType npad_id;
1147 INSERT_PADDING_WORDS_NOINIT(1);
1148 u64 applet_resource_user_id;
1149 };
1150 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1151
1152 const auto parameters{rp.PopRaw<Parameters>()};
1153
1154 Core::HID::NpadIdType new_npad_id{};
1155 auto controller = GetResourceManager()->GetNpad();
1156 controller->SetNpadMode(new_npad_id, parameters.npad_id, NPad::NpadJoyDeviceType::Left,
1157 NPad::NpadJoyAssignmentMode::Single);
1158
1159 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1160 parameters.applet_resource_user_id);
1161
1162 IPC::ResponseBuilder rb{ctx, 2};
1163 rb.Push(ResultSuccess);
1164}
1165
1166void IHidServer::SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx) {
1167 IPC::RequestParser rp{ctx};
1168 struct Parameters {
1169 Core::HID::NpadIdType npad_id;
1170 INSERT_PADDING_WORDS_NOINIT(1);
1171 u64 applet_resource_user_id;
1172 NPad::NpadJoyDeviceType npad_joy_device_type;
1173 };
1174 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1175
1176 const auto parameters{rp.PopRaw<Parameters>()};
1177
1178 Core::HID::NpadIdType new_npad_id{};
1179 auto controller = GetResourceManager()->GetNpad();
1180 controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type,
1181 NPad::NpadJoyAssignmentMode::Single);
1182
1183 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
1184 parameters.npad_id, parameters.applet_resource_user_id,
1185 parameters.npad_joy_device_type);
1186
1187 IPC::ResponseBuilder rb{ctx, 2};
1188 rb.Push(ResultSuccess);
1189}
1190
1191void IHidServer::SetNpadJoyAssignmentModeDual(HLERequestContext& ctx) {
1192 IPC::RequestParser rp{ctx};
1193 struct Parameters {
1194 Core::HID::NpadIdType npad_id;
1195 INSERT_PADDING_WORDS_NOINIT(1);
1196 u64 applet_resource_user_id;
1197 };
1198 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1199
1200 const auto parameters{rp.PopRaw<Parameters>()};
1201
1202 Core::HID::NpadIdType new_npad_id{};
1203 auto controller = GetResourceManager()->GetNpad();
1204 controller->SetNpadMode(new_npad_id, parameters.npad_id, {}, NPad::NpadJoyAssignmentMode::Dual);
1205
1206 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1207 parameters.applet_resource_user_id); // Spams a lot when controller applet is open
1208
1209 IPC::ResponseBuilder rb{ctx, 2};
1210 rb.Push(ResultSuccess);
1211}
1212
1213void IHidServer::MergeSingleJoyAsDualJoy(HLERequestContext& ctx) {
1214 IPC::RequestParser rp{ctx};
1215 const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()};
1216 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
1217 const auto applet_resource_user_id{rp.Pop<u64>()};
1218
1219 auto controller = GetResourceManager()->GetNpad();
1220 const auto result = controller->MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
1221
1222 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
1223 npad_id_1, npad_id_2, applet_resource_user_id);
1224
1225 IPC::ResponseBuilder rb{ctx, 2};
1226 rb.Push(result);
1227}
1228
1229void IHidServer::StartLrAssignmentMode(HLERequestContext& ctx) {
1230 IPC::RequestParser rp{ctx};
1231 const auto applet_resource_user_id{rp.Pop<u64>()};
1232
1233 GetResourceManager()->GetNpad()->StartLRAssignmentMode();
1234
1235 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1236
1237 IPC::ResponseBuilder rb{ctx, 2};
1238 rb.Push(ResultSuccess);
1239}
1240
1241void IHidServer::StopLrAssignmentMode(HLERequestContext& ctx) {
1242 IPC::RequestParser rp{ctx};
1243 const auto applet_resource_user_id{rp.Pop<u64>()};
1244
1245 GetResourceManager()->GetNpad()->StopLRAssignmentMode();
1246
1247 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1248
1249 IPC::ResponseBuilder rb{ctx, 2};
1250 rb.Push(ResultSuccess);
1251}
1252
1253void IHidServer::SetNpadHandheldActivationMode(HLERequestContext& ctx) {
1254 IPC::RequestParser rp{ctx};
1255 const auto applet_resource_user_id{rp.Pop<u64>()};
1256 const auto activation_mode{rp.PopEnum<NPad::NpadHandheldActivationMode>()};
1257
1258 GetResourceManager()->GetNpad()->SetNpadHandheldActivationMode(activation_mode);
1259
1260 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}",
1261 applet_resource_user_id, activation_mode);
1262
1263 IPC::ResponseBuilder rb{ctx, 2};
1264 rb.Push(ResultSuccess);
1265}
1266
1267void IHidServer::GetNpadHandheldActivationMode(HLERequestContext& ctx) {
1268 IPC::RequestParser rp{ctx};
1269 const auto applet_resource_user_id{rp.Pop<u64>()};
1270
1271 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1272
1273 IPC::ResponseBuilder rb{ctx, 4};
1274 rb.Push(ResultSuccess);
1275 rb.PushEnum(GetResourceManager()->GetNpad()->GetNpadHandheldActivationMode());
1276}
1277
1278void IHidServer::SwapNpadAssignment(HLERequestContext& ctx) {
1279 IPC::RequestParser rp{ctx};
1280 const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()};
1281 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
1282 const auto applet_resource_user_id{rp.Pop<u64>()};
1283
1284 auto controller = GetResourceManager()->GetNpad();
1285 const auto result = controller->SwapNpadAssignment(npad_id_1, npad_id_2);
1286
1287 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
1288 npad_id_1, npad_id_2, applet_resource_user_id);
1289
1290 IPC::ResponseBuilder rb{ctx, 2};
1291 rb.Push(result);
1292}
1293
1294void IHidServer::IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx) {
1295 IPC::RequestParser rp{ctx};
1296 struct Parameters {
1297 Core::HID::NpadIdType npad_id;
1298 INSERT_PADDING_WORDS_NOINIT(1);
1299 u64 applet_resource_user_id;
1300 };
1301 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1302
1303 const auto parameters{rp.PopRaw<Parameters>()};
1304
1305 bool is_enabled = false;
1306 auto controller = GetResourceManager()->GetNpad();
1307 const auto result =
1308 controller->IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled);
1309
1310 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
1311 parameters.npad_id, parameters.applet_resource_user_id);
1312
1313 IPC::ResponseBuilder rb{ctx, 3};
1314 rb.Push(result);
1315 rb.Push(is_enabled);
1316}
1317
1318void IHidServer::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx) {
1319 IPC::RequestParser rp{ctx};
1320 struct Parameters {
1321 bool is_enabled;
1322 INSERT_PADDING_BYTES_NOINIT(3);
1323 Core::HID::NpadIdType npad_id;
1324 u64 applet_resource_user_id;
1325 };
1326 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1327
1328 const auto parameters{rp.PopRaw<Parameters>()};
1329
1330 auto controller = GetResourceManager()->GetNpad();
1331 const auto result = controller->SetUnintendedHomeButtonInputProtectionEnabled(
1332 parameters.is_enabled, parameters.npad_id);
1333
1334 LOG_DEBUG(Service_HID,
1335 "(STUBBED) called, is_enabled={}, npad_id={}, applet_resource_user_id={}",
1336 parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id);
1337
1338 IPC::ResponseBuilder rb{ctx, 2};
1339 rb.Push(result);
1340}
1341
1342void IHidServer::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx) {
1343 IPC::RequestParser rp{ctx};
1344 struct Parameters {
1345 Core::HID::NpadIdType npad_id;
1346 INSERT_PADDING_WORDS_NOINIT(1);
1347 u64 applet_resource_user_id;
1348 NPad::NpadJoyDeviceType npad_joy_device_type;
1349 };
1350 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1351
1352 const auto parameters{rp.PopRaw<Parameters>()};
1353
1354 Core::HID::NpadIdType new_npad_id{};
1355 auto controller = GetResourceManager()->GetNpad();
1356 const auto is_reassigned =
1357 controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type,
1358 NPad::NpadJoyAssignmentMode::Single);
1359
1360 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
1361 parameters.npad_id, parameters.applet_resource_user_id,
1362 parameters.npad_joy_device_type);
1363
1364 IPC::ResponseBuilder rb{ctx, 4};
1365 rb.Push(ResultSuccess);
1366 rb.Push(is_reassigned);
1367 rb.PushEnum(new_npad_id);
1368}
1369
1370void IHidServer::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) {
1371 IPC::RequestParser rp{ctx};
1372 struct Parameters {
1373 bool analog_stick_use_center_clamp;
1374 INSERT_PADDING_BYTES_NOINIT(7);
1375 u64 applet_resource_user_id;
1376 };
1377 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1378
1379 const auto parameters{rp.PopRaw<Parameters>()};
1380
1381 GetResourceManager()->GetNpad()->SetAnalogStickUseCenterClamp(
1382 parameters.analog_stick_use_center_clamp);
1383
1384 LOG_WARNING(Service_HID,
1385 "(STUBBED) called, analog_stick_use_center_clamp={}, applet_resource_user_id={}",
1386 parameters.analog_stick_use_center_clamp, parameters.applet_resource_user_id);
1387
1388 IPC::ResponseBuilder rb{ctx, 2};
1389 rb.Push(ResultSuccess);
1390}
1391
1392void IHidServer::SetNpadCaptureButtonAssignment(HLERequestContext& ctx) {
1393 IPC::RequestParser rp{ctx};
1394 struct Parameters {
1395 Core::HID::NpadStyleSet npad_styleset;
1396 INSERT_PADDING_WORDS_NOINIT(1);
1397 u64 applet_resource_user_id;
1398 Core::HID::NpadButton button;
1399 };
1400 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1401
1402 const auto parameters{rp.PopRaw<Parameters>()};
1403
1404 LOG_WARNING(Service_HID,
1405 "(STUBBED) called, npad_styleset={}, applet_resource_user_id={}, button={}",
1406 parameters.npad_styleset, parameters.applet_resource_user_id, parameters.button);
1407
1408 IPC::ResponseBuilder rb{ctx, 2};
1409 rb.Push(ResultSuccess);
1410}
1411
1412void IHidServer::ClearNpadCaptureButtonAssignment(HLERequestContext& ctx) {
1413 IPC::RequestParser rp{ctx};
1414 const auto applet_resource_user_id{rp.Pop<u64>()};
1415
1416 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1417 applet_resource_user_id);
1418
1419 IPC::ResponseBuilder rb{ctx, 2};
1420 rb.Push(ResultSuccess);
1421}
1422
1423void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) {
1424 IPC::RequestParser rp{ctx};
1425 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
1426 const auto controller = GetResourceManager()->GetNpad();
1427
1428 Core::HID::VibrationDeviceInfo vibration_device_info;
1429 bool check_device_index = false;
1430
1431 switch (vibration_device_handle.npad_type) {
1432 case Core::HID::NpadStyleIndex::ProController:
1433 case Core::HID::NpadStyleIndex::Handheld:
1434 case Core::HID::NpadStyleIndex::JoyconDual:
1435 case Core::HID::NpadStyleIndex::JoyconLeft:
1436 case Core::HID::NpadStyleIndex::JoyconRight:
1437 vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator;
1438 check_device_index = true;
1439 break;
1440 case Core::HID::NpadStyleIndex::GameCube:
1441 vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm;
1442 break;
1443 case Core::HID::NpadStyleIndex::N64:
1444 vibration_device_info.type = Core::HID::VibrationDeviceType::N64;
1445 break;
1446 default:
1447 vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown;
1448 break;
1449 }
1450
1451 vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
1452 if (check_device_index) {
1453 switch (vibration_device_handle.device_index) {
1454 case Core::HID::DeviceIndex::Left:
1455 vibration_device_info.position = Core::HID::VibrationDevicePosition::Left;
1456 break;
1457 case Core::HID::DeviceIndex::Right:
1458 vibration_device_info.position = Core::HID::VibrationDevicePosition::Right;
1459 break;
1460 case Core::HID::DeviceIndex::None:
1461 default:
1462 ASSERT_MSG(false, "DeviceIndex should never be None!");
1463 break;
1464 }
1465 }
1466
1467 LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}",
1468 vibration_device_info.type, vibration_device_info.position);
1469
1470 const auto result = IsVibrationHandleValid(vibration_device_handle);
1471 if (result.IsError()) {
1472 IPC::ResponseBuilder rb{ctx, 2};
1473 rb.Push(result);
1474 return;
1475 }
1476
1477 IPC::ResponseBuilder rb{ctx, 4};
1478 rb.Push(ResultSuccess);
1479 rb.PushRaw(vibration_device_info);
1480}
1481
1482void IHidServer::SendVibrationValue(HLERequestContext& ctx) {
1483 IPC::RequestParser rp{ctx};
1484 struct Parameters {
1485 Core::HID::VibrationDeviceHandle vibration_device_handle;
1486 Core::HID::VibrationValue vibration_value;
1487 INSERT_PADDING_WORDS_NOINIT(1);
1488 u64 applet_resource_user_id;
1489 };
1490 static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
1491
1492 const auto parameters{rp.PopRaw<Parameters>()};
1493
1494 GetResourceManager()->GetNpad()->VibrateController(parameters.vibration_device_handle,
1495 parameters.vibration_value);
1496
1497 LOG_DEBUG(Service_HID,
1498 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1499 parameters.vibration_device_handle.npad_type,
1500 parameters.vibration_device_handle.npad_id,
1501 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1502
1503 IPC::ResponseBuilder rb{ctx, 2};
1504 rb.Push(ResultSuccess);
1505}
1506
1507void IHidServer::GetActualVibrationValue(HLERequestContext& ctx) {
1508 IPC::RequestParser rp{ctx};
1509 struct Parameters {
1510 Core::HID::VibrationDeviceHandle vibration_device_handle;
1511 INSERT_PADDING_WORDS_NOINIT(1);
1512 u64 applet_resource_user_id;
1513 };
1514 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1515
1516 const auto parameters{rp.PopRaw<Parameters>()};
1517
1518 LOG_DEBUG(Service_HID,
1519 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1520 parameters.vibration_device_handle.npad_type,
1521 parameters.vibration_device_handle.npad_id,
1522 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1523
1524 IPC::ResponseBuilder rb{ctx, 6};
1525 rb.Push(ResultSuccess);
1526 rb.PushRaw(
1527 GetResourceManager()->GetNpad()->GetLastVibration(parameters.vibration_device_handle));
1528}
1529
1530void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) {
1531 LOG_DEBUG(Service_HID, "called");
1532
1533 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1534 rb.Push(ResultSuccess);
1535 rb.PushIpcInterface<IActiveVibrationDeviceList>(system, GetResourceManager());
1536}
1537
1538void IHidServer::PermitVibration(HLERequestContext& ctx) {
1539 IPC::RequestParser rp{ctx};
1540 const auto can_vibrate{rp.Pop<bool>()};
1541
1542 // nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value
1543 // by converting it to a bool
1544 Settings::values.vibration_enabled.SetValue(can_vibrate);
1545
1546 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
1547
1548 IPC::ResponseBuilder rb{ctx, 2};
1549 rb.Push(ResultSuccess);
1550}
1551
1552void IHidServer::IsVibrationPermitted(HLERequestContext& ctx) {
1553 LOG_DEBUG(Service_HID, "called");
1554
1555 // nnSDK checks if a float is greater than zero. We return the bool we stored earlier
1556 const auto is_enabled = Settings::values.vibration_enabled.GetValue();
1557
1558 IPC::ResponseBuilder rb{ctx, 3};
1559 rb.Push(ResultSuccess);
1560 rb.Push(is_enabled);
1561}
1562
1563void IHidServer::SendVibrationValues(HLERequestContext& ctx) {
1564 IPC::RequestParser rp{ctx};
1565 const auto applet_resource_user_id{rp.Pop<u64>()};
1566
1567 const auto handle_data = ctx.ReadBuffer(0);
1568 const auto handle_count = ctx.GetReadBufferNumElements<Core::HID::VibrationDeviceHandle>(0);
1569 const auto vibration_data = ctx.ReadBuffer(1);
1570 const auto vibration_count = ctx.GetReadBufferNumElements<Core::HID::VibrationValue>(1);
1571
1572 auto vibration_device_handles =
1573 std::span(reinterpret_cast<const Core::HID::VibrationDeviceHandle*>(handle_data.data()),
1574 handle_count);
1575 auto vibration_values = std::span(
1576 reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count);
1577
1578 GetResourceManager()->GetNpad()->VibrateControllers(vibration_device_handles, vibration_values);
1579
1580 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1581
1582 IPC::ResponseBuilder rb{ctx, 2};
1583 rb.Push(ResultSuccess);
1584}
1585
1586void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) {
1587 IPC::RequestParser rp{ctx};
1588 struct Parameters {
1589 Core::HID::VibrationDeviceHandle vibration_device_handle;
1590 INSERT_PADDING_WORDS_NOINIT(1);
1591 u64 applet_resource_user_id;
1592 Core::HID::VibrationGcErmCommand gc_erm_command;
1593 };
1594 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1595
1596 const auto parameters{rp.PopRaw<Parameters>()};
1597
1598 /**
1599 * Note: This uses yuzu-specific behavior such that the StopHard command produces
1600 * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined below,
1601 * in order to differentiate between Stop and StopHard commands.
1602 * This is done to reuse the controller vibration functions made for regular controllers.
1603 */
1604 const auto vibration_value = [parameters] {
1605 switch (parameters.gc_erm_command) {
1606 case Core::HID::VibrationGcErmCommand::Stop:
1607 return Core::HID::VibrationValue{
1608 .low_amplitude = 0.0f,
1609 .low_frequency = 160.0f,
1610 .high_amplitude = 0.0f,
1611 .high_frequency = 320.0f,
1612 };
1613 case Core::HID::VibrationGcErmCommand::Start:
1614 return Core::HID::VibrationValue{
1615 .low_amplitude = 1.0f,
1616 .low_frequency = 160.0f,
1617 .high_amplitude = 1.0f,
1618 .high_frequency = 320.0f,
1619 };
1620 case Core::HID::VibrationGcErmCommand::StopHard:
1621 return Core::HID::VibrationValue{
1622 .low_amplitude = 0.0f,
1623 .low_frequency = 0.0f,
1624 .high_amplitude = 0.0f,
1625 .high_frequency = 0.0f,
1626 };
1627 default:
1628 return Core::HID::DEFAULT_VIBRATION_VALUE;
1629 }
1630 }();
1631
1632 GetResourceManager()->GetNpad()->VibrateController(parameters.vibration_device_handle,
1633 vibration_value);
1634
1635 LOG_DEBUG(Service_HID,
1636 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, "
1637 "gc_erm_command={}",
1638 parameters.vibration_device_handle.npad_type,
1639 parameters.vibration_device_handle.npad_id,
1640 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id,
1641 parameters.gc_erm_command);
1642
1643 IPC::ResponseBuilder rb{ctx, 2};
1644 rb.Push(ResultSuccess);
1645}
1646
1647void IHidServer::GetActualVibrationGcErmCommand(HLERequestContext& ctx) {
1648 IPC::RequestParser rp{ctx};
1649 struct Parameters {
1650 Core::HID::VibrationDeviceHandle vibration_device_handle;
1651 INSERT_PADDING_WORDS_NOINIT(1);
1652 u64 applet_resource_user_id;
1653 };
1654
1655 const auto parameters{rp.PopRaw<Parameters>()};
1656
1657 const auto last_vibration =
1658 GetResourceManager()->GetNpad()->GetLastVibration(parameters.vibration_device_handle);
1659
1660 const auto gc_erm_command = [last_vibration] {
1661 if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) {
1662 return Core::HID::VibrationGcErmCommand::Start;
1663 }
1664
1665 /**
1666 * Note: This uses yuzu-specific behavior such that the StopHard command produces
1667 * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined in the HID function
1668 * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands.
1669 * This is done to reuse the controller vibration functions made for regular controllers.
1670 */
1671 if (last_vibration.low_frequency == 0.0f && last_vibration.high_frequency == 0.0f) {
1672 return Core::HID::VibrationGcErmCommand::StopHard;
1673 }
1674
1675 return Core::HID::VibrationGcErmCommand::Stop;
1676 }();
1677
1678 LOG_DEBUG(Service_HID,
1679 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1680 parameters.vibration_device_handle.npad_type,
1681 parameters.vibration_device_handle.npad_id,
1682 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1683
1684 IPC::ResponseBuilder rb{ctx, 4};
1685 rb.Push(ResultSuccess);
1686 rb.PushEnum(gc_erm_command);
1687}
1688
1689void IHidServer::BeginPermitVibrationSession(HLERequestContext& ctx) {
1690 IPC::RequestParser rp{ctx};
1691 const auto applet_resource_user_id{rp.Pop<u64>()};
1692
1693 GetResourceManager()->GetNpad()->SetPermitVibrationSession(true);
1694
1695 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1696
1697 IPC::ResponseBuilder rb{ctx, 2};
1698 rb.Push(ResultSuccess);
1699}
1700
1701void IHidServer::EndPermitVibrationSession(HLERequestContext& ctx) {
1702 GetResourceManager()->GetNpad()->SetPermitVibrationSession(false);
1703
1704 LOG_DEBUG(Service_HID, "called");
1705
1706 IPC::ResponseBuilder rb{ctx, 2};
1707 rb.Push(ResultSuccess);
1708}
1709
1710void IHidServer::IsVibrationDeviceMounted(HLERequestContext& ctx) {
1711 IPC::RequestParser rp{ctx};
1712 struct Parameters {
1713 Core::HID::VibrationDeviceHandle vibration_device_handle;
1714 INSERT_PADDING_WORDS_NOINIT(1);
1715 u64 applet_resource_user_id;
1716 };
1717 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1718
1719 const auto parameters{rp.PopRaw<Parameters>()};
1720
1721 LOG_DEBUG(Service_HID,
1722 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1723 parameters.vibration_device_handle.npad_type,
1724 parameters.vibration_device_handle.npad_id,
1725 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1726
1727 IPC::ResponseBuilder rb{ctx, 3};
1728 rb.Push(ResultSuccess);
1729 rb.Push(GetResourceManager()->GetNpad()->IsVibrationDeviceMounted(
1730 parameters.vibration_device_handle));
1731}
1732
1733void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) {
1734 IPC::RequestParser rp{ctx};
1735 const auto applet_resource_user_id{rp.Pop<u64>()};
1736
1737 LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1738
1739 Result result = ResultSuccess;
1740 auto console_sixaxis = GetResourceManager()->GetConsoleSixAxis();
1741
1742 if (!firmware_settings->IsDeviceManaged()) {
1743 result = console_sixaxis->Activate();
1744 }
1745
1746 if (result.IsSuccess()) {
1747 result = console_sixaxis->Activate(applet_resource_user_id);
1748 }
1749
1750 IPC::ResponseBuilder rb{ctx, 2};
1751 rb.Push(result);
1752}
1753
1754void IHidServer::StartConsoleSixAxisSensor(HLERequestContext& ctx) {
1755 IPC::RequestParser rp{ctx};
1756 struct Parameters {
1757 Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle;
1758 INSERT_PADDING_WORDS_NOINIT(1);
1759 u64 applet_resource_user_id;
1760 };
1761 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1762
1763 const auto parameters{rp.PopRaw<Parameters>()};
1764
1765 LOG_WARNING(Service_HID,
1766 "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
1767 parameters.console_sixaxis_handle.unknown_1,
1768 parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id);
1769
1770 IPC::ResponseBuilder rb{ctx, 2};
1771 rb.Push(ResultSuccess);
1772}
1773
1774void IHidServer::StopConsoleSixAxisSensor(HLERequestContext& ctx) {
1775 IPC::RequestParser rp{ctx};
1776 struct Parameters {
1777 Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle;
1778 INSERT_PADDING_WORDS_NOINIT(1);
1779 u64 applet_resource_user_id;
1780 };
1781 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1782
1783 const auto parameters{rp.PopRaw<Parameters>()};
1784
1785 LOG_WARNING(Service_HID,
1786 "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
1787 parameters.console_sixaxis_handle.unknown_1,
1788 parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id);
1789
1790 IPC::ResponseBuilder rb{ctx, 2};
1791 rb.Push(ResultSuccess);
1792}
1793
1794void IHidServer::ActivateSevenSixAxisSensor(HLERequestContext& ctx) {
1795 IPC::RequestParser rp{ctx};
1796 const auto applet_resource_user_id{rp.Pop<u64>()};
1797
1798 LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1799
1800 Result result = ResultSuccess;
1801 auto seven_sixaxis = GetResourceManager()->GetSevenSixAxis();
1802
1803 if (!firmware_settings->IsDeviceManaged()) {
1804 result = seven_sixaxis->Activate();
1805 }
1806
1807 if (result.IsSuccess()) {
1808 seven_sixaxis->Activate(applet_resource_user_id);
1809 }
1810
1811 IPC::ResponseBuilder rb{ctx, 2};
1812 rb.Push(ResultSuccess);
1813}
1814
1815void IHidServer::StartSevenSixAxisSensor(HLERequestContext& ctx) {
1816 IPC::RequestParser rp{ctx};
1817 const auto applet_resource_user_id{rp.Pop<u64>()};
1818
1819 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1820 applet_resource_user_id);
1821
1822 IPC::ResponseBuilder rb{ctx, 2};
1823 rb.Push(ResultSuccess);
1824}
1825
1826void IHidServer::StopSevenSixAxisSensor(HLERequestContext& ctx) {
1827 IPC::RequestParser rp{ctx};
1828 const auto applet_resource_user_id{rp.Pop<u64>()};
1829
1830 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1831 applet_resource_user_id);
1832
1833 IPC::ResponseBuilder rb{ctx, 2};
1834 rb.Push(ResultSuccess);
1835}
1836
1837void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) {
1838 IPC::RequestParser rp{ctx};
1839 const auto applet_resource_user_id{rp.Pop<u64>()};
1840 const auto t_mem_1_size{rp.Pop<u64>()};
1841 const auto t_mem_2_size{rp.Pop<u64>()};
1842 const auto t_mem_1_handle{ctx.GetCopyHandle(0)};
1843 const auto t_mem_2_handle{ctx.GetCopyHandle(1)};
1844
1845 ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes");
1846 ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes");
1847
1848 auto t_mem_1 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
1849 t_mem_1_handle);
1850
1851 if (t_mem_1.IsNull()) {
1852 LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle);
1853 IPC::ResponseBuilder rb{ctx, 2};
1854 rb.Push(ResultUnknown);
1855 return;
1856 }
1857
1858 auto t_mem_2 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
1859 t_mem_2_handle);
1860
1861 if (t_mem_2.IsNull()) {
1862 LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle);
1863 IPC::ResponseBuilder rb{ctx, 2};
1864 rb.Push(ResultUnknown);
1865 return;
1866 }
1867
1868 ASSERT_MSG(t_mem_1->GetSize() == 0x1000, "t_mem_1 has incorrect size");
1869 ASSERT_MSG(t_mem_2->GetSize() == 0x7F000, "t_mem_2 has incorrect size");
1870
1871 // Activate console six axis controller
1872 GetResourceManager()->GetConsoleSixAxis()->Activate();
1873 GetResourceManager()->GetSevenSixAxis()->Activate();
1874
1875 GetResourceManager()->GetSevenSixAxis()->SetTransferMemoryAddress(t_mem_1->GetSourceAddress());
1876
1877 LOG_WARNING(Service_HID,
1878 "called, t_mem_1_handle=0x{:08X}, t_mem_2_handle=0x{:08X}, "
1879 "applet_resource_user_id={}",
1880 t_mem_1_handle, t_mem_2_handle, applet_resource_user_id);
1881
1882 IPC::ResponseBuilder rb{ctx, 2};
1883 rb.Push(ResultSuccess);
1884}
1885
1886void IHidServer::FinalizeSevenSixAxisSensor(HLERequestContext& ctx) {
1887 IPC::RequestParser rp{ctx};
1888 const auto applet_resource_user_id{rp.Pop<u64>()};
1889
1890 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1891 applet_resource_user_id);
1892
1893 IPC::ResponseBuilder rb{ctx, 2};
1894 rb.Push(ResultSuccess);
1895}
1896
1897void IHidServer::ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx) {
1898 IPC::RequestParser rp{ctx};
1899 const auto applet_resource_user_id{rp.Pop<u64>()};
1900
1901 GetResourceManager()->GetSevenSixAxis()->ResetTimestamp();
1902
1903 LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1904
1905 IPC::ResponseBuilder rb{ctx, 2};
1906 rb.Push(ResultSuccess);
1907}
1908
1909void IHidServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
1910 IPC::RequestParser rp{ctx};
1911
1912 LOG_WARNING(Service_HID, "(STUBBED) called");
1913
1914 IPC::ResponseBuilder rb{ctx, 3};
1915 rb.Push(ResultSuccess);
1916 rb.Push(false);
1917}
1918
1919void IHidServer::GetPalmaConnectionHandle(HLERequestContext& ctx) {
1920 IPC::RequestParser rp{ctx};
1921 struct Parameters {
1922 Core::HID::NpadIdType npad_id;
1923 INSERT_PADDING_WORDS_NOINIT(1);
1924 u64 applet_resource_user_id;
1925 };
1926 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1927
1928 const auto parameters{rp.PopRaw<Parameters>()};
1929
1930 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
1931 parameters.npad_id, parameters.applet_resource_user_id);
1932
1933 Palma::PalmaConnectionHandle handle;
1934 auto controller = GetResourceManager()->GetPalma();
1935 const auto result = controller->GetPalmaConnectionHandle(parameters.npad_id, handle);
1936
1937 IPC::ResponseBuilder rb{ctx, 4};
1938 rb.Push(result);
1939 rb.PushRaw(handle);
1940}
1941
1942void IHidServer::InitializePalma(HLERequestContext& ctx) {
1943 IPC::RequestParser rp{ctx};
1944 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
1945
1946 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1947
1948 auto controller = GetResourceManager()->GetPalma();
1949 const auto result = controller->InitializePalma(connection_handle);
1950
1951 IPC::ResponseBuilder rb{ctx, 2};
1952 rb.Push(result);
1953}
1954
1955void IHidServer::AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx) {
1956 IPC::RequestParser rp{ctx};
1957 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
1958
1959 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1960
1961 auto controller = GetResourceManager()->GetPalma();
1962
1963 IPC::ResponseBuilder rb{ctx, 2, 1};
1964 rb.Push(ResultSuccess);
1965 rb.PushCopyObjects(controller->AcquirePalmaOperationCompleteEvent(connection_handle));
1966}
1967
1968void IHidServer::GetPalmaOperationInfo(HLERequestContext& ctx) {
1969 IPC::RequestParser rp{ctx};
1970 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
1971
1972 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1973
1974 Palma::PalmaOperationType operation_type;
1975 Palma::PalmaOperationData data;
1976 auto controller = GetResourceManager()->GetPalma();
1977 const auto result = controller->GetPalmaOperationInfo(connection_handle, operation_type, data);
1978
1979 if (result.IsError()) {
1980 IPC::ResponseBuilder rb{ctx, 2};
1981 rb.Push(result);
1982 }
1983
1984 ctx.WriteBuffer(data);
1985 IPC::ResponseBuilder rb{ctx, 4};
1986 rb.Push(result);
1987 rb.Push(static_cast<u64>(operation_type));
1988}
1989
1990void IHidServer::PlayPalmaActivity(HLERequestContext& ctx) {
1991 IPC::RequestParser rp{ctx};
1992 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
1993 const auto palma_activity{rp.Pop<u64>()};
1994
1995 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}",
1996 connection_handle.npad_id, palma_activity);
1997
1998 auto controller = GetResourceManager()->GetPalma();
1999 const auto result = controller->PlayPalmaActivity(connection_handle, palma_activity);
2000
2001 IPC::ResponseBuilder rb{ctx, 2};
2002 rb.Push(result);
2003}
2004
2005void IHidServer::SetPalmaFrModeType(HLERequestContext& ctx) {
2006 IPC::RequestParser rp{ctx};
2007 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2008 const auto fr_mode{rp.PopEnum<Palma::PalmaFrModeType>()};
2009
2010 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}",
2011 connection_handle.npad_id, fr_mode);
2012
2013 auto controller = GetResourceManager()->GetPalma();
2014 const auto result = controller->SetPalmaFrModeType(connection_handle, fr_mode);
2015
2016 IPC::ResponseBuilder rb{ctx, 2};
2017 rb.Push(result);
2018}
2019
2020void IHidServer::ReadPalmaStep(HLERequestContext& ctx) {
2021 IPC::RequestParser rp{ctx};
2022 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2023
2024 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2025
2026 auto controller = GetResourceManager()->GetPalma();
2027 const auto result = controller->ReadPalmaStep(connection_handle);
2028
2029 IPC::ResponseBuilder rb{ctx, 2};
2030 rb.Push(result);
2031}
2032
2033void IHidServer::EnablePalmaStep(HLERequestContext& ctx) {
2034 IPC::RequestParser rp{ctx};
2035 struct Parameters {
2036 bool is_enabled;
2037 INSERT_PADDING_WORDS_NOINIT(1);
2038 Palma::PalmaConnectionHandle connection_handle;
2039 };
2040 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2041
2042 const auto parameters{rp.PopRaw<Parameters>()};
2043
2044 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}",
2045 parameters.connection_handle.npad_id, parameters.is_enabled);
2046
2047 auto controller = GetResourceManager()->GetPalma();
2048 const auto result =
2049 controller->EnablePalmaStep(parameters.connection_handle, parameters.is_enabled);
2050
2051 IPC::ResponseBuilder rb{ctx, 2};
2052 rb.Push(result);
2053}
2054
2055void IHidServer::ResetPalmaStep(HLERequestContext& ctx) {
2056 IPC::RequestParser rp{ctx};
2057 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2058
2059 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2060
2061 auto controller = GetResourceManager()->GetPalma();
2062 const auto result = controller->ResetPalmaStep(connection_handle);
2063
2064 IPC::ResponseBuilder rb{ctx, 2};
2065 rb.Push(result);
2066}
2067
2068void IHidServer::ReadPalmaApplicationSection(HLERequestContext& ctx) {
2069 LOG_WARNING(Service_HID, "(STUBBED) called");
2070
2071 IPC::ResponseBuilder rb{ctx, 2};
2072 rb.Push(ResultSuccess);
2073}
2074
2075void IHidServer::WritePalmaApplicationSection(HLERequestContext& ctx) {
2076 LOG_WARNING(Service_HID, "(STUBBED) called");
2077
2078 IPC::ResponseBuilder rb{ctx, 2};
2079 rb.Push(ResultSuccess);
2080}
2081
2082void IHidServer::ReadPalmaUniqueCode(HLERequestContext& ctx) {
2083 IPC::RequestParser rp{ctx};
2084 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2085
2086 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2087
2088 GetResourceManager()->GetPalma()->ReadPalmaUniqueCode(connection_handle);
2089
2090 IPC::ResponseBuilder rb{ctx, 2};
2091 rb.Push(ResultSuccess);
2092}
2093
2094void IHidServer::SetPalmaUniqueCodeInvalid(HLERequestContext& ctx) {
2095 IPC::RequestParser rp{ctx};
2096 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2097
2098 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2099
2100 GetResourceManager()->GetPalma()->SetPalmaUniqueCodeInvalid(connection_handle);
2101
2102 IPC::ResponseBuilder rb{ctx, 2};
2103 rb.Push(ResultSuccess);
2104}
2105
2106void IHidServer::WritePalmaActivityEntry(HLERequestContext& ctx) {
2107 LOG_CRITICAL(Service_HID, "(STUBBED) called");
2108
2109 IPC::ResponseBuilder rb{ctx, 2};
2110 rb.Push(ResultSuccess);
2111}
2112
2113void IHidServer::WritePalmaRgbLedPatternEntry(HLERequestContext& ctx) {
2114 IPC::RequestParser rp{ctx};
2115 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2116 const auto unknown{rp.Pop<u64>()};
2117
2118 [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
2119
2120 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}",
2121 connection_handle.npad_id, unknown);
2122
2123 GetResourceManager()->GetPalma()->WritePalmaRgbLedPatternEntry(connection_handle, unknown);
2124
2125 IPC::ResponseBuilder rb{ctx, 2};
2126 rb.Push(ResultSuccess);
2127}
2128
2129void IHidServer::WritePalmaWaveEntry(HLERequestContext& ctx) {
2130 IPC::RequestParser rp{ctx};
2131 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2132 const auto wave_set{rp.PopEnum<Palma::PalmaWaveSet>()};
2133 const auto unknown{rp.Pop<u64>()};
2134 const auto t_mem_size{rp.Pop<u64>()};
2135 const auto t_mem_handle{ctx.GetCopyHandle(0)};
2136 const auto size{rp.Pop<u64>()};
2137
2138 ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes");
2139
2140 auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
2141 t_mem_handle);
2142
2143 if (t_mem.IsNull()) {
2144 LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
2145 IPC::ResponseBuilder rb{ctx, 2};
2146 rb.Push(ResultUnknown);
2147 return;
2148 }
2149
2150 ASSERT_MSG(t_mem->GetSize() == 0x3000, "t_mem has incorrect size");
2151
2152 LOG_WARNING(Service_HID,
2153 "(STUBBED) called, connection_handle={}, wave_set={}, unknown={}, "
2154 "t_mem_handle=0x{:08X}, t_mem_size={}, size={}",
2155 connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size);
2156
2157 GetResourceManager()->GetPalma()->WritePalmaWaveEntry(connection_handle, wave_set,
2158 t_mem->GetSourceAddress(), t_mem_size);
2159
2160 IPC::ResponseBuilder rb{ctx, 2};
2161 rb.Push(ResultSuccess);
2162}
2163
2164void IHidServer::SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) {
2165 IPC::RequestParser rp{ctx};
2166 struct Parameters {
2167 s32 database_id_version;
2168 INSERT_PADDING_WORDS_NOINIT(1);
2169 Palma::PalmaConnectionHandle connection_handle;
2170 };
2171 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2172
2173 const auto parameters{rp.PopRaw<Parameters>()};
2174
2175 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}",
2176 parameters.connection_handle.npad_id, parameters.database_id_version);
2177
2178 GetResourceManager()->GetPalma()->SetPalmaDataBaseIdentificationVersion(
2179 parameters.connection_handle, parameters.database_id_version);
2180
2181 IPC::ResponseBuilder rb{ctx, 2};
2182 rb.Push(ResultSuccess);
2183}
2184
2185void IHidServer::GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) {
2186 IPC::RequestParser rp{ctx};
2187 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2188
2189 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2190
2191 GetResourceManager()->GetPalma()->GetPalmaDataBaseIdentificationVersion(connection_handle);
2192
2193 IPC::ResponseBuilder rb{ctx, 2};
2194 rb.Push(ResultSuccess);
2195}
2196
2197void IHidServer::SuspendPalmaFeature(HLERequestContext& ctx) {
2198 LOG_WARNING(Service_HID, "(STUBBED) called");
2199
2200 IPC::ResponseBuilder rb{ctx, 2};
2201 rb.Push(ResultSuccess);
2202}
2203
2204void IHidServer::GetPalmaOperationResult(HLERequestContext& ctx) {
2205 IPC::RequestParser rp{ctx};
2206 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2207
2208 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2209
2210 const auto result =
2211 GetResourceManager()->GetPalma()->GetPalmaOperationResult(connection_handle);
2212
2213 IPC::ResponseBuilder rb{ctx, 2};
2214 rb.Push(result);
2215}
2216
2217void IHidServer::ReadPalmaPlayLog(HLERequestContext& ctx) {
2218 LOG_WARNING(Service_HID, "(STUBBED) called");
2219
2220 IPC::ResponseBuilder rb{ctx, 2};
2221 rb.Push(ResultSuccess);
2222}
2223
2224void IHidServer::ResetPalmaPlayLog(HLERequestContext& ctx) {
2225 LOG_WARNING(Service_HID, "(STUBBED) called");
2226
2227 IPC::ResponseBuilder rb{ctx, 2};
2228 rb.Push(ResultSuccess);
2229}
2230
2231void IHidServer::SetIsPalmaAllConnectable(HLERequestContext& ctx) {
2232 IPC::RequestParser rp{ctx};
2233 struct Parameters {
2234 bool is_palma_all_connectable;
2235 INSERT_PADDING_BYTES_NOINIT(7);
2236 u64 applet_resource_user_id;
2237 };
2238 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2239
2240 const auto parameters{rp.PopRaw<Parameters>()};
2241
2242 LOG_WARNING(Service_HID,
2243 "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}",
2244 parameters.is_palma_all_connectable, parameters.applet_resource_user_id);
2245
2246 GetResourceManager()->GetPalma()->SetIsPalmaAllConnectable(parameters.is_palma_all_connectable);
2247
2248 IPC::ResponseBuilder rb{ctx, 2};
2249 rb.Push(ResultSuccess);
2250}
2251
2252void IHidServer::SetIsPalmaPairedConnectable(HLERequestContext& ctx) {
2253 LOG_WARNING(Service_HID, "(STUBBED) called");
2254
2255 IPC::ResponseBuilder rb{ctx, 2};
2256 rb.Push(ResultSuccess);
2257}
2258
2259void IHidServer::PairPalma(HLERequestContext& ctx) {
2260 IPC::RequestParser rp{ctx};
2261 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2262
2263 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2264
2265 GetResourceManager()->GetPalma()->PairPalma(connection_handle);
2266
2267 IPC::ResponseBuilder rb{ctx, 2};
2268 rb.Push(ResultSuccess);
2269}
2270
2271void IHidServer::SetPalmaBoostMode(HLERequestContext& ctx) {
2272 IPC::RequestParser rp{ctx};
2273 const auto palma_boost_mode{rp.Pop<bool>()};
2274
2275 LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode);
2276
2277 GetResourceManager()->GetPalma()->SetPalmaBoostMode(palma_boost_mode);
2278
2279 IPC::ResponseBuilder rb{ctx, 2};
2280 rb.Push(ResultSuccess);
2281}
2282
2283void IHidServer::CancelWritePalmaWaveEntry(HLERequestContext& ctx) {
2284 LOG_WARNING(Service_HID, "(STUBBED) called");
2285
2286 IPC::ResponseBuilder rb{ctx, 2};
2287 rb.Push(ResultSuccess);
2288}
2289
2290void IHidServer::EnablePalmaBoostMode(HLERequestContext& ctx) {
2291 LOG_WARNING(Service_HID, "(STUBBED) called");
2292
2293 IPC::ResponseBuilder rb{ctx, 2};
2294 rb.Push(ResultSuccess);
2295}
2296
2297void IHidServer::GetPalmaBluetoothAddress(HLERequestContext& ctx) {
2298 LOG_WARNING(Service_HID, "(STUBBED) called");
2299
2300 IPC::ResponseBuilder rb{ctx, 2};
2301 rb.Push(ResultSuccess);
2302}
2303
2304void IHidServer::SetDisallowedPalmaConnection(HLERequestContext& ctx) {
2305 LOG_WARNING(Service_HID, "(STUBBED) called");
2306
2307 IPC::ResponseBuilder rb{ctx, 2};
2308 rb.Push(ResultSuccess);
2309}
2310
2311void IHidServer::SetNpadCommunicationMode(HLERequestContext& ctx) {
2312 IPC::RequestParser rp{ctx};
2313 const auto applet_resource_user_id{rp.Pop<u64>()};
2314 const auto communication_mode{rp.PopEnum<NPad::NpadCommunicationMode>()};
2315
2316 GetResourceManager()->GetNpad()->SetNpadCommunicationMode(communication_mode);
2317
2318 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, communication_mode={}",
2319 applet_resource_user_id, communication_mode);
2320
2321 IPC::ResponseBuilder rb{ctx, 2};
2322 rb.Push(ResultSuccess);
2323}
2324
2325void IHidServer::GetNpadCommunicationMode(HLERequestContext& ctx) {
2326 IPC::RequestParser rp{ctx};
2327
2328 LOG_WARNING(Service_HID, "(STUBBED) called");
2329
2330 IPC::ResponseBuilder rb{ctx, 4};
2331 rb.Push(ResultSuccess);
2332 rb.PushEnum(GetResourceManager()->GetNpad()->GetNpadCommunicationMode());
2333}
2334
2335void IHidServer::SetTouchScreenConfiguration(HLERequestContext& ctx) {
2336 IPC::RequestParser rp{ctx};
2337 const auto touchscreen_mode{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()};
2338 const auto applet_resource_user_id{rp.Pop<u64>()};
2339
2340 LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}",
2341 touchscreen_mode.mode, applet_resource_user_id);
2342
2343 IPC::ResponseBuilder rb{ctx, 2};
2344 rb.Push(ResultSuccess);
2345}
2346
2347void IHidServer::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) {
2348 IPC::RequestParser rp{ctx};
2349 struct Parameters {
2350 s32 unknown;
2351 INSERT_PADDING_WORDS_NOINIT(1);
2352 u64 applet_resource_user_id;
2353 };
2354 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2355
2356 const auto parameters{rp.PopRaw<Parameters>()};
2357
2358 LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}",
2359 parameters.unknown, parameters.applet_resource_user_id);
2360
2361 IPC::ResponseBuilder rb{ctx, 3};
2362 rb.Push(ResultSuccess);
2363 rb.Push(false);
2364}
2365
2366std::shared_ptr<ResourceManager> IHidServer::GetResourceManager() {
2367 resource_manager->Initialize();
2368 return resource_manager;
2369}
2370
2371} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_server.h b/src/core/hle/service/hid/hid_server.h
new file mode 100644
index 000000000..eb2e8e7f4
--- /dev/null
+++ b/src/core/hle/service/hid/hid_server.h
@@ -0,0 +1,149 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Core {
9class System;
10}
11
12namespace Service::HID {
13class ResourceManager;
14class HidFirmwareSettings;
15
16class IHidServer final : public ServiceFramework<IHidServer> {
17public:
18 explicit IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
19 std::shared_ptr<HidFirmwareSettings> settings);
20 ~IHidServer() override;
21
22 std::shared_ptr<ResourceManager> GetResourceManager();
23
24private:
25 void CreateAppletResource(HLERequestContext& ctx);
26 void ActivateDebugPad(HLERequestContext& ctx);
27 void ActivateTouchScreen(HLERequestContext& ctx);
28 void ActivateMouse(HLERequestContext& ctx);
29 void ActivateKeyboard(HLERequestContext& ctx);
30 void SendKeyboardLockKeyEvent(HLERequestContext& ctx);
31 void AcquireXpadIdEventHandle(HLERequestContext& ctx);
32 void ReleaseXpadIdEventHandle(HLERequestContext& ctx);
33 void ActivateXpad(HLERequestContext& ctx);
34 void GetXpadIds(HLERequestContext& ctx);
35 void ActivateJoyXpad(HLERequestContext& ctx);
36 void GetJoyXpadLifoHandle(HLERequestContext& ctx);
37 void GetJoyXpadIds(HLERequestContext& ctx);
38 void ActivateSixAxisSensor(HLERequestContext& ctx);
39 void DeactivateSixAxisSensor(HLERequestContext& ctx);
40 void GetSixAxisSensorLifoHandle(HLERequestContext& ctx);
41 void ActivateJoySixAxisSensor(HLERequestContext& ctx);
42 void DeactivateJoySixAxisSensor(HLERequestContext& ctx);
43 void GetJoySixAxisSensorLifoHandle(HLERequestContext& ctx);
44 void StartSixAxisSensor(HLERequestContext& ctx);
45 void StopSixAxisSensor(HLERequestContext& ctx);
46 void IsSixAxisSensorFusionEnabled(HLERequestContext& ctx);
47 void EnableSixAxisSensorFusion(HLERequestContext& ctx);
48 void SetSixAxisSensorFusionParameters(HLERequestContext& ctx);
49 void GetSixAxisSensorFusionParameters(HLERequestContext& ctx);
50 void ResetSixAxisSensorFusionParameters(HLERequestContext& ctx);
51 void SetGyroscopeZeroDriftMode(HLERequestContext& ctx);
52 void GetGyroscopeZeroDriftMode(HLERequestContext& ctx);
53 void ResetGyroscopeZeroDriftMode(HLERequestContext& ctx);
54 void IsSixAxisSensorAtRest(HLERequestContext& ctx);
55 void IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx);
56 void EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx);
57 void IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx);
58 void LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx);
59 void GetSixAxisSensorIcInformation(HLERequestContext& ctx);
60 void ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx);
61 void ActivateGesture(HLERequestContext& ctx);
62 void SetSupportedNpadStyleSet(HLERequestContext& ctx);
63 void GetSupportedNpadStyleSet(HLERequestContext& ctx);
64 void SetSupportedNpadIdType(HLERequestContext& ctx);
65 void ActivateNpad(HLERequestContext& ctx);
66 void DeactivateNpad(HLERequestContext& ctx);
67 void AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx);
68 void DisconnectNpad(HLERequestContext& ctx);
69 void GetPlayerLedPattern(HLERequestContext& ctx);
70 void ActivateNpadWithRevision(HLERequestContext& ctx);
71 void SetNpadJoyHoldType(HLERequestContext& ctx);
72 void GetNpadJoyHoldType(HLERequestContext& ctx);
73 void SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx);
74 void SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx);
75 void SetNpadJoyAssignmentModeDual(HLERequestContext& ctx);
76 void MergeSingleJoyAsDualJoy(HLERequestContext& ctx);
77 void StartLrAssignmentMode(HLERequestContext& ctx);
78 void StopLrAssignmentMode(HLERequestContext& ctx);
79 void SetNpadHandheldActivationMode(HLERequestContext& ctx);
80 void GetNpadHandheldActivationMode(HLERequestContext& ctx);
81 void SwapNpadAssignment(HLERequestContext& ctx);
82 void IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx);
83 void EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx);
84 void SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx);
85 void SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx);
86 void SetNpadCaptureButtonAssignment(HLERequestContext& ctx);
87 void ClearNpadCaptureButtonAssignment(HLERequestContext& ctx);
88 void GetVibrationDeviceInfo(HLERequestContext& ctx);
89 void SendVibrationValue(HLERequestContext& ctx);
90 void GetActualVibrationValue(HLERequestContext& ctx);
91 void CreateActiveVibrationDeviceList(HLERequestContext& ctx);
92 void PermitVibration(HLERequestContext& ctx);
93 void IsVibrationPermitted(HLERequestContext& ctx);
94 void SendVibrationValues(HLERequestContext& ctx);
95 void SendVibrationGcErmCommand(HLERequestContext& ctx);
96 void GetActualVibrationGcErmCommand(HLERequestContext& ctx);
97 void BeginPermitVibrationSession(HLERequestContext& ctx);
98 void EndPermitVibrationSession(HLERequestContext& ctx);
99 void IsVibrationDeviceMounted(HLERequestContext& ctx);
100 void ActivateConsoleSixAxisSensor(HLERequestContext& ctx);
101 void StartConsoleSixAxisSensor(HLERequestContext& ctx);
102 void StopConsoleSixAxisSensor(HLERequestContext& ctx);
103 void ActivateSevenSixAxisSensor(HLERequestContext& ctx);
104 void StartSevenSixAxisSensor(HLERequestContext& ctx);
105 void StopSevenSixAxisSensor(HLERequestContext& ctx);
106 void InitializeSevenSixAxisSensor(HLERequestContext& ctx);
107 void FinalizeSevenSixAxisSensor(HLERequestContext& ctx);
108 void ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx);
109 void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
110 void GetPalmaConnectionHandle(HLERequestContext& ctx);
111 void InitializePalma(HLERequestContext& ctx);
112 void AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx);
113 void GetPalmaOperationInfo(HLERequestContext& ctx);
114 void PlayPalmaActivity(HLERequestContext& ctx);
115 void SetPalmaFrModeType(HLERequestContext& ctx);
116 void ReadPalmaStep(HLERequestContext& ctx);
117 void EnablePalmaStep(HLERequestContext& ctx);
118 void ResetPalmaStep(HLERequestContext& ctx);
119 void ReadPalmaApplicationSection(HLERequestContext& ctx);
120 void WritePalmaApplicationSection(HLERequestContext& ctx);
121 void ReadPalmaUniqueCode(HLERequestContext& ctx);
122 void SetPalmaUniqueCodeInvalid(HLERequestContext& ctx);
123 void WritePalmaActivityEntry(HLERequestContext& ctx);
124 void WritePalmaRgbLedPatternEntry(HLERequestContext& ctx);
125 void WritePalmaWaveEntry(HLERequestContext& ctx);
126 void SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
127 void GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
128 void SuspendPalmaFeature(HLERequestContext& ctx);
129 void GetPalmaOperationResult(HLERequestContext& ctx);
130 void ReadPalmaPlayLog(HLERequestContext& ctx);
131 void ResetPalmaPlayLog(HLERequestContext& ctx);
132 void SetIsPalmaAllConnectable(HLERequestContext& ctx);
133 void SetIsPalmaPairedConnectable(HLERequestContext& ctx);
134 void PairPalma(HLERequestContext& ctx);
135 void SetPalmaBoostMode(HLERequestContext& ctx);
136 void CancelWritePalmaWaveEntry(HLERequestContext& ctx);
137 void EnablePalmaBoostMode(HLERequestContext& ctx);
138 void GetPalmaBluetoothAddress(HLERequestContext& ctx);
139 void SetDisallowedPalmaConnection(HLERequestContext& ctx);
140 void SetNpadCommunicationMode(HLERequestContext& ctx);
141 void GetNpadCommunicationMode(HLERequestContext& ctx);
142 void SetTouchScreenConfiguration(HLERequestContext& ctx);
143 void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx);
144
145 std::shared_ptr<ResourceManager> resource_manager;
146 std::shared_ptr<HidFirmwareSettings> firmware_settings;
147};
148
149} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp
new file mode 100644
index 000000000..b56d0347a
--- /dev/null
+++ b/src/core/hle/service/hid/hid_system_server.cpp
@@ -0,0 +1,539 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hid/hid_core.h"
5#include "core/hle/service/hid/controllers/npad.h"
6#include "core/hle/service/hid/controllers/touchscreen.h"
7#include "core/hle/service/hid/errors.h"
8#include "core/hle/service/hid/hid_system_server.h"
9#include "core/hle/service/hid/resource_manager.h"
10#include "core/hle/service/ipc_helpers.h"
11
12namespace Service::HID {
13
14IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource)
15 : ServiceFramework{system_, "hid:sys"}, service_context{system_, service_name},
16 resource_manager{resource} {
17 // clang-format off
18 static const FunctionInfo functions[] = {
19 {31, nullptr, "SendKeyboardLockKeyEvent"},
20 {101, nullptr, "AcquireHomeButtonEventHandle"},
21 {111, nullptr, "ActivateHomeButton"},
22 {121, nullptr, "AcquireSleepButtonEventHandle"},
23 {131, nullptr, "ActivateSleepButton"},
24 {141, nullptr, "AcquireCaptureButtonEventHandle"},
25 {151, nullptr, "ActivateCaptureButton"},
26 {161, nullptr, "GetPlatformConfig"},
27 {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"},
28 {211, nullptr, "GetNpadsWithNfc"},
29 {212, nullptr, "AcquireNfcActivateEventHandle"},
30 {213, nullptr, "ActivateNfc"},
31 {214, nullptr, "GetXcdHandleForNpadWithNfc"},
32 {215, nullptr, "IsNfcActivated"},
33 {230, nullptr, "AcquireIrSensorEventHandle"},
34 {231, nullptr, "ActivateIrSensor"},
35 {232, nullptr, "GetIrSensorState"},
36 {233, nullptr, "GetXcdHandleForNpadWithIrSensor"},
37 {301, nullptr, "ActivateNpadSystem"},
38 {303, &IHidSystemServer::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"},
39 {304, &IHidSystemServer::EnableAssigningSingleOnSlSrPress, "EnableAssigningSingleOnSlSrPress"},
40 {305, &IHidSystemServer::DisableAssigningSingleOnSlSrPress, "DisableAssigningSingleOnSlSrPress"},
41 {306, &IHidSystemServer::GetLastActiveNpad, "GetLastActiveNpad"},
42 {307, nullptr, "GetNpadSystemExtStyle"},
43 {308, &IHidSystemServer::ApplyNpadSystemCommonPolicyFull, "ApplyNpadSystemCommonPolicyFull"},
44 {309, &IHidSystemServer::GetNpadFullKeyGripColor, "GetNpadFullKeyGripColor"},
45 {310, &IHidSystemServer::GetMaskedSupportedNpadStyleSet, "GetMaskedSupportedNpadStyleSet"},
46 {311, nullptr, "SetNpadPlayerLedBlinkingDevice"},
47 {312, &IHidSystemServer::SetSupportedNpadStyleSetAll, "SetSupportedNpadStyleSetAll"},
48 {313, nullptr, "GetNpadCaptureButtonAssignment"},
49 {314, nullptr, "GetAppletFooterUiType"},
50 {315, &IHidSystemServer::GetAppletDetailedUiType, "GetAppletDetailedUiType"},
51 {316, &IHidSystemServer::GetNpadInterfaceType, "GetNpadInterfaceType"},
52 {317, &IHidSystemServer::GetNpadLeftRightInterfaceType, "GetNpadLeftRightInterfaceType"},
53 {318, &IHidSystemServer::HasBattery, "HasBattery"},
54 {319, &IHidSystemServer::HasLeftRightBattery, "HasLeftRightBattery"},
55 {321, &IHidSystemServer::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"},
56 {322, &IHidSystemServer::GetIrSensorState, "GetIrSensorState"},
57 {323, nullptr, "GetXcdHandleForNpadWithIrSensor"},
58 {324, nullptr, "GetUniquePadButtonSet"},
59 {325, nullptr, "GetUniquePadColor"},
60 {326, nullptr, "GetUniquePadAppletDetailedUiType"},
61 {327, nullptr, "GetAbstractedPadIdDataFromNpad"},
62 {328, nullptr, "AttachAbstractedPadToNpad"},
63 {329, nullptr, "DetachAbstractedPadAll"},
64 {330, nullptr, "CheckAbstractedPadConnection"},
65 {500, nullptr, "SetAppletResourceUserId"},
66 {501, nullptr, "RegisterAppletResourceUserId"},
67 {502, nullptr, "UnregisterAppletResourceUserId"},
68 {503, nullptr, "EnableAppletToGetInput"},
69 {504, nullptr, "SetAruidValidForVibration"},
70 {505, nullptr, "EnableAppletToGetSixAxisSensor"},
71 {506, nullptr, "EnableAppletToGetPadInput"},
72 {507, nullptr, "EnableAppletToGetTouchScreen"},
73 {510, nullptr, "SetVibrationMasterVolume"},
74 {511, nullptr, "GetVibrationMasterVolume"},
75 {512, nullptr, "BeginPermitVibrationSession"},
76 {513, nullptr, "EndPermitVibrationSession"},
77 {514, nullptr, "Unknown514"},
78 {520, nullptr, "EnableHandheldHids"},
79 {521, nullptr, "DisableHandheldHids"},
80 {522, nullptr, "SetJoyConRailEnabled"},
81 {523, nullptr, "IsJoyConRailEnabled"},
82 {524, nullptr, "IsHandheldHidsEnabled"},
83 {525, nullptr, "IsJoyConAttachedOnAllRail"},
84 {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"},
85 {541, nullptr, "GetPlayReportControllerUsages"},
86 {542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"},
87 {543, nullptr, "GetRegisteredDevicesOld"},
88 {544, &IHidSystemServer::AcquireConnectionTriggerTimeoutEvent, "AcquireConnectionTriggerTimeoutEvent"},
89 {545, nullptr, "SendConnectionTrigger"},
90 {546, &IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport, "AcquireDeviceRegisteredEventForControllerSupport"},
91 {547, nullptr, "GetAllowedBluetoothLinksCount"},
92 {548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"},
93 {549, nullptr, "GetConnectableRegisteredDevices"},
94 {700, nullptr, "ActivateUniquePad"},
95 {702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"},
96 {703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"},
97 {751, &IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"},
98 {800, nullptr, "ListSixAxisSensorHandles"},
99 {801, nullptr, "IsSixAxisSensorUserCalibrationSupported"},
100 {802, nullptr, "ResetSixAxisSensorCalibrationValues"},
101 {803, nullptr, "StartSixAxisSensorUserCalibration"},
102 {804, nullptr, "CancelSixAxisSensorUserCalibration"},
103 {805, nullptr, "GetUniquePadBluetoothAddress"},
104 {806, nullptr, "DisconnectUniquePad"},
105 {807, nullptr, "GetUniquePadType"},
106 {808, nullptr, "GetUniquePadInterface"},
107 {809, nullptr, "GetUniquePadSerialNumber"},
108 {810, nullptr, "GetUniquePadControllerNumber"},
109 {811, nullptr, "GetSixAxisSensorUserCalibrationStage"},
110 {812, nullptr, "GetConsoleUniqueSixAxisSensorHandle"},
111 {821, nullptr, "StartAnalogStickManualCalibration"},
112 {822, nullptr, "RetryCurrentAnalogStickManualCalibrationStage"},
113 {823, nullptr, "CancelAnalogStickManualCalibration"},
114 {824, nullptr, "ResetAnalogStickManualCalibration"},
115 {825, nullptr, "GetAnalogStickState"},
116 {826, nullptr, "GetAnalogStickManualCalibrationStage"},
117 {827, nullptr, "IsAnalogStickButtonPressed"},
118 {828, nullptr, "IsAnalogStickInReleasePosition"},
119 {829, nullptr, "IsAnalogStickInCircumference"},
120 {830, nullptr, "SetNotificationLedPattern"},
121 {831, nullptr, "SetNotificationLedPatternWithTimeout"},
122 {832, nullptr, "PrepareHidsForNotificationWake"},
123 {850, &IHidSystemServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
124 {851, nullptr, "EnableUsbFullKeyController"},
125 {852, nullptr, "IsUsbConnected"},
126 {870, &IHidSystemServer::IsHandheldButtonPressedOnConsoleMode, "IsHandheldButtonPressedOnConsoleMode"},
127 {900, nullptr, "ActivateInputDetector"},
128 {901, nullptr, "NotifyInputDetector"},
129 {1000, &IHidSystemServer::InitializeFirmwareUpdate, "InitializeFirmwareUpdate"},
130 {1001, nullptr, "GetFirmwareVersion"},
131 {1002, nullptr, "GetAvailableFirmwareVersion"},
132 {1003, nullptr, "IsFirmwareUpdateAvailable"},
133 {1004, nullptr, "CheckFirmwareUpdateRequired"},
134 {1005, nullptr, "StartFirmwareUpdate"},
135 {1006, nullptr, "AbortFirmwareUpdate"},
136 {1007, nullptr, "GetFirmwareUpdateState"},
137 {1008, nullptr, "ActivateAudioControl"},
138 {1009, nullptr, "AcquireAudioControlEventHandle"},
139 {1010, nullptr, "GetAudioControlStates"},
140 {1011, nullptr, "DeactivateAudioControl"},
141 {1050, nullptr, "IsSixAxisSensorAccurateUserCalibrationSupported"},
142 {1051, nullptr, "StartSixAxisSensorAccurateUserCalibration"},
143 {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"},
144 {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"},
145 {1100, nullptr, "GetHidbusSystemServiceObject"},
146 {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"},
147 {1130, nullptr, "InitializeUsbFirmwareUpdate"},
148 {1131, nullptr, "FinalizeUsbFirmwareUpdate"},
149 {1132, nullptr, "CheckUsbFirmwareUpdateRequired"},
150 {1133, nullptr, "StartUsbFirmwareUpdate"},
151 {1134, nullptr, "GetUsbFirmwareUpdateState"},
152 {1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"},
153 {1150, nullptr, "SetTouchScreenMagnification"},
154 {1151, nullptr, "GetTouchScreenFirmwareVersion"},
155 {1152, nullptr, "SetTouchScreenDefaultConfiguration"},
156 {1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
157 {1154, nullptr, "IsFirmwareAvailableForNotification"},
158 {1155, nullptr, "SetForceHandheldStyleVibration"},
159 {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"},
160 {1157, nullptr, "CancelConnectionTrigger"},
161 {1200, nullptr, "IsButtonConfigSupported"},
162 {1201, nullptr, "IsButtonConfigEmbeddedSupported"},
163 {1202, nullptr, "DeleteButtonConfig"},
164 {1203, nullptr, "DeleteButtonConfigEmbedded"},
165 {1204, nullptr, "SetButtonConfigEnabled"},
166 {1205, nullptr, "SetButtonConfigEmbeddedEnabled"},
167 {1206, nullptr, "IsButtonConfigEnabled"},
168 {1207, nullptr, "IsButtonConfigEmbeddedEnabled"},
169 {1208, nullptr, "SetButtonConfigEmbedded"},
170 {1209, nullptr, "SetButtonConfigFull"},
171 {1210, nullptr, "SetButtonConfigLeft"},
172 {1211, nullptr, "SetButtonConfigRight"},
173 {1212, nullptr, "GetButtonConfigEmbedded"},
174 {1213, nullptr, "GetButtonConfigFull"},
175 {1214, nullptr, "GetButtonConfigLeft"},
176 {1215, nullptr, "GetButtonConfigRight"},
177 {1250, nullptr, "IsCustomButtonConfigSupported"},
178 {1251, nullptr, "IsDefaultButtonConfigEmbedded"},
179 {1252, nullptr, "IsDefaultButtonConfigFull"},
180 {1253, nullptr, "IsDefaultButtonConfigLeft"},
181 {1254, nullptr, "IsDefaultButtonConfigRight"},
182 {1255, nullptr, "IsButtonConfigStorageEmbeddedEmpty"},
183 {1256, nullptr, "IsButtonConfigStorageFullEmpty"},
184 {1257, nullptr, "IsButtonConfigStorageLeftEmpty"},
185 {1258, nullptr, "IsButtonConfigStorageRightEmpty"},
186 {1259, nullptr, "GetButtonConfigStorageEmbeddedDeprecated"},
187 {1260, nullptr, "GetButtonConfigStorageFullDeprecated"},
188 {1261, nullptr, "GetButtonConfigStorageLeftDeprecated"},
189 {1262, nullptr, "GetButtonConfigStorageRightDeprecated"},
190 {1263, nullptr, "SetButtonConfigStorageEmbeddedDeprecated"},
191 {1264, nullptr, "SetButtonConfigStorageFullDeprecated"},
192 {1265, nullptr, "SetButtonConfigStorageLeftDeprecated"},
193 {1266, nullptr, "SetButtonConfigStorageRightDeprecated"},
194 {1267, nullptr, "DeleteButtonConfigStorageEmbedded"},
195 {1268, nullptr, "DeleteButtonConfigStorageFull"},
196 {1269, nullptr, "DeleteButtonConfigStorageLeft"},
197 {1270, nullptr, "DeleteButtonConfigStorageRight"},
198 {1271, nullptr, "IsUsingCustomButtonConfig"},
199 {1272, nullptr, "IsAnyCustomButtonConfigEnabled"},
200 {1273, nullptr, "SetAllCustomButtonConfigEnabled"},
201 {1274, nullptr, "SetDefaultButtonConfig"},
202 {1275, nullptr, "SetAllDefaultButtonConfig"},
203 {1276, nullptr, "SetHidButtonConfigEmbedded"},
204 {1277, nullptr, "SetHidButtonConfigFull"},
205 {1278, nullptr, "SetHidButtonConfigLeft"},
206 {1279, nullptr, "SetHidButtonConfigRight"},
207 {1280, nullptr, "GetHidButtonConfigEmbedded"},
208 {1281, nullptr, "GetHidButtonConfigFull"},
209 {1282, nullptr, "GetHidButtonConfigLeft"},
210 {1283, nullptr, "GetHidButtonConfigRight"},
211 {1284, nullptr, "GetButtonConfigStorageEmbedded"},
212 {1285, nullptr, "GetButtonConfigStorageFull"},
213 {1286, nullptr, "GetButtonConfigStorageLeft"},
214 {1287, nullptr, "GetButtonConfigStorageRight"},
215 {1288, nullptr, "SetButtonConfigStorageEmbedded"},
216 {1289, nullptr, "SetButtonConfigStorageFull"},
217 {1290, nullptr, "DeleteButtonConfigStorageRight"},
218 {1291, nullptr, "DeleteButtonConfigStorageRight"},
219 };
220 // clang-format on
221
222 RegisterHandlers(functions);
223
224 joy_detach_event = service_context.CreateEvent("IHidSystemServer::JoyDetachEvent");
225 acquire_device_registered_event =
226 service_context.CreateEvent("IHidSystemServer::AcquireDeviceRegisteredEvent");
227 acquire_connection_trigger_timeout_event =
228 service_context.CreateEvent("IHidSystemServer::AcquireConnectionTriggerTimeoutEvent");
229 unique_pad_connection_event =
230 service_context.CreateEvent("IHidSystemServer::AcquireUniquePadConnectionEventHandle");
231}
232
233IHidSystemServer::~IHidSystemServer() {
234 service_context.CloseEvent(joy_detach_event);
235 service_context.CloseEvent(acquire_device_registered_event);
236 service_context.CloseEvent(acquire_connection_trigger_timeout_event);
237 service_context.CloseEvent(unique_pad_connection_event);
238};
239
240void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) {
241 LOG_WARNING(Service_HID, "called");
242
243 GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy();
244
245 IPC::ResponseBuilder rb{ctx, 2};
246 rb.Push(ResultSuccess);
247}
248
249void IHidSystemServer::EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx) {
250 LOG_WARNING(Service_HID, "(STUBBED) called");
251
252 IPC::ResponseBuilder rb{ctx, 2};
253 rb.Push(ResultSuccess);
254}
255
256void IHidSystemServer::DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx) {
257 LOG_WARNING(Service_HID, "(STUBBED) called");
258
259 IPC::ResponseBuilder rb{ctx, 2};
260 rb.Push(ResultSuccess);
261}
262
263void IHidSystemServer::GetLastActiveNpad(HLERequestContext& ctx) {
264 LOG_DEBUG(Service_HID, "(STUBBED) called"); // Spams a lot when controller applet is running
265
266 IPC::ResponseBuilder rb{ctx, 3};
267 rb.Push(ResultSuccess);
268 rb.PushEnum(system.HIDCore().GetLastActiveController());
269}
270
271void IHidSystemServer::ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx) {
272 LOG_WARNING(Service_HID, "called");
273
274 GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy();
275
276 IPC::ResponseBuilder rb{ctx, 2};
277 rb.Push(ResultSuccess);
278}
279
280void IHidSystemServer::GetNpadFullKeyGripColor(HLERequestContext& ctx) {
281 IPC::RequestParser rp{ctx};
282 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
283
284 LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
285 npad_id_type); // Spams a lot when controller applet is running
286
287 Core::HID::NpadColor left_color{};
288 Core::HID::NpadColor right_color{};
289 // TODO: Get colors from Npad
290
291 IPC::ResponseBuilder rb{ctx, 4};
292 rb.Push(ResultSuccess);
293 rb.PushRaw(left_color);
294 rb.PushRaw(right_color);
295}
296
297void IHidSystemServer::GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx) {
298 IPC::RequestParser rp{ctx};
299
300 LOG_INFO(Service_HID, "(STUBBED) called");
301
302 Core::HID::NpadStyleSet supported_styleset =
303 GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw;
304
305 IPC::ResponseBuilder rb{ctx, 3};
306 rb.Push(ResultSuccess);
307 rb.PushEnum(supported_styleset);
308}
309
310void IHidSystemServer::SetSupportedNpadStyleSetAll(HLERequestContext& ctx) {
311 IPC::RequestParser rp{ctx};
312
313 LOG_INFO(Service_HID, "(STUBBED) called");
314
315 Core::HID::NpadStyleSet supported_styleset =
316 GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw;
317
318 IPC::ResponseBuilder rb{ctx, 3};
319 rb.Push(ResultSuccess);
320 rb.PushEnum(supported_styleset);
321}
322
323void IHidSystemServer::GetAppletDetailedUiType(HLERequestContext& ctx) {
324 IPC::RequestParser rp{ctx};
325 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
326
327 LOG_DEBUG(Service_HID, "called, npad_id_type={}",
328 npad_id_type); // Spams a lot when controller applet is running
329
330 const NPad::AppletDetailedUiType detailed_ui_type =
331 GetResourceManager()->GetNpad()->GetAppletDetailedUiType(npad_id_type);
332
333 IPC::ResponseBuilder rb{ctx, 3};
334 rb.Push(ResultSuccess);
335 rb.PushRaw(detailed_ui_type);
336}
337
338void IHidSystemServer::GetNpadInterfaceType(HLERequestContext& ctx) {
339 IPC::RequestParser rp{ctx};
340 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
341
342 LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
343 npad_id_type); // Spams a lot when controller applet is running
344
345 IPC::ResponseBuilder rb{ctx, 3};
346 rb.Push(ResultSuccess);
347 rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth);
348}
349
350void IHidSystemServer::GetNpadLeftRightInterfaceType(HLERequestContext& ctx) {
351 IPC::RequestParser rp{ctx};
352 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
353
354 LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
355 npad_id_type); // Spams a lot when controller applet is running
356
357 IPC::ResponseBuilder rb{ctx, 4};
358 rb.Push(ResultSuccess);
359 rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth);
360 rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth);
361}
362
363void IHidSystemServer::HasBattery(HLERequestContext& ctx) {
364 IPC::RequestParser rp{ctx};
365 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
366
367 LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
368 npad_id_type); // Spams a lot when controller applet is running
369
370 IPC::ResponseBuilder rb{ctx, 3};
371 rb.Push(ResultSuccess);
372 rb.Push(false);
373}
374
375void IHidSystemServer::HasLeftRightBattery(HLERequestContext& ctx) {
376 IPC::RequestParser rp{ctx};
377 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
378
379 LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
380 npad_id_type); // Spams a lot when controller applet is running
381
382 struct LeftRightBattery {
383 bool left;
384 bool right;
385 };
386
387 LeftRightBattery left_right_battery{
388 .left = false,
389 .right = false,
390 };
391
392 IPC::ResponseBuilder rb{ctx, 3};
393 rb.Push(ResultSuccess);
394 rb.PushRaw(left_right_battery);
395}
396
397void IHidSystemServer::GetUniquePadsFromNpad(HLERequestContext& ctx) {
398 IPC::RequestParser rp{ctx};
399 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
400
401 LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
402 npad_id_type); // Spams a lot when controller applet is running
403
404 const std::vector<Core::HID::UniquePadId> unique_pads{};
405
406 if (!unique_pads.empty()) {
407 ctx.WriteBuffer(unique_pads);
408 }
409
410 IPC::ResponseBuilder rb{ctx, 3};
411 rb.Push(ResultSuccess);
412 rb.Push(static_cast<u32>(unique_pads.size()));
413}
414
415void IHidSystemServer::GetIrSensorState(HLERequestContext& ctx) {
416 IPC::RequestParser rp{ctx};
417
418 LOG_WARNING(Service_HID, "(STUBBED) called");
419
420 IPC::ResponseBuilder rb{ctx, 2};
421 rb.Push(ResultSuccess);
422}
423
424void IHidSystemServer::AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx) {
425 LOG_INFO(Service_AM, "(STUBBED) called");
426
427 IPC::ResponseBuilder rb{ctx, 2, 1};
428 rb.Push(ResultSuccess);
429 rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
430}
431
432void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx) {
433 LOG_INFO(Service_HID, "(STUBBED) called");
434
435 IPC::ResponseBuilder rb{ctx, 2, 1};
436 rb.Push(ResultSuccess);
437 rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
438}
439
440void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) {
441 LOG_WARNING(Service_HID, "(STUBBED) called");
442
443 struct RegisterData {
444 std::array<u8, 0x68> data;
445 };
446 static_assert(sizeof(RegisterData) == 0x68, "RegisterData is an invalid size");
447 std::vector<RegisterData> registered_devices{};
448
449 if (!registered_devices.empty()) {
450 ctx.WriteBuffer(registered_devices);
451 }
452
453 IPC::ResponseBuilder rb{ctx, 4};
454 rb.Push(ResultSuccess);
455 rb.Push<u64>(registered_devices.size());
456}
457
458void IHidSystemServer::AcquireUniquePadConnectionEventHandle(HLERequestContext& ctx) {
459 LOG_WARNING(Service_HID, "(STUBBED) called");
460
461 IPC::ResponseBuilder rb{ctx, 2, 1};
462 rb.PushCopyObjects(unique_pad_connection_event->GetReadableEvent());
463 rb.Push(ResultSuccess);
464}
465
466void IHidSystemServer::GetUniquePadIds(HLERequestContext& ctx) {
467 LOG_WARNING(Service_HID, "(STUBBED) called");
468
469 IPC::ResponseBuilder rb{ctx, 4};
470 rb.Push(ResultSuccess);
471 rb.Push<u64>(0);
472}
473
474void IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) {
475 LOG_INFO(Service_AM, "called");
476
477 IPC::ResponseBuilder rb{ctx, 2, 1};
478 rb.Push(ResultSuccess);
479 rb.PushCopyObjects(joy_detach_event->GetReadableEvent());
480}
481
482void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
483 const bool is_enabled = false;
484
485 LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled);
486
487 IPC::ResponseBuilder rb{ctx, 3};
488 rb.Push(ResultSuccess);
489 rb.Push(is_enabled);
490}
491
492void IHidSystemServer::IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx) {
493 const bool button_pressed = false;
494
495 LOG_DEBUG(Service_HID, "(STUBBED) called, is_enabled={}",
496 button_pressed); // Spams a lot when controller applet is open
497
498 IPC::ResponseBuilder rb{ctx, 3};
499 rb.Push(ResultSuccess);
500 rb.Push(button_pressed);
501}
502
503void IHidSystemServer::InitializeFirmwareUpdate(HLERequestContext& ctx) {
504 LOG_WARNING(Service_HID, "(STUBBED) called");
505
506 IPC::ResponseBuilder rb{ctx, 2};
507 rb.Push(ResultSuccess);
508}
509
510void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx) {
511 LOG_WARNING(Service_HID, "(STUBBED) called");
512
513 IPC::ResponseBuilder rb{ctx, 2};
514 rb.Push(ResultSuccess);
515}
516
517void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
518 LOG_WARNING(Service_HID, "(STUBBED) called");
519
520 Core::HID::TouchScreenConfigurationForNx touchscreen_config{
521 .mode = Core::HID::TouchScreenModeForNx::Finger,
522 };
523
524 if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
525 touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
526 touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
527 }
528
529 IPC::ResponseBuilder rb{ctx, 6};
530 rb.Push(ResultSuccess);
531 rb.PushRaw(touchscreen_config);
532}
533
534std::shared_ptr<ResourceManager> IHidSystemServer::GetResourceManager() {
535 resource_manager->Initialize();
536 return resource_manager;
537}
538
539} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_system_server.h b/src/core/hle/service/hid/hid_system_server.h
new file mode 100644
index 000000000..822d5e5b9
--- /dev/null
+++ b/src/core/hle/service/hid/hid_system_server.h
@@ -0,0 +1,63 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/kernel_helpers.h"
7#include "core/hle/service/service.h"
8
9namespace Core {
10class System;
11}
12
13namespace Kernel {
14class KEvent;
15}
16
17namespace Service::HID {
18class ResourceManager;
19
20class IHidSystemServer final : public ServiceFramework<IHidSystemServer> {
21public:
22 explicit IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource);
23 ~IHidSystemServer() override;
24
25private:
26 void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx);
27 void EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx);
28 void DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx);
29 void GetLastActiveNpad(HLERequestContext& ctx);
30 void ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx);
31 void GetNpadFullKeyGripColor(HLERequestContext& ctx);
32 void GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx);
33 void SetSupportedNpadStyleSetAll(HLERequestContext& ctx);
34 void GetAppletDetailedUiType(HLERequestContext& ctx);
35 void GetNpadInterfaceType(HLERequestContext& ctx);
36 void GetNpadLeftRightInterfaceType(HLERequestContext& ctx);
37 void HasBattery(HLERequestContext& ctx);
38 void HasLeftRightBattery(HLERequestContext& ctx);
39 void GetUniquePadsFromNpad(HLERequestContext& ctx);
40 void GetIrSensorState(HLERequestContext& ctx);
41 void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx);
42 void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx);
43 void GetRegisteredDevices(HLERequestContext& ctx);
44 void AcquireUniquePadConnectionEventHandle(HLERequestContext& ctx);
45 void GetUniquePadIds(HLERequestContext& ctx);
46 void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx);
47 void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
48 void IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx);
49 void InitializeFirmwareUpdate(HLERequestContext& ctx);
50 void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx);
51 void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
52
53 std::shared_ptr<ResourceManager> GetResourceManager();
54
55 Kernel::KEvent* acquire_connection_trigger_timeout_event;
56 Kernel::KEvent* acquire_device_registered_event;
57 Kernel::KEvent* joy_detach_event;
58 Kernel::KEvent* unique_pad_connection_event;
59 KernelHelpers::ServiceContext service_context;
60 std::shared_ptr<ResourceManager> resource_manager;
61};
62
63} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_util.h b/src/core/hle/service/hid/hid_util.h
new file mode 100644
index 000000000..b87cc10e3
--- /dev/null
+++ b/src/core/hle/service/hid/hid_util.h
@@ -0,0 +1,146 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hid/hid_types.h"
7#include "core/hle/service/hid/errors.h"
8
9namespace Service::HID {
10
11constexpr bool IsNpadIdValid(const Core::HID::NpadIdType npad_id) {
12 switch (npad_id) {
13 case Core::HID::NpadIdType::Player1:
14 case Core::HID::NpadIdType::Player2:
15 case Core::HID::NpadIdType::Player3:
16 case Core::HID::NpadIdType::Player4:
17 case Core::HID::NpadIdType::Player5:
18 case Core::HID::NpadIdType::Player6:
19 case Core::HID::NpadIdType::Player7:
20 case Core::HID::NpadIdType::Player8:
21 case Core::HID::NpadIdType::Other:
22 case Core::HID::NpadIdType::Handheld:
23 return true;
24 default:
25 return false;
26 }
27}
28
29constexpr Result IsSixaxisHandleValid(const Core::HID::SixAxisSensorHandle& handle) {
30 const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(handle.npad_id));
31 const bool device_index = handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
32
33 if (!npad_id) {
34 return InvalidNpadId;
35 }
36 if (!device_index) {
37 return NpadDeviceIndexOutOfRange;
38 }
39
40 return ResultSuccess;
41}
42
43constexpr Result IsVibrationHandleValid(const Core::HID::VibrationDeviceHandle& handle) {
44 switch (handle.npad_type) {
45 case Core::HID::NpadStyleIndex::ProController:
46 case Core::HID::NpadStyleIndex::Handheld:
47 case Core::HID::NpadStyleIndex::JoyconDual:
48 case Core::HID::NpadStyleIndex::JoyconLeft:
49 case Core::HID::NpadStyleIndex::JoyconRight:
50 case Core::HID::NpadStyleIndex::GameCube:
51 case Core::HID::NpadStyleIndex::N64:
52 case Core::HID::NpadStyleIndex::SystemExt:
53 case Core::HID::NpadStyleIndex::System:
54 // These support vibration
55 break;
56 default:
57 return VibrationInvalidStyleIndex;
58 }
59
60 if (!IsNpadIdValid(static_cast<Core::HID::NpadIdType>(handle.npad_id))) {
61 return VibrationInvalidNpadId;
62 }
63
64 if (handle.device_index >= Core::HID::DeviceIndex::MaxDeviceIndex) {
65 return VibrationDeviceIndexOutOfRange;
66 }
67
68 return ResultSuccess;
69}
70
71/// Converts a Core::HID::NpadIdType to an array index.
72constexpr size_t NpadIdTypeToIndex(Core::HID::NpadIdType npad_id_type) {
73 switch (npad_id_type) {
74 case Core::HID::NpadIdType::Player1:
75 return 0;
76 case Core::HID::NpadIdType::Player2:
77 return 1;
78 case Core::HID::NpadIdType::Player3:
79 return 2;
80 case Core::HID::NpadIdType::Player4:
81 return 3;
82 case Core::HID::NpadIdType::Player5:
83 return 4;
84 case Core::HID::NpadIdType::Player6:
85 return 5;
86 case Core::HID::NpadIdType::Player7:
87 return 6;
88 case Core::HID::NpadIdType::Player8:
89 return 7;
90 case Core::HID::NpadIdType::Handheld:
91 return 8;
92 case Core::HID::NpadIdType::Other:
93 return 9;
94 default:
95 return 8;
96 }
97}
98
99/// Converts an array index to a Core::HID::NpadIdType
100constexpr Core::HID::NpadIdType IndexToNpadIdType(size_t index) {
101 switch (index) {
102 case 0:
103 return Core::HID::NpadIdType::Player1;
104 case 1:
105 return Core::HID::NpadIdType::Player2;
106 case 2:
107 return Core::HID::NpadIdType::Player3;
108 case 3:
109 return Core::HID::NpadIdType::Player4;
110 case 4:
111 return Core::HID::NpadIdType::Player5;
112 case 5:
113 return Core::HID::NpadIdType::Player6;
114 case 6:
115 return Core::HID::NpadIdType::Player7;
116 case 7:
117 return Core::HID::NpadIdType::Player8;
118 case 8:
119 return Core::HID::NpadIdType::Handheld;
120 case 9:
121 return Core::HID::NpadIdType::Other;
122 default:
123 return Core::HID::NpadIdType::Invalid;
124 }
125}
126
127constexpr Core::HID::NpadStyleSet GetStylesetByIndex(std::size_t index) {
128 switch (index) {
129 case 0:
130 return Core::HID::NpadStyleSet::Fullkey;
131 case 1:
132 return Core::HID::NpadStyleSet::Handheld;
133 case 2:
134 return Core::HID::NpadStyleSet::JoyDual;
135 case 3:
136 return Core::HID::NpadStyleSet::JoyLeft;
137 case 4:
138 return Core::HID::NpadStyleSet::JoyRight;
139 case 5:
140 return Core::HID::NpadStyleSet::Palma;
141 default:
142 return Core::HID::NpadStyleSet::None;
143 }
144}
145
146} // namespace Service::HID
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index 221c33b86..39b9a4474 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -12,6 +12,7 @@
12#include "core/hle/kernel/k_transfer_memory.h" 12#include "core/hle/kernel/k_transfer_memory.h"
13#include "core/hle/kernel/kernel.h" 13#include "core/hle/kernel/kernel.h"
14#include "core/hle/service/hid/errors.h" 14#include "core/hle/service/hid/errors.h"
15#include "core/hle/service/hid/hid_util.h"
15#include "core/hle/service/hid/irs.h" 16#include "core/hle/service/hid/irs.h"
16#include "core/hle/service/hid/irsensor/clustering_processor.h" 17#include "core/hle/service/hid/irsensor/clustering_processor.h"
17#include "core/hle/service/hid/irsensor/image_transfer_processor.h" 18#include "core/hle/service/hid/irsensor/image_transfer_processor.h"
@@ -138,7 +139,7 @@ void IRS::RunMomentProcessor(HLERequestContext& ctx) {
138 139
139 if (result.IsSuccess()) { 140 if (result.IsSuccess()) {
140 auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); 141 auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
141 MakeProcessor<MomentProcessor>(parameters.camera_handle, device); 142 MakeProcessorWithCoreContext<MomentProcessor>(parameters.camera_handle, device);
142 auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); 143 auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle);
143 image_transfer_processor.SetConfig(parameters.processor_config); 144 image_transfer_processor.SetConfig(parameters.processor_config);
144 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, 145 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
@@ -320,7 +321,7 @@ void IRS::GetNpadIrCameraHandle(HLERequestContext& ctx) {
320 } 321 }
321 322
322 Core::IrSensor::IrCameraHandle camera_handle{ 323 Core::IrSensor::IrCameraHandle camera_handle{
323 .npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)), 324 .npad_id = static_cast<u8>(HID::NpadIdTypeToIndex(npad_id)),
324 .npad_type = Core::HID::NpadStyleIndex::None, 325 .npad_type = Core::HID::NpadStyleIndex::None,
325 }; 326 };
326 327
@@ -545,7 +546,7 @@ void IRS::ActivateIrsensorWithFunctionLevel(HLERequestContext& ctx) {
545 546
546Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const { 547Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const {
547 if (camera_handle.npad_id > 548 if (camera_handle.npad_id >
548 static_cast<u8>(NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) { 549 static_cast<u8>(HID::NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) {
549 return InvalidIrCameraHandle; 550 return InvalidIrCameraHandle;
550 } 551 }
551 if (camera_handle.npad_type != Core::HID::NpadStyleIndex::None) { 552 if (camera_handle.npad_type != Core::HID::NpadStyleIndex::None) {
diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h
index a8fa19025..c8e6dab17 100644
--- a/src/core/hle/service/hid/irs.h
+++ b/src/core/hle/service/hid/irs.h
@@ -3,15 +3,12 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/core.h"
6#include "core/hid/hid_types.h" 7#include "core/hid/hid_types.h"
7#include "core/hid/irs_types.h" 8#include "core/hid/irs_types.h"
8#include "core/hle/service/hid/irsensor/processor_base.h" 9#include "core/hle/service/hid/irsensor/processor_base.h"
9#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
10 11
11namespace Core {
12class System;
13}
14
15namespace Core::HID { 12namespace Core::HID {
16class EmulatedController; 13class EmulatedController;
17} // namespace Core::HID 14} // namespace Core::HID
diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.cpp b/src/core/hle/service/hid/irsensor/clustering_processor.cpp
index e2f4ae876..c559eb0d5 100644
--- a/src/core/hle/service/hid/irsensor/clustering_processor.cpp
+++ b/src/core/hle/service/hid/irsensor/clustering_processor.cpp
@@ -3,16 +3,18 @@
3 3
4#include <queue> 4#include <queue>
5 5
6#include "core/core.h"
7#include "core/core_timing.h"
6#include "core/hid/emulated_controller.h" 8#include "core/hid/emulated_controller.h"
7#include "core/hid/hid_core.h" 9#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/irsensor/clustering_processor.h" 10#include "core/hle/service/hid/irsensor/clustering_processor.h"
9 11
10namespace Service::IRS { 12namespace Service::IRS {
11ClusteringProcessor::ClusteringProcessor(Core::HID::HIDCore& hid_core_, 13ClusteringProcessor::ClusteringProcessor(Core::System& system_,
12 Core::IrSensor::DeviceFormat& device_format, 14 Core::IrSensor::DeviceFormat& device_format,
13 std::size_t npad_index) 15 std::size_t npad_index)
14 : device{device_format} { 16 : device{device_format}, system{system_} {
15 npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index); 17 npad_device = system.HIDCore().GetEmulatedControllerByIndex(npad_index);
16 18
17 device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor; 19 device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor;
18 device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; 20 device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
@@ -48,7 +50,7 @@ void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType ty
48 } 50 }
49 51
50 next_state = {}; 52 next_state = {};
51 const auto camera_data = npad_device->GetCamera(); 53 const auto& camera_data = npad_device->GetCamera();
52 auto filtered_image = camera_data.data; 54 auto filtered_image = camera_data.data;
53 55
54 RemoveLowIntensityData(filtered_image); 56 RemoveLowIntensityData(filtered_image);
@@ -83,7 +85,7 @@ void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType ty
83 } 85 }
84 86
85 next_state.sampling_number = camera_data.sample; 87 next_state.sampling_number = camera_data.sample;
86 next_state.timestamp = next_state.timestamp + 131; 88 next_state.timestamp = system.CoreTiming().GetGlobalTimeNs().count();
87 next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; 89 next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
88 shared_memory->clustering_lifo.WriteNextEntry(next_state); 90 shared_memory->clustering_lifo.WriteNextEntry(next_state);
89 91
@@ -202,14 +204,14 @@ ClusteringProcessor::ClusteringData ClusteringProcessor::MergeCluster(
202} 204}
203 205
204u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const { 206u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const {
205 if ((y * width) + x > data.size()) { 207 if ((y * width) + x >= data.size()) {
206 return 0; 208 return 0;
207 } 209 }
208 return data[(y * width) + x]; 210 return data[(y * width) + x];
209} 211}
210 212
211void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) { 213void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) {
212 if ((y * width) + x > data.size()) { 214 if ((y * width) + x >= data.size()) {
213 return; 215 return;
214 } 216 }
215 data[(y * width) + x] = value; 217 data[(y * width) + x] = value;
diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.h b/src/core/hle/service/hid/irsensor/clustering_processor.h
index dc01a8ea7..83f34734a 100644
--- a/src/core/hle/service/hid/irsensor/clustering_processor.h
+++ b/src/core/hle/service/hid/irsensor/clustering_processor.h
@@ -8,6 +8,10 @@
8#include "core/hle/service/hid/irs_ring_lifo.h" 8#include "core/hle/service/hid/irs_ring_lifo.h"
9#include "core/hle/service/hid/irsensor/processor_base.h" 9#include "core/hle/service/hid/irsensor/processor_base.h"
10 10
11namespace Core {
12class System;
13}
14
11namespace Core::HID { 15namespace Core::HID {
12class EmulatedController; 16class EmulatedController;
13} // namespace Core::HID 17} // namespace Core::HID
@@ -15,8 +19,7 @@ class EmulatedController;
15namespace Service::IRS { 19namespace Service::IRS {
16class ClusteringProcessor final : public ProcessorBase { 20class ClusteringProcessor final : public ProcessorBase {
17public: 21public:
18 explicit ClusteringProcessor(Core::HID::HIDCore& hid_core_, 22 explicit ClusteringProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format,
19 Core::IrSensor::DeviceFormat& device_format,
20 std::size_t npad_index); 23 std::size_t npad_index);
21 ~ClusteringProcessor() override; 24 ~ClusteringProcessor() override;
22 25
@@ -106,5 +109,7 @@ private:
106 Core::IrSensor::DeviceFormat& device; 109 Core::IrSensor::DeviceFormat& device;
107 Core::HID::EmulatedController* npad_device; 110 Core::HID::EmulatedController* npad_device;
108 int callback_key{}; 111 int callback_key{};
112
113 Core::System& system;
109}; 114};
110} // namespace Service::IRS 115} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp
index 803a6277c..22067a591 100644
--- a/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp
+++ b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp
@@ -49,7 +49,7 @@ void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType
49 return; 49 return;
50 } 50 }
51 51
52 const auto camera_data = npad_device->GetCamera(); 52 const auto& camera_data = npad_device->GetCamera();
53 53
54 // This indicates how much ambient light is present 54 // This indicates how much ambient light is present
55 processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; 55 processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
diff --git a/src/core/hle/service/hid/irsensor/moment_processor.cpp b/src/core/hle/service/hid/irsensor/moment_processor.cpp
index dbaca420a..cf045bda7 100644
--- a/src/core/hle/service/hid/irsensor/moment_processor.cpp
+++ b/src/core/hle/service/hid/irsensor/moment_processor.cpp
@@ -1,24 +1,137 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hid/emulated_controller.h"
7#include "core/hid/hid_core.h"
4#include "core/hle/service/hid/irsensor/moment_processor.h" 8#include "core/hle/service/hid/irsensor/moment_processor.h"
5 9
6namespace Service::IRS { 10namespace Service::IRS {
7MomentProcessor::MomentProcessor(Core::IrSensor::DeviceFormat& device_format) 11static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size40x30;
8 : device(device_format) { 12static constexpr std::size_t ImageWidth = 40;
13static constexpr std::size_t ImageHeight = 30;
14
15MomentProcessor::MomentProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format,
16 std::size_t npad_index)
17 : device(device_format), system{system_} {
18 npad_device = system.HIDCore().GetEmulatedControllerByIndex(npad_index);
19
9 device.mode = Core::IrSensor::IrSensorMode::MomentProcessor; 20 device.mode = Core::IrSensor::IrSensorMode::MomentProcessor;
10 device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; 21 device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
11 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; 22 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
23
24 shared_memory = std::construct_at(
25 reinterpret_cast<MomentSharedMemory*>(&device_format.state.processor_raw_data));
26
27 Core::HID::ControllerUpdateCallback engine_callback{
28 .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
29 .is_npad_service = true,
30 };
31 callback_key = npad_device->SetCallback(engine_callback);
12} 32}
13 33
14MomentProcessor::~MomentProcessor() = default; 34MomentProcessor::~MomentProcessor() {
35 npad_device->DeleteCallback(callback_key);
36};
15 37
16void MomentProcessor::StartProcessor() {} 38void MomentProcessor::StartProcessor() {
39 device.camera_status = Core::IrSensor::IrCameraStatus::Available;
40 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
41}
17 42
18void MomentProcessor::SuspendProcessor() {} 43void MomentProcessor::SuspendProcessor() {}
19 44
20void MomentProcessor::StopProcessor() {} 45void MomentProcessor::StopProcessor() {}
21 46
47void MomentProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
48 if (type != Core::HID::ControllerTriggerType::IrSensor) {
49 return;
50 }
51
52 next_state = {};
53 const auto& camera_data = npad_device->GetCamera();
54
55 const auto window_width = static_cast<std::size_t>(current_config.window_of_interest.width);
56 const auto window_height = static_cast<std::size_t>(current_config.window_of_interest.height);
57 const auto window_start_x = static_cast<std::size_t>(current_config.window_of_interest.x);
58 const auto window_start_y = static_cast<std::size_t>(current_config.window_of_interest.y);
59
60 const std::size_t block_width = window_width / Columns;
61 const std::size_t block_height = window_height / Rows;
62
63 for (std::size_t row = 0; row < Rows; row++) {
64 for (std::size_t column = 0; column < Columns; column++) {
65 const size_t x_pos = (column * block_width) + window_start_x;
66 const size_t y_pos = (row * block_height) + window_start_y;
67 auto& statistic = next_state.statistic[column + (row * Columns)];
68 statistic = GetStatistic(camera_data.data, x_pos, y_pos, block_width, block_height);
69 }
70 }
71
72 next_state.sampling_number = camera_data.sample;
73 next_state.timestamp = system.CoreTiming().GetGlobalTimeNs().count();
74 next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
75 shared_memory->moment_lifo.WriteNextEntry(next_state);
76
77 if (!IsProcessorActive()) {
78 StartProcessor();
79 }
80}
81
82u8 MomentProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const {
83 if ((y * ImageWidth) + x >= data.size()) {
84 return 0;
85 }
86 return data[(y * ImageWidth) + x];
87}
88
89MomentProcessor::MomentStatistic MomentProcessor::GetStatistic(const std::vector<u8>& data,
90 std::size_t start_x,
91 std::size_t start_y,
92 std::size_t width,
93 std::size_t height) const {
94 // The actual implementation is always 320x240
95 static constexpr std::size_t RealWidth = 320;
96 static constexpr std::size_t RealHeight = 240;
97 static constexpr std::size_t Threshold = 30;
98 MomentStatistic statistic{};
99 std::size_t active_points{};
100
101 // Sum all data points on the block that meet with the threshold
102 for (std::size_t y = 0; y < width; y++) {
103 for (std::size_t x = 0; x < height; x++) {
104 const size_t x_pos = x + start_x;
105 const size_t y_pos = y + start_y;
106 const auto pixel =
107 GetPixel(data, x_pos * ImageWidth / RealWidth, y_pos * ImageHeight / RealHeight);
108
109 if (pixel < Threshold) {
110 continue;
111 }
112
113 statistic.average_intensity += pixel;
114
115 statistic.centroid.x += static_cast<float>(x_pos);
116 statistic.centroid.y += static_cast<float>(y_pos);
117
118 active_points++;
119 }
120 }
121
122 // Return an empty field if no points were available
123 if (active_points == 0) {
124 return {};
125 }
126
127 // Finally calculate the actual centroid and average intensity
128 statistic.centroid.x /= static_cast<float>(active_points);
129 statistic.centroid.y /= static_cast<float>(active_points);
130 statistic.average_intensity /= static_cast<f32>(width * height);
131
132 return statistic;
133}
134
22void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) { 135void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) {
23 current_config.camera_config.exposure_time = config.camera_config.exposure_time; 136 current_config.camera_config.exposure_time = config.camera_config.exposure_time;
24 current_config.camera_config.gain = config.camera_config.gain; 137 current_config.camera_config.gain = config.camera_config.gain;
@@ -29,6 +142,8 @@ void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig conf
29 current_config.preprocess = 142 current_config.preprocess =
30 static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess); 143 static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess);
31 current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold; 144 current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold;
145
146 npad_device->SetCameraFormat(format);
32} 147}
33 148
34} // namespace Service::IRS 149} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/moment_processor.h b/src/core/hle/service/hid/irsensor/moment_processor.h
index d4bd22e0f..398cfbdc1 100644
--- a/src/core/hle/service/hid/irsensor/moment_processor.h
+++ b/src/core/hle/service/hid/irsensor/moment_processor.h
@@ -6,12 +6,22 @@
6#include "common/bit_field.h" 6#include "common/bit_field.h"
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/hid/irs_types.h" 8#include "core/hid/irs_types.h"
9#include "core/hle/service/hid/irs_ring_lifo.h"
9#include "core/hle/service/hid/irsensor/processor_base.h" 10#include "core/hle/service/hid/irsensor/processor_base.h"
10 11
12namespace Core {
13class System;
14}
15
16namespace Core::HID {
17class EmulatedController;
18} // namespace Core::HID
19
11namespace Service::IRS { 20namespace Service::IRS {
12class MomentProcessor final : public ProcessorBase { 21class MomentProcessor final : public ProcessorBase {
13public: 22public:
14 explicit MomentProcessor(Core::IrSensor::DeviceFormat& device_format); 23 explicit MomentProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format,
24 std::size_t npad_index);
15 ~MomentProcessor() override; 25 ~MomentProcessor() override;
16 26
17 // Called when the processor is initialized 27 // Called when the processor is initialized
@@ -27,6 +37,9 @@ public:
27 void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config); 37 void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config);
28 38
29private: 39private:
40 static constexpr std::size_t Columns = 8;
41 static constexpr std::size_t Rows = 6;
42
30 // This is nn::irsensor::MomentProcessorConfig 43 // This is nn::irsensor::MomentProcessorConfig
31 struct MomentProcessorConfig { 44 struct MomentProcessorConfig {
32 Core::IrSensor::CameraConfig camera_config; 45 Core::IrSensor::CameraConfig camera_config;
@@ -50,12 +63,29 @@ private:
50 u64 timestamp; 63 u64 timestamp;
51 Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; 64 Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
52 INSERT_PADDING_BYTES(4); 65 INSERT_PADDING_BYTES(4);
53 std::array<MomentStatistic, 0x30> stadistic; 66 std::array<MomentStatistic, Columns * Rows> statistic;
54 }; 67 };
55 static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size"); 68 static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size");
56 69
70 struct MomentSharedMemory {
71 Service::IRS::Lifo<MomentProcessorState, 6> moment_lifo;
72 };
73 static_assert(sizeof(MomentSharedMemory) == 0xE20, "MomentSharedMemory is an invalid size");
74
75 void OnControllerUpdate(Core::HID::ControllerTriggerType type);
76 u8 GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const;
77 MomentStatistic GetStatistic(const std::vector<u8>& data, std::size_t start_x,
78 std::size_t start_y, std::size_t width, std::size_t height) const;
79
80 MomentSharedMemory* shared_memory = nullptr;
81 MomentProcessorState next_state{};
82
57 MomentProcessorConfig current_config{}; 83 MomentProcessorConfig current_config{};
58 Core::IrSensor::DeviceFormat& device; 84 Core::IrSensor::DeviceFormat& device;
85 Core::HID::EmulatedController* npad_device;
86 int callback_key{};
87
88 Core::System& system;
59}; 89};
60 90
61} // namespace Service::IRS 91} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/resource_manager.cpp b/src/core/hle/service/hid/resource_manager.cpp
new file mode 100644
index 000000000..e76d4eea9
--- /dev/null
+++ b/src/core/hle/service/hid/resource_manager.cpp
@@ -0,0 +1,241 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "common/logging/log.h"
5#include "core/core.h"
6#include "core/core_timing.h"
7#include "core/hid/hid_core.h"
8#include "core/hle/kernel/k_shared_memory.h"
9#include "core/hle/service/hid/resource_manager.h"
10#include "core/hle/service/ipc_helpers.h"
11
12#include "core/hle/service/hid/controllers/console_six_axis.h"
13#include "core/hle/service/hid/controllers/debug_pad.h"
14#include "core/hle/service/hid/controllers/gesture.h"
15#include "core/hle/service/hid/controllers/keyboard.h"
16#include "core/hle/service/hid/controllers/mouse.h"
17#include "core/hle/service/hid/controllers/npad.h"
18#include "core/hle/service/hid/controllers/palma.h"
19#include "core/hle/service/hid/controllers/seven_six_axis.h"
20#include "core/hle/service/hid/controllers/six_axis.h"
21#include "core/hle/service/hid/controllers/stubbed.h"
22#include "core/hle/service/hid/controllers/touchscreen.h"
23#include "core/hle/service/hid/controllers/xpad.h"
24
25namespace Service::HID {
26
27// Updating period for each HID device.
28// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
29// Correct npad_update_ns is 4ms this is overclocked to lower input lag
30constexpr auto npad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz)
31constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz)
32constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
33constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
34
35ResourceManager::ResourceManager(Core::System& system_)
36 : system{system_}, service_context{system_, "hid"} {}
37
38ResourceManager::~ResourceManager() = default;
39
40void ResourceManager::Initialize() {
41 if (is_initialized) {
42 return;
43 }
44
45 u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer();
46 debug_pad = std::make_shared<DebugPad>(system.HIDCore(), shared_memory);
47 mouse = std::make_shared<Mouse>(system.HIDCore(), shared_memory);
48 debug_mouse = std::make_shared<DebugMouse>(system.HIDCore(), shared_memory);
49 keyboard = std::make_shared<Keyboard>(system.HIDCore(), shared_memory);
50 unique_pad = std::make_shared<UniquePad>(system.HIDCore(), shared_memory);
51 npad = std::make_shared<NPad>(system.HIDCore(), shared_memory, service_context);
52 gesture = std::make_shared<Gesture>(system.HIDCore(), shared_memory);
53 touch_screen = std::make_shared<TouchScreen>(system.HIDCore(), shared_memory);
54 xpad = std::make_shared<XPad>(system.HIDCore(), shared_memory);
55
56 palma = std::make_shared<Palma>(system.HIDCore(), shared_memory, service_context);
57
58 home_button = std::make_shared<HomeButton>(system.HIDCore(), shared_memory);
59 sleep_button = std::make_shared<SleepButton>(system.HIDCore(), shared_memory);
60 capture_button = std::make_shared<CaptureButton>(system.HIDCore(), shared_memory);
61
62 six_axis = std::make_shared<SixAxis>(system.HIDCore(), npad);
63 console_six_axis = std::make_shared<ConsoleSixAxis>(system.HIDCore(), shared_memory);
64 seven_six_axis = std::make_shared<SevenSixAxis>(system);
65
66 home_button->SetCommonHeaderOffset(0x4C00);
67 sleep_button->SetCommonHeaderOffset(0x4E00);
68 capture_button->SetCommonHeaderOffset(0x5000);
69 unique_pad->SetCommonHeaderOffset(0x5A00);
70 debug_mouse->SetCommonHeaderOffset(0x3DC00);
71
72 // Homebrew doesn't try to activate some controllers, so we activate them by default
73 npad->Activate();
74 six_axis->Activate();
75 touch_screen->Activate();
76
77 system.HIDCore().ReloadInputDevices();
78 is_initialized = true;
79}
80std::shared_ptr<CaptureButton> ResourceManager::GetCaptureButton() const {
81 return capture_button;
82}
83
84std::shared_ptr<ConsoleSixAxis> ResourceManager::GetConsoleSixAxis() const {
85 return console_six_axis;
86}
87
88std::shared_ptr<DebugMouse> ResourceManager::GetDebugMouse() const {
89 return debug_mouse;
90}
91
92std::shared_ptr<DebugPad> ResourceManager::GetDebugPad() const {
93 return debug_pad;
94}
95
96std::shared_ptr<Gesture> ResourceManager::GetGesture() const {
97 return gesture;
98}
99
100std::shared_ptr<HomeButton> ResourceManager::GetHomeButton() const {
101 return home_button;
102}
103
104std::shared_ptr<Keyboard> ResourceManager::GetKeyboard() const {
105 return keyboard;
106}
107
108std::shared_ptr<Mouse> ResourceManager::GetMouse() const {
109 return mouse;
110}
111
112std::shared_ptr<NPad> ResourceManager::GetNpad() const {
113 return npad;
114}
115
116std::shared_ptr<Palma> ResourceManager::GetPalma() const {
117 return palma;
118}
119
120std::shared_ptr<SevenSixAxis> ResourceManager::GetSevenSixAxis() const {
121 return seven_six_axis;
122}
123
124std::shared_ptr<SixAxis> ResourceManager::GetSixAxis() const {
125 return six_axis;
126}
127
128std::shared_ptr<SleepButton> ResourceManager::GetSleepButton() const {
129 return sleep_button;
130}
131
132std::shared_ptr<TouchScreen> ResourceManager::GetTouchScreen() const {
133 return touch_screen;
134}
135
136std::shared_ptr<UniquePad> ResourceManager::GetUniquePad() const {
137 return unique_pad;
138}
139
140void ResourceManager::UpdateControllers(std::uintptr_t user_data,
141 std::chrono::nanoseconds ns_late) {
142 auto& core_timing = system.CoreTiming();
143 debug_pad->OnUpdate(core_timing);
144 unique_pad->OnUpdate(core_timing);
145 gesture->OnUpdate(core_timing);
146 touch_screen->OnUpdate(core_timing);
147 palma->OnUpdate(core_timing);
148 home_button->OnUpdate(core_timing);
149 sleep_button->OnUpdate(core_timing);
150 capture_button->OnUpdate(core_timing);
151 xpad->OnUpdate(core_timing);
152}
153
154void ResourceManager::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
155 auto& core_timing = system.CoreTiming();
156 npad->OnUpdate(core_timing);
157}
158
159void ResourceManager::UpdateMouseKeyboard(std::uintptr_t user_data,
160 std::chrono::nanoseconds ns_late) {
161 auto& core_timing = system.CoreTiming();
162 mouse->OnUpdate(core_timing);
163 debug_mouse->OnUpdate(core_timing);
164 keyboard->OnUpdate(core_timing);
165}
166
167void ResourceManager::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
168 auto& core_timing = system.CoreTiming();
169 six_axis->OnUpdate(core_timing);
170 seven_six_axis->OnUpdate(core_timing);
171 console_six_axis->OnUpdate(core_timing);
172}
173
174IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource)
175 : ServiceFramework{system_, "IAppletResource"} {
176 static const FunctionInfo functions[] = {
177 {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
178 };
179 RegisterHandlers(functions);
180
181 resource->Initialize();
182
183 // Register update callbacks
184 npad_update_event = Core::Timing::CreateEvent(
185 "HID::UpdatePadCallback",
186 [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
187 -> std::optional<std::chrono::nanoseconds> {
188 const auto guard = LockService();
189 resource->UpdateNpad(user_data, ns_late);
190 return std::nullopt;
191 });
192 default_update_event = Core::Timing::CreateEvent(
193 "HID::UpdateDefaultCallback",
194 [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
195 -> std::optional<std::chrono::nanoseconds> {
196 const auto guard = LockService();
197 resource->UpdateControllers(user_data, ns_late);
198 return std::nullopt;
199 });
200 mouse_keyboard_update_event = Core::Timing::CreateEvent(
201 "HID::UpdateMouseKeyboardCallback",
202 [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
203 -> std::optional<std::chrono::nanoseconds> {
204 const auto guard = LockService();
205 resource->UpdateMouseKeyboard(user_data, ns_late);
206 return std::nullopt;
207 });
208 motion_update_event = Core::Timing::CreateEvent(
209 "HID::UpdateMotionCallback",
210 [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
211 -> std::optional<std::chrono::nanoseconds> {
212 const auto guard = LockService();
213 resource->UpdateMotion(user_data, ns_late);
214 return std::nullopt;
215 });
216
217 system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event);
218 system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns,
219 default_update_event);
220 system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns,
221 mouse_keyboard_update_event);
222 system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns,
223 motion_update_event);
224}
225
226IAppletResource::~IAppletResource() {
227 system.CoreTiming().UnscheduleEvent(npad_update_event, 0);
228 system.CoreTiming().UnscheduleEvent(default_update_event, 0);
229 system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
230 system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
231}
232
233void IAppletResource::GetSharedMemoryHandle(HLERequestContext& ctx) {
234 LOG_DEBUG(Service_HID, "called");
235
236 IPC::ResponseBuilder rb{ctx, 2, 1};
237 rb.Push(ResultSuccess);
238 rb.PushCopyObjects(&system.Kernel().GetHidSharedMem());
239}
240
241} // namespace Service::HID
diff --git a/src/core/hle/service/hid/resource_manager.h b/src/core/hle/service/hid/resource_manager.h
new file mode 100644
index 000000000..2b6a9b5e6
--- /dev/null
+++ b/src/core/hle/service/hid/resource_manager.h
@@ -0,0 +1,111 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/kernel_helpers.h"
7#include "core/hle/service/service.h"
8
9namespace Core::Timing {
10struct EventType;
11}
12
13namespace Service::HID {
14class Controller_Stubbed;
15class ConsoleSixAxis;
16class DebugPad;
17class Gesture;
18class Keyboard;
19class Mouse;
20class NPad;
21class Palma;
22class SevenSixAxis;
23class SixAxis;
24class TouchScreen;
25class XPad;
26
27using CaptureButton = Controller_Stubbed;
28using DebugMouse = Controller_Stubbed;
29using HomeButton = Controller_Stubbed;
30using SleepButton = Controller_Stubbed;
31using UniquePad = Controller_Stubbed;
32
33class ResourceManager {
34
35public:
36 explicit ResourceManager(Core::System& system_);
37 ~ResourceManager();
38
39 void Initialize();
40
41 std::shared_ptr<CaptureButton> GetCaptureButton() const;
42 std::shared_ptr<ConsoleSixAxis> GetConsoleSixAxis() const;
43 std::shared_ptr<DebugMouse> GetDebugMouse() const;
44 std::shared_ptr<DebugPad> GetDebugPad() const;
45 std::shared_ptr<Gesture> GetGesture() const;
46 std::shared_ptr<HomeButton> GetHomeButton() const;
47 std::shared_ptr<Keyboard> GetKeyboard() const;
48 std::shared_ptr<Mouse> GetMouse() const;
49 std::shared_ptr<NPad> GetNpad() const;
50 std::shared_ptr<Palma> GetPalma() const;
51 std::shared_ptr<SevenSixAxis> GetSevenSixAxis() const;
52 std::shared_ptr<SixAxis> GetSixAxis() const;
53 std::shared_ptr<SleepButton> GetSleepButton() const;
54 std::shared_ptr<TouchScreen> GetTouchScreen() const;
55 std::shared_ptr<UniquePad> GetUniquePad() const;
56
57 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
58 void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
59 void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
60 void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
61
62private:
63 bool is_initialized{false};
64
65 std::shared_ptr<CaptureButton> capture_button = nullptr;
66 std::shared_ptr<ConsoleSixAxis> console_six_axis = nullptr;
67 std::shared_ptr<DebugMouse> debug_mouse = nullptr;
68 std::shared_ptr<DebugPad> debug_pad = nullptr;
69 std::shared_ptr<Gesture> gesture = nullptr;
70 std::shared_ptr<HomeButton> home_button = nullptr;
71 std::shared_ptr<Keyboard> keyboard = nullptr;
72 std::shared_ptr<Mouse> mouse = nullptr;
73 std::shared_ptr<NPad> npad = nullptr;
74 std::shared_ptr<Palma> palma = nullptr;
75 std::shared_ptr<SevenSixAxis> seven_six_axis = nullptr;
76 std::shared_ptr<SixAxis> six_axis = nullptr;
77 std::shared_ptr<SleepButton> sleep_button = nullptr;
78 std::shared_ptr<TouchScreen> touch_screen = nullptr;
79 std::shared_ptr<UniquePad> unique_pad = nullptr;
80 std::shared_ptr<XPad> xpad = nullptr;
81
82 // TODO: Create these resources
83 // std::shared_ptr<AudioControl> audio_control = nullptr;
84 // std::shared_ptr<ButtonConfig> button_config = nullptr;
85 // std::shared_ptr<Config> config = nullptr;
86 // std::shared_ptr<Connection> connection = nullptr;
87 // std::shared_ptr<CustomConfig> custom_config = nullptr;
88 // std::shared_ptr<Digitizer> digitizer = nullptr;
89 // std::shared_ptr<Hdls> hdls = nullptr;
90 // std::shared_ptr<PlayReport> play_report = nullptr;
91 // std::shared_ptr<Rail> rail = nullptr;
92
93 Core::System& system;
94 KernelHelpers::ServiceContext service_context;
95};
96
97class IAppletResource final : public ServiceFramework<IAppletResource> {
98public:
99 explicit IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource);
100 ~IAppletResource() override;
101
102private:
103 void GetSharedMemoryHandle(HLERequestContext& ctx);
104
105 std::shared_ptr<Core::Timing::EventType> npad_update_event;
106 std::shared_ptr<Core::Timing::EventType> default_update_event;
107 std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
108 std::shared_ptr<Core::Timing::EventType> motion_update_event;
109};
110
111} // namespace Service::HID
diff --git a/src/core/hle/service/hid/ring_lifo.h b/src/core/hle/service/hid/ring_lifo.h
index 65eb7ea02..0816784e0 100644
--- a/src/core/hle/service/hid/ring_lifo.h
+++ b/src/core/hle/service/hid/ring_lifo.h
@@ -32,15 +32,15 @@ struct Lifo {
32 } 32 }
33 33
34 std::size_t GetPreviousEntryIndex() const { 34 std::size_t GetPreviousEntryIndex() const {
35 return static_cast<size_t>((buffer_tail + total_buffer_count - 1) % total_buffer_count); 35 return static_cast<size_t>((buffer_tail + max_buffer_size - 1) % max_buffer_size);
36 } 36 }
37 37
38 std::size_t GetNextEntryIndex() const { 38 std::size_t GetNextEntryIndex() const {
39 return static_cast<size_t>((buffer_tail + 1) % total_buffer_count); 39 return static_cast<size_t>((buffer_tail + 1) % max_buffer_size);
40 } 40 }
41 41
42 void WriteNextEntry(const State& new_state) { 42 void WriteNextEntry(const State& new_state) {
43 if (buffer_count < total_buffer_count - 1) { 43 if (buffer_count < static_cast<s64>(max_buffer_size) - 1) {
44 buffer_count++; 44 buffer_count++;
45 } 45 }
46 buffer_tail = GetNextEntryIndex(); 46 buffer_tail = GetNextEntryIndex();
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index 7927f8264..961f89a14 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -115,12 +115,20 @@ public:
115 {400, nullptr, "InitializeSystem"}, 115 {400, nullptr, "InitializeSystem"},
116 {401, nullptr, "FinalizeSystem"}, 116 {401, nullptr, "FinalizeSystem"},
117 {402, nullptr, "SetOperationMode"}, 117 {402, nullptr, "SetOperationMode"},
118 {403, nullptr, "InitializeSystem2"}, 118 {403, &ISystemLocalCommunicationService::InitializeSystem2, "InitializeSystem2"},
119 }; 119 };
120 // clang-format on 120 // clang-format on
121 121
122 RegisterHandlers(functions); 122 RegisterHandlers(functions);
123 } 123 }
124
125private:
126 void InitializeSystem2(HLERequestContext& ctx) {
127 LOG_WARNING(Service_LDN, "(STUBBED) called");
128
129 IPC::ResponseBuilder rb{ctx, 2};
130 rb.Push(ResultSuccess);
131 }
124}; 132};
125 133
126class IUserLocalCommunicationService final 134class IUserLocalCommunicationService final
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index c73035c77..97b6a9385 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -286,9 +286,14 @@ public:
286 rb.Push(ResultSuccess); 286 rb.Push(ResultSuccess);
287 } 287 }
288 288
289 bool ValidateRegionForMap(Kernel::KPageTable& page_table, VAddr start, std::size_t size) const { 289 bool ValidateRegionForMap(Kernel::KProcessPageTable& page_table, VAddr start,
290 std::size_t size) const {
290 const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize}; 291 const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize};
291 const auto start_info{page_table.QueryInfo(start - 1)}; 292
293 Kernel::KMemoryInfo start_info;
294 Kernel::Svc::PageInfo page_info;
295 R_ASSERT(
296 page_table.QueryInfo(std::addressof(start_info), std::addressof(page_info), start - 1));
292 297
293 if (start_info.GetState() != Kernel::KMemoryState::Free) { 298 if (start_info.GetState() != Kernel::KMemoryState::Free) {
294 return {}; 299 return {};
@@ -298,7 +303,9 @@ public:
298 return {}; 303 return {};
299 } 304 }
300 305
301 const auto end_info{page_table.QueryInfo(start + size)}; 306 Kernel::KMemoryInfo end_info;
307 R_ASSERT(page_table.QueryInfo(std::addressof(end_info), std::addressof(page_info),
308 start + size));
302 309
303 if (end_info.GetState() != Kernel::KMemoryState::Free) { 310 if (end_info.GetState() != Kernel::KMemoryState::Free) {
304 return {}; 311 return {};
@@ -307,7 +314,7 @@ public:
307 return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize()); 314 return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize());
308 } 315 }
309 316
310 Result GetAvailableMapRegion(Kernel::KPageTable& page_table, u64 size, VAddr& out_addr) { 317 Result GetAvailableMapRegion(Kernel::KProcessPageTable& page_table, u64 size, VAddr& out_addr) {
311 size = Common::AlignUp(size, Kernel::PageSize); 318 size = Common::AlignUp(size, Kernel::PageSize);
312 size += page_table.GetNumGuardPages() * Kernel::PageSize * 4; 319 size += page_table.GetNumGuardPages() * Kernel::PageSize * 4;
313 320
@@ -391,12 +398,8 @@ public:
391 398
392 if (bss_size) { 399 if (bss_size) {
393 auto block_guard = detail::ScopeExit([&] { 400 auto block_guard = detail::ScopeExit([&] {
394 page_table.UnmapCodeMemory( 401 page_table.UnmapCodeMemory(addr + nro_size, bss_addr, bss_size);
395 addr + nro_size, bss_addr, bss_size, 402 page_table.UnmapCodeMemory(addr, nro_addr, nro_size);
396 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
397 page_table.UnmapCodeMemory(
398 addr, nro_addr, nro_size,
399 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
400 }); 403 });
401 404
402 const Result result{page_table.MapCodeMemory(addr + nro_size, bss_addr, bss_size)}; 405 const Result result{page_table.MapCodeMemory(addr + nro_size, bss_addr, bss_size)};
@@ -578,21 +581,17 @@ public:
578 auto& page_table{system.ApplicationProcess()->GetPageTable()}; 581 auto& page_table{system.ApplicationProcess()->GetPageTable()};
579 582
580 if (info.bss_size != 0) { 583 if (info.bss_size != 0) {
581 R_TRY(page_table.UnmapCodeMemory( 584 R_TRY(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size +
582 info.nro_address + info.text_size + info.ro_size + info.data_size, info.bss_address, 585 info.data_size,
583 info.bss_size, Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); 586 info.bss_address, info.bss_size));
584 } 587 }
585 588
586 R_TRY(page_table.UnmapCodeMemory( 589 R_TRY(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size,
587 info.nro_address + info.text_size + info.ro_size, 590 info.src_addr + info.text_size + info.ro_size,
588 info.src_addr + info.text_size + info.ro_size, info.data_size, 591 info.data_size));
589 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); 592 R_TRY(page_table.UnmapCodeMemory(info.nro_address + info.text_size,
590 R_TRY(page_table.UnmapCodeMemory( 593 info.src_addr + info.text_size, info.ro_size));
591 info.nro_address + info.text_size, info.src_addr + info.text_size, info.ro_size, 594 R_TRY(page_table.UnmapCodeMemory(info.nro_address, info.src_addr, info.text_size));
592 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
593 R_TRY(page_table.UnmapCodeMemory(
594 info.nro_address, info.src_addr, info.text_size,
595 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
596 return ResultSuccess; 595 return ResultSuccess;
597 } 596 }
598 597
diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp
index a71d26157..ad534177d 100644
--- a/src/core/hle/service/nfc/common/device_manager.cpp
+++ b/src/core/hle/service/nfc/common/device_manager.cpp
@@ -7,6 +7,7 @@
7#include "core/core.h" 7#include "core/core.h"
8#include "core/hid/hid_types.h" 8#include "core/hid/hid_types.h"
9#include "core/hle/kernel/k_event.h" 9#include "core/hle/kernel/k_event.h"
10#include "core/hle/service/hid/hid_util.h"
10#include "core/hle/service/ipc_helpers.h" 11#include "core/hle/service/ipc_helpers.h"
11#include "core/hle/service/nfc/common/device.h" 12#include "core/hle/service/nfc/common/device.h"
12#include "core/hle/service/nfc/common/device_manager.h" 13#include "core/hle/service/nfc/common/device_manager.h"
@@ -24,7 +25,7 @@ DeviceManager::DeviceManager(Core::System& system_, KernelHelpers::ServiceContex
24 25
25 for (u32 device_index = 0; device_index < devices.size(); device_index++) { 26 for (u32 device_index = 0; device_index < devices.size(); device_index++) {
26 devices[device_index] = 27 devices[device_index] =
27 std::make_shared<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system, 28 std::make_shared<NfcDevice>(HID::IndexToNpadIdType(device_index), system,
28 service_context, availability_change_event); 29 service_context, availability_change_event);
29 } 30 }
30 31
diff --git a/src/core/hle/service/nvdrv/devices/ioctl_serialization.h b/src/core/hle/service/nvdrv/devices/ioctl_serialization.h
new file mode 100644
index 000000000..b12bcd138
--- /dev/null
+++ b/src/core/hle/service/nvdrv/devices/ioctl_serialization.h
@@ -0,0 +1,159 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <span>
7#include <vector>
8
9#include "common/concepts.h"
10#include "core/hle/service/nvdrv/devices/nvdevice.h"
11
12namespace Service::Nvidia::Devices {
13
14struct IoctlOneArgTraits {
15 template <typename T, typename R, typename A, typename... B>
16 static A GetFirstArgImpl(R (T::*)(A, B...));
17};
18
19struct IoctlTwoArgTraits {
20 template <typename T, typename R, typename A, typename B, typename... C>
21 static A GetFirstArgImpl(R (T::*)(A, B, C...));
22
23 template <typename T, typename R, typename A, typename B, typename... C>
24 static B GetSecondArgImpl(R (T::*)(A, B, C...));
25};
26
27struct Null {};
28
29// clang-format off
30
31template <typename FixedArg, typename VarArg, typename InlInVarArg, typename InlOutVarArg, typename F>
32NvResult WrapGeneric(F&& callable, std::span<const u8> input, std::span<const u8> inline_input, std::span<u8> output, std::span<u8> inline_output) {
33 constexpr bool HasFixedArg = !std::is_same_v<FixedArg, Null>;
34 constexpr bool HasVarArg = !std::is_same_v<VarArg, Null>;
35 constexpr bool HasInlInVarArg = !std::is_same_v<InlInVarArg, Null>;
36 constexpr bool HasInlOutVarArg = !std::is_same_v<InlOutVarArg, Null>;
37
38 // Declare the fixed-size input value.
39 FixedArg fixed{};
40 size_t var_offset = 0;
41
42 if constexpr (HasFixedArg) {
43 // Read the fixed-size input value.
44 var_offset = std::min(sizeof(FixedArg), input.size());
45 if (var_offset > 0) {
46 std::memcpy(&fixed, input.data(), var_offset);
47 }
48 }
49
50 // Read the variable-sized inputs.
51 const size_t num_var_args = HasVarArg ? ((input.size() - var_offset) / sizeof(VarArg)) : 0;
52 std::vector<VarArg> var_args(num_var_args);
53 if constexpr (HasVarArg) {
54 if (num_var_args > 0) {
55 std::memcpy(var_args.data(), input.data() + var_offset, num_var_args * sizeof(VarArg));
56 }
57 }
58
59 const size_t num_inl_in_var_args = HasInlInVarArg ? (inline_input.size() / sizeof(InlInVarArg)) : 0;
60 std::vector<InlInVarArg> inl_in_var_args(num_inl_in_var_args);
61 if constexpr (HasInlInVarArg) {
62 if (num_inl_in_var_args > 0) {
63 std::memcpy(inl_in_var_args.data(), inline_input.data(), num_inl_in_var_args * sizeof(InlInVarArg));
64 }
65 }
66
67 // Construct inline output data.
68 const size_t num_inl_out_var_args = HasInlOutVarArg ? (inline_output.size() / sizeof(InlOutVarArg)) : 0;
69 std::vector<InlOutVarArg> inl_out_var_args(num_inl_out_var_args);
70
71 // Perform the call.
72 NvResult result = callable(fixed, var_args, inl_in_var_args, inl_out_var_args);
73
74 // Copy outputs.
75 if constexpr (HasFixedArg) {
76 if (output.size() > 0) {
77 std::memcpy(output.data(), &fixed, std::min(output.size(), sizeof(FixedArg)));
78 }
79 }
80
81 if constexpr (HasVarArg) {
82 if (num_var_args > 0 && output.size() > var_offset) {
83 const size_t max_var_size = output.size() - var_offset;
84 std::memcpy(output.data() + var_offset, var_args.data(), std::min(max_var_size, num_var_args * sizeof(VarArg)));
85 }
86 }
87
88 // Copy inline outputs.
89 if constexpr (HasInlOutVarArg) {
90 if (num_inl_out_var_args > 0) {
91 std::memcpy(inline_output.data(), inl_out_var_args.data(), num_inl_out_var_args * sizeof(InlOutVarArg));
92 }
93 }
94
95 // We're done.
96 return result;
97}
98
99template <typename Self, typename F, typename... Rest>
100NvResult WrapFixed(Self* self, F&& callable, std::span<const u8> input, std::span<u8> output, Rest&&... rest) {
101 using FixedArg = typename std::remove_reference_t<decltype(IoctlOneArgTraits::GetFirstArgImpl(callable))>;
102
103 const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult {
104 return (self->*callable)(fixed, std::forward<Rest>(rest)...);
105 };
106
107 return WrapGeneric<FixedArg, Null, Null, Null>(std::move(Callable), input, {}, output, {});
108}
109
110template <typename Self, typename F, typename... Rest>
111NvResult WrapFixedInlOut(Self* self, F&& callable, std::span<const u8> input, std::span<u8> output, std::span<u8> inline_output, Rest&&... rest) {
112 using FixedArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetFirstArgImpl(callable))>;
113 using InlOutVarArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetSecondArgImpl(callable))>::value_type;
114
115 const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult {
116 return (self->*callable)(fixed, inl_out, std::forward<Rest>(rest)...);
117 };
118
119 return WrapGeneric<FixedArg, Null, Null, InlOutVarArg>(std::move(Callable), input, {}, output, inline_output);
120}
121
122template <typename Self, typename F, typename... Rest>
123NvResult WrapVariable(Self* self, F&& callable, std::span<const u8> input, std::span<u8> output, Rest&&... rest) {
124 using VarArg = typename std::remove_reference_t<decltype(IoctlOneArgTraits::GetFirstArgImpl(callable))>::value_type;
125
126 const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult {
127 return (self->*callable)(var, std::forward<Rest>(rest)...);
128 };
129
130 return WrapGeneric<Null, VarArg, Null, Null>(std::move(Callable), input, {}, output, {});
131}
132
133template <typename Self, typename F, typename... Rest>
134NvResult WrapFixedVariable(Self* self, F&& callable, std::span<const u8> input, std::span<u8> output, Rest&&... rest) {
135 using FixedArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetFirstArgImpl(callable))>;
136 using VarArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetSecondArgImpl(callable))>::value_type;
137
138 const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult {
139 return (self->*callable)(fixed, var, std::forward<Rest>(rest)...);
140 };
141
142 return WrapGeneric<FixedArg, VarArg, Null, Null>(std::move(Callable), input, {}, output, {});
143}
144
145template <typename Self, typename F, typename... Rest>
146NvResult WrapFixedInlIn(Self* self, F&& callable, std::span<const u8> input, std::span<const u8> inline_input, std::span<u8> output, Rest&&... rest) {
147 using FixedArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetFirstArgImpl(callable))>;
148 using InlInVarArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetSecondArgImpl(callable))>::value_type;
149
150 const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult {
151 return (self->*callable)(fixed, inl_in, std::forward<Rest>(rest)...);
152 };
153
154 return WrapGeneric<FixedArg, Null, InlInVarArg, Null>(std::move(Callable), input, inline_input, output, {});
155}
156
157// clang-format on
158
159} // namespace Service::Nvidia::Devices
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 7d7bb8687..6b3639008 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -11,6 +11,7 @@
11#include "core/core.h" 11#include "core/core.h"
12#include "core/hle/service/nvdrv/core/container.h" 12#include "core/hle/service/nvdrv/core/container.h"
13#include "core/hle/service/nvdrv/core/nvmap.h" 13#include "core/hle/service/nvdrv/core/nvmap.h"
14#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
14#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" 15#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
15#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" 16#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
16#include "core/hle/service/nvdrv/nvdrv.h" 17#include "core/hle/service/nvdrv/nvdrv.h"
@@ -33,21 +34,21 @@ NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> i
33 case 'A': 34 case 'A':
34 switch (command.cmd) { 35 switch (command.cmd) {
35 case 0x1: 36 case 0x1:
36 return BindChannel(input, output); 37 return WrapFixed(this, &nvhost_as_gpu::BindChannel, input, output);
37 case 0x2: 38 case 0x2:
38 return AllocateSpace(input, output); 39 return WrapFixed(this, &nvhost_as_gpu::AllocateSpace, input, output);
39 case 0x3: 40 case 0x3:
40 return FreeSpace(input, output); 41 return WrapFixed(this, &nvhost_as_gpu::FreeSpace, input, output);
41 case 0x5: 42 case 0x5:
42 return UnmapBuffer(input, output); 43 return WrapFixed(this, &nvhost_as_gpu::UnmapBuffer, input, output);
43 case 0x6: 44 case 0x6:
44 return MapBufferEx(input, output); 45 return WrapFixed(this, &nvhost_as_gpu::MapBufferEx, input, output);
45 case 0x8: 46 case 0x8:
46 return GetVARegions(input, output); 47 return WrapFixed(this, &nvhost_as_gpu::GetVARegions1, input, output);
47 case 0x9: 48 case 0x9:
48 return AllocAsEx(input, output); 49 return WrapFixed(this, &nvhost_as_gpu::AllocAsEx, input, output);
49 case 0x14: 50 case 0x14:
50 return Remap(input, output); 51 return WrapVariable(this, &nvhost_as_gpu::Remap, input, output);
51 default: 52 default:
52 break; 53 break;
53 } 54 }
@@ -72,7 +73,8 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> i
72 case 'A': 73 case 'A':
73 switch (command.cmd) { 74 switch (command.cmd) {
74 case 0x8: 75 case 0x8:
75 return GetVARegions(input, output, inline_output); 76 return WrapFixedInlOut(this, &nvhost_as_gpu::GetVARegions3, input, output,
77 inline_output);
76 default: 78 default:
77 break; 79 break;
78 } 80 }
@@ -87,10 +89,7 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> i
87void nvhost_as_gpu::OnOpen(DeviceFD fd) {} 89void nvhost_as_gpu::OnOpen(DeviceFD fd) {}
88void nvhost_as_gpu::OnClose(DeviceFD fd) {} 90void nvhost_as_gpu::OnClose(DeviceFD fd) {}
89 91
90NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::span<u8> output) { 92NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
91 IoctlAllocAsEx params{};
92 std::memcpy(&params, input.data(), input.size());
93
94 LOG_DEBUG(Service_NVDRV, "called, big_page_size=0x{:X}", params.big_page_size); 93 LOG_DEBUG(Service_NVDRV, "called, big_page_size=0x{:X}", params.big_page_size);
95 94
96 std::scoped_lock lock(mutex); 95 std::scoped_lock lock(mutex);
@@ -141,10 +140,7 @@ NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::span<u8> outpu
141 return NvResult::Success; 140 return NvResult::Success;
142} 141}
143 142
144NvResult nvhost_as_gpu::AllocateSpace(std::span<const u8> input, std::span<u8> output) { 143NvResult nvhost_as_gpu::AllocateSpace(IoctlAllocSpace& params) {
145 IoctlAllocSpace params{};
146 std::memcpy(&params, input.data(), input.size());
147
148 LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, 144 LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages,
149 params.page_size, params.flags); 145 params.page_size, params.flags);
150 146
@@ -194,7 +190,6 @@ NvResult nvhost_as_gpu::AllocateSpace(std::span<const u8> input, std::span<u8> o
194 .big_pages = params.page_size != VM::YUZU_PAGESIZE, 190 .big_pages = params.page_size != VM::YUZU_PAGESIZE,
195 }; 191 };
196 192
197 std::memcpy(output.data(), &params, output.size());
198 return NvResult::Success; 193 return NvResult::Success;
199} 194}
200 195
@@ -222,10 +217,7 @@ void nvhost_as_gpu::FreeMappingLocked(u64 offset) {
222 mapping_map.erase(offset); 217 mapping_map.erase(offset);
223} 218}
224 219
225NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::span<u8> output) { 220NvResult nvhost_as_gpu::FreeSpace(IoctlFreeSpace& params) {
226 IoctlFreeSpace params{};
227 std::memcpy(&params, input.data(), input.size());
228
229 LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset, 221 LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset,
230 params.pages, params.page_size); 222 params.pages, params.page_size);
231 223
@@ -264,18 +256,11 @@ NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::span<u8> outpu
264 return NvResult::BadValue; 256 return NvResult::BadValue;
265 } 257 }
266 258
267 std::memcpy(output.data(), &params, output.size());
268 return NvResult::Success; 259 return NvResult::Success;
269} 260}
270 261
271NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::span<u8> output) { 262NvResult nvhost_as_gpu::Remap(std::span<IoctlRemapEntry> entries) {
272 const auto num_entries = input.size() / sizeof(IoctlRemapEntry); 263 LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", entries.size());
273
274 LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries);
275
276 std::scoped_lock lock(mutex);
277 entries.resize_destructive(num_entries);
278 std::memcpy(entries.data(), input.data(), input.size());
279 264
280 if (!vm.initialised) { 265 if (!vm.initialised) {
281 return NvResult::BadValue; 266 return NvResult::BadValue;
@@ -317,14 +302,10 @@ NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::span<u8> output) {
317 } 302 }
318 } 303 }
319 304
320 std::memcpy(output.data(), entries.data(), output.size());
321 return NvResult::Success; 305 return NvResult::Success;
322} 306}
323 307
324NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::span<u8> output) { 308NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
325 IoctlMapBufferEx params{};
326 std::memcpy(&params, input.data(), input.size());
327
328 LOG_DEBUG(Service_NVDRV, 309 LOG_DEBUG(Service_NVDRV,
329 "called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}" 310 "called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}"
330 ", offset={}", 311 ", offset={}",
@@ -421,14 +402,10 @@ NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::span<u8> out
421 mapping_map[params.offset] = mapping; 402 mapping_map[params.offset] = mapping;
422 } 403 }
423 404
424 std::memcpy(output.data(), &params, output.size());
425 return NvResult::Success; 405 return NvResult::Success;
426} 406}
427 407
428NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::span<u8> output) { 408NvResult nvhost_as_gpu::UnmapBuffer(IoctlUnmapBuffer& params) {
429 IoctlUnmapBuffer params{};
430 std::memcpy(&params, input.data(), input.size());
431
432 LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); 409 LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
433 410
434 std::scoped_lock lock(mutex); 411 std::scoped_lock lock(mutex);
@@ -464,9 +441,7 @@ NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::span<u8> out
464 return NvResult::Success; 441 return NvResult::Success;
465} 442}
466 443
467NvResult nvhost_as_gpu::BindChannel(std::span<const u8> input, std::span<u8> output) { 444NvResult nvhost_as_gpu::BindChannel(IoctlBindChannel& params) {
468 IoctlBindChannel params{};
469 std::memcpy(&params, input.data(), input.size());
470 LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); 445 LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
471 446
472 auto gpu_channel_device = module.GetDevice<nvhost_gpu>(params.fd); 447 auto gpu_channel_device = module.GetDevice<nvhost_gpu>(params.fd);
@@ -493,10 +468,7 @@ void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) {
493 }; 468 };
494} 469}
495 470
496NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> output) { 471NvResult nvhost_as_gpu::GetVARegions1(IoctlGetVaRegions& params) {
497 IoctlGetVaRegions params{};
498 std::memcpy(&params, input.data(), input.size());
499
500 LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, 472 LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
501 params.buf_size); 473 params.buf_size);
502 474
@@ -508,15 +480,10 @@ NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> ou
508 480
509 GetVARegionsImpl(params); 481 GetVARegionsImpl(params);
510 482
511 std::memcpy(output.data(), &params, output.size());
512 return NvResult::Success; 483 return NvResult::Success;
513} 484}
514 485
515NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> output, 486NvResult nvhost_as_gpu::GetVARegions3(IoctlGetVaRegions& params, std::span<VaRegion> regions) {
516 std::span<u8> inline_output) {
517 IoctlGetVaRegions params{};
518 std::memcpy(&params, input.data(), input.size());
519
520 LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, 487 LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
521 params.buf_size); 488 params.buf_size);
522 489
@@ -528,9 +495,10 @@ NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> ou
528 495
529 GetVARegionsImpl(params); 496 GetVARegionsImpl(params);
530 497
531 std::memcpy(output.data(), &params, output.size()); 498 const size_t num_regions = std::min(params.regions.size(), regions.size());
532 std::memcpy(inline_output.data(), &params.regions[0], sizeof(VaRegion)); 499 for (size_t i = 0; i < num_regions; i++) {
533 std::memcpy(inline_output.data() + sizeof(VaRegion), &params.regions[1], sizeof(VaRegion)); 500 regions[i] = params.regions[i];
501 }
534 502
535 return NvResult::Success; 503 return NvResult::Success;
536} 504}
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 2af3e1260..932997e75 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -139,18 +139,17 @@ private:
139 static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2, 139 static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2,
140 "IoctlGetVaRegions is incorrect size"); 140 "IoctlGetVaRegions is incorrect size");
141 141
142 NvResult AllocAsEx(std::span<const u8> input, std::span<u8> output); 142 NvResult AllocAsEx(IoctlAllocAsEx& params);
143 NvResult AllocateSpace(std::span<const u8> input, std::span<u8> output); 143 NvResult AllocateSpace(IoctlAllocSpace& params);
144 NvResult Remap(std::span<const u8> input, std::span<u8> output); 144 NvResult Remap(std::span<IoctlRemapEntry> params);
145 NvResult MapBufferEx(std::span<const u8> input, std::span<u8> output); 145 NvResult MapBufferEx(IoctlMapBufferEx& params);
146 NvResult UnmapBuffer(std::span<const u8> input, std::span<u8> output); 146 NvResult UnmapBuffer(IoctlUnmapBuffer& params);
147 NvResult FreeSpace(std::span<const u8> input, std::span<u8> output); 147 NvResult FreeSpace(IoctlFreeSpace& params);
148 NvResult BindChannel(std::span<const u8> input, std::span<u8> output); 148 NvResult BindChannel(IoctlBindChannel& params);
149 149
150 void GetVARegionsImpl(IoctlGetVaRegions& params); 150 void GetVARegionsImpl(IoctlGetVaRegions& params);
151 NvResult GetVARegions(std::span<const u8> input, std::span<u8> output); 151 NvResult GetVARegions1(IoctlGetVaRegions& params);
152 NvResult GetVARegions(std::span<const u8> input, std::span<u8> output, 152 NvResult GetVARegions3(IoctlGetVaRegions& params, std::span<VaRegion> regions);
153 std::span<u8> inline_output);
154 153
155 void FreeMappingLocked(u64 offset); 154 void FreeMappingLocked(u64 offset);
156 155
@@ -213,7 +212,6 @@ private:
213 bool initialised{}; 212 bool initialised{};
214 } vm; 213 } vm;
215 std::shared_ptr<Tegra::MemoryManager> gmmu; 214 std::shared_ptr<Tegra::MemoryManager> gmmu;
216 Common::ScratchBuffer<IoctlRemapEntry> entries;
217 215
218 // s32 channel{}; 216 // s32 channel{};
219 // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE}; 217 // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE};
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 4d55554b4..b8dd34e24 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -14,6 +14,7 @@
14#include "core/hle/kernel/k_event.h" 14#include "core/hle/kernel/k_event.h"
15#include "core/hle/service/nvdrv/core/container.h" 15#include "core/hle/service/nvdrv/core/container.h"
16#include "core/hle/service/nvdrv/core/syncpoint_manager.h" 16#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
17#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
17#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" 18#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h"
18#include "video_core/gpu.h" 19#include "video_core/gpu.h"
19#include "video_core/host1x/host1x.h" 20#include "video_core/host1x/host1x.h"
@@ -40,19 +41,19 @@ NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inp
40 case 0x0: 41 case 0x0:
41 switch (command.cmd) { 42 switch (command.cmd) {
42 case 0x1b: 43 case 0x1b:
43 return NvOsGetConfigU32(input, output); 44 return WrapFixed(this, &nvhost_ctrl::NvOsGetConfigU32, input, output);
44 case 0x1c: 45 case 0x1c:
45 return IocCtrlClearEventWait(input, output); 46 return WrapFixed(this, &nvhost_ctrl::IocCtrlClearEventWait, input, output);
46 case 0x1d: 47 case 0x1d:
47 return IocCtrlEventWait(input, output, true); 48 return WrapFixed(this, &nvhost_ctrl::IocCtrlEventWait, input, output, true);
48 case 0x1e: 49 case 0x1e:
49 return IocCtrlEventWait(input, output, false); 50 return WrapFixed(this, &nvhost_ctrl::IocCtrlEventWait, input, output, false);
50 case 0x1f: 51 case 0x1f:
51 return IocCtrlEventRegister(input, output); 52 return WrapFixed(this, &nvhost_ctrl::IocCtrlEventRegister, input, output);
52 case 0x20: 53 case 0x20:
53 return IocCtrlEventUnregister(input, output); 54 return WrapFixed(this, &nvhost_ctrl::IocCtrlEventUnregister, input, output);
54 case 0x21: 55 case 0x21:
55 return IocCtrlEventUnregisterBatch(input, output); 56 return WrapFixed(this, &nvhost_ctrl::IocCtrlEventUnregisterBatch, input, output);
56 } 57 }
57 break; 58 break;
58 default: 59 default:
@@ -79,25 +80,19 @@ void nvhost_ctrl::OnOpen(DeviceFD fd) {}
79 80
80void nvhost_ctrl::OnClose(DeviceFD fd) {} 81void nvhost_ctrl::OnClose(DeviceFD fd) {}
81 82
82NvResult nvhost_ctrl::NvOsGetConfigU32(std::span<const u8> input, std::span<u8> output) { 83NvResult nvhost_ctrl::NvOsGetConfigU32(IocGetConfigParams& params) {
83 IocGetConfigParams params{};
84 std::memcpy(&params, input.data(), sizeof(params));
85 LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(), 84 LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(),
86 params.param_str.data()); 85 params.param_str.data());
87 return NvResult::ConfigVarNotFound; // Returns error on production mode 86 return NvResult::ConfigVarNotFound; // Returns error on production mode
88} 87}
89 88
90NvResult nvhost_ctrl::IocCtrlEventWait(std::span<const u8> input, std::span<u8> output, 89NvResult nvhost_ctrl::IocCtrlEventWait(IocCtrlEventWaitParams& params, bool is_allocation) {
91 bool is_allocation) {
92 IocCtrlEventWaitParams params{};
93 std::memcpy(&params, input.data(), sizeof(params));
94 LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_allocation={}", 90 LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_allocation={}",
95 params.fence.id, params.fence.value, params.timeout, is_allocation); 91 params.fence.id, params.fence.value, params.timeout, is_allocation);
96 92
97 bool must_unmark_fail = !is_allocation; 93 bool must_unmark_fail = !is_allocation;
98 const u32 event_id = params.value.raw; 94 const u32 event_id = params.value.raw;
99 SCOPE_EXIT({ 95 SCOPE_EXIT({
100 std::memcpy(output.data(), &params, sizeof(params));
101 if (must_unmark_fail) { 96 if (must_unmark_fail) {
102 events[event_id].fails = 0; 97 events[event_id].fails = 0;
103 } 98 }
@@ -231,9 +226,7 @@ NvResult nvhost_ctrl::FreeEvent(u32 slot) {
231 return NvResult::Success; 226 return NvResult::Success;
232} 227}
233 228
234NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::span<u8> output) { 229NvResult nvhost_ctrl::IocCtrlEventRegister(IocCtrlEventRegisterParams& params) {
235 IocCtrlEventRegisterParams params{};
236 std::memcpy(&params, input.data(), sizeof(params));
237 const u32 event_id = params.user_event_id; 230 const u32 event_id = params.user_event_id;
238 LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); 231 LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
239 if (event_id >= MaxNvEvents) { 232 if (event_id >= MaxNvEvents) {
@@ -252,9 +245,7 @@ NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::span<
252 return NvResult::Success; 245 return NvResult::Success;
253} 246}
254 247
255NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::span<u8> output) { 248NvResult nvhost_ctrl::IocCtrlEventUnregister(IocCtrlEventUnregisterParams& params) {
256 IocCtrlEventUnregisterParams params{};
257 std::memcpy(&params, input.data(), sizeof(params));
258 const u32 event_id = params.user_event_id & 0x00FF; 249 const u32 event_id = params.user_event_id & 0x00FF;
259 LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); 250 LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
260 251
@@ -262,9 +253,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::spa
262 return FreeEvent(event_id); 253 return FreeEvent(event_id);
263} 254}
264 255
265NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input, std::span<u8> output) { 256NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(IocCtrlEventUnregisterBatchParams& params) {
266 IocCtrlEventUnregisterBatchParams params{};
267 std::memcpy(&params, input.data(), sizeof(params));
268 u64 event_mask = params.user_events; 257 u64 event_mask = params.user_events;
269 LOG_DEBUG(Service_NVDRV, " called, event_mask: {:X}", event_mask); 258 LOG_DEBUG(Service_NVDRV, " called, event_mask: {:X}", event_mask);
270 259
@@ -280,10 +269,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input, std
280 return NvResult::Success; 269 return NvResult::Success;
281} 270}
282 271
283NvResult nvhost_ctrl::IocCtrlClearEventWait(std::span<const u8> input, std::span<u8> output) { 272NvResult nvhost_ctrl::IocCtrlClearEventWait(IocCtrlEventClearParams& params) {
284 IocCtrlEventClearParams params{};
285 std::memcpy(&params, input.data(), sizeof(params));
286
287 u32 event_id = params.event_id.slot; 273 u32 event_id = params.event_id.slot;
288 LOG_DEBUG(Service_NVDRV, "called, event_id: {:X}", event_id); 274 LOG_DEBUG(Service_NVDRV, "called, event_id: {:X}", event_id);
289 275
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 2efed4862..992124b60 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -186,12 +186,12 @@ private:
186 static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8, 186 static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8,
187 "IocCtrlEventKill is incorrect size"); 187 "IocCtrlEventKill is incorrect size");
188 188
189 NvResult NvOsGetConfigU32(std::span<const u8> input, std::span<u8> output); 189 NvResult NvOsGetConfigU32(IocGetConfigParams& params);
190 NvResult IocCtrlEventWait(std::span<const u8> input, std::span<u8> output, bool is_allocation); 190 NvResult IocCtrlEventRegister(IocCtrlEventRegisterParams& params);
191 NvResult IocCtrlEventRegister(std::span<const u8> input, std::span<u8> output); 191 NvResult IocCtrlEventUnregister(IocCtrlEventUnregisterParams& params);
192 NvResult IocCtrlEventUnregister(std::span<const u8> input, std::span<u8> output); 192 NvResult IocCtrlEventUnregisterBatch(IocCtrlEventUnregisterBatchParams& params);
193 NvResult IocCtrlEventUnregisterBatch(std::span<const u8> input, std::span<u8> output); 193 NvResult IocCtrlEventWait(IocCtrlEventWaitParams& params, bool is_allocation);
194 NvResult IocCtrlClearEventWait(std::span<const u8> input, std::span<u8> output); 194 NvResult IocCtrlClearEventWait(IocCtrlEventClearParams& params);
195 195
196 NvResult FreeEvent(u32 slot); 196 NvResult FreeEvent(u32 slot);
197 197
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 6081d92e9..61a2df121 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -6,6 +6,7 @@
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/core_timing.h" 8#include "core/core_timing.h"
9#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
9#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" 10#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
10#include "core/hle/service/nvdrv/nvdrv.h" 11#include "core/hle/service/nvdrv/nvdrv.h"
11 12
@@ -27,23 +28,23 @@ NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8>
27 case 'G': 28 case 'G':
28 switch (command.cmd) { 29 switch (command.cmd) {
29 case 0x1: 30 case 0x1:
30 return ZCullGetCtxSize(input, output); 31 return WrapFixed(this, &nvhost_ctrl_gpu::ZCullGetCtxSize, input, output);
31 case 0x2: 32 case 0x2:
32 return ZCullGetInfo(input, output); 33 return WrapFixed(this, &nvhost_ctrl_gpu::ZCullGetInfo, input, output);
33 case 0x3: 34 case 0x3:
34 return ZBCSetTable(input, output); 35 return WrapFixed(this, &nvhost_ctrl_gpu::ZBCSetTable, input, output);
35 case 0x4: 36 case 0x4:
36 return ZBCQueryTable(input, output); 37 return WrapFixed(this, &nvhost_ctrl_gpu::ZBCQueryTable, input, output);
37 case 0x5: 38 case 0x5:
38 return GetCharacteristics(input, output); 39 return WrapFixed(this, &nvhost_ctrl_gpu::GetCharacteristics1, input, output);
39 case 0x6: 40 case 0x6:
40 return GetTPCMasks(input, output); 41 return WrapFixed(this, &nvhost_ctrl_gpu::GetTPCMasks1, input, output);
41 case 0x7: 42 case 0x7:
42 return FlushL2(input, output); 43 return WrapFixed(this, &nvhost_ctrl_gpu::FlushL2, input, output);
43 case 0x14: 44 case 0x14:
44 return GetActiveSlotMask(input, output); 45 return WrapFixed(this, &nvhost_ctrl_gpu::GetActiveSlotMask, input, output);
45 case 0x1c: 46 case 0x1c:
46 return GetGpuTime(input, output); 47 return WrapFixed(this, &nvhost_ctrl_gpu::GetGpuTime, input, output);
47 default: 48 default:
48 break; 49 break;
49 } 50 }
@@ -65,9 +66,11 @@ NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8>
65 case 'G': 66 case 'G':
66 switch (command.cmd) { 67 switch (command.cmd) {
67 case 0x5: 68 case 0x5:
68 return GetCharacteristics(input, output, inline_output); 69 return WrapFixedInlOut(this, &nvhost_ctrl_gpu::GetCharacteristics3, input, output,
70 inline_output);
69 case 0x6: 71 case 0x6:
70 return GetTPCMasks(input, output, inline_output); 72 return WrapFixedInlOut(this, &nvhost_ctrl_gpu::GetTPCMasks3, input, output,
73 inline_output);
71 default: 74 default:
72 break; 75 break;
73 } 76 }
@@ -82,10 +85,8 @@ NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8>
82void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {} 85void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {}
83void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {} 86void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {}
84 87
85NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::span<u8> output) { 88NvResult nvhost_ctrl_gpu::GetCharacteristics1(IoctlCharacteristics& params) {
86 LOG_DEBUG(Service_NVDRV, "called"); 89 LOG_DEBUG(Service_NVDRV, "called");
87 IoctlCharacteristics params{};
88 std::memcpy(&params, input.data(), input.size());
89 params.gc.arch = 0x120; 90 params.gc.arch = 0x120;
90 params.gc.impl = 0xb; 91 params.gc.impl = 0xb;
91 params.gc.rev = 0xa1; 92 params.gc.rev = 0xa1;
@@ -123,15 +124,13 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::spa
123 params.gc.gr_compbit_store_base_hw = 0x0; 124 params.gc.gr_compbit_store_base_hw = 0x0;
124 params.gpu_characteristics_buf_size = 0xA0; 125 params.gpu_characteristics_buf_size = 0xA0;
125 params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED) 126 params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
126 std::memcpy(output.data(), &params, output.size());
127 return NvResult::Success; 127 return NvResult::Success;
128} 128}
129 129
130NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::span<u8> output, 130NvResult nvhost_ctrl_gpu::GetCharacteristics3(
131 std::span<u8> inline_output) { 131 IoctlCharacteristics& params, std::span<IoctlGpuCharacteristics> gpu_characteristics) {
132 LOG_DEBUG(Service_NVDRV, "called"); 132 LOG_DEBUG(Service_NVDRV, "called");
133 IoctlCharacteristics params{}; 133
134 std::memcpy(&params, input.data(), input.size());
135 params.gc.arch = 0x120; 134 params.gc.arch = 0x120;
136 params.gc.impl = 0xb; 135 params.gc.impl = 0xb;
137 params.gc.rev = 0xa1; 136 params.gc.rev = 0xa1;
@@ -169,70 +168,47 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::spa
169 params.gc.gr_compbit_store_base_hw = 0x0; 168 params.gc.gr_compbit_store_base_hw = 0x0;
170 params.gpu_characteristics_buf_size = 0xA0; 169 params.gpu_characteristics_buf_size = 0xA0;
171 params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED) 170 params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
172 171 if (!gpu_characteristics.empty()) {
173 std::memcpy(output.data(), &params, output.size()); 172 gpu_characteristics.front() = params.gc;
174 std::memcpy(inline_output.data(), &params.gc, inline_output.size()); 173 }
175 return NvResult::Success; 174 return NvResult::Success;
176} 175}
177 176
178NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::span<u8> output) { 177NvResult nvhost_ctrl_gpu::GetTPCMasks1(IoctlGpuGetTpcMasksArgs& params) {
179 IoctlGpuGetTpcMasksArgs params{};
180 std::memcpy(&params, input.data(), input.size());
181 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); 178 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
182 if (params.mask_buffer_size != 0) { 179 if (params.mask_buffer_size != 0) {
183 params.tcp_mask = 3; 180 params.tcp_mask = 3;
184 } 181 }
185 std::memcpy(output.data(), &params, output.size());
186 return NvResult::Success; 182 return NvResult::Success;
187} 183}
188 184
189NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::span<u8> output, 185NvResult nvhost_ctrl_gpu::GetTPCMasks3(IoctlGpuGetTpcMasksArgs& params, std::span<u32> tpc_mask) {
190 std::span<u8> inline_output) {
191 IoctlGpuGetTpcMasksArgs params{};
192 std::memcpy(&params, input.data(), input.size());
193 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); 186 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
194 if (params.mask_buffer_size != 0) { 187 if (params.mask_buffer_size != 0) {
195 params.tcp_mask = 3; 188 params.tcp_mask = 3;
196 } 189 }
197 std::memcpy(output.data(), &params, output.size()); 190 if (!tpc_mask.empty()) {
198 std::memcpy(inline_output.data(), &params.tcp_mask, inline_output.size()); 191 tpc_mask.front() = params.tcp_mask;
192 }
199 return NvResult::Success; 193 return NvResult::Success;
200} 194}
201 195
202NvResult nvhost_ctrl_gpu::GetActiveSlotMask(std::span<const u8> input, std::span<u8> output) { 196NvResult nvhost_ctrl_gpu::GetActiveSlotMask(IoctlActiveSlotMask& params) {
203 LOG_DEBUG(Service_NVDRV, "called"); 197 LOG_DEBUG(Service_NVDRV, "called");
204 198
205 IoctlActiveSlotMask params{};
206 if (input.size() > 0) {
207 std::memcpy(&params, input.data(), input.size());
208 }
209 params.slot = 0x07; 199 params.slot = 0x07;
210 params.mask = 0x01; 200 params.mask = 0x01;
211 std::memcpy(output.data(), &params, output.size());
212 return NvResult::Success; 201 return NvResult::Success;
213} 202}
214 203
215NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(std::span<const u8> input, std::span<u8> output) { 204NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(IoctlZcullGetCtxSize& params) {
216 LOG_DEBUG(Service_NVDRV, "called"); 205 LOG_DEBUG(Service_NVDRV, "called");
217
218 IoctlZcullGetCtxSize params{};
219 if (input.size() > 0) {
220 std::memcpy(&params, input.data(), input.size());
221 }
222 params.size = 0x1; 206 params.size = 0x1;
223 std::memcpy(output.data(), &params, output.size());
224 return NvResult::Success; 207 return NvResult::Success;
225} 208}
226 209
227NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::span<u8> output) { 210NvResult nvhost_ctrl_gpu::ZCullGetInfo(IoctlNvgpuGpuZcullGetInfoArgs& params) {
228 LOG_DEBUG(Service_NVDRV, "called"); 211 LOG_DEBUG(Service_NVDRV, "called");
229
230 IoctlNvgpuGpuZcullGetInfoArgs params{};
231
232 if (input.size() > 0) {
233 std::memcpy(&params, input.data(), input.size());
234 }
235
236 params.width_align_pixels = 0x20; 212 params.width_align_pixels = 0x20;
237 params.height_align_pixels = 0x20; 213 params.height_align_pixels = 0x20;
238 params.pixel_squares_by_aliquots = 0x400; 214 params.pixel_squares_by_aliquots = 0x400;
@@ -243,53 +219,28 @@ NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::span<u8>
243 params.subregion_width_align_pixels = 0x20; 219 params.subregion_width_align_pixels = 0x20;
244 params.subregion_height_align_pixels = 0x40; 220 params.subregion_height_align_pixels = 0x40;
245 params.subregion_count = 0x10; 221 params.subregion_count = 0x10;
246 std::memcpy(output.data(), &params, output.size());
247 return NvResult::Success; 222 return NvResult::Success;
248} 223}
249 224
250NvResult nvhost_ctrl_gpu::ZBCSetTable(std::span<const u8> input, std::span<u8> output) { 225NvResult nvhost_ctrl_gpu::ZBCSetTable(IoctlZbcSetTable& params) {
251 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 226 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
252
253 IoctlZbcSetTable params{};
254 std::memcpy(&params, input.data(), input.size());
255 // TODO(ogniK): What does this even actually do? 227 // TODO(ogniK): What does this even actually do?
256
257 // Prevent null pointer being passed as arg 1
258 if (output.empty()) {
259 LOG_WARNING(Service_NVDRV, "Avoiding passing null pointer to memcpy");
260 } else {
261 std::memcpy(output.data(), &params, output.size());
262 }
263 return NvResult::Success; 228 return NvResult::Success;
264} 229}
265 230
266NvResult nvhost_ctrl_gpu::ZBCQueryTable(std::span<const u8> input, std::span<u8> output) { 231NvResult nvhost_ctrl_gpu::ZBCQueryTable(IoctlZbcQueryTable& params) {
267 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 232 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
268
269 IoctlZbcQueryTable params{};
270 std::memcpy(&params, input.data(), input.size());
271 // TODO : To implement properly
272 std::memcpy(output.data(), &params, output.size());
273 return NvResult::Success; 233 return NvResult::Success;
274} 234}
275 235
276NvResult nvhost_ctrl_gpu::FlushL2(std::span<const u8> input, std::span<u8> output) { 236NvResult nvhost_ctrl_gpu::FlushL2(IoctlFlushL2& params) {
277 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 237 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
278
279 IoctlFlushL2 params{};
280 std::memcpy(&params, input.data(), input.size());
281 // TODO : To implement properly
282 std::memcpy(output.data(), &params, output.size());
283 return NvResult::Success; 238 return NvResult::Success;
284} 239}
285 240
286NvResult nvhost_ctrl_gpu::GetGpuTime(std::span<const u8> input, std::span<u8> output) { 241NvResult nvhost_ctrl_gpu::GetGpuTime(IoctlGetGpuTime& params) {
287 LOG_DEBUG(Service_NVDRV, "called"); 242 LOG_DEBUG(Service_NVDRV, "called");
288
289 IoctlGetGpuTime params{};
290 std::memcpy(&params, input.data(), input.size());
291 params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count()); 243 params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count());
292 std::memcpy(output.data(), &params, output.size());
293 return NvResult::Success; 244 return NvResult::Success;
294} 245}
295 246
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 97995551c..d170299bd 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -151,21 +151,20 @@ private:
151 }; 151 };
152 static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size"); 152 static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size");
153 153
154 NvResult GetCharacteristics(std::span<const u8> input, std::span<u8> output); 154 NvResult GetCharacteristics1(IoctlCharacteristics& params);
155 NvResult GetCharacteristics(std::span<const u8> input, std::span<u8> output, 155 NvResult GetCharacteristics3(IoctlCharacteristics& params,
156 std::span<u8> inline_output); 156 std::span<IoctlGpuCharacteristics> gpu_characteristics);
157 157
158 NvResult GetTPCMasks(std::span<const u8> input, std::span<u8> output); 158 NvResult GetTPCMasks1(IoctlGpuGetTpcMasksArgs& params);
159 NvResult GetTPCMasks(std::span<const u8> input, std::span<u8> output, 159 NvResult GetTPCMasks3(IoctlGpuGetTpcMasksArgs& params, std::span<u32> tpc_mask);
160 std::span<u8> inline_output); 160
161 161 NvResult GetActiveSlotMask(IoctlActiveSlotMask& params);
162 NvResult GetActiveSlotMask(std::span<const u8> input, std::span<u8> output); 162 NvResult ZCullGetCtxSize(IoctlZcullGetCtxSize& params);
163 NvResult ZCullGetCtxSize(std::span<const u8> input, std::span<u8> output); 163 NvResult ZCullGetInfo(IoctlNvgpuGpuZcullGetInfoArgs& params);
164 NvResult ZCullGetInfo(std::span<const u8> input, std::span<u8> output); 164 NvResult ZBCSetTable(IoctlZbcSetTable& params);
165 NvResult ZBCSetTable(std::span<const u8> input, std::span<u8> output); 165 NvResult ZBCQueryTable(IoctlZbcQueryTable& params);
166 NvResult ZBCQueryTable(std::span<const u8> input, std::span<u8> output); 166 NvResult FlushL2(IoctlFlushL2& params);
167 NvResult FlushL2(std::span<const u8> input, std::span<u8> output); 167 NvResult GetGpuTime(IoctlGetGpuTime& params);
168 NvResult GetGpuTime(std::span<const u8> input, std::span<u8> output);
169 168
170 EventInterface& events_interface; 169 EventInterface& events_interface;
171 170
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 46a25fcab..b0395c2f0 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -8,6 +8,7 @@
8#include "core/hle/service/nvdrv/core/container.h" 8#include "core/hle/service/nvdrv/core/container.h"
9#include "core/hle/service/nvdrv/core/nvmap.h" 9#include "core/hle/service/nvdrv/core/nvmap.h"
10#include "core/hle/service/nvdrv/core/syncpoint_manager.h" 10#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
11#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
11#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" 12#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
12#include "core/hle/service/nvdrv/nvdrv.h" 13#include "core/hle/service/nvdrv/nvdrv.h"
13#include "core/memory.h" 14#include "core/memory.h"
@@ -52,7 +53,7 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
52 case 0x0: 53 case 0x0:
53 switch (command.cmd) { 54 switch (command.cmd) {
54 case 0x3: 55 case 0x3:
55 return GetWaitbase(input, output); 56 return WrapFixed(this, &nvhost_gpu::GetWaitbase, input, output);
56 default: 57 default:
57 break; 58 break;
58 } 59 }
@@ -60,25 +61,25 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
60 case 'H': 61 case 'H':
61 switch (command.cmd) { 62 switch (command.cmd) {
62 case 0x1: 63 case 0x1:
63 return SetNVMAPfd(input, output); 64 return WrapFixed(this, &nvhost_gpu::SetNVMAPfd, input, output);
64 case 0x3: 65 case 0x3:
65 return ChannelSetTimeout(input, output); 66 return WrapFixed(this, &nvhost_gpu::ChannelSetTimeout, input, output);
66 case 0x8: 67 case 0x8:
67 return SubmitGPFIFOBase(input, output, false); 68 return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, false);
68 case 0x9: 69 case 0x9:
69 return AllocateObjectContext(input, output); 70 return WrapFixed(this, &nvhost_gpu::AllocateObjectContext, input, output);
70 case 0xb: 71 case 0xb:
71 return ZCullBind(input, output); 72 return WrapFixed(this, &nvhost_gpu::ZCullBind, input, output);
72 case 0xc: 73 case 0xc:
73 return SetErrorNotifier(input, output); 74 return WrapFixed(this, &nvhost_gpu::SetErrorNotifier, input, output);
74 case 0xd: 75 case 0xd:
75 return SetChannelPriority(input, output); 76 return WrapFixed(this, &nvhost_gpu::SetChannelPriority, input, output);
76 case 0x1a: 77 case 0x1a:
77 return AllocGPFIFOEx2(input, output); 78 return WrapFixed(this, &nvhost_gpu::AllocGPFIFOEx2, input, output);
78 case 0x1b: 79 case 0x1b:
79 return SubmitGPFIFOBase(input, output, true); 80 return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, true);
80 case 0x1d: 81 case 0x1d:
81 return ChannelSetTimeslice(input, output); 82 return WrapFixed(this, &nvhost_gpu::ChannelSetTimeslice, input, output);
82 default: 83 default:
83 break; 84 break;
84 } 85 }
@@ -86,9 +87,9 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
86 case 'G': 87 case 'G':
87 switch (command.cmd) { 88 switch (command.cmd) {
88 case 0x14: 89 case 0x14:
89 return SetClientData(input, output); 90 return WrapFixed(this, &nvhost_gpu::SetClientData, input, output);
90 case 0x15: 91 case 0x15:
91 return GetClientData(input, output); 92 return WrapFixed(this, &nvhost_gpu::GetClientData, input, output);
92 default: 93 default:
93 break; 94 break;
94 } 95 }
@@ -104,7 +105,8 @@ NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> inpu
104 case 'H': 105 case 'H':
105 switch (command.cmd) { 106 switch (command.cmd) {
106 case 0x1b: 107 case 0x1b:
107 return SubmitGPFIFOBase(input, inline_input, output); 108 return WrapFixedInlIn(this, &nvhost_gpu::SubmitGPFIFOBase2, input, inline_input,
109 output);
108 } 110 }
109 break; 111 break;
110 } 112 }
@@ -121,63 +123,45 @@ NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inpu
121void nvhost_gpu::OnOpen(DeviceFD fd) {} 123void nvhost_gpu::OnOpen(DeviceFD fd) {}
122void nvhost_gpu::OnClose(DeviceFD fd) {} 124void nvhost_gpu::OnClose(DeviceFD fd) {}
123 125
124NvResult nvhost_gpu::SetNVMAPfd(std::span<const u8> input, std::span<u8> output) { 126NvResult nvhost_gpu::SetNVMAPfd(IoctlSetNvmapFD& params) {
125 IoctlSetNvmapFD params{};
126 std::memcpy(&params, input.data(), input.size());
127 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 127 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
128 128
129 nvmap_fd = params.nvmap_fd; 129 nvmap_fd = params.nvmap_fd;
130 return NvResult::Success; 130 return NvResult::Success;
131} 131}
132 132
133NvResult nvhost_gpu::SetClientData(std::span<const u8> input, std::span<u8> output) { 133NvResult nvhost_gpu::SetClientData(IoctlClientData& params) {
134 LOG_DEBUG(Service_NVDRV, "called"); 134 LOG_DEBUG(Service_NVDRV, "called");
135
136 IoctlClientData params{};
137 std::memcpy(&params, input.data(), input.size());
138 user_data = params.data; 135 user_data = params.data;
139 return NvResult::Success; 136 return NvResult::Success;
140} 137}
141 138
142NvResult nvhost_gpu::GetClientData(std::span<const u8> input, std::span<u8> output) { 139NvResult nvhost_gpu::GetClientData(IoctlClientData& params) {
143 LOG_DEBUG(Service_NVDRV, "called"); 140 LOG_DEBUG(Service_NVDRV, "called");
144
145 IoctlClientData params{};
146 std::memcpy(&params, input.data(), input.size());
147 params.data = user_data; 141 params.data = user_data;
148 std::memcpy(output.data(), &params, output.size());
149 return NvResult::Success; 142 return NvResult::Success;
150} 143}
151 144
152NvResult nvhost_gpu::ZCullBind(std::span<const u8> input, std::span<u8> output) { 145NvResult nvhost_gpu::ZCullBind(IoctlZCullBind& params) {
153 std::memcpy(&zcull_params, input.data(), input.size()); 146 zcull_params = params;
154 LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va, 147 LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va,
155 zcull_params.mode); 148 zcull_params.mode);
156
157 std::memcpy(output.data(), &zcull_params, output.size());
158 return NvResult::Success; 149 return NvResult::Success;
159} 150}
160 151
161NvResult nvhost_gpu::SetErrorNotifier(std::span<const u8> input, std::span<u8> output) { 152NvResult nvhost_gpu::SetErrorNotifier(IoctlSetErrorNotifier& params) {
162 IoctlSetErrorNotifier params{};
163 std::memcpy(&params, input.data(), input.size());
164 LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset, 153 LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset,
165 params.size, params.mem); 154 params.size, params.mem);
166
167 std::memcpy(output.data(), &params, output.size());
168 return NvResult::Success; 155 return NvResult::Success;
169} 156}
170 157
171NvResult nvhost_gpu::SetChannelPriority(std::span<const u8> input, std::span<u8> output) { 158NvResult nvhost_gpu::SetChannelPriority(IoctlChannelSetPriority& params) {
172 std::memcpy(&channel_priority, input.data(), input.size()); 159 channel_priority = params.priority;
173 LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority); 160 LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority);
174
175 return NvResult::Success; 161 return NvResult::Success;
176} 162}
177 163
178NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::span<u8> output) { 164NvResult nvhost_gpu::AllocGPFIFOEx2(IoctlAllocGpfifoEx2& params) {
179 IoctlAllocGpfifoEx2 params{};
180 std::memcpy(&params, input.data(), input.size());
181 LOG_WARNING(Service_NVDRV, 165 LOG_WARNING(Service_NVDRV,
182 "(STUBBED) called, num_entries={:X}, flags={:X}, unk0={:X}, " 166 "(STUBBED) called, num_entries={:X}, flags={:X}, unk0={:X}, "
183 "unk1={:X}, unk2={:X}, unk3={:X}", 167 "unk1={:X}, unk2={:X}, unk3={:X}",
@@ -193,18 +177,14 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::span<u8> out
193 177
194 params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint); 178 params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint);
195 179
196 std::memcpy(output.data(), &params, output.size());
197 return NvResult::Success; 180 return NvResult::Success;
198} 181}
199 182
200NvResult nvhost_gpu::AllocateObjectContext(std::span<const u8> input, std::span<u8> output) { 183NvResult nvhost_gpu::AllocateObjectContext(IoctlAllocObjCtx& params) {
201 IoctlAllocObjCtx params{};
202 std::memcpy(&params, input.data(), input.size());
203 LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num, 184 LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num,
204 params.flags); 185 params.flags);
205 186
206 params.obj_id = 0x0; 187 params.obj_id = 0x0;
207 std::memcpy(output.data(), &params, output.size());
208 return NvResult::Success; 188 return NvResult::Success;
209} 189}
210 190
@@ -248,8 +228,7 @@ static boost::container::small_vector<Tegra::CommandHeader, 512> BuildIncrementW
248 return result; 228 return result;
249} 229}
250 230
251NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::span<u8> output, 231NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandList&& entries) {
252 Tegra::CommandList&& entries) {
253 LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, 232 LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
254 params.num_entries, params.flags.raw); 233 params.num_entries, params.flags.raw);
255 234
@@ -290,65 +269,55 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::span<u8> o
290 269
291 flags.raw = 0; 270 flags.raw = 0;
292 271
293 std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
294 return NvResult::Success; 272 return NvResult::Success;
295} 273}
296 274
297NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<u8> output, 275NvResult nvhost_gpu::SubmitGPFIFOBase1(IoctlSubmitGpfifo& params,
298 bool kickoff) { 276 std::span<Tegra::CommandListHeader> commands, bool kickoff) {
299 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 277 if (params.num_entries > commands.size()) {
300 UNIMPLEMENTED(); 278 UNIMPLEMENTED();
301 return NvResult::InvalidSize; 279 return NvResult::InvalidSize;
302 } 280 }
303 IoctlSubmitGpfifo params{};
304 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
305 Tegra::CommandList entries(params.num_entries);
306 281
282 Tegra::CommandList entries(params.num_entries);
307 if (kickoff) { 283 if (kickoff) {
308 system.ApplicationMemory().ReadBlock(params.address, entries.command_lists.data(), 284 system.ApplicationMemory().ReadBlock(params.address, entries.command_lists.data(),
309 params.num_entries * sizeof(Tegra::CommandListHeader)); 285 params.num_entries * sizeof(Tegra::CommandListHeader));
310 } else { 286 } else {
311 std::memcpy(entries.command_lists.data(), &input[sizeof(IoctlSubmitGpfifo)], 287 std::memcpy(entries.command_lists.data(), commands.data(),
312 params.num_entries * sizeof(Tegra::CommandListHeader)); 288 params.num_entries * sizeof(Tegra::CommandListHeader));
313 } 289 }
314 290
315 return SubmitGPFIFOImpl(params, output, std::move(entries)); 291 return SubmitGPFIFOImpl(params, std::move(entries));
316} 292}
317 293
318NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline, 294NvResult nvhost_gpu::SubmitGPFIFOBase2(IoctlSubmitGpfifo& params,
319 std::span<u8> output) { 295 std::span<const Tegra::CommandListHeader> commands) {
320 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 296 if (params.num_entries > commands.size()) {
321 UNIMPLEMENTED(); 297 UNIMPLEMENTED();
322 return NvResult::InvalidSize; 298 return NvResult::InvalidSize;
323 } 299 }
324 IoctlSubmitGpfifo params{}; 300
325 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
326 Tegra::CommandList entries(params.num_entries); 301 Tegra::CommandList entries(params.num_entries);
327 std::memcpy(entries.command_lists.data(), input_inline.data(), input_inline.size()); 302 std::memcpy(entries.command_lists.data(), commands.data(),
328 return SubmitGPFIFOImpl(params, output, std::move(entries)); 303 params.num_entries * sizeof(Tegra::CommandListHeader));
304 return SubmitGPFIFOImpl(params, std::move(entries));
329} 305}
330 306
331NvResult nvhost_gpu::GetWaitbase(std::span<const u8> input, std::span<u8> output) { 307NvResult nvhost_gpu::GetWaitbase(IoctlGetWaitbase& params) {
332 IoctlGetWaitbase params{};
333 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
334 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); 308 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
335 309
336 params.value = 0; // Seems to be hard coded at 0 310 params.value = 0; // Seems to be hard coded at 0
337 std::memcpy(output.data(), &params, output.size());
338 return NvResult::Success; 311 return NvResult::Success;
339} 312}
340 313
341NvResult nvhost_gpu::ChannelSetTimeout(std::span<const u8> input, std::span<u8> output) { 314NvResult nvhost_gpu::ChannelSetTimeout(IoctlChannelSetTimeout& params) {
342 IoctlChannelSetTimeout params{};
343 std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout));
344 LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout); 315 LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout);
345 316
346 return NvResult::Success; 317 return NvResult::Success;
347} 318}
348 319
349NvResult nvhost_gpu::ChannelSetTimeslice(std::span<const u8> input, std::span<u8> output) { 320NvResult nvhost_gpu::ChannelSetTimeslice(IoctlSetTimeslice& params) {
350 IoctlSetTimeslice params{};
351 std::memcpy(&params, input.data(), sizeof(IoctlSetTimeslice));
352 LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice); 321 LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice);
353 322
354 channel_timeslice = params.timeslice; 323 channel_timeslice = params.timeslice;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index 529c20526..88fd228ff 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -186,23 +186,24 @@ private:
186 u32_le channel_priority{}; 186 u32_le channel_priority{};
187 u32_le channel_timeslice{}; 187 u32_le channel_timeslice{};
188 188
189 NvResult SetNVMAPfd(std::span<const u8> input, std::span<u8> output); 189 NvResult SetNVMAPfd(IoctlSetNvmapFD& params);
190 NvResult SetClientData(std::span<const u8> input, std::span<u8> output); 190 NvResult SetClientData(IoctlClientData& params);
191 NvResult GetClientData(std::span<const u8> input, std::span<u8> output); 191 NvResult GetClientData(IoctlClientData& params);
192 NvResult ZCullBind(std::span<const u8> input, std::span<u8> output); 192 NvResult ZCullBind(IoctlZCullBind& params);
193 NvResult SetErrorNotifier(std::span<const u8> input, std::span<u8> output); 193 NvResult SetErrorNotifier(IoctlSetErrorNotifier& params);
194 NvResult SetChannelPriority(std::span<const u8> input, std::span<u8> output); 194 NvResult SetChannelPriority(IoctlChannelSetPriority& params);
195 NvResult AllocGPFIFOEx2(std::span<const u8> input, std::span<u8> output); 195 NvResult AllocGPFIFOEx2(IoctlAllocGpfifoEx2& params);
196 NvResult AllocateObjectContext(std::span<const u8> input, std::span<u8> output); 196 NvResult AllocateObjectContext(IoctlAllocObjCtx& params);
197 NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::span<u8> output, 197
198 Tegra::CommandList&& entries); 198 NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandList&& entries);
199 NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<u8> output, 199 NvResult SubmitGPFIFOBase1(IoctlSubmitGpfifo& params,
200 bool kickoff = false); 200 std::span<Tegra::CommandListHeader> commands, bool kickoff = false);
201 NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline, 201 NvResult SubmitGPFIFOBase2(IoctlSubmitGpfifo& params,
202 std::span<u8> output); 202 std::span<const Tegra::CommandListHeader> commands);
203 NvResult GetWaitbase(std::span<const u8> input, std::span<u8> output); 203
204 NvResult ChannelSetTimeout(std::span<const u8> input, std::span<u8> output); 204 NvResult GetWaitbase(IoctlGetWaitbase& params);
205 NvResult ChannelSetTimeslice(std::span<const u8> input, std::span<u8> output); 205 NvResult ChannelSetTimeout(IoctlChannelSetTimeout& params);
206 NvResult ChannelSetTimeslice(IoctlSetTimeslice& params);
206 207
207 EventInterface& events_interface; 208 EventInterface& events_interface;
208 NvCore::Container& core; 209 NvCore::Container& core;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index a174442a6..f43914e1b 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -6,6 +6,7 @@
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/hle/service/nvdrv/core/container.h" 8#include "core/hle/service/nvdrv/core/container.h"
9#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
9#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" 10#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
10#include "video_core/renderer_base.h" 11#include "video_core/renderer_base.h"
11 12
@@ -25,18 +26,18 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in
25 if (!host1x_file.fd_to_id.contains(fd)) { 26 if (!host1x_file.fd_to_id.contains(fd)) {
26 host1x_file.fd_to_id[fd] = host1x_file.nvdec_next_id++; 27 host1x_file.fd_to_id[fd] = host1x_file.nvdec_next_id++;
27 } 28 }
28 return Submit(fd, input, output); 29 return WrapFixedVariable(this, &nvhost_nvdec::Submit, input, output, fd);
29 } 30 }
30 case 0x2: 31 case 0x2:
31 return GetSyncpoint(input, output); 32 return WrapFixed(this, &nvhost_nvdec::GetSyncpoint, input, output);
32 case 0x3: 33 case 0x3:
33 return GetWaitbase(input, output); 34 return WrapFixed(this, &nvhost_nvdec::GetWaitbase, input, output);
34 case 0x7: 35 case 0x7:
35 return SetSubmitTimeout(input, output); 36 return WrapFixed(this, &nvhost_nvdec::SetSubmitTimeout, input, output);
36 case 0x9: 37 case 0x9:
37 return MapBuffer(input, output); 38 return WrapFixedVariable(this, &nvhost_nvdec::MapBuffer, input, output);
38 case 0xa: 39 case 0xa:
39 return UnmapBuffer(input, output); 40 return WrapFixedVariable(this, &nvhost_nvdec::UnmapBuffer, input, output);
40 default: 41 default:
41 break; 42 break;
42 } 43 }
@@ -44,7 +45,7 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in
44 case 'H': 45 case 'H':
45 switch (command.cmd) { 46 switch (command.cmd) {
46 case 0x1: 47 case 0x1:
47 return SetNVMAPfd(input); 48 return WrapFixed(this, &nvhost_nvdec::SetNVMAPfd, input, output);
48 default: 49 default:
49 break; 50 break;
50 } 51 }
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
index 61649aa4a..74c701b95 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -29,6 +29,9 @@ std::size_t SliceVectors(std::span<const u8> input, std::vector<T>& dst, std::si
29 return 0; 29 return 0;
30 } 30 }
31 const size_t bytes_copied = count * sizeof(T); 31 const size_t bytes_copied = count * sizeof(T);
32 if (input.size() < offset + bytes_copied) {
33 return 0;
34 }
32 std::memcpy(dst.data(), input.data() + offset, bytes_copied); 35 std::memcpy(dst.data(), input.data() + offset, bytes_copied);
33 return bytes_copied; 36 return bytes_copied;
34} 37}
@@ -41,6 +44,9 @@ std::size_t WriteVectors(std::span<u8> dst, const std::vector<T>& src, std::size
41 return 0; 44 return 0;
42 } 45 }
43 const size_t bytes_copied = src.size() * sizeof(T); 46 const size_t bytes_copied = src.size() * sizeof(T);
47 if (dst.size() < offset + bytes_copied) {
48 return 0;
49 }
44 std::memcpy(dst.data() + offset, src.data(), bytes_copied); 50 std::memcpy(dst.data() + offset, src.data(), bytes_copied);
45 return bytes_copied; 51 return bytes_copied;
46} 52}
@@ -63,18 +69,14 @@ nvhost_nvdec_common::~nvhost_nvdec_common() {
63 core.Host1xDeviceFile().syncpts_accumulated.push_back(channel_syncpoint); 69 core.Host1xDeviceFile().syncpts_accumulated.push_back(channel_syncpoint);
64} 70}
65 71
66NvResult nvhost_nvdec_common::SetNVMAPfd(std::span<const u8> input) { 72NvResult nvhost_nvdec_common::SetNVMAPfd(IoctlSetNvmapFD& params) {
67 IoctlSetNvmapFD params{};
68 std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD));
69 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 73 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
70 74
71 nvmap_fd = params.nvmap_fd; 75 nvmap_fd = params.nvmap_fd;
72 return NvResult::Success; 76 return NvResult::Success;
73} 77}
74 78
75NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input, std::span<u8> output) { 79NvResult nvhost_nvdec_common::Submit(IoctlSubmit& params, std::span<u8> data, DeviceFD fd) {
76 IoctlSubmit params{};
77 std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
78 LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count); 80 LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count);
79 81
80 // Instantiate param buffers 82 // Instantiate param buffers
@@ -85,12 +87,12 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input, std
85 std::vector<u32> fence_thresholds(params.fence_count); 87 std::vector<u32> fence_thresholds(params.fence_count);
86 88
87 // Slice input into their respective buffers 89 // Slice input into their respective buffers
88 std::size_t offset = sizeof(IoctlSubmit); 90 std::size_t offset = 0;
89 offset += SliceVectors(input, command_buffers, params.cmd_buffer_count, offset); 91 offset += SliceVectors(data, command_buffers, params.cmd_buffer_count, offset);
90 offset += SliceVectors(input, relocs, params.relocation_count, offset); 92 offset += SliceVectors(data, relocs, params.relocation_count, offset);
91 offset += SliceVectors(input, reloc_shifts, params.relocation_count, offset); 93 offset += SliceVectors(data, reloc_shifts, params.relocation_count, offset);
92 offset += SliceVectors(input, syncpt_increments, params.syncpoint_count, offset); 94 offset += SliceVectors(data, syncpt_increments, params.syncpoint_count, offset);
93 offset += SliceVectors(input, fence_thresholds, params.fence_count, offset); 95 offset += SliceVectors(data, fence_thresholds, params.fence_count, offset);
94 96
95 auto& gpu = system.GPU(); 97 auto& gpu = system.GPU();
96 if (gpu.UseNvdec()) { 98 if (gpu.UseNvdec()) {
@@ -108,72 +110,51 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input, std
108 cmdlist.size() * sizeof(u32)); 110 cmdlist.size() * sizeof(u32));
109 gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist); 111 gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist);
110 } 112 }
111 std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
112 // Some games expect command_buffers to be written back 113 // Some games expect command_buffers to be written back
113 offset = sizeof(IoctlSubmit); 114 offset = 0;
114 offset += WriteVectors(output, command_buffers, offset); 115 offset += WriteVectors(data, command_buffers, offset);
115 offset += WriteVectors(output, relocs, offset); 116 offset += WriteVectors(data, relocs, offset);
116 offset += WriteVectors(output, reloc_shifts, offset); 117 offset += WriteVectors(data, reloc_shifts, offset);
117 offset += WriteVectors(output, syncpt_increments, offset); 118 offset += WriteVectors(data, syncpt_increments, offset);
118 offset += WriteVectors(output, fence_thresholds, offset); 119 offset += WriteVectors(data, fence_thresholds, offset);
119 120
120 return NvResult::Success; 121 return NvResult::Success;
121} 122}
122 123
123NvResult nvhost_nvdec_common::GetSyncpoint(std::span<const u8> input, std::span<u8> output) { 124NvResult nvhost_nvdec_common::GetSyncpoint(IoctlGetSyncpoint& params) {
124 IoctlGetSyncpoint params{};
125 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
126 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); 125 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param);
127
128 // const u32 id{NvCore::SyncpointManager::channel_syncpoints[static_cast<u32>(channel_type)]};
129 params.value = channel_syncpoint; 126 params.value = channel_syncpoint;
130 std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
131
132 return NvResult::Success; 127 return NvResult::Success;
133} 128}
134 129
135NvResult nvhost_nvdec_common::GetWaitbase(std::span<const u8> input, std::span<u8> output) { 130NvResult nvhost_nvdec_common::GetWaitbase(IoctlGetWaitbase& params) {
136 IoctlGetWaitbase params{};
137 LOG_CRITICAL(Service_NVDRV, "called WAITBASE"); 131 LOG_CRITICAL(Service_NVDRV, "called WAITBASE");
138 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
139 params.value = 0; // Seems to be hard coded at 0 132 params.value = 0; // Seems to be hard coded at 0
140 std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase));
141 return NvResult::Success; 133 return NvResult::Success;
142} 134}
143 135
144NvResult nvhost_nvdec_common::MapBuffer(std::span<const u8> input, std::span<u8> output) { 136NvResult nvhost_nvdec_common::MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries) {
145 IoctlMapBuffer params{}; 137 const size_t num_entries = std::min(params.num_entries, static_cast<u32>(entries.size()));
146 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer)); 138 for (size_t i = 0; i < num_entries; i++) {
147 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); 139 entries[i].map_address = nvmap.PinHandle(entries[i].map_handle);
148
149 SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
150
151 for (auto& cmd_buffer : cmd_buffer_handles) {
152 cmd_buffer.map_address = nvmap.PinHandle(cmd_buffer.map_handle);
153 } 140 }
154 std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer));
155 std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(),
156 cmd_buffer_handles.size() * sizeof(MapBufferEntry));
157 141
158 return NvResult::Success; 142 return NvResult::Success;
159} 143}
160 144
161NvResult nvhost_nvdec_common::UnmapBuffer(std::span<const u8> input, std::span<u8> output) { 145NvResult nvhost_nvdec_common::UnmapBuffer(IoctlMapBuffer& params,
162 IoctlMapBuffer params{}; 146 std::span<MapBufferEntry> entries) {
163 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer)); 147 const size_t num_entries = std::min(params.num_entries, static_cast<u32>(entries.size()));
164 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); 148 for (size_t i = 0; i < num_entries; i++) {
165 149 nvmap.UnpinHandle(entries[i].map_handle);
166 SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer)); 150 entries[i] = {};
167 for (auto& cmd_buffer : cmd_buffer_handles) {
168 nvmap.UnpinHandle(cmd_buffer.map_handle);
169 } 151 }
170 152
171 std::memset(output.data(), 0, output.size()); 153 params = {};
172 return NvResult::Success; 154 return NvResult::Success;
173} 155}
174 156
175NvResult nvhost_nvdec_common::SetSubmitTimeout(std::span<const u8> input, std::span<u8> output) { 157NvResult nvhost_nvdec_common::SetSubmitTimeout(u32 timeout) {
176 std::memcpy(&submit_timeout, input.data(), input.size());
177 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 158 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
178 return NvResult::Success; 159 return NvResult::Success;
179} 160}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
index 9bb573bfe..7ce748e18 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
@@ -107,13 +107,13 @@ protected:
107 static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size"); 107 static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
108 108
109 /// Ioctl command implementations 109 /// Ioctl command implementations
110 NvResult SetNVMAPfd(std::span<const u8> input); 110 NvResult SetNVMAPfd(IoctlSetNvmapFD&);
111 NvResult Submit(DeviceFD fd, std::span<const u8> input, std::span<u8> output); 111 NvResult Submit(IoctlSubmit& params, std::span<u8> input, DeviceFD fd);
112 NvResult GetSyncpoint(std::span<const u8> input, std::span<u8> output); 112 NvResult GetSyncpoint(IoctlGetSyncpoint& params);
113 NvResult GetWaitbase(std::span<const u8> input, std::span<u8> output); 113 NvResult GetWaitbase(IoctlGetWaitbase& params);
114 NvResult MapBuffer(std::span<const u8> input, std::span<u8> output); 114 NvResult MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries);
115 NvResult UnmapBuffer(std::span<const u8> input, std::span<u8> output); 115 NvResult UnmapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries);
116 NvResult SetSubmitTimeout(std::span<const u8> input, std::span<u8> output); 116 NvResult SetSubmitTimeout(u32 timeout);
117 117
118 Kernel::KEvent* QueryEvent(u32 event_id) override; 118 Kernel::KEvent* QueryEvent(u32 event_id) override;
119 119
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
index a05c8cdae..9e6b86458 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
@@ -5,6 +5,7 @@
5 5
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
8#include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h" 9#include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h"
9 10
10namespace Service::Nvidia::Devices { 11namespace Service::Nvidia::Devices {
@@ -18,7 +19,7 @@ NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in
18 case 'H': 19 case 'H':
19 switch (command.cmd) { 20 switch (command.cmd) {
20 case 0x1: 21 case 0x1:
21 return SetNVMAPfd(input, output); 22 return WrapFixed(this, &nvhost_nvjpg::SetNVMAPfd, input, output);
22 default: 23 default:
23 break; 24 break;
24 } 25 }
@@ -46,9 +47,7 @@ NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
46void nvhost_nvjpg::OnOpen(DeviceFD fd) {} 47void nvhost_nvjpg::OnOpen(DeviceFD fd) {}
47void nvhost_nvjpg::OnClose(DeviceFD fd) {} 48void nvhost_nvjpg::OnClose(DeviceFD fd) {}
48 49
49NvResult nvhost_nvjpg::SetNVMAPfd(std::span<const u8> input, std::span<u8> output) { 50NvResult nvhost_nvjpg::SetNVMAPfd(IoctlSetNvmapFD& params) {
50 IoctlSetNvmapFD params{};
51 std::memcpy(&params, input.data(), input.size());
52 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 51 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
53 52
54 nvmap_fd = params.nvmap_fd; 53 nvmap_fd = params.nvmap_fd;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
index 5623e0d47..790c97f6a 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
@@ -33,7 +33,7 @@ private:
33 33
34 s32_le nvmap_fd{}; 34 s32_le nvmap_fd{};
35 35
36 NvResult SetNVMAPfd(std::span<const u8> input, std::span<u8> output); 36 NvResult SetNVMAPfd(IoctlSetNvmapFD& params);
37}; 37};
38 38
39} // namespace Service::Nvidia::Devices 39} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index c0b8684c3..87f8d7c22 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -5,6 +5,7 @@
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/core.h" 6#include "core/core.h"
7#include "core/hle/service/nvdrv/core/container.h" 7#include "core/hle/service/nvdrv/core/container.h"
8#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
8#include "core/hle/service/nvdrv/devices/nvhost_vic.h" 9#include "core/hle/service/nvdrv/devices/nvhost_vic.h"
9#include "video_core/renderer_base.h" 10#include "video_core/renderer_base.h"
10 11
@@ -25,16 +26,16 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
25 if (!host1x_file.fd_to_id.contains(fd)) { 26 if (!host1x_file.fd_to_id.contains(fd)) {
26 host1x_file.fd_to_id[fd] = host1x_file.vic_next_id++; 27 host1x_file.fd_to_id[fd] = host1x_file.vic_next_id++;
27 } 28 }
28 return Submit(fd, input, output); 29 return WrapFixedVariable(this, &nvhost_vic::Submit, input, output, fd);
29 } 30 }
30 case 0x2: 31 case 0x2:
31 return GetSyncpoint(input, output); 32 return WrapFixed(this, &nvhost_vic::GetSyncpoint, input, output);
32 case 0x3: 33 case 0x3:
33 return GetWaitbase(input, output); 34 return WrapFixed(this, &nvhost_vic::GetWaitbase, input, output);
34 case 0x9: 35 case 0x9:
35 return MapBuffer(input, output); 36 return WrapFixedVariable(this, &nvhost_vic::MapBuffer, input, output);
36 case 0xa: 37 case 0xa:
37 return UnmapBuffer(input, output); 38 return WrapFixedVariable(this, &nvhost_vic::UnmapBuffer, input, output);
38 default: 39 default:
39 break; 40 break;
40 } 41 }
@@ -42,7 +43,7 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
42 case 'H': 43 case 'H':
43 switch (command.cmd) { 44 switch (command.cmd) {
44 case 0x1: 45 case 0x1:
45 return SetNVMAPfd(input); 46 return WrapFixed(this, &nvhost_vic::SetNVMAPfd, input, output);
46 default: 47 default:
47 break; 48 break;
48 } 49 }
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 968eaa175..71b2e62ec 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -13,6 +13,7 @@
13#include "core/hle/kernel/k_process.h" 13#include "core/hle/kernel/k_process.h"
14#include "core/hle/service/nvdrv/core/container.h" 14#include "core/hle/service/nvdrv/core/container.h"
15#include "core/hle/service/nvdrv/core/nvmap.h" 15#include "core/hle/service/nvdrv/core/nvmap.h"
16#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
16#include "core/hle/service/nvdrv/devices/nvmap.h" 17#include "core/hle/service/nvdrv/devices/nvmap.h"
17#include "core/memory.h" 18#include "core/memory.h"
18 19
@@ -31,17 +32,17 @@ NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
31 case 0x1: 32 case 0x1:
32 switch (command.cmd) { 33 switch (command.cmd) {
33 case 0x1: 34 case 0x1:
34 return IocCreate(input, output); 35 return WrapFixed(this, &nvmap::IocCreate, input, output);
35 case 0x3: 36 case 0x3:
36 return IocFromId(input, output); 37 return WrapFixed(this, &nvmap::IocFromId, input, output);
37 case 0x4: 38 case 0x4:
38 return IocAlloc(input, output); 39 return WrapFixed(this, &nvmap::IocAlloc, input, output);
39 case 0x5: 40 case 0x5:
40 return IocFree(input, output); 41 return WrapFixed(this, &nvmap::IocFree, input, output);
41 case 0x9: 42 case 0x9:
42 return IocParam(input, output); 43 return WrapFixed(this, &nvmap::IocParam, input, output);
43 case 0xe: 44 case 0xe:
44 return IocGetId(input, output); 45 return WrapFixed(this, &nvmap::IocGetId, input, output);
45 default: 46 default:
46 break; 47 break;
47 } 48 }
@@ -69,9 +70,7 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, st
69void nvmap::OnOpen(DeviceFD fd) {} 70void nvmap::OnOpen(DeviceFD fd) {}
70void nvmap::OnClose(DeviceFD fd) {} 71void nvmap::OnClose(DeviceFD fd) {}
71 72
72NvResult nvmap::IocCreate(std::span<const u8> input, std::span<u8> output) { 73NvResult nvmap::IocCreate(IocCreateParams& params) {
73 IocCreateParams params;
74 std::memcpy(&params, input.data(), sizeof(params));
75 LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size); 74 LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size);
76 75
77 std::shared_ptr<NvCore::NvMap::Handle> handle_description{}; 76 std::shared_ptr<NvCore::NvMap::Handle> handle_description{};
@@ -85,13 +84,10 @@ NvResult nvmap::IocCreate(std::span<const u8> input, std::span<u8> output) {
85 params.handle = handle_description->id; 84 params.handle = handle_description->id;
86 LOG_DEBUG(Service_NVDRV, "handle: {}, size: 0x{:X}", handle_description->id, params.size); 85 LOG_DEBUG(Service_NVDRV, "handle: {}, size: 0x{:X}", handle_description->id, params.size);
87 86
88 std::memcpy(output.data(), &params, sizeof(params));
89 return NvResult::Success; 87 return NvResult::Success;
90} 88}
91 89
92NvResult nvmap::IocAlloc(std::span<const u8> input, std::span<u8> output) { 90NvResult nvmap::IocAlloc(IocAllocParams& params) {
93 IocAllocParams params;
94 std::memcpy(&params, input.data(), sizeof(params));
95 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address); 91 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address);
96 92
97 if (!params.handle) { 93 if (!params.handle) {
@@ -133,14 +129,10 @@ NvResult nvmap::IocAlloc(std::span<const u8> input, std::span<u8> output) {
133 handle_description->size, 129 handle_description->size,
134 Kernel::KMemoryPermission::None, true, false) 130 Kernel::KMemoryPermission::None, true, false)
135 .IsSuccess()); 131 .IsSuccess());
136 std::memcpy(output.data(), &params, sizeof(params));
137 return result; 132 return result;
138} 133}
139 134
140NvResult nvmap::IocGetId(std::span<const u8> input, std::span<u8> output) { 135NvResult nvmap::IocGetId(IocGetIdParams& params) {
141 IocGetIdParams params;
142 std::memcpy(&params, input.data(), sizeof(params));
143
144 LOG_DEBUG(Service_NVDRV, "called"); 136 LOG_DEBUG(Service_NVDRV, "called");
145 137
146 // See the comment in FromId for extra info on this function 138 // See the comment in FromId for extra info on this function
@@ -157,14 +149,10 @@ NvResult nvmap::IocGetId(std::span<const u8> input, std::span<u8> output) {
157 } 149 }
158 150
159 params.id = handle_description->id; 151 params.id = handle_description->id;
160 std::memcpy(output.data(), &params, sizeof(params));
161 return NvResult::Success; 152 return NvResult::Success;
162} 153}
163 154
164NvResult nvmap::IocFromId(std::span<const u8> input, std::span<u8> output) { 155NvResult nvmap::IocFromId(IocFromIdParams& params) {
165 IocFromIdParams params;
166 std::memcpy(&params, input.data(), sizeof(params));
167
168 LOG_DEBUG(Service_NVDRV, "called, id:{}", params.id); 156 LOG_DEBUG(Service_NVDRV, "called, id:{}", params.id);
169 157
170 // Handles and IDs are always the same value in nvmap however IDs can be used globally given the 158 // Handles and IDs are always the same value in nvmap however IDs can be used globally given the
@@ -188,16 +176,12 @@ NvResult nvmap::IocFromId(std::span<const u8> input, std::span<u8> output) {
188 return result; 176 return result;
189 } 177 }
190 params.handle = handle_description->id; 178 params.handle = handle_description->id;
191 std::memcpy(output.data(), &params, sizeof(params));
192 return NvResult::Success; 179 return NvResult::Success;
193} 180}
194 181
195NvResult nvmap::IocParam(std::span<const u8> input, std::span<u8> output) { 182NvResult nvmap::IocParam(IocParamParams& params) {
196 enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 }; 183 enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 };
197 184
198 IocParamParams params;
199 std::memcpy(&params, input.data(), sizeof(params));
200
201 LOG_DEBUG(Service_NVDRV, "called type={}", params.param); 185 LOG_DEBUG(Service_NVDRV, "called type={}", params.param);
202 186
203 if (!params.handle) { 187 if (!params.handle) {
@@ -237,14 +221,10 @@ NvResult nvmap::IocParam(std::span<const u8> input, std::span<u8> output) {
237 return NvResult::BadValue; 221 return NvResult::BadValue;
238 } 222 }
239 223
240 std::memcpy(output.data(), &params, sizeof(params));
241 return NvResult::Success; 224 return NvResult::Success;
242} 225}
243 226
244NvResult nvmap::IocFree(std::span<const u8> input, std::span<u8> output) { 227NvResult nvmap::IocFree(IocFreeParams& params) {
245 IocFreeParams params;
246 std::memcpy(&params, input.data(), sizeof(params));
247
248 LOG_DEBUG(Service_NVDRV, "called"); 228 LOG_DEBUG(Service_NVDRV, "called");
249 229
250 if (!params.handle) { 230 if (!params.handle) {
@@ -267,7 +247,6 @@ NvResult nvmap::IocFree(std::span<const u8> input, std::span<u8> output) {
267 // This is possible when there's internal dups or other duplicates. 247 // This is possible when there's internal dups or other duplicates.
268 } 248 }
269 249
270 std::memcpy(output.data(), &params, sizeof(params));
271 return NvResult::Success; 250 return NvResult::Success;
272} 251}
273 252
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index 4c0cc71cd..049c11028 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -99,12 +99,12 @@ public:
99 }; 99 };
100 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); 100 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
101 101
102 NvResult IocCreate(std::span<const u8> input, std::span<u8> output); 102 NvResult IocCreate(IocCreateParams& params);
103 NvResult IocAlloc(std::span<const u8> input, std::span<u8> output); 103 NvResult IocAlloc(IocAllocParams& params);
104 NvResult IocGetId(std::span<const u8> input, std::span<u8> output); 104 NvResult IocGetId(IocGetIdParams& params);
105 NvResult IocFromId(std::span<const u8> input, std::span<u8> output); 105 NvResult IocFromId(IocFromIdParams& params);
106 NvResult IocParam(std::span<const u8> input, std::span<u8> output); 106 NvResult IocParam(IocParamParams& params);
107 NvResult IocFree(std::span<const u8> input, std::span<u8> output); 107 NvResult IocFree(IocFreeParams& params);
108 108
109private: 109private:
110 /// Id to use for the next handle that is created. 110 /// Id to use for the next handle that is created.
diff --git a/src/core/hle/service/nvnflinger/buffer_item.h b/src/core/hle/service/nvnflinger/buffer_item.h
index 3da8cc3aa..7fd808f54 100644
--- a/src/core/hle/service/nvnflinger/buffer_item.h
+++ b/src/core/hle/service/nvnflinger/buffer_item.h
@@ -15,7 +15,7 @@
15 15
16namespace Service::android { 16namespace Service::android {
17 17
18struct GraphicBuffer; 18class GraphicBuffer;
19 19
20class BufferItem final { 20class BufferItem final {
21public: 21public:
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
index 51291539d..d91886bed 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
@@ -5,7 +5,6 @@
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp 5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp
6 6
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/hle/service/nvdrv/core/nvmap.h"
9#include "core/hle/service/nvnflinger/buffer_item.h" 8#include "core/hle/service/nvnflinger/buffer_item.h"
10#include "core/hle/service/nvnflinger/buffer_queue_consumer.h" 9#include "core/hle/service/nvnflinger/buffer_queue_consumer.h"
11#include "core/hle/service/nvnflinger/buffer_queue_core.h" 10#include "core/hle/service/nvnflinger/buffer_queue_core.h"
@@ -14,9 +13,8 @@
14 13
15namespace Service::android { 14namespace Service::android {
16 15
17BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_, 16BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_)
18 Service::Nvidia::NvCore::NvMap& nvmap_) 17 : core{std::move(core_)}, slots{core->slots} {}
19 : core{std::move(core_)}, slots{core->slots}, nvmap(nvmap_) {}
20 18
21BufferQueueConsumer::~BufferQueueConsumer() = default; 19BufferQueueConsumer::~BufferQueueConsumer() = default;
22 20
@@ -136,8 +134,6 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
136 134
137 slots[slot].buffer_state = BufferState::Free; 135 slots[slot].buffer_state = BufferState::Free;
138 136
139 nvmap.FreeHandle(slots[slot].graphic_buffer->BufferId(), true);
140
141 listener = core->connected_producer_listener; 137 listener = core->connected_producer_listener;
142 138
143 LOG_DEBUG(Service_Nvnflinger, "releasing slot {}", slot); 139 LOG_DEBUG(Service_Nvnflinger, "releasing slot {}", slot);
@@ -175,6 +171,25 @@ Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_
175 return Status::NoError; 171 return Status::NoError;
176} 172}
177 173
174Status BufferQueueConsumer::Disconnect() {
175 LOG_DEBUG(Service_Nvnflinger, "called");
176
177 std::scoped_lock lock{core->mutex};
178
179 if (core->consumer_listener == nullptr) {
180 LOG_ERROR(Service_Nvnflinger, "no consumer is connected");
181 return Status::BadValue;
182 }
183
184 core->is_abandoned = true;
185 core->consumer_listener = nullptr;
186 core->queue.clear();
187 core->FreeAllBuffersLocked();
188 core->SignalDequeueCondition();
189
190 return Status::NoError;
191}
192
178Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) { 193Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) {
179 if (out_slot_mask == nullptr) { 194 if (out_slot_mask == nullptr) {
180 LOG_ERROR(Service_Nvnflinger, "out_slot_mask may not be nullptr"); 195 LOG_ERROR(Service_Nvnflinger, "out_slot_mask may not be nullptr");
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.h b/src/core/hle/service/nvnflinger/buffer_queue_consumer.h
index 50ed0bb5f..0a61e8dbd 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.h
+++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.h
@@ -13,10 +13,6 @@
13#include "core/hle/service/nvnflinger/buffer_queue_defs.h" 13#include "core/hle/service/nvnflinger/buffer_queue_defs.h"
14#include "core/hle/service/nvnflinger/status.h" 14#include "core/hle/service/nvnflinger/status.h"
15 15
16namespace Service::Nvidia::NvCore {
17class NvMap;
18} // namespace Service::Nvidia::NvCore
19
20namespace Service::android { 16namespace Service::android {
21 17
22class BufferItem; 18class BufferItem;
@@ -25,19 +21,18 @@ class IConsumerListener;
25 21
26class BufferQueueConsumer final { 22class BufferQueueConsumer final {
27public: 23public:
28 explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_, 24 explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_);
29 Service::Nvidia::NvCore::NvMap& nvmap_);
30 ~BufferQueueConsumer(); 25 ~BufferQueueConsumer();
31 26
32 Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present); 27 Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present);
33 Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence); 28 Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence);
34 Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app); 29 Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app);
30 Status Disconnect();
35 Status GetReleasedBuffers(u64* out_slot_mask); 31 Status GetReleasedBuffers(u64* out_slot_mask);
36 32
37private: 33private:
38 std::shared_ptr<BufferQueueCore> core; 34 std::shared_ptr<BufferQueueCore> core;
39 BufferQueueDefs::SlotsType& slots; 35 BufferQueueDefs::SlotsType& slots;
40 Service::Nvidia::NvCore::NvMap& nvmap;
41}; 36};
42 37
43} // namespace Service::android 38} // namespace Service::android
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp
index ed66f6f5b..4ed5e5978 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp
@@ -14,24 +14,12 @@ BufferQueueCore::BufferQueueCore() = default;
14 14
15BufferQueueCore::~BufferQueueCore() = default; 15BufferQueueCore::~BufferQueueCore() = default;
16 16
17void BufferQueueCore::NotifyShutdown() {
18 std::scoped_lock lock{mutex};
19
20 is_shutting_down = true;
21
22 SignalDequeueCondition();
23}
24
25void BufferQueueCore::SignalDequeueCondition() { 17void BufferQueueCore::SignalDequeueCondition() {
26 dequeue_possible.store(true); 18 dequeue_possible.store(true);
27 dequeue_condition.notify_all(); 19 dequeue_condition.notify_all();
28} 20}
29 21
30bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) { 22bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) {
31 if (is_shutting_down) {
32 return false;
33 }
34
35 dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); }); 23 dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); });
36 dequeue_possible.store(false); 24 dequeue_possible.store(false);
37 25
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.h b/src/core/hle/service/nvnflinger/buffer_queue_core.h
index 9164f08a0..e513d183b 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_core.h
+++ b/src/core/hle/service/nvnflinger/buffer_queue_core.h
@@ -34,8 +34,6 @@ public:
34 BufferQueueCore(); 34 BufferQueueCore();
35 ~BufferQueueCore(); 35 ~BufferQueueCore();
36 36
37 void NotifyShutdown();
38
39private: 37private:
40 void SignalDequeueCondition(); 38 void SignalDequeueCondition();
41 bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk); 39 bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk);
@@ -74,7 +72,6 @@ private:
74 u32 transform_hint{}; 72 u32 transform_hint{};
75 bool is_allocating{}; 73 bool is_allocating{};
76 mutable std::condition_variable_any is_allocating_condition; 74 mutable std::condition_variable_any is_allocating_condition;
77 bool is_shutting_down{};
78}; 75};
79 76
80} // namespace Service::android 77} // namespace Service::android
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
index 6e7a49658..5d8762d25 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
@@ -13,7 +13,6 @@
13#include "core/hle/kernel/kernel.h" 13#include "core/hle/kernel/kernel.h"
14#include "core/hle/service/hle_ipc.h" 14#include "core/hle/service/hle_ipc.h"
15#include "core/hle/service/kernel_helpers.h" 15#include "core/hle/service/kernel_helpers.h"
16#include "core/hle/service/nvdrv/core/nvmap.h"
17#include "core/hle/service/nvnflinger/buffer_queue_core.h" 16#include "core/hle/service/nvnflinger/buffer_queue_core.h"
18#include "core/hle/service/nvnflinger/buffer_queue_producer.h" 17#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
19#include "core/hle/service/nvnflinger/consumer_listener.h" 18#include "core/hle/service/nvnflinger/consumer_listener.h"
@@ -533,8 +532,6 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
533 item.is_droppable = core->dequeue_buffer_cannot_block || async; 532 item.is_droppable = core->dequeue_buffer_cannot_block || async;
534 item.swap_interval = swap_interval; 533 item.swap_interval = swap_interval;
535 534
536 nvmap.DuplicateHandle(item.graphic_buffer->BufferId(), true);
537
538 sticky_transform = sticky_transform_; 535 sticky_transform = sticky_transform_;
539 536
540 if (core->queue.empty()) { 537 if (core->queue.empty()) {
@@ -744,19 +741,13 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
744 return Status::NoError; 741 return Status::NoError;
745 } 742 }
746 743
747 // HACK: We are not Android. Remove handle for items in queue, and clear queue.
748 // Allows synchronous destruction of nvmap handles.
749 for (auto& item : core->queue) {
750 nvmap.FreeHandle(item.graphic_buffer->BufferId(), true);
751 }
752 core->queue.clear();
753
754 switch (api) { 744 switch (api) {
755 case NativeWindowApi::Egl: 745 case NativeWindowApi::Egl:
756 case NativeWindowApi::Cpu: 746 case NativeWindowApi::Cpu:
757 case NativeWindowApi::Media: 747 case NativeWindowApi::Media:
758 case NativeWindowApi::Camera: 748 case NativeWindowApi::Camera:
759 if (core->connected_api == api) { 749 if (core->connected_api == api) {
750 core->queue.clear();
760 core->FreeAllBuffersLocked(); 751 core->FreeAllBuffersLocked();
761 core->connected_producer_listener = nullptr; 752 core->connected_producer_listener = nullptr;
762 core->connected_api = NativeWindowApi::NoConnectedApi; 753 core->connected_api = NativeWindowApi::NoConnectedApi;
@@ -785,7 +776,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
785} 776}
786 777
787Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, 778Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
788 const std::shared_ptr<GraphicBuffer>& buffer) { 779 const std::shared_ptr<NvGraphicBuffer>& buffer) {
789 LOG_DEBUG(Service_Nvnflinger, "slot {}", slot); 780 LOG_DEBUG(Service_Nvnflinger, "slot {}", slot);
790 781
791 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { 782 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
@@ -796,7 +787,7 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
796 787
797 slots[slot] = {}; 788 slots[slot] = {};
798 slots[slot].fence = Fence::NoFence(); 789 slots[slot].fence = Fence::NoFence();
799 slots[slot].graphic_buffer = buffer; 790 slots[slot].graphic_buffer = std::make_shared<GraphicBuffer>(nvmap, buffer);
800 slots[slot].frame_number = 0; 791 slots[slot].frame_number = 0;
801 792
802 // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for 793 // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for
@@ -839,7 +830,7 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u
839 } 830 }
840 case TransactionId::SetPreallocatedBuffer: { 831 case TransactionId::SetPreallocatedBuffer: {
841 const auto slot = parcel_in.Read<s32>(); 832 const auto slot = parcel_in.Read<s32>();
842 const auto buffer = parcel_in.ReadObject<GraphicBuffer>(); 833 const auto buffer = parcel_in.ReadObject<NvGraphicBuffer>();
843 834
844 status = SetPreallocatedBuffer(slot, buffer); 835 status = SetPreallocatedBuffer(slot, buffer);
845 break; 836 break;
@@ -867,7 +858,7 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u
867 858
868 status = RequestBuffer(slot, &buf); 859 status = RequestBuffer(slot, &buf);
869 860
870 parcel_out.WriteFlattenedObject(buf); 861 parcel_out.WriteFlattenedObject<NvGraphicBuffer>(buf.get());
871 break; 862 break;
872 } 863 }
873 case TransactionId::QueueBuffer: { 864 case TransactionId::QueueBuffer: {
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.h b/src/core/hle/service/nvnflinger/buffer_queue_producer.h
index d4201c104..64c17d56c 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_producer.h
+++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.h
@@ -38,6 +38,7 @@ namespace Service::android {
38 38
39class BufferQueueCore; 39class BufferQueueCore;
40class IProducerListener; 40class IProducerListener;
41struct NvGraphicBuffer;
41 42
42class BufferQueueProducer final : public IBinder { 43class BufferQueueProducer final : public IBinder {
43public: 44public:
@@ -65,7 +66,7 @@ public:
65 bool producer_controlled_by_app, QueueBufferOutput* output); 66 bool producer_controlled_by_app, QueueBufferOutput* output);
66 67
67 Status Disconnect(NativeWindowApi api); 68 Status Disconnect(NativeWindowApi api);
68 Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<GraphicBuffer>& buffer); 69 Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<NvGraphicBuffer>& buffer);
69 70
70private: 71private:
71 BufferQueueProducer(const BufferQueueProducer&) = delete; 72 BufferQueueProducer(const BufferQueueProducer&) = delete;
diff --git a/src/core/hle/service/nvnflinger/buffer_slot.h b/src/core/hle/service/nvnflinger/buffer_slot.h
index d8c9dec3b..d25bca049 100644
--- a/src/core/hle/service/nvnflinger/buffer_slot.h
+++ b/src/core/hle/service/nvnflinger/buffer_slot.h
@@ -13,7 +13,7 @@
13 13
14namespace Service::android { 14namespace Service::android {
15 15
16struct GraphicBuffer; 16class GraphicBuffer;
17 17
18enum class BufferState : u32 { 18enum class BufferState : u32 {
19 Free = 0, 19 Free = 0,
diff --git a/src/core/hle/service/nvnflinger/buffer_transform_flags.h b/src/core/hle/service/nvnflinger/buffer_transform_flags.h
index 67aa5dad6..ffe579718 100644
--- a/src/core/hle/service/nvnflinger/buffer_transform_flags.h
+++ b/src/core/hle/service/nvnflinger/buffer_transform_flags.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "common/common_funcs.h"
6#include "common/common_types.h" 7#include "common/common_types.h"
7 8
8namespace Service::android { 9namespace Service::android {
@@ -21,5 +22,6 @@ enum class BufferTransformFlags : u32 {
21 /// Rotate source image 270 degrees clockwise 22 /// Rotate source image 270 degrees clockwise
22 Rotate270 = 0x07, 23 Rotate270 = 0x07,
23}; 24};
25DECLARE_ENUM_FLAG_OPERATORS(BufferTransformFlags);
24 26
25} // namespace Service::android 27} // namespace Service::android
diff --git a/src/core/hle/service/nvnflinger/consumer_base.cpp b/src/core/hle/service/nvnflinger/consumer_base.cpp
index 4dcda8dac..1059e72bf 100644
--- a/src/core/hle/service/nvnflinger/consumer_base.cpp
+++ b/src/core/hle/service/nvnflinger/consumer_base.cpp
@@ -27,6 +27,26 @@ void ConsumerBase::Connect(bool controlled_by_app) {
27 consumer->Connect(shared_from_this(), controlled_by_app); 27 consumer->Connect(shared_from_this(), controlled_by_app);
28} 28}
29 29
30void ConsumerBase::Abandon() {
31 LOG_DEBUG(Service_Nvnflinger, "called");
32
33 std::scoped_lock lock{mutex};
34
35 if (!is_abandoned) {
36 this->AbandonLocked();
37 is_abandoned = true;
38 }
39}
40
41void ConsumerBase::AbandonLocked() {
42 for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
43 this->FreeBufferLocked(i);
44 }
45 // disconnect from the BufferQueue
46 consumer->Disconnect();
47 consumer = nullptr;
48}
49
30void ConsumerBase::FreeBufferLocked(s32 slot_index) { 50void ConsumerBase::FreeBufferLocked(s32 slot_index) {
31 LOG_DEBUG(Service_Nvnflinger, "slot_index={}", slot_index); 51 LOG_DEBUG(Service_Nvnflinger, "slot_index={}", slot_index);
32 52
diff --git a/src/core/hle/service/nvnflinger/consumer_base.h b/src/core/hle/service/nvnflinger/consumer_base.h
index 264829414..ea3e9e97a 100644
--- a/src/core/hle/service/nvnflinger/consumer_base.h
+++ b/src/core/hle/service/nvnflinger/consumer_base.h
@@ -24,6 +24,7 @@ class BufferQueueConsumer;
24class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> { 24class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> {
25public: 25public:
26 void Connect(bool controlled_by_app); 26 void Connect(bool controlled_by_app);
27 void Abandon();
27 28
28protected: 29protected:
29 explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_); 30 explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_);
@@ -34,6 +35,7 @@ protected:
34 void OnBuffersReleased() override; 35 void OnBuffersReleased() override;
35 void OnSidebandStreamChanged() override; 36 void OnSidebandStreamChanged() override;
36 37
38 void AbandonLocked();
37 void FreeBufferLocked(s32 slot_index); 39 void FreeBufferLocked(s32 slot_index);
38 Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when); 40 Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when);
39 Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer>& graphic_buffer); 41 Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer>& graphic_buffer);
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
index 2e29bc848..d7db24f42 100644
--- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
+++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
@@ -71,24 +71,17 @@ Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address,
71 R_SUCCEED(); 71 R_SUCCEED();
72} 72}
73 73
74template <typename T>
75std::span<u8> SerializeIoc(T& params) {
76 return std::span(reinterpret_cast<u8*>(std::addressof(params)), sizeof(T));
77}
78
79Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap, u32 size) { 74Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap, u32 size) {
80 // Create a handle. 75 // Create a handle.
81 Nvidia::Devices::nvmap::IocCreateParams create_in_params{ 76 Nvidia::Devices::nvmap::IocCreateParams create_params{
82 .size = size, 77 .size = size,
83 .handle = 0, 78 .handle = 0,
84 }; 79 };
85 Nvidia::Devices::nvmap::IocCreateParams create_out_params{}; 80 R_UNLESS(nvmap.IocCreate(create_params) == Nvidia::NvResult::Success,
86 R_UNLESS(nvmap.IocCreate(SerializeIoc(create_in_params), SerializeIoc(create_out_params)) ==
87 Nvidia::NvResult::Success,
88 VI::ResultOperationFailed); 81 VI::ResultOperationFailed);
89 82
90 // Assign the output handle. 83 // Assign the output handle.
91 *out_nv_map_handle = create_out_params.handle; 84 *out_nv_map_handle = create_params.handle;
92 85
93 // We succeeded. 86 // We succeeded.
94 R_SUCCEED(); 87 R_SUCCEED();
@@ -96,13 +89,10 @@ Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap,
96 89
97Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle) { 90Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle) {
98 // Free the handle. 91 // Free the handle.
99 Nvidia::Devices::nvmap::IocFreeParams free_in_params{ 92 Nvidia::Devices::nvmap::IocFreeParams free_params{
100 .handle = handle, 93 .handle = handle,
101 }; 94 };
102 Nvidia::Devices::nvmap::IocFreeParams free_out_params{}; 95 R_UNLESS(nvmap.IocFree(free_params) == Nvidia::NvResult::Success, VI::ResultOperationFailed);
103 R_UNLESS(nvmap.IocFree(SerializeIoc(free_in_params), SerializeIoc(free_out_params)) ==
104 Nvidia::NvResult::Success,
105 VI::ResultOperationFailed);
106 96
107 // We succeeded. 97 // We succeeded.
108 R_SUCCEED(); 98 R_SUCCEED();
@@ -111,7 +101,7 @@ Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle) {
111Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::ProcessAddress buffer, 101Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::ProcessAddress buffer,
112 u32 size) { 102 u32 size) {
113 // Assign the allocated memory to the handle. 103 // Assign the allocated memory to the handle.
114 Nvidia::Devices::nvmap::IocAllocParams alloc_in_params{ 104 Nvidia::Devices::nvmap::IocAllocParams alloc_params{
115 .handle = handle, 105 .handle = handle,
116 .heap_mask = 0, 106 .heap_mask = 0,
117 .flags = {}, 107 .flags = {},
@@ -119,10 +109,7 @@ Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::Proce
119 .kind = 0, 109 .kind = 0,
120 .address = GetInteger(buffer), 110 .address = GetInteger(buffer),
121 }; 111 };
122 Nvidia::Devices::nvmap::IocAllocParams alloc_out_params{}; 112 R_UNLESS(nvmap.IocAlloc(alloc_params) == Nvidia::NvResult::Success, VI::ResultOperationFailed);
123 R_UNLESS(nvmap.IocAlloc(SerializeIoc(alloc_in_params), SerializeIoc(alloc_out_params)) ==
124 Nvidia::NvResult::Success,
125 VI::ResultOperationFailed);
126 113
127 // We succeeded. 114 // We succeeded.
128 R_SUCCEED(); 115 R_SUCCEED();
@@ -179,7 +166,7 @@ constexpr SharedMemoryPoolLayout SharedBufferPoolLayout = [] {
179}(); 166}();
180 167
181void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) { 168void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) {
182 auto buffer = std::make_shared<android::GraphicBuffer>(); 169 auto buffer = std::make_shared<android::NvGraphicBuffer>();
183 buffer->width = SharedBufferWidth; 170 buffer->width = SharedBufferWidth;
184 buffer->height = SharedBufferHeight; 171 buffer->height = SharedBufferHeight;
185 buffer->stride = SharedBufferBlockLinearStride; 172 buffer->stride = SharedBufferBlockLinearStride;
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp
index bebb45eae..0745434c5 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.cpp
+++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp
@@ -47,7 +47,10 @@ void Nvnflinger::SplitVSync(std::stop_token stop_token) {
47 vsync_signal.Wait(); 47 vsync_signal.Wait();
48 48
49 const auto lock_guard = Lock(); 49 const auto lock_guard = Lock();
50 Compose(); 50
51 if (!is_abandoned) {
52 Compose();
53 }
51 } 54 }
52} 55}
53 56
@@ -98,7 +101,6 @@ Nvnflinger::~Nvnflinger() {
98 } 101 }
99 102
100 ShutdownLayers(); 103 ShutdownLayers();
101 vsync_thread = {};
102 104
103 if (nvdrv) { 105 if (nvdrv) {
104 nvdrv->Close(disp_fd); 106 nvdrv->Close(disp_fd);
@@ -106,12 +108,20 @@ Nvnflinger::~Nvnflinger() {
106} 108}
107 109
108void Nvnflinger::ShutdownLayers() { 110void Nvnflinger::ShutdownLayers() {
109 const auto lock_guard = Lock(); 111 // Abandon consumers.
110 for (auto& display : displays) { 112 {
111 for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { 113 const auto lock_guard = Lock();
112 display.GetLayer(layer).Core().NotifyShutdown(); 114 for (auto& display : displays) {
115 for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) {
116 display.GetLayer(layer).GetConsumer().Abandon();
117 }
113 } 118 }
119
120 is_abandoned = true;
114 } 121 }
122
123 // Join the vsync thread, if it exists.
124 vsync_thread = {};
115} 125}
116 126
117void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { 127void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h
index 959d8b46b..f5d73acdb 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.h
+++ b/src/core/hle/service/nvnflinger/nvnflinger.h
@@ -140,6 +140,8 @@ private:
140 140
141 s32 swap_interval = 1; 141 s32 swap_interval = 1;
142 142
143 bool is_abandoned = false;
144
143 /// Event that handles screen composition. 145 /// Event that handles screen composition.
144 std::shared_ptr<Core::Timing::EventType> multi_composition_event; 146 std::shared_ptr<Core::Timing::EventType> multi_composition_event;
145 std::shared_ptr<Core::Timing::EventType> single_composition_event; 147 std::shared_ptr<Core::Timing::EventType> single_composition_event;
diff --git a/src/core/hle/service/nvnflinger/status.h b/src/core/hle/service/nvnflinger/status.h
index 7af166c40..3fa0fe15b 100644
--- a/src/core/hle/service/nvnflinger/status.h
+++ b/src/core/hle/service/nvnflinger/status.h
@@ -19,7 +19,7 @@ enum class Status : s32 {
19 Busy = -16, 19 Busy = -16,
20 NoInit = -19, 20 NoInit = -19,
21 BadValue = -22, 21 BadValue = -22,
22 InvalidOperation = -37, 22 InvalidOperation = -38,
23 BufferNeedsReallocation = 1, 23 BufferNeedsReallocation = 1,
24 ReleaseAllBuffers = 2, 24 ReleaseAllBuffers = 2,
25}; 25};
diff --git a/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp b/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp
new file mode 100644
index 000000000..ce70946ec
--- /dev/null
+++ b/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp
@@ -0,0 +1,34 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/nvdrv/core/nvmap.h"
5#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
6
7namespace Service::android {
8
9static NvGraphicBuffer GetBuffer(std::shared_ptr<NvGraphicBuffer>& buffer) {
10 if (buffer) {
11 return *buffer;
12 } else {
13 return {};
14 }
15}
16
17GraphicBuffer::GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
18 : NvGraphicBuffer(width_, height_, format_, usage_), m_nvmap(nullptr) {}
19
20GraphicBuffer::GraphicBuffer(Service::Nvidia::NvCore::NvMap& nvmap,
21 std::shared_ptr<NvGraphicBuffer> buffer)
22 : NvGraphicBuffer(GetBuffer(buffer)), m_nvmap(std::addressof(nvmap)) {
23 if (this->BufferId() > 0) {
24 m_nvmap->DuplicateHandle(this->BufferId(), true);
25 }
26}
27
28GraphicBuffer::~GraphicBuffer() {
29 if (m_nvmap != nullptr && this->BufferId() > 0) {
30 m_nvmap->FreeHandle(this->BufferId(), true);
31 }
32}
33
34} // namespace Service::android
diff --git a/src/core/hle/service/nvnflinger/ui/graphic_buffer.h b/src/core/hle/service/nvnflinger/ui/graphic_buffer.h
index 3eac5cedd..da430aa75 100644
--- a/src/core/hle/service/nvnflinger/ui/graphic_buffer.h
+++ b/src/core/hle/service/nvnflinger/ui/graphic_buffer.h
@@ -6,16 +6,22 @@
6 6
7#pragma once 7#pragma once
8 8
9#include <memory>
10
9#include "common/common_funcs.h" 11#include "common/common_funcs.h"
10#include "common/common_types.h" 12#include "common/common_types.h"
11#include "core/hle/service/nvnflinger/pixel_format.h" 13#include "core/hle/service/nvnflinger/pixel_format.h"
12 14
15namespace Service::Nvidia::NvCore {
16class NvMap;
17} // namespace Service::Nvidia::NvCore
18
13namespace Service::android { 19namespace Service::android {
14 20
15struct GraphicBuffer final { 21struct NvGraphicBuffer {
16 constexpr GraphicBuffer() = default; 22 constexpr NvGraphicBuffer() = default;
17 23
18 constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_) 24 constexpr NvGraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
19 : width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_}, 25 : width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_},
20 usage{static_cast<s32>(usage_)} {} 26 usage{static_cast<s32>(usage_)} {}
21 27
@@ -93,6 +99,17 @@ struct GraphicBuffer final {
93 u32 offset{}; 99 u32 offset{};
94 INSERT_PADDING_WORDS(60); 100 INSERT_PADDING_WORDS(60);
95}; 101};
96static_assert(sizeof(GraphicBuffer) == 0x16C, "GraphicBuffer has wrong size"); 102static_assert(sizeof(NvGraphicBuffer) == 0x16C, "NvGraphicBuffer has wrong size");
103
104class GraphicBuffer final : public NvGraphicBuffer {
105public:
106 explicit GraphicBuffer(u32 width, u32 height, PixelFormat format, u32 usage);
107 explicit GraphicBuffer(Service::Nvidia::NvCore::NvMap& nvmap,
108 std::shared_ptr<NvGraphicBuffer> buffer);
109 ~GraphicBuffer();
110
111private:
112 Service::Nvidia::NvCore::NvMap* m_nvmap{};
113};
97 114
98} // namespace Service::android 115} // namespace Service::android
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index ec3af80af..f5edfdc8b 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -19,19 +19,8 @@
19 19
20namespace Service::Set { 20namespace Service::Set {
21 21
22namespace { 22Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system,
23constexpr u64 SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET = 0x05; 23 GetFirmwareVersionType type) {
24
25enum class GetFirmwareVersionType {
26 Version1,
27 Version2,
28};
29
30void GetFirmwareVersionImpl(Core::System& system, HLERequestContext& ctx,
31 GetFirmwareVersionType type) {
32 ASSERT_MSG(ctx.GetWriteBufferSize() == 0x100,
33 "FirmwareVersion output buffer must be 0x100 bytes in size!");
34
35 constexpr u64 FirmwareVersionSystemDataId = 0x0100000000000809; 24 constexpr u64 FirmwareVersionSystemDataId = 0x0100000000000809;
36 auto& fsc = system.GetFileSystemController(); 25 auto& fsc = system.GetFileSystemController();
37 26
@@ -52,39 +41,34 @@ void GetFirmwareVersionImpl(Core::System& system, HLERequestContext& ctx,
52 FileSys::SystemArchive::SynthesizeSystemArchive(FirmwareVersionSystemDataId)); 41 FileSys::SystemArchive::SynthesizeSystemArchive(FirmwareVersionSystemDataId));
53 } 42 }
54 43
55 const auto early_exit_failure = [&ctx](std::string_view desc, Result code) { 44 const auto early_exit_failure = [](std::string_view desc, Result code) {
56 LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).", 45 LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).",
57 desc); 46 desc);
58 IPC::ResponseBuilder rb{ctx, 2}; 47 return code;
59 rb.Push(code);
60 }; 48 };
61 49
62 const auto ver_file = romfs->GetFile("file"); 50 const auto ver_file = romfs->GetFile("file");
63 if (ver_file == nullptr) { 51 if (ver_file == nullptr) {
64 early_exit_failure("The system version archive didn't contain the file 'file'.", 52 return early_exit_failure("The system version archive didn't contain the file 'file'.",
65 FileSys::ERROR_INVALID_ARGUMENT); 53 FileSys::ERROR_INVALID_ARGUMENT);
66 return;
67 } 54 }
68 55
69 auto data = ver_file->ReadAllBytes(); 56 auto data = ver_file->ReadAllBytes();
70 if (data.size() != 0x100) { 57 if (data.size() != sizeof(FirmwareVersionFormat)) {
71 early_exit_failure("The system version file 'file' was not the correct size.", 58 return early_exit_failure("The system version file 'file' was not the correct size.",
72 FileSys::ERROR_OUT_OF_BOUNDS); 59 FileSys::ERROR_OUT_OF_BOUNDS);
73 return;
74 } 60 }
75 61
62 std::memcpy(&out_firmware, data.data(), sizeof(FirmwareVersionFormat));
63
76 // If the command is GetFirmwareVersion (as opposed to GetFirmwareVersion2), hardware will 64 // If the command is GetFirmwareVersion (as opposed to GetFirmwareVersion2), hardware will
77 // zero out the REVISION_MINOR field. 65 // zero out the REVISION_MINOR field.
78 if (type == GetFirmwareVersionType::Version1) { 66 if (type == GetFirmwareVersionType::Version1) {
79 data[SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET] = 0; 67 out_firmware.revision_minor = 0;
80 } 68 }
81 69
82 ctx.WriteBuffer(data); 70 return ResultSuccess;
83
84 IPC::ResponseBuilder rb{ctx, 2};
85 rb.Push(ResultSuccess);
86} 71}
87} // Anonymous namespace
88 72
89void SET_SYS::SetLanguageCode(HLERequestContext& ctx) { 73void SET_SYS::SetLanguageCode(HLERequestContext& ctx) {
90 IPC::RequestParser rp{ctx}; 74 IPC::RequestParser rp{ctx};
@@ -98,12 +82,32 @@ void SET_SYS::SetLanguageCode(HLERequestContext& ctx) {
98 82
99void SET_SYS::GetFirmwareVersion(HLERequestContext& ctx) { 83void SET_SYS::GetFirmwareVersion(HLERequestContext& ctx) {
100 LOG_DEBUG(Service_SET, "called"); 84 LOG_DEBUG(Service_SET, "called");
101 GetFirmwareVersionImpl(system, ctx, GetFirmwareVersionType::Version1); 85
86 FirmwareVersionFormat firmware_data{};
87 const auto result =
88 GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version1);
89
90 if (result.IsSuccess()) {
91 ctx.WriteBuffer(firmware_data);
92 }
93
94 IPC::ResponseBuilder rb{ctx, 2};
95 rb.Push(result);
102} 96}
103 97
104void SET_SYS::GetFirmwareVersion2(HLERequestContext& ctx) { 98void SET_SYS::GetFirmwareVersion2(HLERequestContext& ctx) {
105 LOG_DEBUG(Service_SET, "called"); 99 LOG_DEBUG(Service_SET, "called");
106 GetFirmwareVersionImpl(system, ctx, GetFirmwareVersionType::Version2); 100
101 FirmwareVersionFormat firmware_data{};
102 const auto result =
103 GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version2);
104
105 if (result.IsSuccess()) {
106 ctx.WriteBuffer(firmware_data);
107 }
108
109 IPC::ResponseBuilder rb{ctx, 2};
110 rb.Push(result);
107} 111}
108 112
109void SET_SYS::GetAccountSettings(HLERequestContext& ctx) { 113void SET_SYS::GetAccountSettings(HLERequestContext& ctx) {
@@ -431,8 +435,7 @@ void SET_SYS::GetAutoUpdateEnableFlag(HLERequestContext& ctx) {
431void SET_SYS::GetBatteryPercentageFlag(HLERequestContext& ctx) { 435void SET_SYS::GetBatteryPercentageFlag(HLERequestContext& ctx) {
432 u8 battery_percentage_flag{1}; 436 u8 battery_percentage_flag{1};
433 437
434 LOG_WARNING(Service_SET, "(STUBBED) called, battery_percentage_flag={}", 438 LOG_DEBUG(Service_SET, "(STUBBED) called, battery_percentage_flag={}", battery_percentage_flag);
435 battery_percentage_flag);
436 439
437 IPC::ResponseBuilder rb{ctx, 3}; 440 IPC::ResponseBuilder rb{ctx, 3};
438 rb.Push(ResultSuccess); 441 rb.Push(ResultSuccess);
@@ -492,6 +495,29 @@ void SET_SYS::GetChineseTraditionalInputMethod(HLERequestContext& ctx) {
492 rb.PushEnum(ChineseTraditionalInputMethod::Unknown0); 495 rb.PushEnum(ChineseTraditionalInputMethod::Unknown0);
493} 496}
494 497
498void SET_SYS::GetHomeMenuScheme(HLERequestContext& ctx) {
499 LOG_DEBUG(Service_SET, "(STUBBED) called");
500
501 const HomeMenuScheme default_color = {
502 .main = 0xFF323232,
503 .back = 0xFF323232,
504 .sub = 0xFFFFFFFF,
505 .bezel = 0xFFFFFFFF,
506 .extra = 0xFF000000,
507 };
508
509 IPC::ResponseBuilder rb{ctx, 7};
510 rb.Push(ResultSuccess);
511 rb.PushRaw(default_color);
512}
513
514void SET_SYS::GetHomeMenuSchemeModel(HLERequestContext& ctx) {
515 LOG_WARNING(Service_SET, "(STUBBED) called");
516
517 IPC::ResponseBuilder rb{ctx, 3};
518 rb.Push(ResultSuccess);
519 rb.Push(0);
520}
495void SET_SYS::GetFieldTestingFlag(HLERequestContext& ctx) { 521void SET_SYS::GetFieldTestingFlag(HLERequestContext& ctx) {
496 LOG_WARNING(Service_SET, "(STUBBED) called"); 522 LOG_WARNING(Service_SET, "(STUBBED) called");
497 523
@@ -674,7 +700,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
674 {171, nullptr, "SetChineseTraditionalInputMethod"}, 700 {171, nullptr, "SetChineseTraditionalInputMethod"},
675 {172, nullptr, "GetPtmCycleCountReliability"}, 701 {172, nullptr, "GetPtmCycleCountReliability"},
676 {173, nullptr, "SetPtmCycleCountReliability"}, 702 {173, nullptr, "SetPtmCycleCountReliability"},
677 {174, nullptr, "GetHomeMenuScheme"}, 703 {174, &SET_SYS::GetHomeMenuScheme, "GetHomeMenuScheme"},
678 {175, nullptr, "GetThemeSettings"}, 704 {175, nullptr, "GetThemeSettings"},
679 {176, nullptr, "SetThemeSettings"}, 705 {176, nullptr, "SetThemeSettings"},
680 {177, nullptr, "GetThemeKey"}, 706 {177, nullptr, "GetThemeKey"},
@@ -685,7 +711,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
685 {182, nullptr, "SetT"}, 711 {182, nullptr, "SetT"},
686 {183, nullptr, "GetPlatformRegion"}, 712 {183, nullptr, "GetPlatformRegion"},
687 {184, nullptr, "SetPlatformRegion"}, 713 {184, nullptr, "SetPlatformRegion"},
688 {185, nullptr, "GetHomeMenuSchemeModel"}, 714 {185, &SET_SYS::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"},
689 {186, nullptr, "GetMemoryUsageRateFlag"}, 715 {186, nullptr, "GetMemoryUsageRateFlag"},
690 {187, nullptr, "GetTouchScreenMode"}, 716 {187, nullptr, "GetTouchScreenMode"},
691 {188, nullptr, "SetTouchScreenMode"}, 717 {188, nullptr, "SetTouchScreenMode"},
diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h
index c7dba2a9e..5f770fd32 100644
--- a/src/core/hle/service/set/set_sys.h
+++ b/src/core/hle/service/set/set_sys.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include "common/uuid.h" 6#include "common/uuid.h"
7#include "core/hle/result.h"
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8#include "core/hle/service/time/clock_types.h" 9#include "core/hle/service/time/clock_types.h"
9 10
@@ -12,6 +13,29 @@ class System;
12} 13}
13 14
14namespace Service::Set { 15namespace Service::Set {
16enum class LanguageCode : u64;
17enum class GetFirmwareVersionType {
18 Version1,
19 Version2,
20};
21
22struct FirmwareVersionFormat {
23 u8 major;
24 u8 minor;
25 u8 micro;
26 INSERT_PADDING_BYTES(1);
27 u8 revision_major;
28 u8 revision_minor;
29 INSERT_PADDING_BYTES(2);
30 std::array<char, 0x20> platform;
31 std::array<u8, 0x40> version_hash;
32 std::array<char, 0x18> display_version;
33 std::array<char, 0x80> display_title;
34};
35static_assert(sizeof(FirmwareVersionFormat) == 0x100, "FirmwareVersionFormat is an invalid size");
36
37Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system,
38 GetFirmwareVersionType type);
15 39
16class SET_SYS final : public ServiceFramework<SET_SYS> { 40class SET_SYS final : public ServiceFramework<SET_SYS> {
17public: 41public:
@@ -269,6 +293,16 @@ private:
269 }; 293 };
270 static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size"); 294 static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size");
271 295
296 /// This is nn::settings::system::HomeMenuScheme
297 struct HomeMenuScheme {
298 u32 main;
299 u32 back;
300 u32 sub;
301 u32 bezel;
302 u32 extra;
303 };
304 static_assert(sizeof(HomeMenuScheme) == 0x14, "HomeMenuScheme is incorrect size");
305
272 void SetLanguageCode(HLERequestContext& ctx); 306 void SetLanguageCode(HLERequestContext& ctx);
273 void GetFirmwareVersion(HLERequestContext& ctx); 307 void GetFirmwareVersion(HLERequestContext& ctx);
274 void GetFirmwareVersion2(HLERequestContext& ctx); 308 void GetFirmwareVersion2(HLERequestContext& ctx);
@@ -305,6 +339,8 @@ private:
305 void GetKeyboardLayout(HLERequestContext& ctx); 339 void GetKeyboardLayout(HLERequestContext& ctx);
306 void GetChineseTraditionalInputMethod(HLERequestContext& ctx); 340 void GetChineseTraditionalInputMethod(HLERequestContext& ctx);
307 void GetFieldTestingFlag(HLERequestContext& ctx); 341 void GetFieldTestingFlag(HLERequestContext& ctx);
342 void GetHomeMenuScheme(HLERequestContext& ctx);
343 void GetHomeMenuSchemeModel(HLERequestContext& ctx);
308 344
309 AccountSettings account_settings{ 345 AccountSettings account_settings{
310 .flags = {}, 346 .flags = {},
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 85849d5f3..dd652ca42 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -39,6 +39,18 @@ bool IsConnectionBased(Type type) {
39 } 39 }
40} 40}
41 41
42template <typename T>
43T GetValue(std::span<const u8> buffer) {
44 T t{};
45 std::memcpy(&t, buffer.data(), std::min(sizeof(T), buffer.size()));
46 return t;
47}
48
49template <typename T>
50void PutValue(std::span<u8> buffer, const T& t) {
51 std::memcpy(buffer.data(), &t, std::min(sizeof(T), buffer.size()));
52}
53
42} // Anonymous namespace 54} // Anonymous namespace
43 55
44void BSD::PollWork::Execute(BSD* bsd) { 56void BSD::PollWork::Execute(BSD* bsd) {
@@ -316,22 +328,12 @@ void BSD::SetSockOpt(HLERequestContext& ctx) {
316 const s32 fd = rp.Pop<s32>(); 328 const s32 fd = rp.Pop<s32>();
317 const u32 level = rp.Pop<u32>(); 329 const u32 level = rp.Pop<u32>();
318 const OptName optname = static_cast<OptName>(rp.Pop<u32>()); 330 const OptName optname = static_cast<OptName>(rp.Pop<u32>());
319 331 const auto optval = ctx.ReadBuffer();
320 const auto buffer = ctx.ReadBuffer();
321 const u8* optval = buffer.empty() ? nullptr : buffer.data();
322 size_t optlen = buffer.size();
323
324 std::array<u64, 2> values;
325 if ((optname == OptName::SNDTIMEO || optname == OptName::RCVTIMEO) && buffer.size() == 8) {
326 std::memcpy(values.data(), buffer.data(), sizeof(values));
327 optlen = sizeof(values);
328 optval = reinterpret_cast<const u8*>(values.data());
329 }
330 332
331 LOG_DEBUG(Service, "called. fd={} level={} optname=0x{:x} optlen={}", fd, level, 333 LOG_DEBUG(Service, "called. fd={} level={} optname=0x{:x} optlen={}", fd, level,
332 static_cast<u32>(optname), optlen); 334 static_cast<u32>(optname), optval.size());
333 335
334 BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optlen, optval)); 336 BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval));
335} 337}
336 338
337void BSD::Shutdown(HLERequestContext& ctx) { 339void BSD::Shutdown(HLERequestContext& ctx) {
@@ -521,18 +523,19 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco
521 523
522std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, 524std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
523 s32 nfds, s32 timeout) { 525 s32 nfds, s32 timeout) {
524 if (write_buffer.size() < nfds * sizeof(PollFD)) { 526 if (nfds <= 0) {
525 return {-1, Errno::INVAL};
526 }
527
528 if (nfds == 0) {
529 // When no entries are provided, -1 is returned with errno zero 527 // When no entries are provided, -1 is returned with errno zero
530 return {-1, Errno::SUCCESS}; 528 return {-1, Errno::SUCCESS};
531 } 529 }
530 if (read_buffer.size() < nfds * sizeof(PollFD)) {
531 return {-1, Errno::INVAL};
532 }
533 if (write_buffer.size() < nfds * sizeof(PollFD)) {
534 return {-1, Errno::INVAL};
535 }
532 536
533 const size_t length = std::min(read_buffer.size(), write_buffer.size());
534 std::vector<PollFD> fds(nfds); 537 std::vector<PollFD> fds(nfds);
535 std::memcpy(fds.data(), read_buffer.data(), length); 538 std::memcpy(fds.data(), read_buffer.data(), nfds * sizeof(PollFD));
536 539
537 if (timeout >= 0) { 540 if (timeout >= 0) {
538 const s64 seconds = timeout / 1000; 541 const s64 seconds = timeout / 1000;
@@ -580,7 +583,7 @@ std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<con
580 for (size_t i = 0; i < num; ++i) { 583 for (size_t i = 0; i < num; ++i) {
581 fds[i].revents = Translate(host_pollfds[i].revents); 584 fds[i].revents = Translate(host_pollfds[i].revents);
582 } 585 }
583 std::memcpy(write_buffer.data(), fds.data(), length); 586 std::memcpy(write_buffer.data(), fds.data(), nfds * sizeof(PollFD));
584 587
585 return Translate(result); 588 return Translate(result);
586} 589}
@@ -608,8 +611,7 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
608 new_descriptor.is_connection_based = descriptor.is_connection_based; 611 new_descriptor.is_connection_based = descriptor.is_connection_based;
609 612
610 const SockAddrIn guest_addr_in = Translate(result.sockaddr_in); 613 const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
611 const size_t length = std::min(sizeof(guest_addr_in), write_buffer.size()); 614 PutValue(write_buffer, guest_addr_in);
612 std::memcpy(write_buffer.data(), &guest_addr_in, length);
613 615
614 return {new_fd, Errno::SUCCESS}; 616 return {new_fd, Errno::SUCCESS};
615} 617}
@@ -619,8 +621,7 @@ Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) {
619 return Errno::BADF; 621 return Errno::BADF;
620 } 622 }
621 ASSERT(addr.size() == sizeof(SockAddrIn)); 623 ASSERT(addr.size() == sizeof(SockAddrIn));
622 SockAddrIn addr_in; 624 auto addr_in = GetValue<SockAddrIn>(addr);
623 std::memcpy(&addr_in, addr.data(), sizeof(addr_in));
624 625
625 return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in))); 626 return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
626} 627}
@@ -631,8 +632,7 @@ Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
631 } 632 }
632 633
633 UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn)); 634 UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn));
634 SockAddrIn addr_in; 635 auto addr_in = GetValue<SockAddrIn>(addr);
635 std::memcpy(&addr_in, addr.data(), sizeof(addr_in));
636 636
637 return Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in))); 637 return Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
638} 638}
@@ -650,7 +650,7 @@ Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
650 650
651 ASSERT(write_buffer.size() >= sizeof(guest_addrin)); 651 ASSERT(write_buffer.size() >= sizeof(guest_addrin));
652 write_buffer.resize(sizeof(guest_addrin)); 652 write_buffer.resize(sizeof(guest_addrin));
653 std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); 653 PutValue(write_buffer, guest_addrin);
654 return Translate(bsd_errno); 654 return Translate(bsd_errno);
655} 655}
656 656
@@ -667,7 +667,7 @@ Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
667 667
668 ASSERT(write_buffer.size() >= sizeof(guest_addrin)); 668 ASSERT(write_buffer.size() >= sizeof(guest_addrin));
669 write_buffer.resize(sizeof(guest_addrin)); 669 write_buffer.resize(sizeof(guest_addrin));
670 std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); 670 PutValue(write_buffer, guest_addrin);
671 return Translate(bsd_errno); 671 return Translate(bsd_errno);
672} 672}
673 673
@@ -725,7 +725,7 @@ Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& o
725 optval.size() == sizeof(Errno), { return Errno::INVAL; }, 725 optval.size() == sizeof(Errno), { return Errno::INVAL; },
726 "Incorrect getsockopt option size"); 726 "Incorrect getsockopt option size");
727 optval.resize(sizeof(Errno)); 727 optval.resize(sizeof(Errno));
728 memcpy(optval.data(), &translated_pending_err, sizeof(Errno)); 728 PutValue(optval, translated_pending_err);
729 } 729 }
730 return Translate(getsockopt_err); 730 return Translate(getsockopt_err);
731 } 731 }
@@ -735,7 +735,7 @@ Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& o
735 } 735 }
736} 736}
737 737
738Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval) { 738Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval) {
739 if (!IsFileDescriptorValid(fd)) { 739 if (!IsFileDescriptorValid(fd)) {
740 return Errno::BADF; 740 return Errno::BADF;
741 } 741 }
@@ -748,17 +748,15 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con
748 Network::SocketBase* const socket = file_descriptors[fd]->socket.get(); 748 Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
749 749
750 if (optname == OptName::LINGER) { 750 if (optname == OptName::LINGER) {
751 ASSERT(optlen == sizeof(Linger)); 751 ASSERT(optval.size() == sizeof(Linger));
752 Linger linger; 752 auto linger = GetValue<Linger>(optval);
753 std::memcpy(&linger, optval, sizeof(linger));
754 ASSERT(linger.onoff == 0 || linger.onoff == 1); 753 ASSERT(linger.onoff == 0 || linger.onoff == 1);
755 754
756 return Translate(socket->SetLinger(linger.onoff != 0, linger.linger)); 755 return Translate(socket->SetLinger(linger.onoff != 0, linger.linger));
757 } 756 }
758 757
759 ASSERT(optlen == sizeof(u32)); 758 ASSERT(optval.size() == sizeof(u32));
760 u32 value; 759 auto value = GetValue<u32>(optval);
761 std::memcpy(&value, optval, sizeof(value));
762 760
763 switch (optname) { 761 switch (optname) {
764 case OptName::REUSEADDR: 762 case OptName::REUSEADDR:
@@ -862,7 +860,7 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess
862 } else { 860 } else {
863 ASSERT(addr.size() == sizeof(SockAddrIn)); 861 ASSERT(addr.size() == sizeof(SockAddrIn));
864 const SockAddrIn result = Translate(addr_in); 862 const SockAddrIn result = Translate(addr_in);
865 std::memcpy(addr.data(), &result, sizeof(result)); 863 PutValue(addr, result);
866 } 864 }
867 } 865 }
868 866
@@ -886,8 +884,7 @@ std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> mes
886 Network::SockAddrIn* p_addr_in = nullptr; 884 Network::SockAddrIn* p_addr_in = nullptr;
887 if (!addr.empty()) { 885 if (!addr.empty()) {
888 ASSERT(addr.size() == sizeof(SockAddrIn)); 886 ASSERT(addr.size() == sizeof(SockAddrIn));
889 SockAddrIn guest_addr_in; 887 auto guest_addr_in = GetValue<SockAddrIn>(addr);
890 std::memcpy(&guest_addr_in, addr.data(), sizeof(guest_addr_in));
891 addr_in = Translate(guest_addr_in); 888 addr_in = Translate(guest_addr_in);
892 p_addr_in = &addr_in; 889 p_addr_in = &addr_in;
893 } 890 }
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index 161f22b9b..4f69d382c 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -163,7 +163,7 @@ private:
163 Errno ListenImpl(s32 fd, s32 backlog); 163 Errno ListenImpl(s32 fd, s32 backlog);
164 std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg); 164 std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg);
165 Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval); 165 Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval);
166 Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval); 166 Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval);
167 Errno ShutdownImpl(s32 fd, s32 how); 167 Errno ShutdownImpl(s32 fd, s32 how);
168 std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message); 168 std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
169 std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, 169 std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h
index 9fc01ea90..7149fffeb 100644
--- a/src/core/hle/service/time/clock_types.h
+++ b/src/core/hle/service/time/clock_types.h
@@ -11,6 +11,11 @@
11#include "core/hle/service/time/errors.h" 11#include "core/hle/service/time/errors.h"
12#include "core/hle/service/time/time_zone_types.h" 12#include "core/hle/service/time/time_zone_types.h"
13 13
14// Defined by WinBase.h on Windows
15#ifdef GetCurrentTime
16#undef GetCurrentTime
17#endif
18
14namespace Service::Time::Clock { 19namespace Service::Time::Clock {
15 20
16enum class TimeType : u8 { 21enum class TimeType : u8 {
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index f0b5eff8a..d30f49877 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -35,7 +35,7 @@ static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_cont
35 return { 35 return {
36 buffer_queue_core, 36 buffer_queue_core,
37 std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap), 37 std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap),
38 std::make_unique<android::BufferQueueConsumer>(buffer_queue_core, nvmap)}; 38 std::make_unique<android::BufferQueueConsumer>(buffer_queue_core)};
39} 39}
40 40
41Display::Display(u64 id, std::string name_, 41Display::Display(u64 id, std::string name_,