/** * @file tipc.h * @brief Tiny IPC protocol * @author fincs * @author SciresM * @copyright libnx Authors */ #pragma once #include "hipc.h" #include "cmif.h" #include "service.h" typedef enum TipcCommandType { TipcCommandType_Close = 15, } TipcCommandType; /// tipc Service object structure typedef struct TipcService { Handle session; } TipcService; typedef struct TipcDispatchParams { SfBufferAttrs buffer_attrs; SfBuffer buffers[8]; bool in_send_pid; u32 in_num_handles; Handle in_handles[8]; u32 out_num_objects; TipcService* out_objects; SfOutHandleAttrs out_handle_attrs; Handle* out_handles; } TipcDispatchParams; typedef struct TipcRequestFormat { u32 request_id; u32 data_size; u32 num_in_buffers; u32 num_out_buffers; u32 num_inout_buffers; u32 num_handles; u32 send_pid; } TipcRequestFormat; /** * @brief Creates a tipc service object from an IPC session handle. * @param[out] s TIPC service object. * @param[in] h IPC session handle. */ NX_CONSTEXPR void tipcCreate(TipcService* s, Handle h) { s->session = h; } /** * @brief Closes a tipc service. * @param[in] s TIPC service object. */ NX_INLINE void tipcClose(TipcService* s) { hipcMakeRequestInline(armGetTls(), .type = TipcCommandType_Close); svcSendSyncRequest(s->session); svcCloseHandle(s->session); *s = (TipcService){}; } NX_CONSTEXPR void tipcRequestInBuffer(HipcRequest* req, const void* buffer, size_t size, HipcBufferMode mode) { *req->send_buffers++ = hipcMakeBuffer(buffer, size, mode); } NX_CONSTEXPR void tipcRequestOutBuffer(HipcRequest* req, void* buffer, size_t size, HipcBufferMode mode) { *req->recv_buffers++ = hipcMakeBuffer(buffer, size, mode); } NX_CONSTEXPR void tipcRequestInOutBuffer(HipcRequest* req, void* buffer, size_t size, HipcBufferMode mode) { *req->exch_buffers++ = hipcMakeBuffer(buffer, size, mode); } NX_CONSTEXPR void tipcRequestHandle(HipcRequest* req, Handle handle) { *req->copy_handles++ = handle; } NX_CONSTEXPR void _tipcRequestFormatProcessBuffer(TipcRequestFormat* fmt, u32 attr) { if (!attr) return; const bool is_in = (attr & SfBufferAttr_In) != 0; const bool is_out = (attr & SfBufferAttr_Out) != 0; if (attr & SfBufferAttr_HipcMapAlias) { if (is_in && is_out) fmt->num_inout_buffers ++; else if (is_in) fmt->num_in_buffers ++; else if (is_out) fmt->num_out_buffers ++; } } NX_CONSTEXPR void _tipcRequestProcessBuffer(HipcRequest* req, const SfBuffer* buf, u32 attr) { if (!attr) return; const bool is_in = (attr & SfBufferAttr_In); const bool is_out = (attr & SfBufferAttr_Out); if (attr & SfBufferAttr_HipcMapAlias) { HipcBufferMode mode = HipcBufferMode_Normal; if (attr & SfBufferAttr_HipcMapTransferAllowsNonSecure) mode = HipcBufferMode_NonSecure; if (attr & SfBufferAttr_HipcMapTransferAllowsNonDevice) mode = HipcBufferMode_NonDevice; if (is_in && is_out) tipcRequestInOutBuffer(req, (void*)buf->ptr, buf->size, mode); else if (is_in) tipcRequestInBuffer(req, buf->ptr, buf->size, mode); else if (is_out) tipcRequestOutBuffer(req, (void*)buf->ptr, buf->size, mode); } } NX_INLINE void* tipcMakeRequest( u32 request_id, u32 data_size, bool send_pid, const SfBufferAttrs buffer_attrs, const SfBuffer* buffers, u32 num_handles, const Handle* handles) { TipcRequestFormat fmt = {}; fmt.request_id = request_id + 16; fmt.data_size = data_size; fmt.num_handles = num_handles; fmt.send_pid = send_pid; _tipcRequestFormatProcessBuffer(&fmt, buffer_attrs.attr0); _tipcRequestFormatProcessBuffer(&fmt, buffer_attrs.attr1); _tipcRequestFormatProcessBuffer(&fmt, buffer_attrs.attr2); _tipcRequestFormatProcessBuffer(&fmt, buffer_attrs.attr3); _tipcRequestFormatProcessBuffer(&fmt, buffer_attrs.attr4); _tipcRequestFormatProcessBuffer(&fmt, buffer_attrs.attr5); _tipcRequestFormatProcessBuffer(&fmt, buffer_attrs.attr6); _tipcRequestFormatProcessBuffer(&fmt, buffer_attrs.attr7); HipcRequest req = hipcMakeRequestInline(armGetTls(), .type = fmt.request_id, .num_send_statics = 0, .num_send_buffers = fmt.num_in_buffers, .num_recv_buffers = fmt.num_out_buffers, .num_exch_buffers = fmt.num_inout_buffers, .num_data_words = (data_size + 3) / 4, .num_recv_statics = 0, .send_pid = fmt.send_pid, .num_copy_handles = fmt.num_handles, .num_move_handles = 0, ); for (u32 i = 0; i < num_handles; i ++) tipcRequestHandle(&req, handles[i]); _tipcRequestProcessBuffer(&req, &buffers[0], buffer_attrs.attr0); _tipcRequestProcessBuffer(&req, &buffers[1], buffer_attrs.attr1); _tipcRequestProcessBuffer(&req, &buffers[2], buffer_attrs.attr2); _tipcRequestProcessBuffer(&req, &buffers[3], buffer_attrs.attr3); _tipcRequestProcessBuffer(&req, &buffers[4], buffer_attrs.attr4); _tipcRequestProcessBuffer(&req, &buffers[5], buffer_attrs.attr5); _tipcRequestProcessBuffer(&req, &buffers[6], buffer_attrs.attr6); _tipcRequestProcessBuffer(&req, &buffers[7], buffer_attrs.attr7); return req.data_words; } NX_CONSTEXPR Handle tipcResponseGetCopyHandle(HipcResponse* res) { return *res->copy_handles++; } NX_CONSTEXPR Handle tipcResponseGetMoveHandle(HipcResponse* res) { return *res->move_handles++; } NX_CONSTEXPR void _tipcResponseGetHandle(HipcResponse* res, SfOutHandleAttr type, Handle* out) { switch (type) { default: case SfOutHandleAttr_None: break; case SfOutHandleAttr_HipcCopy: *out = tipcResponseGetCopyHandle(res); break; case SfOutHandleAttr_HipcMove: *out = tipcResponseGetMoveHandle(res); break; } } NX_INLINE Result tipcParseResponse( u32 out_size, void** out_data, u32 num_out_objects, TipcService* out_objects, const SfOutHandleAttrs out_handle_attrs, Handle* out_handles ) { HipcResponse res = hipcParseResponse(armGetTls()); Result rc = *res.data_words++; if (R_FAILED(rc)) return rc; if (out_size) *out_data = res.data_words; for (u32 i = 0; i < num_out_objects; i ++) { tipcCreate(&out_objects[i], tipcResponseGetMoveHandle(&res)); } _tipcResponseGetHandle(&res, out_handle_attrs.attr0, &out_handles[0]); _tipcResponseGetHandle(&res, out_handle_attrs.attr1, &out_handles[1]); _tipcResponseGetHandle(&res, out_handle_attrs.attr2, &out_handles[2]); _tipcResponseGetHandle(&res, out_handle_attrs.attr3, &out_handles[3]); _tipcResponseGetHandle(&res, out_handle_attrs.attr4, &out_handles[4]); _tipcResponseGetHandle(&res, out_handle_attrs.attr5, &out_handles[5]); _tipcResponseGetHandle(&res, out_handle_attrs.attr6, &out_handles[6]); _tipcResponseGetHandle(&res, out_handle_attrs.attr7, &out_handles[7]); return 0; } NX_INLINE Result tipcDispatchImpl( TipcService* s, u32 request_id, const void* in_data, u32 in_data_size, void* out_data, u32 out_data_size, TipcDispatchParams disp ) { void* in = tipcMakeRequest(request_id, in_data_size, disp.in_send_pid, disp.buffer_attrs, disp.buffers, disp.in_num_handles, disp.in_handles); if (in_data_size) __builtin_memcpy(in, in_data, in_data_size); Result rc = svcSendSyncRequest(s->session); if (R_SUCCEEDED(rc)) { void* out = NULL; rc = tipcParseResponse(out_data_size, &out, disp.out_num_objects, disp.out_objects, disp.out_handle_attrs, disp.out_handles); if (R_SUCCEEDED(rc) && out_data && out_data_size) __builtin_memcpy(out_data, out, out_data_size); } return rc; } #define tipcDispatch(_s,_rid,...) \ tipcDispatchImpl((_s),(_rid),NULL,0,NULL,0,(TipcDispatchParams){ __VA_ARGS__ }) #define tipcDispatchIn(_s,_rid,_in,...) \ tipcDispatchImpl((_s),(_rid),&(_in),sizeof(_in),NULL,0,(TipcDispatchParams){ __VA_ARGS__ }) #define tipcDispatchOut(_s,_rid,_out,...) \ tipcDispatchImpl((_s),(_rid),NULL,0,&(_out),sizeof(_out),(TipcDispatchParams){ __VA_ARGS__ }) #define tipcDispatchInOut(_s,_rid,_in,_out,...) \ tipcDispatchImpl((_s),(_rid),&(_in),sizeof(_in),&(_out),sizeof(_out),(TipcDispatchParams){ __VA_ARGS__ })