libnx v4.9.0
Loading...
Searching...
No Matches
service.h
Go to the documentation of this file.
1/**
2 * @file service.h
3 * @brief Service wrapper object
4 * @author fincs
5 * @author SciresM
6 * @copyright libnx Authors
7 */
8#pragma once
9#include <assert.h>
10#include "hipc.h"
11#include "cmif.h"
12
13/// Service object structure
14typedef struct Service {
15 Handle session;
16 u32 own_handle;
17 u32 object_id;
18 u16 pointer_buffer_size;
19} Service;
20
21enum {
22 SfBufferAttr_In = BIT(0),
23 SfBufferAttr_Out = BIT(1),
24 SfBufferAttr_HipcMapAlias = BIT(2),
25 SfBufferAttr_HipcPointer = BIT(3),
26 SfBufferAttr_FixedSize = BIT(4),
27 SfBufferAttr_HipcAutoSelect = BIT(5),
28 SfBufferAttr_HipcMapTransferAllowsNonSecure = BIT(6),
29 SfBufferAttr_HipcMapTransferAllowsNonDevice = BIT(7),
30};
31
32typedef struct SfBufferAttrs {
33 u32 attr0;
34 u32 attr1;
35 u32 attr2;
36 u32 attr3;
37 u32 attr4;
38 u32 attr5;
39 u32 attr6;
40 u32 attr7;
42
43typedef struct SfBuffer {
44 const void* ptr;
45 size_t size;
46} SfBuffer;
47
48typedef enum SfOutHandleAttr {
49 SfOutHandleAttr_None = 0,
50 SfOutHandleAttr_HipcCopy = 1,
51 SfOutHandleAttr_HipcMove = 2,
52} SfOutHandleAttr;
53
54typedef struct SfOutHandleAttrs {
55 SfOutHandleAttr attr0;
56 SfOutHandleAttr attr1;
57 SfOutHandleAttr attr2;
58 SfOutHandleAttr attr3;
59 SfOutHandleAttr attr4;
60 SfOutHandleAttr attr5;
61 SfOutHandleAttr attr6;
62 SfOutHandleAttr attr7;
64
65typedef struct SfDispatchParams {
66 Handle target_session;
67 u32 context;
68
69 SfBufferAttrs buffer_attrs;
70 SfBuffer buffers[8];
71
72 bool in_send_pid;
73
74 u32 in_num_objects;
75 const Service* in_objects[8];
76
77 u32 in_num_handles;
78 Handle in_handles[8];
79
80 u32 out_num_objects;
81 Service* out_objects;
82
83 SfOutHandleAttrs out_handle_attrs;
84 Handle* out_handles;
86
87/**
88 * @brief Returns whether a service has been initialized.
89 * @param[in] s Service object.
90 * @return true if initialized.
91 */
93 return s->session != INVALID_HANDLE;
94}
95
96/**
97 * @brief Returns whether a service is overriden in the homebrew environment.
98 * @param[in] s Service object.
99 * @return true if overriden.
100 */
102 return serviceIsActive(s) && !s->own_handle && !s->object_id;
103}
104
105/**
106 * @brief Returns whether a service is a domain.
107 * @param[in] s Service object.
108 * @return true if a domain.
109 */
111 return serviceIsActive(s) && s->own_handle && s->object_id;
112}
113
114/**
115 * @brief Returns whether a service is a domain subservice.
116 * @param[in] s Service object.
117 * @return true if a domain subservice.
118 */
120 return serviceIsActive(s) && !s->own_handle && s->object_id;
121}
122
123/**
124 * @brief For a domain/domain subservice, return the associated object ID.
125 * @param[in] s Service object, necessarily a domain or domain subservice.
126 * @return The object ID.
127 */
129 return s->object_id;
130}
131
132/**
133 * @brief Creates a service object from an IPC session handle.
134 * @param[out] s Service object.
135 * @param[in] h IPC session handle.
136 */
138{
139 s->session = h;
140 s->own_handle = 1;
141 s->object_id = 0;
142 s->pointer_buffer_size = 0;
143 cmifQueryPointerBufferSize(h, &s->pointer_buffer_size);
144}
145
146/**
147 * @brief Creates a non-domain subservice object from a parent service.
148 * @param[out] s Service object.
149 * @param[in] parent Parent service.
150 * @param[in] h IPC session handle for this subservice.
151 */
153{
154 if (h != INVALID_HANDLE) {
155 s->session = h;
156 s->own_handle = 1;
157 s->object_id = 0;
158 s->pointer_buffer_size = parent->pointer_buffer_size;
159 } else {
160 *s = (Service){};
161 }
162}
163
164/**
165 * @brief Creates a domain subservice object from a parent service.
166 * @param[out] s Service object.
167 * @param[in] parent Parent service, necessarily a domain or domain subservice.
168 * @param[in] object_id Object ID for this subservice.
169 */
171{
172 if (object_id != 0) {
173 s->session = parent->session;
174 s->own_handle = 0;
175 s->object_id = object_id;
176 s->pointer_buffer_size = parent->pointer_buffer_size;
177 } else {
178 *s = (Service){};
179 }
180}
181
182/**
183 * @brief Hints the compiler that a service will always contain a domain object.
184 * @param[in] _s Service object.
185 */
186#define serviceAssumeDomain(_s) do { \
187 if (!(_s)->object_id) \
188 __builtin_unreachable(); \
189} while(0)
190
191/**
192 * @brief Closes a service.
193 * @param[in] s Service object.
194 */
196{
197#if defined(NX_SERVICE_ASSUME_NON_DOMAIN)
198 if (s->object_id)
199 __builtin_unreachable();
200#endif
201
202 if (s->own_handle || s->object_id) {
203 cmifMakeCloseRequest(armGetTls(), s->own_handle ? 0 : s->object_id);
204 svcSendSyncRequest(s->session);
205 if (s->own_handle)
206 svcCloseHandle(s->session);
207 }
208 *s = (Service){};
209}
210
211/**
212 * @brief Clones a service.
213 * @param[in] s Service object.
214 * @param[out] out_s Output service object.
215 */
217{
218#if defined(NX_SERVICE_ASSUME_NON_DOMAIN)
219 if (s->object_id)
220 __builtin_unreachable();
221#endif
222
223 out_s->session = 0;
224 out_s->own_handle = 1;
225 out_s->object_id = s->object_id;
226 out_s->pointer_buffer_size = s->pointer_buffer_size;
227 return cmifCloneCurrentObject(s->session, &out_s->session);
228}
229
230/**
231 * @brief Clones a service with a session manager tag.
232 * @param[in] s Service object.
233 * @param[in] tag Session manager tag (unused in current official server code)
234 * @param[out] out_s Output service object.
235 */
237{
238#if defined(NX_SERVICE_ASSUME_NON_DOMAIN)
239 if (s->object_id)
240 __builtin_unreachable();
241#endif
242
243 out_s->session = 0;
244 out_s->own_handle = 1;
245 out_s->object_id = s->object_id;
246 out_s->pointer_buffer_size = s->pointer_buffer_size;
247 return cmifCloneCurrentObjectEx(s->session, tag, &out_s->session);
248}
249
250/**
251 * @brief Converts a regular service to a domain.
252 * @param[in] s Service object.
253 * @return Result code.
254 */
256{
257 if (!s->own_handle) {
258 // For overridden services, create a clone first.
259 Result rc = cmifCloneCurrentObjectEx(s->session, 0, &s->session);
260 if (R_FAILED(rc))
261 return rc;
262 s->own_handle = 1;
263 }
264
265 return cmifConvertCurrentObjectToDomain(s->session, &s->object_id);
266}
267
268NX_CONSTEXPR void _serviceRequestFormatProcessBuffer(CmifRequestFormat* fmt, u32 attr)
269{
270 if (!attr) return;
271 const bool is_in = (attr & SfBufferAttr_In) != 0;
272 const bool is_out = (attr & SfBufferAttr_Out) != 0;
273
274 if (attr & SfBufferAttr_HipcAutoSelect) {
275 if (is_in)
276 fmt->num_in_auto_buffers ++;
277 if (is_out)
278 fmt->num_out_auto_buffers ++;
279 } else if (attr & SfBufferAttr_HipcPointer) {
280 if (is_in)
281 fmt->num_in_pointers ++;
282 if (is_out) {
283 if (attr & SfBufferAttr_FixedSize)
284 fmt->num_out_fixed_pointers ++;
285 else
286 fmt->num_out_pointers ++;
287 }
288 } else if (attr & SfBufferAttr_HipcMapAlias) {
289 if (is_in && is_out)
290 fmt->num_inout_buffers ++;
291 else if (is_in)
292 fmt->num_in_buffers ++;
293 else if (is_out)
294 fmt->num_out_buffers ++;
295 }
296}
297
298NX_CONSTEXPR void _serviceRequestProcessBuffer(CmifRequest* req, const SfBuffer* buf, u32 attr)
299{
300 if (!attr) return;
301 const bool is_in = (attr & SfBufferAttr_In);
302 const bool is_out = (attr & SfBufferAttr_Out);
303
304 if (attr & SfBufferAttr_HipcAutoSelect) {
305 HipcBufferMode mode = HipcBufferMode_Normal;
306 if (attr & SfBufferAttr_HipcMapTransferAllowsNonSecure)
307 mode = HipcBufferMode_NonSecure;
308 if (attr & SfBufferAttr_HipcMapTransferAllowsNonDevice)
309 mode = HipcBufferMode_NonDevice;
310 if (is_in)
311 cmifRequestInAutoBuffer(req, buf->ptr, buf->size, mode);
312 if (is_out)
313 cmifRequestOutAutoBuffer(req, (void*)buf->ptr, buf->size, mode);
314 } else if (attr & SfBufferAttr_HipcPointer) {
315 if (is_in)
316 cmifRequestInPointer(req, buf->ptr, buf->size);
317 if (is_out) {
318 if (attr & SfBufferAttr_FixedSize)
319 cmifRequestOutFixedPointer(req, (void*)buf->ptr, buf->size);
320 else
321 cmifRequestOutPointer(req, (void*)buf->ptr, buf->size);
322 }
323 } else if (attr & SfBufferAttr_HipcMapAlias) {
324 HipcBufferMode mode = HipcBufferMode_Normal;
325 if (attr & SfBufferAttr_HipcMapTransferAllowsNonSecure)
326 mode = HipcBufferMode_NonSecure;
327 if (attr & SfBufferAttr_HipcMapTransferAllowsNonDevice)
328 mode = HipcBufferMode_NonDevice;
329
330 if (is_in && is_out)
331 cmifRequestInOutBuffer(req, (void*)buf->ptr, buf->size, mode);
332 else if (is_in)
333 cmifRequestInBuffer(req, buf->ptr, buf->size, mode);
334 else if (is_out)
335 cmifRequestOutBuffer(req, (void*)buf->ptr, buf->size, mode);
336 }
337}
338
339NX_INLINE void* serviceMakeRequest(
340 Service* s, u32 request_id, u32 context, u32 data_size, bool send_pid,
341 const SfBufferAttrs buffer_attrs, const SfBuffer* buffers,
342 u32 num_objects, const Service* const* objects,
343 u32 num_handles, const Handle* handles
344) {
345#if defined(NX_SERVICE_ASSUME_NON_DOMAIN)
346 if (s->object_id)
347 __builtin_unreachable();
348#endif
349
350 CmifRequestFormat fmt = {};
351 fmt.object_id = s->object_id;
352 fmt.request_id = request_id;
353 fmt.context = context;
354 fmt.data_size = data_size;
355 fmt.server_pointer_size = s->pointer_buffer_size;
356 fmt.num_objects = num_objects;
357 fmt.num_handles = num_handles;
358 fmt.send_pid = send_pid;
359
360 _serviceRequestFormatProcessBuffer(&fmt, buffer_attrs.attr0);
361 _serviceRequestFormatProcessBuffer(&fmt, buffer_attrs.attr1);
362 _serviceRequestFormatProcessBuffer(&fmt, buffer_attrs.attr2);
363 _serviceRequestFormatProcessBuffer(&fmt, buffer_attrs.attr3);
364 _serviceRequestFormatProcessBuffer(&fmt, buffer_attrs.attr4);
365 _serviceRequestFormatProcessBuffer(&fmt, buffer_attrs.attr5);
366 _serviceRequestFormatProcessBuffer(&fmt, buffer_attrs.attr6);
367 _serviceRequestFormatProcessBuffer(&fmt, buffer_attrs.attr7);
368
369 CmifRequest req = cmifMakeRequest(armGetTls(), fmt);
370
371 if (s->object_id) // TODO: Check behavior of input objects in non-domain sessions
372 for (u32 i = 0; i < num_objects; i ++)
373 cmifRequestObject(&req, objects[i]->object_id);
374
375 for (u32 i = 0; i < num_handles; i ++)
376 cmifRequestHandle(&req, handles[i]);
377
378 _serviceRequestProcessBuffer(&req, &buffers[0], buffer_attrs.attr0);
379 _serviceRequestProcessBuffer(&req, &buffers[1], buffer_attrs.attr1);
380 _serviceRequestProcessBuffer(&req, &buffers[2], buffer_attrs.attr2);
381 _serviceRequestProcessBuffer(&req, &buffers[3], buffer_attrs.attr3);
382 _serviceRequestProcessBuffer(&req, &buffers[4], buffer_attrs.attr4);
383 _serviceRequestProcessBuffer(&req, &buffers[5], buffer_attrs.attr5);
384 _serviceRequestProcessBuffer(&req, &buffers[6], buffer_attrs.attr6);
385 _serviceRequestProcessBuffer(&req, &buffers[7], buffer_attrs.attr7);
386
387 return req.data;
388}
389
390NX_CONSTEXPR void _serviceResponseGetHandle(CmifResponse* res, SfOutHandleAttr type, Handle* out)
391{
392 switch (type) {
393 default:
394 case SfOutHandleAttr_None:
395 break;
396 case SfOutHandleAttr_HipcCopy:
397 *out = cmifResponseGetCopyHandle(res);
398 break;
399 case SfOutHandleAttr_HipcMove:
400 *out = cmifResponseGetMoveHandle(res);
401 break;
402 }
403}
404
405NX_INLINE Result serviceParseResponse(
406 Service* s, u32 out_size, void** out_data,
407 u32 num_out_objects, Service* out_objects,
408 const SfOutHandleAttrs out_handle_attrs, Handle* out_handles
409) {
410#if defined(NX_SERVICE_ASSUME_NON_DOMAIN)
411 if (s->object_id)
412 __builtin_unreachable();
413#endif
414
415 CmifResponse res = {};
416 bool is_domain = s->object_id != 0;
417 Result rc = cmifParseResponse(&res, armGetTls(), is_domain, out_size);
418 if (R_FAILED(rc))
419 return rc;
420
421 if (out_size)
422 *out_data = res.data;
423
424 for (u32 i = 0; i < num_out_objects; i ++) {
425 if (is_domain)
426 serviceCreateDomainSubservice(&out_objects[i], s, cmifResponseGetObject(&res));
427 else // Output objects are marshalled as move handles at the beginning of the list.
428 serviceCreateNonDomainSubservice(&out_objects[i], s, cmifResponseGetMoveHandle(&res));
429 }
430
431 _serviceResponseGetHandle(&res, out_handle_attrs.attr0, &out_handles[0]);
432 _serviceResponseGetHandle(&res, out_handle_attrs.attr1, &out_handles[1]);
433 _serviceResponseGetHandle(&res, out_handle_attrs.attr2, &out_handles[2]);
434 _serviceResponseGetHandle(&res, out_handle_attrs.attr3, &out_handles[3]);
435 _serviceResponseGetHandle(&res, out_handle_attrs.attr4, &out_handles[4]);
436 _serviceResponseGetHandle(&res, out_handle_attrs.attr5, &out_handles[5]);
437 _serviceResponseGetHandle(&res, out_handle_attrs.attr6, &out_handles[6]);
438 _serviceResponseGetHandle(&res, out_handle_attrs.attr7, &out_handles[7]);
439
440 return 0;
441}
442
443NX_INLINE Result serviceDispatchImpl(
444 Service* s, u32 request_id,
445 const void* in_data, u32 in_data_size,
446 void* out_data, u32 out_data_size,
448)
449{
450 // Make a copy of the service struct, so that the compiler can assume that it won't be modified by function calls.
451 Service srv = *s;
452
453 void* in = serviceMakeRequest(&srv, request_id, disp.context,
454 in_data_size, disp.in_send_pid,
455 disp.buffer_attrs, disp.buffers,
456 disp.in_num_objects, disp.in_objects,
457 disp.in_num_handles, disp.in_handles);
458
459 if (in_data_size)
460 __builtin_memcpy(in, in_data, in_data_size);
461
462 Result rc = svcSendSyncRequest(disp.target_session == INVALID_HANDLE ? s->session : disp.target_session);
463 if (R_SUCCEEDED(rc)) {
464 void* out = NULL;
465 rc = serviceParseResponse(&srv,
466 out_data_size, &out,
467 disp.out_num_objects, disp.out_objects,
468 disp.out_handle_attrs, disp.out_handles);
469
470 if (R_SUCCEEDED(rc) && out_data && out_data_size)
471 __builtin_memcpy(out_data, out, out_data_size);
472 }
473
474 return rc;
475}
476
477#ifndef __cplusplus
478
479#define serviceMacroDetectIsSameType(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
480#define serviceMacroDetectIsPointerOrArray(p) (__builtin_classify_type(p) == 5)
481#define serviceMacroDecay(p) (&*__builtin_choose_expr(serviceMacroDetectIsPointerOrArray(p), p, NULL))
482#define serviceMacroDetectIsPointer(p) serviceMacroDetectIsSameType(p, serviceMacroDecay(p))
483
484#else
485
486extern "C++" {
487
488namespace libnx::impl {
489
490 template<typename T> struct is_pointer { static constexpr bool value = false; };
491 template<typename T> struct is_pointer<T*> { static constexpr bool value = true; };
492 template<typename T> struct is_pointer<T* const> { static constexpr bool value = true; };
493 template<typename T> struct is_pointer<T* volatile> { static constexpr bool value = true; };
494 template<typename T> struct is_pointer<T* const volatile> { static constexpr bool value = true; };
495
496}
497
498}
499
500#define serviceMacroDetectIsPointer(p) (::libnx::impl::is_pointer<decltype(p)>::value)
501
502#endif
503
504#define serviceDispatch(_s,_rid,...) \
505 serviceDispatchImpl((_s),(_rid),NULL,0,NULL,0,(SfDispatchParams){ __VA_ARGS__ })
506
507#define serviceDispatchIn(_s,_rid,_in,...) \
508 ({ static_assert(!(serviceMacroDetectIsPointer(_in))); \
509 serviceDispatchImpl((_s),(_rid),&(_in),sizeof(_in),NULL,0,(SfDispatchParams){ __VA_ARGS__ }); })
510
511#define serviceDispatchOut(_s,_rid,_out,...) \
512 ({ static_assert(!(serviceMacroDetectIsPointer(_out))); \
513 serviceDispatchImpl((_s),(_rid),NULL,0,&(_out),sizeof(_out),(SfDispatchParams){ __VA_ARGS__ }); })
514
515#define serviceDispatchInOut(_s,_rid,_in,_out,...) \
516 ({ static_assert(!(serviceMacroDetectIsPointer(_in))); \
517 static_assert(!(serviceMacroDetectIsPointer(_out))); \
518 serviceDispatchImpl((_s),(_rid),&(_in),sizeof(_in),&(_out),sizeof(_out),(SfDispatchParams){ __VA_ARGS__ }); })
Common Message Interface Framework protocol.
Horizon Inter-Process Communication protocol.
#define R_FAILED(res)
Checks whether a result code indicates failure.
Definition result.h:12
#define R_SUCCEEDED(res)
Checks whether a result code indicates success.
Definition result.h:10
static bool serviceIsOverride(Service *s)
Returns whether a service is overriden in the homebrew environment.
Definition service.h:101
static Result serviceClone(Service *s, Service *out_s)
Clones a service.
Definition service.h:216
static bool serviceIsDomainSubservice(Service *s)
Returns whether a service is a domain subservice.
Definition service.h:119
static Result serviceCloneEx(Service *s, u32 tag, Service *out_s)
Clones a service with a session manager tag.
Definition service.h:236
static void serviceCreateNonDomainSubservice(Service *s, Service *parent, Handle h)
Creates a non-domain subservice object from a parent service.
Definition service.h:152
static Result serviceConvertToDomain(Service *s)
Converts a regular service to a domain.
Definition service.h:255
static bool serviceIsActive(Service *s)
Returns whether a service has been initialized.
Definition service.h:92
static void serviceCreateDomainSubservice(Service *s, Service *parent, u32 object_id)
Creates a domain subservice object from a parent service.
Definition service.h:170
static bool serviceIsDomain(Service *s)
Returns whether a service is a domain.
Definition service.h:110
static u32 serviceGetObjectId(Service *s)
For a domain/domain subservice, return the associated object ID.
Definition service.h:128
static void serviceClose(Service *s)
Closes a service.
Definition service.h:195
static void serviceCreate(Service *s, Handle h)
Creates a service object from an IPC session handle.
Definition service.h:137
Definition cmif.h:59
Definition cmif.h:78
Definition cmif.h:87
Service object structure.
Definition service.h:14
Definition service.h:32
Definition service.h:43
Definition service.h:65
Definition service.h:54
Result svcCloseHandle(Handle handle)
Closes a handle, decrementing the reference count of the corresponding kernel object.
Result svcSendSyncRequest(Handle session)
Sends an IPC synchronization request to a session.
static void * armGetTls(void)
Gets the thread local storage buffer.
Definition tls.h:14
#define NX_INLINE
Flags a function as (always) inline.
Definition types.h:86
#define BIT(n)
Creates a bitmask from a bit number.
Definition types.h:54
#define INVALID_HANDLE
Invalid handle.
Definition types.h:96
u32 Handle
Kernel object handle.
Definition types.h:43
uint16_t u16
16-bit unsigned integer.
Definition types.h:20
u32 Result
Function error code result type.
Definition types.h:44
#define NX_CONSTEXPR
Flags a function as constexpr in C++14 and above; or as (always) inline otherwise.
Definition types.h:92
uint32_t u32
32-bit unsigned integer.
Definition types.h:21