summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/hle/kernel/k_client_session.cpp24
-rw-r--r--src/core/hle/kernel/k_client_session.h18
-rw-r--r--src/core/hle/kernel/k_server_session.cpp5
-rw-r--r--src/core/hle/kernel/k_session.h4
-rw-r--r--src/core/hle/kernel/svc/svc_ipc.cpp296
-rw-r--r--src/core/hle/service/sm/sm.cpp2
6 files changed, 244 insertions, 105 deletions
diff --git a/src/core/hle/kernel/k_client_session.cpp b/src/core/hle/kernel/k_client_session.cpp
index 72b66270d..472e8571c 100644
--- a/src/core/hle/kernel/k_client_session.cpp
+++ b/src/core/hle/kernel/k_client_session.cpp
@@ -10,9 +10,7 @@
10 10
11namespace Kernel { 11namespace Kernel {
12 12
13static constexpr u32 MessageBufferSize = 0x100; 13KClientSession::KClientSession(KernelCore& kernel) : KAutoObject{kernel} {}
14
15KClientSession::KClientSession(KernelCore& kernel) : KAutoObjectWithSlabHeapAndContainer{kernel} {}
16KClientSession::~KClientSession() = default; 14KClientSession::~KClientSession() = default;
17 15
18void KClientSession::Destroy() { 16void KClientSession::Destroy() {
@@ -22,18 +20,30 @@ void KClientSession::Destroy() {
22 20
23void KClientSession::OnServerClosed() {} 21void KClientSession::OnServerClosed() {}
24 22
25Result KClientSession::SendSyncRequest() { 23Result KClientSession::SendSyncRequest(uintptr_t address, size_t size) {
24 // Create a session request.
25 KSessionRequest* request = KSessionRequest::Create(m_kernel);
26 R_UNLESS(request != nullptr, ResultOutOfResource);
27 SCOPE_EXIT({ request->Close(); });
28
29 // Initialize the request.
30 request->Initialize(nullptr, address, size);
31
32 // Send the request.
33 R_RETURN(m_parent->OnRequest(request));
34}
35
36Result KClientSession::SendAsyncRequest(KEvent* event, uintptr_t address, size_t size) {
26 // Create a session request. 37 // Create a session request.
27 KSessionRequest* request = KSessionRequest::Create(m_kernel); 38 KSessionRequest* request = KSessionRequest::Create(m_kernel);
28 R_UNLESS(request != nullptr, ResultOutOfResource); 39 R_UNLESS(request != nullptr, ResultOutOfResource);
29 SCOPE_EXIT({ request->Close(); }); 40 SCOPE_EXIT({ request->Close(); });
30 41
31 // Initialize the request. 42 // Initialize the request.
32 request->Initialize(nullptr, GetInteger(GetCurrentThread(m_kernel).GetTlsAddress()), 43 request->Initialize(event, address, size);
33 MessageBufferSize);
34 44
35 // Send the request. 45 // Send the request.
36 R_RETURN(m_parent->GetServerSession().OnRequest(request)); 46 R_RETURN(m_parent->OnRequest(request));
37} 47}
38 48
39} // namespace Kernel 49} // namespace Kernel
diff --git a/src/core/hle/kernel/k_client_session.h b/src/core/hle/kernel/k_client_session.h
index 9b62e55e4..a39213e17 100644
--- a/src/core/hle/kernel/k_client_session.h
+++ b/src/core/hle/kernel/k_client_session.h
@@ -9,24 +9,12 @@
9#include "core/hle/kernel/slab_helpers.h" 9#include "core/hle/kernel/slab_helpers.h"
10#include "core/hle/result.h" 10#include "core/hle/result.h"
11 11
12union Result;
13
14namespace Core::Memory {
15class Memory;
16}
17
18namespace Core::Timing {
19class CoreTiming;
20}
21
22namespace Kernel { 12namespace Kernel {
23 13
24class KernelCore; 14class KernelCore;
25class KSession; 15class KSession;
26class KThread;
27 16
28class KClientSession final 17class KClientSession final : public KAutoObject {
29 : public KAutoObjectWithSlabHeapAndContainer<KClientSession, KAutoObjectWithList> {
30 KERNEL_AUTOOBJECT_TRAITS(KClientSession, KAutoObject); 18 KERNEL_AUTOOBJECT_TRAITS(KClientSession, KAutoObject);
31 19
32public: 20public:
@@ -39,13 +27,13 @@ public:
39 } 27 }
40 28
41 void Destroy() override; 29 void Destroy() override;
42 static void PostDestroy(uintptr_t arg) {}
43 30
44 KSession* GetParent() const { 31 KSession* GetParent() const {
45 return m_parent; 32 return m_parent;
46 } 33 }
47 34
48 Result SendSyncRequest(); 35 Result SendSyncRequest(uintptr_t address, size_t size);
36 Result SendAsyncRequest(KEvent* event, uintptr_t address, size_t size);
49 37
50 void OnServerClosed(); 38 void OnServerClosed();
51 39
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index 3ea653163..ec6812d5a 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -453,6 +453,11 @@ Result KServerSession::ReceiveRequest(std::shared_ptr<Service::HLERequestContext
453 size_t client_buffer_size = request->GetSize(); 453 size_t client_buffer_size = request->GetSize();
454 // bool recv_list_broken = false; 454 // bool recv_list_broken = false;
455 455
456 if (!client_message) {
457 client_message = GetInteger(client_thread->GetTlsAddress());
458 client_buffer_size = MessageBufferSize;
459 }
460
456 // Receive the message. 461 // Receive the message.
457 Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()}; 462 Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()};
458 if (out_context != nullptr) { 463 if (out_context != nullptr) {
diff --git a/src/core/hle/kernel/k_session.h b/src/core/hle/kernel/k_session.h
index f69bab088..3f4dd5989 100644
--- a/src/core/hle/kernel/k_session.h
+++ b/src/core/hle/kernel/k_session.h
@@ -46,6 +46,10 @@ public:
46 return this->GetState() != State::Normal; 46 return this->GetState() != State::Normal;
47 } 47 }
48 48
49 Result OnRequest(KSessionRequest* request) {
50 R_RETURN(m_server.OnRequest(request));
51 }
52
49 KClientSession& GetClientSession() { 53 KClientSession& GetClientSession() {
50 return m_client; 54 return m_client;
51 } 55 }
diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp
index 6b5e1cb8d..47a3e7bb0 100644
--- a/src/core/hle/kernel/svc/svc_ipc.cpp
+++ b/src/core/hle/kernel/svc/svc_ipc.cpp
@@ -7,59 +7,127 @@
7#include "core/hle/kernel/k_client_session.h" 7#include "core/hle/kernel/k_client_session.h"
8#include "core/hle/kernel/k_hardware_timer.h" 8#include "core/hle/kernel/k_hardware_timer.h"
9#include "core/hle/kernel/k_process.h" 9#include "core/hle/kernel/k_process.h"
10#include "core/hle/kernel/k_scoped_resource_reservation.h"
10#include "core/hle/kernel/k_server_session.h" 11#include "core/hle/kernel/k_server_session.h"
12#include "core/hle/kernel/k_session.h"
11#include "core/hle/kernel/svc.h" 13#include "core/hle/kernel/svc.h"
12#include "core/hle/kernel/svc_results.h" 14#include "core/hle/kernel/svc_results.h"
13 15
14namespace Kernel::Svc { 16namespace Kernel::Svc {
15 17
16/// Makes a blocking IPC call to a service. 18namespace {
17Result SendSyncRequest(Core::System& system, Handle handle) { 19
18 // Get the client session from its handle. 20Result SendSyncRequestImpl(KernelCore& kernel, uintptr_t message, size_t buffer_size,
21 Handle session_handle) {
22 // Get the client session.
19 KScopedAutoObject session = 23 KScopedAutoObject session =
20 GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KClientSession>(handle); 24 GetCurrentProcess(kernel).GetHandleTable().GetObject<KClientSession>(session_handle);
21 R_UNLESS(session.IsNotNull(), ResultInvalidHandle); 25 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
22 26
23 LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}", handle); 27 // Get the parent, and persist a reference to it until we're done.
28 KScopedAutoObject parent = session->GetParent();
29 ASSERT(parent.IsNotNull());
24 30
25 R_RETURN(session->SendSyncRequest()); 31 // Send the request.
32 R_RETURN(session->SendSyncRequest(message, buffer_size));
26} 33}
27 34
28Result SendSyncRequestWithUserBuffer(Core::System& system, uint64_t message_buffer, 35Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t message,
29 uint64_t message_buffer_size, Handle session_handle) { 36 size_t buffer_size, KPhysicalAddress message_paddr,
30 UNIMPLEMENTED(); 37 KSynchronizationObject** objs, int32_t num_objects, Handle reply_target,
31 R_THROW(ResultNotImplemented); 38 int64_t timeout_ns) {
32} 39 // Reply to the target, if one is specified.
40 if (reply_target != InvalidHandle) {
41 KScopedAutoObject session =
42 GetCurrentProcess(kernel).GetHandleTable().GetObject<KServerSession>(reply_target);
43 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
33 44
34Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_handle, 45 // If we fail to reply, we want to set the output index to -1.
35 uint64_t message_buffer, uint64_t message_buffer_size, 46 ON_RESULT_FAILURE {
36 Handle session_handle) { 47 *out_index = -1;
37 UNIMPLEMENTED(); 48 };
38 R_THROW(ResultNotImplemented); 49
50 // Send the reply.
51 R_TRY(session->SendReply());
52 // R_TRY(session->SendReply(message, buffer_size, message_paddr));
53 }
54
55 // Receive a message.
56 {
57 // Convert the timeout from nanoseconds to ticks.
58 // NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
59 s64 timeout;
60 if (timeout_ns > 0) {
61 const s64 offset_tick(timeout_ns);
62 if (offset_tick > 0) {
63 timeout = kernel.HardwareTimer().GetTick() + offset_tick + 2;
64 if (timeout <= 0) {
65 timeout = std::numeric_limits<s64>::max();
66 }
67 } else {
68 timeout = std::numeric_limits<s64>::max();
69 }
70 } else {
71 timeout = timeout_ns;
72 }
73
74 // Wait for a message.
75 while (true) {
76 // Wait for an object.
77 s32 index;
78 Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs,
79 num_objects, timeout);
80 if (ResultTimedOut == result) {
81 R_THROW(result);
82 }
83
84 // Receive the request.
85 if (R_SUCCEEDED(result)) {
86 KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
87 if (session != nullptr) {
88 // result = session->ReceiveRequest(message, buffer_size, message_paddr);
89 result = session->ReceiveRequest();
90 if (ResultNotFound == result) {
91 continue;
92 }
93 }
94 }
95
96 *out_index = index;
97 R_RETURN(result);
98 }
99 }
39} 100}
40 101
41Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_addr, s32 num_handles, 102Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t message,
42 Handle reply_target, s64 timeout_ns) { 103 size_t buffer_size, KPhysicalAddress message_paddr,
104 KProcessAddress user_handles, int32_t num_handles, Handle reply_target,
105 int64_t timeout_ns) {
43 // Ensure number of handles is valid. 106 // Ensure number of handles is valid.
44 R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange); 107 R_UNLESS(0 <= num_handles && num_handles <= Svc::ArgumentHandleCountMax, ResultOutOfRange);
45 108
46 // Get the synchronization context. 109 // Get the synchronization context.
47 auto& kernel = system.Kernel(); 110 auto& process = GetCurrentProcess(kernel);
48 auto& handle_table = GetCurrentProcess(kernel).GetHandleTable(); 111 auto& thread = GetCurrentThread(kernel);
49 auto objs = GetCurrentThread(kernel).GetSynchronizationObjectBuffer(); 112 auto& handle_table = process.GetHandleTable();
50 auto handles = GetCurrentThread(kernel).GetHandleBuffer(); 113 KSynchronizationObject** objs = thread.GetSynchronizationObjectBuffer().data();
114 Handle* handles = thread.GetHandleBuffer().data();
51 115
52 // Copy user handles. 116 // Copy user handles.
53 if (num_handles > 0) { 117 if (num_handles > 0) {
54 // Get the handles. 118 // Ensure that we can try to get the handles.
55 R_UNLESS(GetCurrentMemory(kernel).ReadBlock(handles_addr, handles.data(), 119 R_UNLESS(process.GetPageTable().Contains(user_handles, num_handles * sizeof(Handle)),
56 sizeof(Handle) * num_handles),
57 ResultInvalidPointer); 120 ResultInvalidPointer);
58 121
122 // Get the handles
123 R_UNLESS(
124 GetCurrentMemory(kernel).ReadBlock(user_handles, handles, sizeof(Handle) * num_handles),
125 ResultInvalidPointer);
126
59 // Convert the handles to objects. 127 // Convert the handles to objects.
60 R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>( 128 R_UNLESS(
61 objs.data(), handles.data(), num_handles), 129 handle_table.GetMultipleObjects<KSynchronizationObject>(objs, handles, num_handles),
62 ResultInvalidHandle); 130 ResultInvalidHandle);
63 } 131 }
64 132
65 // Ensure handles are closed when we're done. 133 // Ensure handles are closed when we're done.
@@ -69,69 +137,135 @@ Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_ad
69 } 137 }
70 }); 138 });
71 139
72 // Reply to the target, if one is specified. 140 R_RETURN(ReplyAndReceiveImpl(kernel, out_index, message, buffer_size, message_paddr, objs,
73 if (reply_target != InvalidHandle) { 141 num_handles, reply_target, timeout_ns));
74 KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target); 142}
75 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
76 143
77 // If we fail to reply, we want to set the output index to -1. 144} // namespace
145
146/// Makes a blocking IPC call to a service.
147Result SendSyncRequest(Core::System& system, Handle session_handle) {
148 R_RETURN(SendSyncRequestImpl(system.Kernel(), 0, 0, session_handle));
149}
150
151Result SendSyncRequestWithUserBuffer(Core::System& system, uint64_t message, uint64_t buffer_size,
152 Handle session_handle) {
153 auto& kernel = system.Kernel();
154
155 // Validate that the message buffer is page aligned and does not overflow.
156 R_UNLESS(Common::IsAligned(message, PageSize), ResultInvalidAddress);
157 R_UNLESS(buffer_size > 0, ResultInvalidSize);
158 R_UNLESS(Common::IsAligned(buffer_size, PageSize), ResultInvalidSize);
159 R_UNLESS(message < message + buffer_size, ResultInvalidCurrentMemory);
160
161 // Get the process page table.
162 auto& page_table = GetCurrentProcess(kernel).GetPageTable();
163
164 // Lock the message buffer.
165 R_TRY(page_table.LockForIpcUserBuffer(nullptr, message, buffer_size));
166
167 {
168 // If we fail to send the message, unlock the message buffer.
78 ON_RESULT_FAILURE { 169 ON_RESULT_FAILURE {
79 *out_index = -1; 170 page_table.UnlockForIpcUserBuffer(message, buffer_size);
80 }; 171 };
81 172
82 // Send the reply. 173 // Send the request.
83 R_TRY(session->SendReply()); 174 ASSERT(message != 0);
175 R_TRY(SendSyncRequestImpl(kernel, message, buffer_size, session_handle));
84 } 176 }
85 177
86 // Convert the timeout from nanoseconds to ticks. 178 // We successfully processed, so try to unlock the message buffer.
87 // NOTE: Nintendo does not use this conversion logic in WaitSynchronization... 179 R_RETURN(page_table.UnlockForIpcUserBuffer(message, buffer_size));
88 s64 timeout; 180}
89 if (timeout_ns > 0) {
90 const s64 offset_tick(timeout_ns);
91 if (offset_tick > 0) {
92 timeout = kernel.HardwareTimer().GetTick() + offset_tick + 2;
93 if (timeout <= 0) {
94 timeout = std::numeric_limits<s64>::max();
95 }
96 } else {
97 timeout = std::numeric_limits<s64>::max();
98 }
99 } else {
100 timeout = timeout_ns;
101 }
102 181
103 // Wait for a message. 182Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_handle,
104 while (true) { 183 uint64_t message, uint64_t buffer_size,
105 // Wait for an object. 184 Handle session_handle) {
106 s32 index; 185 // Get the process and handle table.
107 Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs.data(), 186 auto& process = GetCurrentProcess(system.Kernel());
108 num_handles, timeout); 187 auto& handle_table = process.GetHandleTable();
109 if (result == ResultTimedOut) {
110 R_RETURN(result);
111 }
112 188
113 // Receive the request. 189 // Reserve a new event from the process resource limit.
114 if (R_SUCCEEDED(result)) { 190 KScopedResourceReservation event_reservation(std::addressof(process),
115 KServerSession* session = objs[index]->DynamicCast<KServerSession*>(); 191 Svc::LimitableResource::EventCountMax);
116 if (session != nullptr) { 192 R_UNLESS(event_reservation.Succeeded(), ResultLimitReached);
117 result = session->ReceiveRequest();
118 if (result == ResultNotFound) {
119 continue;
120 }
121 }
122 }
123 193
124 *out_index = index; 194 // Get the client session.
125 R_RETURN(result); 195 KScopedAutoObject session = process.GetHandleTable().GetObject<KClientSession>(session_handle);
126 } 196 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
197
198 // Get the parent, and persist a reference to it until we're done.
199 KScopedAutoObject parent = session->GetParent();
200 ASSERT(parent.IsNotNull());
201
202 // Create a new event.
203 KEvent* event = KEvent::Create(system.Kernel());
204 R_UNLESS(event != nullptr, ResultOutOfResource);
205
206 // Initialize the event.
207 event->Initialize(std::addressof(process));
208
209 // Commit our reservation.
210 event_reservation.Commit();
211
212 // At end of scope, kill the standing references to the sub events.
213 SCOPE_EXIT({
214 event->GetReadableEvent().Close();
215 event->Close();
216 });
217
218 // Register the event.
219 KEvent::Register(system.Kernel(), event);
220
221 // Add the readable event to the handle table.
222 R_TRY(handle_table.Add(out_event_handle, std::addressof(event->GetReadableEvent())));
223
224 // Ensure that if we fail to send the request, we close the readable handle.
225 ON_RESULT_FAILURE {
226 handle_table.Remove(*out_event_handle);
227 };
228
229 // Send the async request.
230 R_RETURN(session->SendAsyncRequest(event, message, buffer_size));
127} 231}
128 232
129Result ReplyAndReceiveWithUserBuffer(Core::System& system, int32_t* out_index, 233Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles, s32 num_handles,
130 uint64_t message_buffer, uint64_t message_buffer_size, 234 Handle reply_target, s64 timeout_ns) {
131 uint64_t handles, int32_t num_handles, Handle reply_target, 235 R_RETURN(ReplyAndReceiveImpl(system.Kernel(), out_index, 0, 0, 0, handles, num_handles,
132 int64_t timeout_ns) { 236 reply_target, timeout_ns));
133 UNIMPLEMENTED(); 237}
134 R_THROW(ResultNotImplemented); 238
239Result ReplyAndReceiveWithUserBuffer(Core::System& system, int32_t* out_index, uint64_t message,
240 uint64_t buffer_size, uint64_t handles, int32_t num_handles,
241 Handle reply_target, int64_t timeout_ns) {
242 // Validate that the message buffer is page aligned and does not overflow.
243 R_UNLESS(Common::IsAligned(message, PageSize), ResultInvalidAddress);
244 R_UNLESS(buffer_size > 0, ResultInvalidSize);
245 R_UNLESS(Common::IsAligned(buffer_size, PageSize), ResultInvalidSize);
246 R_UNLESS(message < message + buffer_size, ResultInvalidCurrentMemory);
247
248 // Get the process page table.
249 auto& page_table = GetCurrentProcess(system.Kernel()).GetPageTable();
250
251 // Lock the message buffer, getting its physical address.
252 KPhysicalAddress message_paddr;
253 R_TRY(page_table.LockForIpcUserBuffer(std::addressof(message_paddr), message, buffer_size));
254
255 {
256 // If we fail to send the message, unlock the message buffer.
257 ON_RESULT_FAILURE {
258 page_table.UnlockForIpcUserBuffer(message, buffer_size);
259 };
260
261 // Reply/Receive the request.
262 ASSERT(message != 0);
263 R_TRY(ReplyAndReceiveImpl(system.Kernel(), out_index, message, buffer_size, message_paddr,
264 handles, num_handles, reply_target, timeout_ns));
265 }
266
267 // We successfully processed, so try to unlock the message buffer.
268 R_RETURN(page_table.UnlockForIpcUserBuffer(message, buffer_size));
135} 269}
136 270
137Result SendSyncRequest64(Core::System& system, Handle session_handle) { 271Result SendSyncRequest64(Core::System& system, Handle session_handle) {
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 9ab718e0a..e0cde9a05 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -192,8 +192,6 @@ Result SM::GetServiceImpl(Kernel::KClientSession** out_client_session, HLEReques
192 return result; 192 return result;
193 } 193 }
194 194
195 LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetId());
196
197 *out_client_session = session; 195 *out_client_session = session;
198 return ResultSuccess; 196 return ResultSuccess;
199} 197}