|  |  |  | @@ -7,71 +7,39 @@ | 
		
	
		
			
				|  |  |  |  | #include "core/hle/kernel/k_client_session.h" | 
		
	
		
			
				|  |  |  |  | #include "core/hle/kernel/k_hardware_timer.h" | 
		
	
		
			
				|  |  |  |  | #include "core/hle/kernel/k_process.h" | 
		
	
		
			
				|  |  |  |  | #include "core/hle/kernel/k_scoped_resource_reservation.h" | 
		
	
		
			
				|  |  |  |  | #include "core/hle/kernel/k_server_session.h" | 
		
	
		
			
				|  |  |  |  | #include "core/hle/kernel/k_session.h" | 
		
	
		
			
				|  |  |  |  | #include "core/hle/kernel/svc.h" | 
		
	
		
			
				|  |  |  |  | #include "core/hle/kernel/svc_results.h" | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | namespace Kernel::Svc { | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | /// Makes a blocking IPC call to a service. | 
		
	
		
			
				|  |  |  |  | Result SendSyncRequest(Core::System& system, Handle handle) { | 
		
	
		
			
				|  |  |  |  |     // Get the client session from its handle. | 
		
	
		
			
				|  |  |  |  | namespace { | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | Result SendSyncRequestImpl(KernelCore& kernel, uintptr_t message, size_t buffer_size, | 
		
	
		
			
				|  |  |  |  |                            Handle session_handle) { | 
		
	
		
			
				|  |  |  |  |     // Get the client session. | 
		
	
		
			
				|  |  |  |  |     KScopedAutoObject session = | 
		
	
		
			
				|  |  |  |  |         GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KClientSession>(handle); | 
		
	
		
			
				|  |  |  |  |         GetCurrentProcess(kernel).GetHandleTable().GetObject<KClientSession>(session_handle); | 
		
	
		
			
				|  |  |  |  |     R_UNLESS(session.IsNotNull(), ResultInvalidHandle); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}", handle); | 
		
	
		
			
				|  |  |  |  |     // Get the parent, and persist a reference to it until we're done. | 
		
	
		
			
				|  |  |  |  |     KScopedAutoObject parent = session->GetParent(); | 
		
	
		
			
				|  |  |  |  |     ASSERT(parent.IsNotNull()); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     R_RETURN(session->SendSyncRequest()); | 
		
	
		
			
				|  |  |  |  |     // Send the request. | 
		
	
		
			
				|  |  |  |  |     R_RETURN(session->SendSyncRequest(message, buffer_size)); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | Result SendSyncRequestWithUserBuffer(Core::System& system, uint64_t message_buffer, | 
		
	
		
			
				|  |  |  |  |                                      uint64_t message_buffer_size, Handle session_handle) { | 
		
	
		
			
				|  |  |  |  |     UNIMPLEMENTED(); | 
		
	
		
			
				|  |  |  |  |     R_THROW(ResultNotImplemented); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_handle, | 
		
	
		
			
				|  |  |  |  |                                       uint64_t message_buffer, uint64_t message_buffer_size, | 
		
	
		
			
				|  |  |  |  |                                       Handle session_handle) { | 
		
	
		
			
				|  |  |  |  |     UNIMPLEMENTED(); | 
		
	
		
			
				|  |  |  |  |     R_THROW(ResultNotImplemented); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_addr, s32 num_handles, | 
		
	
		
			
				|  |  |  |  |                        Handle reply_target, s64 timeout_ns) { | 
		
	
		
			
				|  |  |  |  |     // Ensure number of handles is valid. | 
		
	
		
			
				|  |  |  |  |     R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Get the synchronization context. | 
		
	
		
			
				|  |  |  |  |     auto& kernel = system.Kernel(); | 
		
	
		
			
				|  |  |  |  |     auto& handle_table = GetCurrentProcess(kernel).GetHandleTable(); | 
		
	
		
			
				|  |  |  |  |     auto objs = GetCurrentThread(kernel).GetSynchronizationObjectBuffer(); | 
		
	
		
			
				|  |  |  |  |     auto handles = GetCurrentThread(kernel).GetHandleBuffer(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Copy user handles. | 
		
	
		
			
				|  |  |  |  |     if (num_handles > 0) { | 
		
	
		
			
				|  |  |  |  |         // Get the handles. | 
		
	
		
			
				|  |  |  |  |         R_UNLESS(GetCurrentMemory(kernel).ReadBlock(handles_addr, handles.data(), | 
		
	
		
			
				|  |  |  |  |                                                     sizeof(Handle) * num_handles), | 
		
	
		
			
				|  |  |  |  |                  ResultInvalidPointer); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         // Convert the handles to objects. | 
		
	
		
			
				|  |  |  |  |         R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>( | 
		
	
		
			
				|  |  |  |  |                      objs.data(), handles.data(), num_handles), | 
		
	
		
			
				|  |  |  |  |                  ResultInvalidHandle); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Ensure handles are closed when we're done. | 
		
	
		
			
				|  |  |  |  |     SCOPE_EXIT({ | 
		
	
		
			
				|  |  |  |  |         for (auto i = 0; i < num_handles; ++i) { | 
		
	
		
			
				|  |  |  |  |             objs[i]->Close(); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     }); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t message, | 
		
	
		
			
				|  |  |  |  |                            size_t buffer_size, KPhysicalAddress message_paddr, | 
		
	
		
			
				|  |  |  |  |                            KSynchronizationObject** objs, int32_t num_objects, Handle reply_target, | 
		
	
		
			
				|  |  |  |  |                            int64_t timeout_ns) { | 
		
	
		
			
				|  |  |  |  |     // Reply to the target, if one is specified. | 
		
	
		
			
				|  |  |  |  |     if (reply_target != InvalidHandle) { | 
		
	
		
			
				|  |  |  |  |         KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target); | 
		
	
		
			
				|  |  |  |  |         KScopedAutoObject session = | 
		
	
		
			
				|  |  |  |  |             GetCurrentProcess(kernel).GetHandleTable().GetObject<KServerSession>(reply_target); | 
		
	
		
			
				|  |  |  |  |         R_UNLESS(session.IsNotNull(), ResultInvalidHandle); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         // If we fail to reply, we want to set the output index to -1. | 
		
	
	
		
			
				
					
					|  |  |  | @@ -81,57 +49,223 @@ Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_ad | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         // Send the reply. | 
		
	
		
			
				|  |  |  |  |         R_TRY(session->SendReply()); | 
		
	
		
			
				|  |  |  |  |         // R_TRY(session->SendReply(message, buffer_size, message_paddr)); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Convert the timeout from nanoseconds to ticks. | 
		
	
		
			
				|  |  |  |  |     // NOTE: Nintendo does not use this conversion logic in WaitSynchronization... | 
		
	
		
			
				|  |  |  |  |     s64 timeout; | 
		
	
		
			
				|  |  |  |  |     if (timeout_ns > 0) { | 
		
	
		
			
				|  |  |  |  |         const s64 offset_tick(timeout_ns); | 
		
	
		
			
				|  |  |  |  |         if (offset_tick > 0) { | 
		
	
		
			
				|  |  |  |  |             timeout = kernel.HardwareTimer().GetTick() + offset_tick + 2; | 
		
	
		
			
				|  |  |  |  |             if (timeout <= 0) { | 
		
	
		
			
				|  |  |  |  |     // Receive a message. | 
		
	
		
			
				|  |  |  |  |     { | 
		
	
		
			
				|  |  |  |  |         // Convert the timeout from nanoseconds to ticks. | 
		
	
		
			
				|  |  |  |  |         // NOTE: Nintendo does not use this conversion logic in WaitSynchronization... | 
		
	
		
			
				|  |  |  |  |         s64 timeout; | 
		
	
		
			
				|  |  |  |  |         if (timeout_ns > 0) { | 
		
	
		
			
				|  |  |  |  |             const s64 offset_tick(timeout_ns); | 
		
	
		
			
				|  |  |  |  |             if (offset_tick > 0) { | 
		
	
		
			
				|  |  |  |  |                 timeout = kernel.HardwareTimer().GetTick() + offset_tick + 2; | 
		
	
		
			
				|  |  |  |  |                 if (timeout <= 0) { | 
		
	
		
			
				|  |  |  |  |                     timeout = std::numeric_limits<s64>::max(); | 
		
	
		
			
				|  |  |  |  |                 } | 
		
	
		
			
				|  |  |  |  |             } else { | 
		
	
		
			
				|  |  |  |  |                 timeout = std::numeric_limits<s64>::max(); | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |         } else { | 
		
	
		
			
				|  |  |  |  |             timeout = std::numeric_limits<s64>::max(); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } else { | 
		
	
		
			
				|  |  |  |  |         timeout = timeout_ns; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Wait for a message. | 
		
	
		
			
				|  |  |  |  |     while (true) { | 
		
	
		
			
				|  |  |  |  |         // Wait for an object. | 
		
	
		
			
				|  |  |  |  |         s32 index; | 
		
	
		
			
				|  |  |  |  |         Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs.data(), | 
		
	
		
			
				|  |  |  |  |                                                      num_handles, timeout); | 
		
	
		
			
				|  |  |  |  |         if (result == ResultTimedOut) { | 
		
	
		
			
				|  |  |  |  |             R_RETURN(result); | 
		
	
		
			
				|  |  |  |  |             timeout = timeout_ns; | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         // Receive the request. | 
		
	
		
			
				|  |  |  |  |         if (R_SUCCEEDED(result)) { | 
		
	
		
			
				|  |  |  |  |             KServerSession* session = objs[index]->DynamicCast<KServerSession*>(); | 
		
	
		
			
				|  |  |  |  |             if (session != nullptr) { | 
		
	
		
			
				|  |  |  |  |                 result = session->ReceiveRequest(); | 
		
	
		
			
				|  |  |  |  |                 if (result == ResultNotFound) { | 
		
	
		
			
				|  |  |  |  |                     continue; | 
		
	
		
			
				|  |  |  |  |         // Wait for a message. | 
		
	
		
			
				|  |  |  |  |         while (true) { | 
		
	
		
			
				|  |  |  |  |             // Wait for an object. | 
		
	
		
			
				|  |  |  |  |             s32 index; | 
		
	
		
			
				|  |  |  |  |             Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs, | 
		
	
		
			
				|  |  |  |  |                                                          num_objects, timeout); | 
		
	
		
			
				|  |  |  |  |             if (ResultTimedOut == result) { | 
		
	
		
			
				|  |  |  |  |                 R_THROW(result); | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |             // Receive the request. | 
		
	
		
			
				|  |  |  |  |             if (R_SUCCEEDED(result)) { | 
		
	
		
			
				|  |  |  |  |                 KServerSession* session = objs[index]->DynamicCast<KServerSession*>(); | 
		
	
		
			
				|  |  |  |  |                 if (session != nullptr) { | 
		
	
		
			
				|  |  |  |  |                     // result = session->ReceiveRequest(message, buffer_size, message_paddr); | 
		
	
		
			
				|  |  |  |  |                     result = session->ReceiveRequest(); | 
		
	
		
			
				|  |  |  |  |                     if (ResultNotFound == result) { | 
		
	
		
			
				|  |  |  |  |                         continue; | 
		
	
		
			
				|  |  |  |  |                     } | 
		
	
		
			
				|  |  |  |  |                 } | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         *out_index = index; | 
		
	
		
			
				|  |  |  |  |         R_RETURN(result); | 
		
	
		
			
				|  |  |  |  |             *out_index = index; | 
		
	
		
			
				|  |  |  |  |             R_RETURN(result); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | Result ReplyAndReceiveWithUserBuffer(Core::System& system, int32_t* out_index, | 
		
	
		
			
				|  |  |  |  |                                      uint64_t message_buffer, uint64_t message_buffer_size, | 
		
	
		
			
				|  |  |  |  |                                      uint64_t handles, int32_t num_handles, Handle reply_target, | 
		
	
		
			
				|  |  |  |  |                                      int64_t timeout_ns) { | 
		
	
		
			
				|  |  |  |  |     UNIMPLEMENTED(); | 
		
	
		
			
				|  |  |  |  |     R_THROW(ResultNotImplemented); | 
		
	
		
			
				|  |  |  |  | Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t message, | 
		
	
		
			
				|  |  |  |  |                            size_t buffer_size, KPhysicalAddress message_paddr, | 
		
	
		
			
				|  |  |  |  |                            KProcessAddress user_handles, int32_t num_handles, Handle reply_target, | 
		
	
		
			
				|  |  |  |  |                            int64_t timeout_ns) { | 
		
	
		
			
				|  |  |  |  |     // Ensure number of handles is valid. | 
		
	
		
			
				|  |  |  |  |     R_UNLESS(0 <= num_handles && num_handles <= Svc::ArgumentHandleCountMax, ResultOutOfRange); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Get the synchronization context. | 
		
	
		
			
				|  |  |  |  |     auto& process = GetCurrentProcess(kernel); | 
		
	
		
			
				|  |  |  |  |     auto& thread = GetCurrentThread(kernel); | 
		
	
		
			
				|  |  |  |  |     auto& handle_table = process.GetHandleTable(); | 
		
	
		
			
				|  |  |  |  |     KSynchronizationObject** objs = thread.GetSynchronizationObjectBuffer().data(); | 
		
	
		
			
				|  |  |  |  |     Handle* handles = thread.GetHandleBuffer().data(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Copy user handles. | 
		
	
		
			
				|  |  |  |  |     if (num_handles > 0) { | 
		
	
		
			
				|  |  |  |  |         // Ensure that we can try to get the handles. | 
		
	
		
			
				|  |  |  |  |         R_UNLESS(process.GetPageTable().Contains(user_handles, num_handles * sizeof(Handle)), | 
		
	
		
			
				|  |  |  |  |                  ResultInvalidPointer); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         // Get the handles | 
		
	
		
			
				|  |  |  |  |         R_UNLESS( | 
		
	
		
			
				|  |  |  |  |             GetCurrentMemory(kernel).ReadBlock(user_handles, handles, sizeof(Handle) * num_handles), | 
		
	
		
			
				|  |  |  |  |             ResultInvalidPointer); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         // Convert the handles to objects. | 
		
	
		
			
				|  |  |  |  |         R_UNLESS( | 
		
	
		
			
				|  |  |  |  |             handle_table.GetMultipleObjects<KSynchronizationObject>(objs, handles, num_handles), | 
		
	
		
			
				|  |  |  |  |             ResultInvalidHandle); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Ensure handles are closed when we're done. | 
		
	
		
			
				|  |  |  |  |     SCOPE_EXIT({ | 
		
	
		
			
				|  |  |  |  |         for (auto i = 0; i < num_handles; ++i) { | 
		
	
		
			
				|  |  |  |  |             objs[i]->Close(); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     }); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     R_RETURN(ReplyAndReceiveImpl(kernel, out_index, message, buffer_size, message_paddr, objs, | 
		
	
		
			
				|  |  |  |  |                                  num_handles, reply_target, timeout_ns)); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | } // namespace | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | /// Makes a blocking IPC call to a service. | 
		
	
		
			
				|  |  |  |  | Result SendSyncRequest(Core::System& system, Handle session_handle) { | 
		
	
		
			
				|  |  |  |  |     R_RETURN(SendSyncRequestImpl(system.Kernel(), 0, 0, session_handle)); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | Result SendSyncRequestWithUserBuffer(Core::System& system, uint64_t message, uint64_t buffer_size, | 
		
	
		
			
				|  |  |  |  |                                      Handle session_handle) { | 
		
	
		
			
				|  |  |  |  |     auto& kernel = system.Kernel(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Validate that the message buffer is page aligned and does not overflow. | 
		
	
		
			
				|  |  |  |  |     R_UNLESS(Common::IsAligned(message, PageSize), ResultInvalidAddress); | 
		
	
		
			
				|  |  |  |  |     R_UNLESS(buffer_size > 0, ResultInvalidSize); | 
		
	
		
			
				|  |  |  |  |     R_UNLESS(Common::IsAligned(buffer_size, PageSize), ResultInvalidSize); | 
		
	
		
			
				|  |  |  |  |     R_UNLESS(message < message + buffer_size, ResultInvalidCurrentMemory); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Get the process page table. | 
		
	
		
			
				|  |  |  |  |     auto& page_table = GetCurrentProcess(kernel).GetPageTable(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Lock the message buffer. | 
		
	
		
			
				|  |  |  |  |     R_TRY(page_table.LockForIpcUserBuffer(nullptr, message, buffer_size)); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     { | 
		
	
		
			
				|  |  |  |  |         // If we fail to send the message, unlock the message buffer. | 
		
	
		
			
				|  |  |  |  |         ON_RESULT_FAILURE { | 
		
	
		
			
				|  |  |  |  |             page_table.UnlockForIpcUserBuffer(message, buffer_size); | 
		
	
		
			
				|  |  |  |  |         }; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         // Send the request. | 
		
	
		
			
				|  |  |  |  |         ASSERT(message != 0); | 
		
	
		
			
				|  |  |  |  |         R_TRY(SendSyncRequestImpl(kernel, message, buffer_size, session_handle)); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // We successfully processed, so try to unlock the message buffer. | 
		
	
		
			
				|  |  |  |  |     R_RETURN(page_table.UnlockForIpcUserBuffer(message, buffer_size)); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_handle, | 
		
	
		
			
				|  |  |  |  |                                       uint64_t message, uint64_t buffer_size, | 
		
	
		
			
				|  |  |  |  |                                       Handle session_handle) { | 
		
	
		
			
				|  |  |  |  |     // Get the process and handle table. | 
		
	
		
			
				|  |  |  |  |     auto& process = GetCurrentProcess(system.Kernel()); | 
		
	
		
			
				|  |  |  |  |     auto& handle_table = process.GetHandleTable(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Reserve a new event from the process resource limit. | 
		
	
		
			
				|  |  |  |  |     KScopedResourceReservation event_reservation(std::addressof(process), | 
		
	
		
			
				|  |  |  |  |                                                  Svc::LimitableResource::EventCountMax); | 
		
	
		
			
				|  |  |  |  |     R_UNLESS(event_reservation.Succeeded(), ResultLimitReached); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Get the client session. | 
		
	
		
			
				|  |  |  |  |     KScopedAutoObject session = process.GetHandleTable().GetObject<KClientSession>(session_handle); | 
		
	
		
			
				|  |  |  |  |     R_UNLESS(session.IsNotNull(), ResultInvalidHandle); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Get the parent, and persist a reference to it until we're done. | 
		
	
		
			
				|  |  |  |  |     KScopedAutoObject parent = session->GetParent(); | 
		
	
		
			
				|  |  |  |  |     ASSERT(parent.IsNotNull()); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Create a new event. | 
		
	
		
			
				|  |  |  |  |     KEvent* event = KEvent::Create(system.Kernel()); | 
		
	
		
			
				|  |  |  |  |     R_UNLESS(event != nullptr, ResultOutOfResource); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Initialize the event. | 
		
	
		
			
				|  |  |  |  |     event->Initialize(std::addressof(process)); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Commit our reservation. | 
		
	
		
			
				|  |  |  |  |     event_reservation.Commit(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // At end of scope, kill the standing references to the sub events. | 
		
	
		
			
				|  |  |  |  |     SCOPE_EXIT({ | 
		
	
		
			
				|  |  |  |  |         event->GetReadableEvent().Close(); | 
		
	
		
			
				|  |  |  |  |         event->Close(); | 
		
	
		
			
				|  |  |  |  |     }); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Register the event. | 
		
	
		
			
				|  |  |  |  |     KEvent::Register(system.Kernel(), event); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Add the readable event to the handle table. | 
		
	
		
			
				|  |  |  |  |     R_TRY(handle_table.Add(out_event_handle, std::addressof(event->GetReadableEvent()))); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Ensure that if we fail to send the request, we close the readable handle. | 
		
	
		
			
				|  |  |  |  |     ON_RESULT_FAILURE { | 
		
	
		
			
				|  |  |  |  |         handle_table.Remove(*out_event_handle); | 
		
	
		
			
				|  |  |  |  |     }; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Send the async request. | 
		
	
		
			
				|  |  |  |  |     R_RETURN(session->SendAsyncRequest(event, message, buffer_size)); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles, s32 num_handles, | 
		
	
		
			
				|  |  |  |  |                        Handle reply_target, s64 timeout_ns) { | 
		
	
		
			
				|  |  |  |  |     R_RETURN(ReplyAndReceiveImpl(system.Kernel(), out_index, 0, 0, 0, handles, num_handles, | 
		
	
		
			
				|  |  |  |  |                                  reply_target, timeout_ns)); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | Result ReplyAndReceiveWithUserBuffer(Core::System& system, int32_t* out_index, uint64_t message, | 
		
	
		
			
				|  |  |  |  |                                      uint64_t buffer_size, uint64_t handles, int32_t num_handles, | 
		
	
		
			
				|  |  |  |  |                                      Handle reply_target, int64_t timeout_ns) { | 
		
	
		
			
				|  |  |  |  |     // Validate that the message buffer is page aligned and does not overflow. | 
		
	
		
			
				|  |  |  |  |     R_UNLESS(Common::IsAligned(message, PageSize), ResultInvalidAddress); | 
		
	
		
			
				|  |  |  |  |     R_UNLESS(buffer_size > 0, ResultInvalidSize); | 
		
	
		
			
				|  |  |  |  |     R_UNLESS(Common::IsAligned(buffer_size, PageSize), ResultInvalidSize); | 
		
	
		
			
				|  |  |  |  |     R_UNLESS(message < message + buffer_size, ResultInvalidCurrentMemory); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Get the process page table. | 
		
	
		
			
				|  |  |  |  |     auto& page_table = GetCurrentProcess(system.Kernel()).GetPageTable(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Lock the message buffer, getting its physical address. | 
		
	
		
			
				|  |  |  |  |     KPhysicalAddress message_paddr; | 
		
	
		
			
				|  |  |  |  |     R_TRY(page_table.LockForIpcUserBuffer(std::addressof(message_paddr), message, buffer_size)); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     { | 
		
	
		
			
				|  |  |  |  |         // If we fail to send the message, unlock the message buffer. | 
		
	
		
			
				|  |  |  |  |         ON_RESULT_FAILURE { | 
		
	
		
			
				|  |  |  |  |             page_table.UnlockForIpcUserBuffer(message, buffer_size); | 
		
	
		
			
				|  |  |  |  |         }; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         // Reply/Receive the request. | 
		
	
		
			
				|  |  |  |  |         ASSERT(message != 0); | 
		
	
		
			
				|  |  |  |  |         R_TRY(ReplyAndReceiveImpl(system.Kernel(), out_index, message, buffer_size, message_paddr, | 
		
	
		
			
				|  |  |  |  |                                   handles, num_handles, reply_target, timeout_ns)); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // We successfully processed, so try to unlock the message buffer. | 
		
	
		
			
				|  |  |  |  |     R_RETURN(page_table.UnlockForIpcUserBuffer(message, buffer_size)); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | Result SendSyncRequest64(Core::System& system, Handle session_handle) { | 
		
	
	
		
			
				
					
					|  |  |  |   |