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