libnx  v4.6.0
cmif.h
Go to the documentation of this file.
1 /**
2  * @file cmif.h
3  * @brief Common Message Interface Framework protocol
4  * @author fincs
5  * @author SciresM
6  * @copyright libnx Authors
7  */
8 #pragma once
9 #include "hipc.h"
10 
11 #define CMIF_IN_HEADER_MAGIC 0x49434653 // "SFCI"
12 #define CMIF_OUT_HEADER_MAGIC 0x4F434653 // "SFCO"
13 
14 typedef enum CmifCommandType {
15  CmifCommandType_Invalid = 0,
16  CmifCommandType_LegacyRequest = 1,
17  CmifCommandType_Close = 2,
18  CmifCommandType_LegacyControl = 3,
19  CmifCommandType_Request = 4,
20  CmifCommandType_Control = 5,
21  CmifCommandType_RequestWithContext = 6,
22  CmifCommandType_ControlWithContext = 7,
23 } CmifCommandType;
24 
25 typedef enum CmifDomainRequestType {
26  CmifDomainRequestType_Invalid = 0,
27  CmifDomainRequestType_SendMessage = 1,
28  CmifDomainRequestType_Close = 2,
29 } CmifDomainRequestType;
30 
31 typedef struct CmifInHeader {
32  u32 magic;
33  u32 version;
34  u32 command_id;
35  u32 token;
36 } CmifInHeader;
37 
38 typedef struct CmifOutHeader {
39  u32 magic;
40  u32 version;
41  Result result;
42  u32 token;
44 
45 typedef struct CmifDomainInHeader {
46  u8 type;
47  u8 num_in_objects;
48  u16 data_size;
49  u32 object_id;
50  u32 padding;
51  u32 token;
53 
54 typedef struct CmifDomainOutHeader {
55  u32 num_out_objects;
56  u32 padding[3];
58 
59 typedef struct CmifRequestFormat {
60  u32 object_id;
61  u32 request_id;
62  u32 context;
63  u32 data_size;
64  u32 server_pointer_size;
65  u32 num_in_auto_buffers;
66  u32 num_out_auto_buffers;
67  u32 num_in_buffers;
68  u32 num_out_buffers;
69  u32 num_inout_buffers;
70  u32 num_in_pointers;
71  u32 num_out_pointers;
72  u32 num_out_fixed_pointers;
73  u32 num_objects;
74  u32 num_handles;
75  u32 send_pid;
77 
78 typedef struct CmifRequest {
79  HipcRequest hipc;
80  void* data;
81  u16* out_pointer_sizes;
82  u32* objects;
83  u32 server_pointer_size;
84  u32 cur_in_ptr_id;
85 } CmifRequest;
86 
87 typedef struct CmifResponse {
88  void* data;
89  u32* objects;
90  Handle* copy_handles;
91  Handle* move_handles;
92 } CmifResponse;
93 
94 NX_INLINE void* cmifGetAlignedDataStart(u32* data_words, void* base)
95 {
96  intptr_t data_start = ((u8*)data_words - (u8*)base + 15) &~ 15;
97  return (u8*)base + data_start;
98 }
99 
100 NX_INLINE CmifRequest cmifMakeRequest(void* base, CmifRequestFormat fmt)
101 {
102  // First of all, we need to figure out what size we need.
103  u32 actual_size = 16;
104  if (fmt.object_id)
105  actual_size += sizeof(CmifDomainInHeader) + fmt.num_objects*sizeof(u32);
106  actual_size += sizeof(CmifInHeader) + fmt.data_size;
107  actual_size = (actual_size + 1) &~ 1; // hword-align
108  u32 out_pointer_size_table_offset = actual_size;
109  u32 out_pointer_size_table_size = fmt.num_out_auto_buffers + fmt.num_out_pointers;
110  actual_size += sizeof(u16)*out_pointer_size_table_size;
111  u32 num_data_words = (actual_size + 3) / 4;
112 
113  CmifRequest req = {};
114  req.hipc = hipcMakeRequestInline(base,
115  .type = fmt.context ? CmifCommandType_RequestWithContext : CmifCommandType_Request,
116  .num_send_statics = fmt.num_in_auto_buffers + fmt.num_in_pointers,
117  .num_send_buffers = fmt.num_in_auto_buffers + fmt.num_in_buffers,
118  .num_recv_buffers = fmt.num_out_auto_buffers + fmt.num_out_buffers,
119  .num_exch_buffers = fmt.num_inout_buffers,
120  .num_data_words = num_data_words,
121  .num_recv_statics = out_pointer_size_table_size + fmt.num_out_fixed_pointers,
122  .send_pid = fmt.send_pid,
123  .num_copy_handles = fmt.num_handles,
124  .num_move_handles = 0,
125  );
126 
127  CmifInHeader* hdr = NULL;
128  void* start = cmifGetAlignedDataStart(req.hipc.data_words, base);
129  if (fmt.object_id) {
130  CmifDomainInHeader* domain_hdr = (CmifDomainInHeader*)start;
131  u32 payload_size = sizeof(CmifInHeader) + fmt.data_size;
132  *domain_hdr = (CmifDomainInHeader){
133  .type = CmifDomainRequestType_SendMessage,
134  .num_in_objects = (u8)fmt.num_objects,
135  .data_size = (u16)payload_size,
136  .object_id = fmt.object_id,
137  .padding = 0,
138  .token = fmt.context,
139  };
140  hdr = (CmifInHeader*)(domain_hdr+1);
141  req.objects = (u32*)((u8*)hdr + payload_size);
142  } else
143  hdr = (CmifInHeader*)start;
144 
145  *hdr = (CmifInHeader){
146  .magic = CMIF_IN_HEADER_MAGIC,
147  .version = fmt.context ? 1U : 0U,
148  .command_id = fmt.request_id,
149  .token = fmt.object_id ? 0U : fmt.context,
150  };
151 
152  req.data = hdr+1;
153  req.out_pointer_sizes = (u16*)(void*)((u8*)(void*)req.hipc.data_words + out_pointer_size_table_offset);
154  req.server_pointer_size = fmt.server_pointer_size;
155 
156  return req;
157 }
158 
159 NX_INLINE void* cmifMakeControlRequest(void* base, u32 request_id, u32 size)
160 {
161  u32 actual_size = 16 + sizeof(CmifInHeader) + size;
162  HipcRequest hipc = hipcMakeRequestInline(base,
163  .type = CmifCommandType_Control,
164  .num_data_words = (actual_size + 3) / 4,
165  );
166  CmifInHeader* hdr = (CmifInHeader*)cmifGetAlignedDataStart(hipc.data_words, base);
167  *hdr = (CmifInHeader){
168  .magic = CMIF_IN_HEADER_MAGIC,
169  .version = 0,
170  .command_id = request_id,
171  .token = 0,
172  };
173  return hdr+1;
174 }
175 
176 NX_INLINE void cmifMakeCloseRequest(void* base, u32 object_id)
177 {
178  if (object_id) {
179  HipcRequest hipc = hipcMakeRequestInline(base,
180  .type = CmifCommandType_Request,
181  .num_data_words = (16 + sizeof(CmifDomainInHeader)) / 4,
182  );
183  CmifDomainInHeader* domain_hdr = (CmifDomainInHeader*)cmifGetAlignedDataStart(hipc.data_words, base);
184  *domain_hdr = (CmifDomainInHeader){
185  .type = CmifDomainRequestType_Close,
186  .object_id = object_id,
187  };
188  } else {
189  hipcMakeRequestInline(base,
190  .type = CmifCommandType_Close,
191  );
192  }
193 }
194 
195 NX_CONSTEXPR void cmifRequestInBuffer(CmifRequest* req, const void* buffer, size_t size, HipcBufferMode mode)
196 {
197  *req->hipc.send_buffers++ = hipcMakeBuffer(buffer, size, mode);
198 }
199 
200 NX_CONSTEXPR void cmifRequestOutBuffer(CmifRequest* req, void* buffer, size_t size, HipcBufferMode mode)
201 {
202  *req->hipc.recv_buffers++ = hipcMakeBuffer(buffer, size, mode);
203 }
204 
205 NX_CONSTEXPR void cmifRequestInOutBuffer(CmifRequest* req, void* buffer, size_t size, HipcBufferMode mode)
206 {
207  *req->hipc.exch_buffers++ = hipcMakeBuffer(buffer, size, mode);
208 }
209 
210 NX_CONSTEXPR void cmifRequestInPointer(CmifRequest* req, const void* buffer, size_t size)
211 {
212  *req->hipc.send_statics++ = hipcMakeSendStatic(buffer, size, req->cur_in_ptr_id++);
213  req->server_pointer_size -= size;
214 }
215 
216 NX_CONSTEXPR void cmifRequestOutFixedPointer(CmifRequest* req, void* buffer, size_t size)
217 {
218  *req->hipc.recv_list++ = hipcMakeRecvStatic(buffer, size);
219  req->server_pointer_size -= size;
220 }
221 
222 NX_CONSTEXPR void cmifRequestOutPointer(CmifRequest* req, void* buffer, size_t size)
223 {
224  cmifRequestOutFixedPointer(req, buffer, size);
225  *req->out_pointer_sizes++ = size;
226 }
227 
228 NX_CONSTEXPR void cmifRequestInAutoBuffer(CmifRequest* req, const void* buffer, size_t size, HipcBufferMode mode)
229 {
230  if (req->server_pointer_size && size <= req->server_pointer_size) {
231  cmifRequestInPointer(req, buffer, size);
232  cmifRequestInBuffer(req, NULL, 0, mode);
233  } else {
234  cmifRequestInPointer(req, NULL, 0);
235  cmifRequestInBuffer(req, buffer, size, mode);
236  }
237 }
238 
239 NX_CONSTEXPR void cmifRequestOutAutoBuffer(CmifRequest* req, void* buffer, size_t size, HipcBufferMode mode)
240 {
241  if (req->server_pointer_size && size <= req->server_pointer_size) {
242  cmifRequestOutPointer(req, buffer, size);
243  cmifRequestOutBuffer(req, NULL, 0, mode);
244  } else {
245  cmifRequestOutPointer(req, NULL, 0);
246  cmifRequestOutBuffer(req, buffer, size, mode);
247  }
248 }
249 
250 NX_CONSTEXPR void cmifRequestObject(CmifRequest* req, u32 object_id)
251 {
252  *req->objects++ = object_id;
253 }
254 
255 NX_CONSTEXPR void cmifRequestHandle(CmifRequest* req, Handle handle)
256 {
257  *req->hipc.copy_handles++ = handle;
258 }
259 
260 NX_INLINE Result cmifParseResponse(CmifResponse* res, void* base, bool is_domain, u32 size)
261 {
262  HipcResponse hipc = hipcParseResponse(base);
263  void* start = cmifGetAlignedDataStart(hipc.data_words, base);
264 
265  CmifOutHeader* hdr = NULL;
266  u32* objects = NULL;
267  if (is_domain) {
268  CmifDomainOutHeader* domain_hdr = (CmifDomainOutHeader*)start;
269  hdr = (CmifOutHeader*)(domain_hdr+1);
270  objects = (u32*)((u8*)hdr + sizeof(CmifOutHeader) + size);
271  }
272  else
273  hdr = (CmifOutHeader*)start;
274 
275  if (hdr->magic != CMIF_OUT_HEADER_MAGIC)
276  return MAKERESULT(Module_Libnx, LibnxError_InvalidCmifOutHeader);
277  if (R_FAILED(hdr->result))
278  return hdr->result;
279 
280  *res = (CmifResponse){
281  .data = hdr+1,
282  .objects = objects,
283  .copy_handles = hipc.copy_handles,
284  .move_handles = hipc.move_handles,
285  };
286 
287  return 0;
288 }
289 
290 NX_CONSTEXPR u32 cmifResponseGetObject(CmifResponse* res)
291 {
292  return *res->objects++;
293 }
294 
295 NX_CONSTEXPR Handle cmifResponseGetCopyHandle(CmifResponse* res)
296 {
297  return *res->copy_handles++;
298 }
299 
300 NX_CONSTEXPR Handle cmifResponseGetMoveHandle(CmifResponse* res)
301 {
302  return *res->move_handles++;
303 }
304 
305 NX_INLINE Result cmifConvertCurrentObjectToDomain(Handle h, u32* out_object_id)
306 {
307  cmifMakeControlRequest(armGetTls(), 0, 0);
308  Result rc = svcSendSyncRequest(h);
309  if (R_SUCCEEDED(rc)) {
310  CmifResponse resp = {};
311  rc = cmifParseResponse(&resp, armGetTls(), false, sizeof(u32));
312  if (R_SUCCEEDED(rc) && out_object_id)
313  *out_object_id = *(u32*)resp.data;
314  }
315  return rc;
316 }
317 
318 NX_INLINE Result cmifCopyFromCurrentDomain(Handle h, u32 object_id, Handle* out_h)
319 {
320  void* raw = cmifMakeControlRequest(armGetTls(), 1, sizeof(u32));
321  *(u32*)raw = object_id;
322  Result rc = svcSendSyncRequest(h);
323  if (R_SUCCEEDED(rc)) {
324  CmifResponse resp = {};
325  rc = cmifParseResponse(&resp, armGetTls(), false, 0);
326  if (R_SUCCEEDED(rc) && out_h)
327  *out_h = resp.move_handles[0];
328  }
329  return rc;
330 }
331 
332 NX_INLINE Result cmifCloneCurrentObject(Handle h, Handle* out_h)
333 {
334  cmifMakeControlRequest(armGetTls(), 2, 0);
335  Result rc = svcSendSyncRequest(h);
336  if (R_SUCCEEDED(rc)) {
337  CmifResponse resp = {};
338  rc = cmifParseResponse(&resp, armGetTls(), false, 0);
339  if (R_SUCCEEDED(rc) && out_h)
340  *out_h = resp.move_handles[0];
341  }
342  return rc;
343 }
344 
345 NX_INLINE Result cmifQueryPointerBufferSize(Handle h, u16* out_size)
346 {
347  cmifMakeControlRequest(armGetTls(), 3, 0);
348  Result rc = svcSendSyncRequest(h);
349  if (R_SUCCEEDED(rc)) {
350  CmifResponse resp = {};
351  rc = cmifParseResponse(&resp, armGetTls(), false, sizeof(u16));
352  if (R_SUCCEEDED(rc) && out_size)
353  *out_size = *(u16*)resp.data;
354  }
355  return rc;
356 }
357 
358 NX_INLINE Result cmifCloneCurrentObjectEx(Handle h, u32 tag, Handle* out_h)
359 {
360  void* raw = cmifMakeControlRequest(armGetTls(), 4, sizeof(u32));
361  *(u32*)raw = tag;
362  Result rc = svcSendSyncRequest(h);
363  if (R_SUCCEEDED(rc)) {
364  CmifResponse resp = {};
365  rc = cmifParseResponse(&resp, armGetTls(), false, 0);
366  if (R_SUCCEEDED(rc) && out_h)
367  *out_h = resp.move_handles[0];
368  }
369  return rc;
370 }
Horizon Inter-Process Communication protocol.
#define R_FAILED(res)
Checks whether a result code indicates failure.
Definition: result.h:12
#define MAKERESULT(module, description)
Builds a result code from its constituent components.
Definition: result.h:21
#define R_SUCCEEDED(res)
Checks whether a result code indicates success.
Definition: result.h:10
Definition: cmif.h:45
Definition: cmif.h:54
Definition: cmif.h:31
Definition: cmif.h:38
Definition: cmif.h:59
Definition: cmif.h:78
Definition: cmif.h:87
Definition: hipc.h:72
Definition: hipc.h:89
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
uint8_t u8
8-bit unsigned integer.
Definition: types.h:19
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