diff --git a/nx/include/switch.h b/nx/include/switch.h index 04be7e9c..9b3079e7 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -42,6 +42,7 @@ extern "C" { #include "switch/sf/cmif.h" #include "switch/sf/service.h" #include "switch/sf/sessionmgr.h" +#include "switch/sf/tipc.h" #include "switch/services/sm.h" #include "switch/services/smm.h" diff --git a/nx/include/switch/sf/tipc.h b/nx/include/switch/sf/tipc.h new file mode 100644 index 00000000..60758272 --- /dev/null +++ b/nx/include/switch/sf/tipc.h @@ -0,0 +1,273 @@ +/** + * @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( + const TipcService *s, 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( + const TipcService* s, 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 +) +{ + // Make a copy of the service struct, so that the compiler can assume that it won't be modified by function calls. + const TipcService srv = *s; + + void* in = tipcMakeRequest(&srv, 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(&srv, + 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__ })