summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp1
-rw-r--r--src/core/hle/ipc_helpers.h3
-rw-r--r--src/core/hle/kernel/k_client_session.cpp5
-rw-r--r--src/core/hle/kernel/k_client_session.h3
-rw-r--r--src/core/hle/kernel/k_server_session.cpp243
-rw-r--r--src/core/hle/kernel/k_server_session.h37
-rw-r--r--src/core/hle/kernel/svc.cpp170
-rw-r--r--src/core/hle/kernel/svc_wrap.h32
-rw-r--r--src/core/hle/service/ns/ns.cpp30
-rw-r--r--src/core/hle/service/ns/ns.h3
-rw-r--r--src/core/hle/service/ptm/ts.cpp15
-rw-r--r--src/core/hle/service/ptm/ts.h1
-rw-r--r--src/core/hle/service/set/set_sys.cpp79
-rw-r--r--src/core/hle/service/set/set_sys.h2
-rw-r--r--src/core/hle/service/sm/sm.cpp3
15 files changed, 572 insertions, 55 deletions
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 1d46f6d40..22b5d5656 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -111,6 +111,7 @@ public:
111 LOG_ERROR(Core_ARM, 111 LOG_ERROR(Core_ARM,
112 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, 112 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
113 num_instructions, memory.Read32(pc)); 113 num_instructions, memory.Read32(pc));
114 ReturnException(pc, ARM_Interface::no_execute);
114 } 115 }
115 116
116 void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op, 117 void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op,
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index d631c0357..0cc26a211 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -152,7 +152,8 @@ public:
152 Kernel::LimitableResource::Sessions, 1); 152 Kernel::LimitableResource::Sessions, 1);
153 153
154 auto* session = Kernel::KSession::Create(kernel); 154 auto* session = Kernel::KSession::Create(kernel);
155 session->Initialize(nullptr, iface->GetServiceName()); 155 session->Initialize(nullptr, iface->GetServiceName(),
156 std::make_shared<Kernel::SessionRequestManager>(kernel));
156 157
157 context->AddMoveObject(&session->GetClientSession()); 158 context->AddMoveObject(&session->GetClientSession());
158 iface->ClientConnected(&session->GetServerSession()); 159 iface->ClientConnected(&session->GetServerSession());
diff --git a/src/core/hle/kernel/k_client_session.cpp b/src/core/hle/kernel/k_client_session.cpp
index b2a887b14..8892c5b7c 100644
--- a/src/core/hle/kernel/k_client_session.cpp
+++ b/src/core/hle/kernel/k_client_session.cpp
@@ -21,10 +21,9 @@ void KClientSession::Destroy() {
21 21
22void KClientSession::OnServerClosed() {} 22void KClientSession::OnServerClosed() {}
23 23
24Result KClientSession::SendSyncRequest(KThread* thread, Core::Memory::Memory& memory, 24Result KClientSession::SendSyncRequest() {
25 Core::Timing::CoreTiming& core_timing) {
26 // Signal the server session that new data is available 25 // Signal the server session that new data is available
27 return parent->GetServerSession().HandleSyncRequest(thread, memory, core_timing); 26 return parent->GetServerSession().OnRequest();
28} 27}
29 28
30} // namespace Kernel 29} // namespace Kernel
diff --git a/src/core/hle/kernel/k_client_session.h b/src/core/hle/kernel/k_client_session.h
index 0c750d756..b4a19c546 100644
--- a/src/core/hle/kernel/k_client_session.h
+++ b/src/core/hle/kernel/k_client_session.h
@@ -46,8 +46,7 @@ public:
46 return parent; 46 return parent;
47 } 47 }
48 48
49 Result SendSyncRequest(KThread* thread, Core::Memory::Memory& memory, 49 Result SendSyncRequest();
50 Core::Timing::CoreTiming& core_timing);
51 50
52 void OnServerClosed(); 51 void OnServerClosed();
53 52
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index 802c646a6..4252c9adb 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -7,6 +7,8 @@
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/scope_exit.h"
11#include "core/core.h"
10#include "core/core_timing.h" 12#include "core/core_timing.h"
11#include "core/hle/ipc_helpers.h" 13#include "core/hle/ipc_helpers.h"
12#include "core/hle/kernel/hle_ipc.h" 14#include "core/hle/kernel/hle_ipc.h"
@@ -18,13 +20,19 @@
18#include "core/hle/kernel/k_server_session.h" 20#include "core/hle/kernel/k_server_session.h"
19#include "core/hle/kernel/k_session.h" 21#include "core/hle/kernel/k_session.h"
20#include "core/hle/kernel/k_thread.h" 22#include "core/hle/kernel/k_thread.h"
23#include "core/hle/kernel/k_thread_queue.h"
21#include "core/hle/kernel/kernel.h" 24#include "core/hle/kernel/kernel.h"
22#include "core/hle/kernel/service_thread.h" 25#include "core/hle/kernel/service_thread.h"
23#include "core/memory.h" 26#include "core/memory.h"
24 27
25namespace Kernel { 28namespace Kernel {
26 29
27KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {} 30using ThreadQueueImplForKServerSessionRequest = KThreadQueue;
31
32static constexpr u32 MessageBufferSize = 0x100;
33
34KServerSession::KServerSession(KernelCore& kernel_)
35 : KSynchronizationObject{kernel_}, m_lock{kernel_} {}
28 36
29KServerSession::~KServerSession() = default; 37KServerSession::~KServerSession() = default;
30 38
@@ -33,17 +41,14 @@ void KServerSession::Initialize(KSession* parent_session_, std::string&& name_,
33 // Set member variables. 41 // Set member variables.
34 parent = parent_session_; 42 parent = parent_session_;
35 name = std::move(name_); 43 name = std::move(name_);
36 44 manager = manager_;
37 if (manager_) {
38 manager = manager_;
39 } else {
40 manager = std::make_shared<SessionRequestManager>(kernel);
41 }
42} 45}
43 46
44void KServerSession::Destroy() { 47void KServerSession::Destroy() {
45 parent->OnServerClosed(); 48 parent->OnServerClosed();
46 49
50 this->CleanupRequests();
51
47 parent->Close(); 52 parent->Close();
48 53
49 // Release host emulation members. 54 // Release host emulation members.
@@ -54,13 +59,13 @@ void KServerSession::Destroy() {
54} 59}
55 60
56void KServerSession::OnClientClosed() { 61void KServerSession::OnClientClosed() {
57 if (manager->HasSessionHandler()) { 62 if (manager && manager->HasSessionHandler()) {
58 manager->SessionHandler().ClientDisconnected(this); 63 manager->SessionHandler().ClientDisconnected(this);
59 } 64 }
60} 65}
61 66
62bool KServerSession::IsSignaled() const { 67bool KServerSession::IsSignaled() const {
63 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 68 ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel));
64 69
65 // If the client is closed, we're always signaled. 70 // If the client is closed, we're always signaled.
66 if (parent->IsClientClosed()) { 71 if (parent->IsClientClosed()) {
@@ -68,7 +73,7 @@ bool KServerSession::IsSignaled() const {
68 } 73 }
69 74
70 // Otherwise, we're signaled if we have a request and aren't handling one. 75 // Otherwise, we're signaled if we have a request and aren't handling one.
71 return false; 76 return !m_thread_request_list.empty() && m_current_thread_request == nullptr;
72} 77}
73 78
74void KServerSession::AppendDomainHandler(SessionRequestHandlerPtr handler) { 79void KServerSession::AppendDomainHandler(SessionRequestHandlerPtr handler) {
@@ -173,9 +178,221 @@ Result KServerSession::CompleteSyncRequest(HLERequestContext& context) {
173 return result; 178 return result;
174} 179}
175 180
176Result KServerSession::HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory, 181Result KServerSession::OnRequest() {
177 Core::Timing::CoreTiming& core_timing) { 182 // Create the wait queue.
178 return QueueSyncRequest(thread, memory); 183 ThreadQueueImplForKServerSessionRequest wait_queue{kernel};
184
185 {
186 // Lock the scheduler.
187 KScopedSchedulerLock sl{kernel};
188
189 // Ensure that we can handle new requests.
190 R_UNLESS(!parent->IsServerClosed(), ResultSessionClosed);
191
192 // Check that we're not terminating.
193 R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested);
194
195 if (manager) {
196 // HLE request.
197 auto& memory{kernel.System().Memory()};
198 this->QueueSyncRequest(GetCurrentThreadPointer(kernel), memory);
199 } else {
200 // Non-HLE request.
201 auto* thread{GetCurrentThreadPointer(kernel)};
202
203 // Get whether we're empty.
204 const bool was_empty = m_thread_request_list.empty();
205
206 // Add the thread to the list.
207 thread->Open();
208 m_thread_request_list.push_back(thread);
209
210 // If we were empty, signal.
211 if (was_empty) {
212 this->NotifyAvailable();
213 }
214 }
215
216 // This is a synchronous request, so we should wait for our request to complete.
217 GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
218 GetCurrentThread(kernel).BeginWait(&wait_queue);
219 }
220
221 return GetCurrentThread(kernel).GetWaitResult();
222}
223
224Result KServerSession::SendReply() {
225 // Lock the session.
226 KScopedLightLock lk(m_lock);
227
228 // Get the request.
229 KThread* client_thread;
230 {
231 KScopedSchedulerLock sl{kernel};
232
233 // Get the current request.
234 client_thread = m_current_thread_request;
235 R_UNLESS(client_thread != nullptr, ResultInvalidState);
236
237 // Clear the current request, since we're processing it.
238 m_current_thread_request = nullptr;
239 if (!m_thread_request_list.empty()) {
240 this->NotifyAvailable();
241 }
242 }
243
244 // Close reference to the request once we're done processing it.
245 SCOPE_EXIT({ client_thread->Close(); });
246
247 // Extract relevant information from the request.
248 // const uintptr_t client_message = request->GetAddress();
249 // const size_t client_buffer_size = request->GetSize();
250 // KThread *client_thread = request->GetThread();
251 // KEvent *event = request->GetEvent();
252
253 // Check whether we're closed.
254 const bool closed = (client_thread == nullptr || parent->IsClientClosed());
255
256 Result result = ResultSuccess;
257 if (!closed) {
258 // If we're not closed, send the reply.
259 Core::Memory::Memory& memory{kernel.System().Memory()};
260 KThread* server_thread{GetCurrentThreadPointer(kernel)};
261 UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
262
263 auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
264 auto* dst_msg_buffer = memory.GetPointer(client_thread->GetTLSAddress());
265 std::memcpy(dst_msg_buffer, src_msg_buffer, MessageBufferSize);
266 } else {
267 result = ResultSessionClosed;
268 }
269
270 // Select a result for the client.
271 Result client_result = result;
272 if (closed && R_SUCCEEDED(result)) {
273 result = ResultSessionClosed;
274 client_result = ResultSessionClosed;
275 } else {
276 result = ResultSuccess;
277 }
278
279 // If there's a client thread, update it.
280 if (client_thread != nullptr) {
281 // End the client thread's wait.
282 KScopedSchedulerLock sl{kernel};
283
284 if (!client_thread->IsTerminationRequested()) {
285 client_thread->EndWait(client_result);
286 }
287 }
288
289 return result;
290}
291
292Result KServerSession::ReceiveRequest() {
293 // Lock the session.
294 KScopedLightLock lk(m_lock);
295
296 // Get the request and client thread.
297 // KSessionRequest *request;
298 KThread* client_thread;
299
300 {
301 KScopedSchedulerLock sl{kernel};
302
303 // Ensure that we can service the request.
304 R_UNLESS(!parent->IsClientClosed(), ResultSessionClosed);
305
306 // Ensure we aren't already servicing a request.
307 R_UNLESS(m_current_thread_request == nullptr, ResultNotFound);
308
309 // Ensure we have a request to service.
310 R_UNLESS(!m_thread_request_list.empty(), ResultNotFound);
311
312 // Pop the first request from the list.
313 client_thread = m_thread_request_list.front();
314 m_thread_request_list.pop_front();
315
316 // Get the thread for the request.
317 R_UNLESS(client_thread != nullptr, ResultSessionClosed);
318
319 // Open the client thread.
320 client_thread->Open();
321 }
322
323 // SCOPE_EXIT({ client_thread->Close(); });
324
325 // Set the request as our current.
326 m_current_thread_request = client_thread;
327
328 // Receive the message.
329 Core::Memory::Memory& memory{kernel.System().Memory()};
330 KThread* server_thread{GetCurrentThreadPointer(kernel)};
331 UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
332
333 auto* src_msg_buffer = memory.GetPointer(client_thread->GetTLSAddress());
334 auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
335 std::memcpy(dst_msg_buffer, src_msg_buffer, MessageBufferSize);
336
337 // We succeeded.
338 return ResultSuccess;
339}
340
341void KServerSession::CleanupRequests() {
342 KScopedLightLock lk(m_lock);
343
344 // Clean up any pending requests.
345 while (true) {
346 // Get the next request.
347 // KSessionRequest *request = nullptr;
348 KThread* client_thread = nullptr;
349 {
350 KScopedSchedulerLock sl{kernel};
351
352 if (m_current_thread_request) {
353 // Choose the current request if we have one.
354 client_thread = m_current_thread_request;
355 m_current_thread_request = nullptr;
356 } else if (!m_thread_request_list.empty()) {
357 // Pop the request from the front of the list.
358 client_thread = m_thread_request_list.front();
359 m_thread_request_list.pop_front();
360 }
361 }
362
363 // If there's no request, we're done.
364 if (client_thread == nullptr) {
365 break;
366 }
367
368 // Close a reference to the request once it's cleaned up.
369 SCOPE_EXIT({ client_thread->Close(); });
370
371 // Extract relevant information from the request.
372 // const uintptr_t client_message = request->GetAddress();
373 // const size_t client_buffer_size = request->GetSize();
374 // KThread *client_thread = request->GetThread();
375 // KEvent *event = request->GetEvent();
376
377 // KProcess *server_process = request->GetServerProcess();
378 // KProcess *client_process = (client_thread != nullptr) ?
379 // client_thread->GetOwnerProcess() : nullptr;
380 // KProcessPageTable *client_page_table = (client_process != nullptr) ?
381 // &client_process->GetPageTable() : nullptr;
382
383 // Cleanup the mappings.
384 // Result result = CleanupMap(request, server_process, client_page_table);
385
386 // If there's a client thread, update it.
387 if (client_thread != nullptr) {
388 // End the client thread's wait.
389 KScopedSchedulerLock sl{kernel};
390
391 if (!client_thread->IsTerminationRequested()) {
392 client_thread->EndWait(ResultSessionClosed);
393 }
394 }
395 }
179} 396}
180 397
181} // namespace Kernel 398} // namespace Kernel
diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h
index 6d0821945..748d52826 100644
--- a/src/core/hle/kernel/k_server_session.h
+++ b/src/core/hle/kernel/k_server_session.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <list>
6#include <memory> 7#include <memory>
7#include <string> 8#include <string>
8#include <utility> 9#include <utility>
@@ -10,6 +11,7 @@
10#include <boost/intrusive/list.hpp> 11#include <boost/intrusive/list.hpp>
11 12
12#include "core/hle/kernel/hle_ipc.h" 13#include "core/hle/kernel/hle_ipc.h"
14#include "core/hle/kernel/k_light_lock.h"
13#include "core/hle/kernel/k_synchronization_object.h" 15#include "core/hle/kernel/k_synchronization_object.h"
14#include "core/hle/result.h" 16#include "core/hle/result.h"
15 17
@@ -59,25 +61,15 @@ public:
59 void OnClientClosed(); 61 void OnClientClosed();
60 62
61 void ClientConnected(SessionRequestHandlerPtr handler) { 63 void ClientConnected(SessionRequestHandlerPtr handler) {
62 manager->SetSessionHandler(std::move(handler)); 64 if (manager) {
65 manager->SetSessionHandler(std::move(handler));
66 }
63 } 67 }
64 68
65 void ClientDisconnected() { 69 void ClientDisconnected() {
66 manager = nullptr; 70 manager = nullptr;
67 } 71 }
68 72
69 /**
70 * Handle a sync request from the emulated application.
71 *
72 * @param thread Thread that initiated the request.
73 * @param memory Memory context to handle the sync request under.
74 * @param core_timing Core timing context to schedule the request event under.
75 *
76 * @returns Result from the operation.
77 */
78 Result HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory,
79 Core::Timing::CoreTiming& core_timing);
80
81 /// Adds a new domain request handler to the collection of request handlers within 73 /// Adds a new domain request handler to the collection of request handlers within
82 /// this ServerSession instance. 74 /// this ServerSession instance.
83 void AppendDomainHandler(SessionRequestHandlerPtr handler); 75 void AppendDomainHandler(SessionRequestHandlerPtr handler);
@@ -88,7 +80,7 @@ public:
88 80
89 /// Returns true if the session has been converted to a domain, otherwise False 81 /// Returns true if the session has been converted to a domain, otherwise False
90 bool IsDomain() const { 82 bool IsDomain() const {
91 return manager->IsDomain(); 83 return manager && manager->IsDomain();
92 } 84 }
93 85
94 /// Converts the session to a domain at the end of the current command 86 /// Converts the session to a domain at the end of the current command
@@ -101,7 +93,15 @@ public:
101 return manager; 93 return manager;
102 } 94 }
103 95
96 /// TODO: flesh these out to match the real kernel
97 Result OnRequest();
98 Result SendReply();
99 Result ReceiveRequest();
100
104private: 101private:
102 /// Frees up waiting client sessions when this server session is about to die
103 void CleanupRequests();
104
105 /// Queues a sync request from the emulated application. 105 /// Queues a sync request from the emulated application.
106 Result QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory); 106 Result QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory);
107 107
@@ -112,7 +112,7 @@ private:
112 /// object handle. 112 /// object handle.
113 Result HandleDomainSyncRequest(Kernel::HLERequestContext& context); 113 Result HandleDomainSyncRequest(Kernel::HLERequestContext& context);
114 114
115 /// This session's HLE request handlers 115 /// This session's HLE request handlers; if nullptr, this is not an HLE server
116 std::shared_ptr<SessionRequestManager> manager; 116 std::shared_ptr<SessionRequestManager> manager;
117 117
118 /// When set to True, converts the session to a domain at the end of the command 118 /// When set to True, converts the session to a domain at the end of the command
@@ -120,6 +120,13 @@ private:
120 120
121 /// KSession that owns this KServerSession 121 /// KSession that owns this KServerSession
122 KSession* parent{}; 122 KSession* parent{};
123
124 /// List of threads which are pending a reply.
125 /// FIXME: KSessionRequest
126 std::list<KThread*> m_thread_request_list;
127 KThread* m_current_thread_request{};
128
129 KLightLock m_lock;
123}; 130};
124 131
125} // namespace Kernel 132} // namespace Kernel
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 27e5a805d..510a9b3e3 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -29,6 +29,7 @@
29#include "core/hle/kernel/k_resource_limit.h" 29#include "core/hle/kernel/k_resource_limit.h"
30#include "core/hle/kernel/k_scheduler.h" 30#include "core/hle/kernel/k_scheduler.h"
31#include "core/hle/kernel/k_scoped_resource_reservation.h" 31#include "core/hle/kernel/k_scoped_resource_reservation.h"
32#include "core/hle/kernel/k_session.h"
32#include "core/hle/kernel/k_shared_memory.h" 33#include "core/hle/kernel/k_shared_memory.h"
33#include "core/hle/kernel/k_synchronization_object.h" 34#include "core/hle/kernel/k_synchronization_object.h"
34#include "core/hle/kernel/k_thread.h" 35#include "core/hle/kernel/k_thread.h"
@@ -256,6 +257,93 @@ static Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u3
256 return UnmapMemory(system, dst_addr, src_addr, size); 257 return UnmapMemory(system, dst_addr, src_addr, size);
257} 258}
258 259
260template <typename T>
261Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u64 name) {
262 auto& process = *system.CurrentProcess();
263 auto& handle_table = process.GetHandleTable();
264
265 // Declare the session we're going to allocate.
266 T* session;
267
268 // Reserve a new session from the process resource limit.
269 // FIXME: LimitableResource_SessionCountMax
270 KScopedResourceReservation session_reservation(&process, LimitableResource::Sessions);
271 if (session_reservation.Succeeded()) {
272 session = T::Create(system.Kernel());
273 } else {
274 return ResultLimitReached;
275
276 // // We couldn't reserve a session. Check that we support dynamically expanding the
277 // // resource limit.
278 // R_UNLESS(process.GetResourceLimit() ==
279 // &system.Kernel().GetSystemResourceLimit(), ResultLimitReached);
280 // R_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled(), ResultLimitReached());
281
282 // // Try to allocate a session from unused slab memory.
283 // session = T::CreateFromUnusedSlabMemory();
284 // R_UNLESS(session != nullptr, ResultLimitReached);
285 // ON_RESULT_FAILURE { session->Close(); };
286
287 // // If we're creating a KSession, we want to add two KSessionRequests to the heap, to
288 // // prevent request exhaustion.
289 // // NOTE: Nintendo checks if session->DynamicCast<KSession *>() != nullptr, but there's
290 // // no reason to not do this statically.
291 // if constexpr (std::same_as<T, KSession>) {
292 // for (size_t i = 0; i < 2; i++) {
293 // KSessionRequest* request = KSessionRequest::CreateFromUnusedSlabMemory();
294 // R_UNLESS(request != nullptr, ResultLimitReached);
295 // request->Close();
296 // }
297 // }
298
299 // We successfully allocated a session, so add the object we allocated to the resource
300 // limit.
301 // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::Sessions, 1);
302 }
303
304 // Check that we successfully created a session.
305 R_UNLESS(session != nullptr, ResultOutOfResource);
306
307 // Initialize the session.
308 session->Initialize(nullptr, fmt::format("{}", name));
309
310 // Commit the session reservation.
311 session_reservation.Commit();
312
313 // Ensure that we clean up the session (and its only references are handle table) on function
314 // end.
315 SCOPE_EXIT({
316 session->GetClientSession().Close();
317 session->GetServerSession().Close();
318 });
319
320 // Register the session.
321 T::Register(system.Kernel(), session);
322
323 // Add the server session to the handle table.
324 R_TRY(handle_table.Add(out_server, &session->GetServerSession()));
325
326 // Add the client session to the handle table.
327 const auto result = handle_table.Add(out_client, &session->GetClientSession());
328
329 if (!R_SUCCEEDED(result)) {
330 // Ensure that we maintaing a clean handle state on exit.
331 handle_table.Remove(*out_server);
332 }
333
334 return result;
335}
336
337static Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client,
338 u32 is_light, u64 name) {
339 if (is_light) {
340 // return CreateSession<KLightSession>(system, out_server, out_client, name);
341 return ResultUnknown;
342 } else {
343 return CreateSession<KSession>(system, out_server, out_client, name);
344 }
345}
346
259/// Connect to an OS service given the port name, returns the handle to the port to out 347/// Connect to an OS service given the port name, returns the handle to the port to out
260static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) { 348static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) {
261 auto& memory = system.Memory(); 349 auto& memory = system.Memory();
@@ -295,7 +383,8 @@ static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_n
295 383
296 // Create a session. 384 // Create a session.
297 KClientSession* session{}; 385 KClientSession* session{};
298 R_TRY(port->CreateSession(std::addressof(session))); 386 R_TRY(port->CreateSession(std::addressof(session),
387 std::make_shared<SessionRequestManager>(kernel)));
299 port->Close(); 388 port->Close();
300 389
301 // Register the session in the table, close the extra reference. 390 // Register the session in the table, close the extra reference.
@@ -313,7 +402,7 @@ static Result ConnectToNamedPort32(Core::System& system, Handle* out_handle,
313 return ConnectToNamedPort(system, out_handle, port_name_address); 402 return ConnectToNamedPort(system, out_handle, port_name_address);
314} 403}
315 404
316/// Makes a blocking IPC call to an OS service. 405/// Makes a blocking IPC call to a service.
317static Result SendSyncRequest(Core::System& system, Handle handle) { 406static Result SendSyncRequest(Core::System& system, Handle handle) {
318 auto& kernel = system.Kernel(); 407 auto& kernel = system.Kernel();
319 408
@@ -327,22 +416,75 @@ static Result SendSyncRequest(Core::System& system, Handle handle) {
327 416
328 LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); 417 LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
329 418
330 { 419 return session->SendSyncRequest();
331 KScopedSchedulerLock lock(kernel);
332
333 // This is a synchronous request, so we should wait for our request to complete.
334 GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue));
335 GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
336 session->SendSyncRequest(&GetCurrentThread(kernel), system.Memory(), system.CoreTiming());
337 }
338
339 return GetCurrentThread(kernel).GetWaitResult();
340} 420}
341 421
342static Result SendSyncRequest32(Core::System& system, Handle handle) { 422static Result SendSyncRequest32(Core::System& system, Handle handle) {
343 return SendSyncRequest(system, handle); 423 return SendSyncRequest(system, handle);
344} 424}
345 425
426static Result ReplyAndReceive(Core::System& system, s32* out_index, Handle* handles,
427 s32 num_handles, Handle reply_target, s64 timeout_ns) {
428 auto& kernel = system.Kernel();
429 auto& handle_table = GetCurrentThread(kernel).GetOwnerProcess()->GetHandleTable();
430
431 // Convert handle list to object table.
432 std::vector<KSynchronizationObject*> objs(num_handles);
433 R_UNLESS(
434 handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles, num_handles),
435 ResultInvalidHandle);
436
437 // Ensure handles are closed when we're done.
438 SCOPE_EXIT({
439 for (auto i = 0; i < num_handles; ++i) {
440 objs[i]->Close();
441 }
442 });
443
444 // Reply to the target, if one is specified.
445 if (reply_target != InvalidHandle) {
446 KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target);
447 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
448
449 // If we fail to reply, we want to set the output index to -1.
450 // ON_RESULT_FAILURE { *out_index = -1; };
451
452 // Send the reply.
453 // R_TRY(session->SendReply());
454
455 Result rc = session->SendReply();
456 if (!R_SUCCEEDED(rc)) {
457 *out_index = -1;
458 return rc;
459 }
460 }
461
462 // Wait for a message.
463 while (true) {
464 // Wait for an object.
465 s32 index;
466 Result result = KSynchronizationObject::Wait(kernel, &index, objs.data(),
467 static_cast<s32>(objs.size()), timeout_ns);
468 if (result == ResultTimedOut) {
469 return result;
470 }
471
472 // Receive the request.
473 if (R_SUCCEEDED(result)) {
474 KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
475 if (session != nullptr) {
476 result = session->ReceiveRequest();
477 if (result == ResultNotFound) {
478 continue;
479 }
480 }
481 }
482
483 *out_index = index;
484 return result;
485 }
486}
487
346/// Get the ID for the specified thread. 488/// Get the ID for the specified thread.
347static Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) { 489static Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
348 // Get the thread from its handle. 490 // Get the thread from its handle.
@@ -2860,10 +3002,10 @@ static const FunctionDef SVC_Table_64[] = {
2860 {0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"}, 3002 {0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"},
2861 {0x3E, nullptr, "Unknown3e"}, 3003 {0x3E, nullptr, "Unknown3e"},
2862 {0x3F, nullptr, "Unknown3f"}, 3004 {0x3F, nullptr, "Unknown3f"},
2863 {0x40, nullptr, "CreateSession"}, 3005 {0x40, SvcWrap64<CreateSession>, "CreateSession"},
2864 {0x41, nullptr, "AcceptSession"}, 3006 {0x41, nullptr, "AcceptSession"},
2865 {0x42, nullptr, "ReplyAndReceiveLight"}, 3007 {0x42, nullptr, "ReplyAndReceiveLight"},
2866 {0x43, nullptr, "ReplyAndReceive"}, 3008 {0x43, SvcWrap64<ReplyAndReceive>, "ReplyAndReceive"},
2867 {0x44, nullptr, "ReplyAndReceiveWithUserBuffer"}, 3009 {0x44, nullptr, "ReplyAndReceiveWithUserBuffer"},
2868 {0x45, SvcWrap64<CreateEvent>, "CreateEvent"}, 3010 {0x45, SvcWrap64<CreateEvent>, "CreateEvent"},
2869 {0x46, nullptr, "MapIoRegion"}, 3011 {0x46, nullptr, "MapIoRegion"},
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 4bc49087e..272c54cf7 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -8,6 +8,7 @@
8#include "core/core.h" 8#include "core/core.h"
9#include "core/hle/kernel/svc_types.h" 9#include "core/hle/kernel/svc_types.h"
10#include "core/hle/result.h" 10#include "core/hle/result.h"
11#include "core/memory.h"
11 12
12namespace Kernel { 13namespace Kernel {
13 14
@@ -346,6 +347,37 @@ void SvcWrap64(Core::System& system) {
346 FuncReturn(system, retval); 347 FuncReturn(system, retval);
347} 348}
348 349
350// Used by CreateSession
351template <Result func(Core::System&, Handle*, Handle*, u32, u64)>
352void SvcWrap64(Core::System& system) {
353 Handle param_1 = 0;
354 Handle param_2 = 0;
355 const u32 retval = func(system, &param_1, &param_2, static_cast<u32>(Param(system, 2)),
356 static_cast<u32>(Param(system, 3)))
357 .raw;
358
359 system.CurrentArmInterface().SetReg(1, param_1);
360 system.CurrentArmInterface().SetReg(2, param_2);
361 FuncReturn(system, retval);
362}
363
364// Used by ReplyAndReceive
365template <Result func(Core::System&, s32*, Handle*, s32, Handle, s64)>
366void SvcWrap64(Core::System& system) {
367 s32 param_1 = 0;
368 s32 num_handles = static_cast<s32>(Param(system, 2));
369
370 std::vector<Handle> handles(num_handles);
371 system.Memory().ReadBlock(Param(system, 1), handles.data(), num_handles * sizeof(Handle));
372
373 const u32 retval = func(system, &param_1, handles.data(), num_handles,
374 static_cast<s32>(Param(system, 3)), static_cast<s64>(Param(system, 4)))
375 .raw;
376
377 system.CurrentArmInterface().SetReg(1, param_1);
378 FuncReturn(system, retval);
379}
380
349// Used by WaitForAddress 381// Used by WaitForAddress
350template <Result func(Core::System&, u64, Svc::ArbitrationType, s32, s64)> 382template <Result func(Core::System&, u64, Svc::ArbitrationType, s32, s64)>
351void SvcWrap64(Core::System& system) { 383void SvcWrap64(Core::System& system) {
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index f7318c3cb..f59a1a63d 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -8,6 +8,7 @@
8#include "core/file_sys/patch_manager.h" 8#include "core/file_sys/patch_manager.h"
9#include "core/file_sys/vfs.h" 9#include "core/file_sys/vfs.h"
10#include "core/hle/ipc_helpers.h" 10#include "core/hle/ipc_helpers.h"
11#include "core/hle/service/glue/glue_manager.h"
11#include "core/hle/service/ns/errors.h" 12#include "core/hle/service/ns/errors.h"
12#include "core/hle/service/ns/iplatform_service_manager.h" 13#include "core/hle/service/ns/iplatform_service_manager.h"
13#include "core/hle/service/ns/language.h" 14#include "core/hle/service/ns/language.h"
@@ -581,7 +582,7 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
581 : ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} { 582 : ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} {
582 // clang-format off 583 // clang-format off
583 static const FunctionInfo functions[] = { 584 static const FunctionInfo functions[] = {
584 {0, nullptr, "GetApplicationControlData"}, 585 {0, &IReadOnlyApplicationControlDataInterface::GetApplicationControlData, "GetApplicationControlData"},
585 {1, nullptr, "GetApplicationDesiredLanguage"}, 586 {1, nullptr, "GetApplicationDesiredLanguage"},
586 {2, nullptr, "ConvertApplicationLanguageToLanguageCode"}, 587 {2, nullptr, "ConvertApplicationLanguageToLanguageCode"},
587 {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"}, 588 {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
@@ -594,6 +595,33 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
594 595
595IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default; 596IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default;
596 597
598void IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
599 Kernel::HLERequestContext& ctx) {
600 enum class ApplicationControlSource : u8 {
601 CacheOnly,
602 Storage,
603 StorageOnly,
604 };
605
606 struct RequestParameters {
607 ApplicationControlSource source;
608 u64 application_id;
609 };
610 static_assert(sizeof(RequestParameters) == 0x10, "RequestParameters has incorrect size.");
611
612 IPC::RequestParser rp{ctx};
613 const auto parameters{rp.PopRaw<RequestParameters>()};
614 const auto nacp_data{system.GetARPManager().GetControlProperty(parameters.application_id)};
615 const auto result = nacp_data ? ResultSuccess : ResultUnknown;
616
617 if (nacp_data) {
618 ctx.WriteBuffer(nacp_data->data(), nacp_data->size());
619 }
620
621 IPC::ResponseBuilder rb{ctx, 2};
622 rb.Push(result);
623}
624
597NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} { 625NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} {
598 // clang-format off 626 // clang-format off
599 static const FunctionInfo functions[] = { 627 static const FunctionInfo functions[] = {
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
index 4dc191518..9c18e935c 100644
--- a/src/core/hle/service/ns/ns.h
+++ b/src/core/hle/service/ns/ns.h
@@ -78,6 +78,9 @@ class IReadOnlyApplicationControlDataInterface final
78public: 78public:
79 explicit IReadOnlyApplicationControlDataInterface(Core::System& system_); 79 explicit IReadOnlyApplicationControlDataInterface(Core::System& system_);
80 ~IReadOnlyApplicationControlDataInterface() override; 80 ~IReadOnlyApplicationControlDataInterface() override;
81
82private:
83 void GetApplicationControlData(Kernel::HLERequestContext& ctx);
81}; 84};
82 85
83class NS final : public ServiceFramework<NS> { 86class NS final : public ServiceFramework<NS> {
diff --git a/src/core/hle/service/ptm/ts.cpp b/src/core/hle/service/ptm/ts.cpp
index 65c3f135f..b1a0a5544 100644
--- a/src/core/hle/service/ptm/ts.cpp
+++ b/src/core/hle/service/ptm/ts.cpp
@@ -15,7 +15,7 @@ TS::TS(Core::System& system_) : ServiceFramework{system_, "ts"} {
15 {0, nullptr, "GetTemperatureRange"}, 15 {0, nullptr, "GetTemperatureRange"},
16 {1, &TS::GetTemperature, "GetTemperature"}, 16 {1, &TS::GetTemperature, "GetTemperature"},
17 {2, nullptr, "SetMeasurementMode"}, 17 {2, nullptr, "SetMeasurementMode"},
18 {3, nullptr, "GetTemperatureMilliC"}, 18 {3, &TS::GetTemperatureMilliC, "GetTemperatureMilliC"},
19 {4, nullptr, "OpenSession"}, 19 {4, nullptr, "OpenSession"},
20 }; 20 };
21 // clang-format on 21 // clang-format on
@@ -29,8 +29,6 @@ void TS::GetTemperature(Kernel::HLERequestContext& ctx) {
29 IPC::RequestParser rp{ctx}; 29 IPC::RequestParser rp{ctx};
30 const auto location{rp.PopEnum<Location>()}; 30 const auto location{rp.PopEnum<Location>()};
31 31
32 LOG_WARNING(Service_HID, "(STUBBED) called. location={}", location);
33
34 const s32 temperature = location == Location::Internal ? 35 : 20; 32 const s32 temperature = location == Location::Internal ? 35 : 20;
35 33
36 IPC::ResponseBuilder rb{ctx, 3}; 34 IPC::ResponseBuilder rb{ctx, 3};
@@ -38,4 +36,15 @@ void TS::GetTemperature(Kernel::HLERequestContext& ctx) {
38 rb.Push(temperature); 36 rb.Push(temperature);
39} 37}
40 38
39void TS::GetTemperatureMilliC(Kernel::HLERequestContext& ctx) {
40 IPC::RequestParser rp{ctx};
41 const auto location{rp.PopEnum<Location>()};
42
43 const s32 temperature = location == Location::Internal ? 35000 : 20000;
44
45 IPC::ResponseBuilder rb{ctx, 3};
46 rb.Push(ResultSuccess);
47 rb.Push(temperature);
48}
49
41} // namespace Service::PTM 50} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ts.h b/src/core/hle/service/ptm/ts.h
index 39a734ef7..39d51847e 100644
--- a/src/core/hle/service/ptm/ts.h
+++ b/src/core/hle/service/ptm/ts.h
@@ -20,6 +20,7 @@ private:
20 }; 20 };
21 21
22 void GetTemperature(Kernel::HLERequestContext& ctx); 22 void GetTemperature(Kernel::HLERequestContext& ctx);
23 void GetTemperatureMilliC(Kernel::HLERequestContext& ctx);
23}; 24};
24 25
25} // namespace Service::PTM 26} // namespace Service::PTM
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index 2a0b812c1..d7cea6aac 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -101,6 +101,81 @@ void SET_SYS::SetColorSetId(Kernel::HLERequestContext& ctx) {
101 rb.Push(ResultSuccess); 101 rb.Push(ResultSuccess);
102} 102}
103 103
104// FIXME: implement support for the real system_settings.ini
105
106template <typename T>
107static std::vector<u8> ToBytes(const T& value) {
108 static_assert(std::is_trivially_copyable_v<T>);
109
110 const auto* begin = reinterpret_cast<const u8*>(&value);
111 const auto* end = begin + sizeof(T);
112
113 return std::vector<u8>(begin, end);
114}
115
116using Settings =
117 std::map<std::string, std::map<std::string, std::vector<u8>, std::less<>>, std::less<>>;
118
119static Settings GetSettings() {
120 Settings ret;
121
122 ret["hbloader"]["applet_heap_size"] = ToBytes(u64{0x0});
123 ret["hbloader"]["applet_heap_reservation_size"] = ToBytes(u64{0x8600000});
124
125 return ret;
126}
127
128void SET_SYS::GetSettingsItemValueSize(Kernel::HLERequestContext& ctx) {
129 LOG_DEBUG(Service_SET, "called");
130
131 // The category of the setting. This corresponds to the top-level keys of
132 // system_settings.ini.
133 const auto setting_category_buf{ctx.ReadBuffer(0)};
134 const std::string setting_category{setting_category_buf.begin(), setting_category_buf.end()};
135
136 // The name of the setting. This corresponds to the second-level keys of
137 // system_settings.ini.
138 const auto setting_name_buf{ctx.ReadBuffer(1)};
139 const std::string setting_name{setting_name_buf.begin(), setting_name_buf.end()};
140
141 auto settings{GetSettings()};
142 u64 response_size{0};
143
144 if (settings.contains(setting_category) && settings[setting_category].contains(setting_name)) {
145 response_size = settings[setting_category][setting_name].size();
146 }
147
148 IPC::ResponseBuilder rb{ctx, 4};
149 rb.Push(response_size == 0 ? ResultUnknown : ResultSuccess);
150 rb.Push(response_size);
151}
152
153void SET_SYS::GetSettingsItemValue(Kernel::HLERequestContext& ctx) {
154 LOG_DEBUG(Service_SET, "called");
155
156 // The category of the setting. This corresponds to the top-level keys of
157 // system_settings.ini.
158 const auto setting_category_buf{ctx.ReadBuffer(0)};
159 const std::string setting_category{setting_category_buf.begin(), setting_category_buf.end()};
160
161 // The name of the setting. This corresponds to the second-level keys of
162 // system_settings.ini.
163 const auto setting_name_buf{ctx.ReadBuffer(1)};
164 const std::string setting_name{setting_name_buf.begin(), setting_name_buf.end()};
165
166 auto settings{GetSettings()};
167 Result response{ResultUnknown};
168
169 if (settings.contains(setting_category) && settings[setting_category].contains(setting_name)) {
170 auto setting_value = settings[setting_category][setting_name];
171 ctx.WriteBuffer(setting_value.data(), setting_value.size());
172 response = ResultSuccess;
173 }
174
175 IPC::ResponseBuilder rb{ctx, 2};
176 rb.Push(response);
177}
178
104SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { 179SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
105 // clang-format off 180 // clang-format off
106 static const FunctionInfo functions[] = { 181 static const FunctionInfo functions[] = {
@@ -138,8 +213,8 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
138 {32, nullptr, "SetAccountNotificationSettings"}, 213 {32, nullptr, "SetAccountNotificationSettings"},
139 {35, nullptr, "GetVibrationMasterVolume"}, 214 {35, nullptr, "GetVibrationMasterVolume"},
140 {36, nullptr, "SetVibrationMasterVolume"}, 215 {36, nullptr, "SetVibrationMasterVolume"},
141 {37, nullptr, "GetSettingsItemValueSize"}, 216 {37, &SET_SYS::GetSettingsItemValueSize, "GetSettingsItemValueSize"},
142 {38, nullptr, "GetSettingsItemValue"}, 217 {38, &SET_SYS::GetSettingsItemValue, "GetSettingsItemValue"},
143 {39, nullptr, "GetTvSettings"}, 218 {39, nullptr, "GetTvSettings"},
144 {40, nullptr, "SetTvSettings"}, 219 {40, nullptr, "SetTvSettings"},
145 {41, nullptr, "GetEdid"}, 220 {41, nullptr, "GetEdid"},
diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h
index ac97772b7..258ef8c57 100644
--- a/src/core/hle/service/set/set_sys.h
+++ b/src/core/hle/service/set/set_sys.h
@@ -23,6 +23,8 @@ private:
23 BasicBlack = 1, 23 BasicBlack = 1,
24 }; 24 };
25 25
26 void GetSettingsItemValueSize(Kernel::HLERequestContext& ctx);
27 void GetSettingsItemValue(Kernel::HLERequestContext& ctx);
26 void GetFirmwareVersion(Kernel::HLERequestContext& ctx); 28 void GetFirmwareVersion(Kernel::HLERequestContext& ctx);
27 void GetFirmwareVersion2(Kernel::HLERequestContext& ctx); 29 void GetFirmwareVersion2(Kernel::HLERequestContext& ctx);
28 void GetColorSetId(Kernel::HLERequestContext& ctx); 30 void GetColorSetId(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 246c94623..48e70f93c 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -156,7 +156,8 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext&
156 156
157 // Create a new session. 157 // Create a new session.
158 Kernel::KClientSession* session{}; 158 Kernel::KClientSession* session{};
159 if (const auto result = port->GetClientPort().CreateSession(std::addressof(session)); 159 if (const auto result = port->GetClientPort().CreateSession(
160 std::addressof(session), std::make_shared<Kernel::SessionRequestManager>(kernel));
160 result.IsError()) { 161 result.IsError()) {
161 LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw); 162 LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw);
162 return result; 163 return result;