libnx v4.9.0
Loading...
Searching...
No Matches
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
14typedef 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
25typedef enum CmifDomainRequestType {
26 CmifDomainRequestType_Invalid = 0,
27 CmifDomainRequestType_SendMessage = 1,
28 CmifDomainRequestType_Close = 2,
29} CmifDomainRequestType;
30
31typedef struct CmifInHeader {
32 u32 magic;
33 u32 version;
34 u32 command_id;
35 u32 token;
37
38typedef struct CmifOutHeader {
39 u32 magic;
40 u32 version;
41 Result result;
42 u32 token;
44
45typedef 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
54typedef struct CmifDomainOutHeader {
55 u32 num_out_objects;
56 u32 padding[3];
58
59typedef 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
78typedef 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;
86
87typedef struct CmifResponse {
88 void* data;
89 u32* objects;
90 Handle* copy_handles;
91 Handle* move_handles;
93
94NX_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
100NX_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
159NX_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
176NX_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
195NX_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
200NX_CONSTEXPR void cmifRequestOutBuffer(CmifRequest* req, void* buffer, size_t size, HipcBufferMode mode)
201{
202 *req->hipc.recv_buffers++ = hipcMakeBuffer(buffer, size, mode);
203}
204
205NX_CONSTEXPR void cmifRequestInOutBuffer(CmifRequest* req, void* buffer, size_t size, HipcBufferMode mode)
206{
207 *req->hipc.exch_buffers++ = hipcMakeBuffer(buffer, size, mode);
208}
209
210NX_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
216NX_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
222NX_CONSTEXPR void cmifRequestOutPointer(CmifRequest* req, void* buffer, size_t size)
223{
224 cmifRequestOutFixedPointer(req, buffer, size);
225 *req->out_pointer_sizes++ = size;
226}
227
228NX_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
239NX_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
250NX_CONSTEXPR void cmifRequestObject(CmifRequest* req, u32 object_id)
251{
252 *req->objects++ = object_id;
253}
254
255NX_CONSTEXPR void cmifRequestHandle(CmifRequest* req, Handle handle)
256{
257 *req->hipc.copy_handles++ = handle;
258}
259
260NX_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
290NX_CONSTEXPR u32 cmifResponseGetObject(CmifResponse* res)
291{
292 return *res->objects++;
293}
294
295NX_CONSTEXPR Handle cmifResponseGetCopyHandle(CmifResponse* res)
296{
297 return *res->copy_handles++;
298}
299
300NX_CONSTEXPR Handle cmifResponseGetMoveHandle(CmifResponse* res)
301{
302 return *res->move_handles++;
303}
304
305NX_INLINE Result cmifConvertCurrentObjectToDomain(Handle h, u32* out_object_id)
306{
307 cmifMakeControlRequest(armGetTls(), 0, 0);
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
318NX_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;
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
332NX_INLINE Result cmifCloneCurrentObject(Handle h, Handle* out_h)
333{
334 cmifMakeControlRequest(armGetTls(), 2, 0);
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
345NX_INLINE Result cmifQueryPointerBufferSize(Handle h, u16* out_size)
346{
347 cmifMakeControlRequest(armGetTls(), 3, 0);
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
358NX_INLINE Result cmifCloneCurrentObjectEx(Handle h, u32 tag, Handle* out_h)
359{
360 void* raw = cmifMakeControlRequest(armGetTls(), 4, sizeof(u32));
361 *(u32*)raw = tag;
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