mirror of
https://github.com/switchbrew/libnx.git
synced 2025-06-21 04:22:50 +02:00
371 lines
11 KiB
C
371 lines
11 KiB
C
/**
|
|
* @file cmif.h
|
|
* @brief Common Message Interface Framework protocol
|
|
* @author fincs
|
|
* @author SciresM
|
|
* @copyright libnx Authors
|
|
*/
|
|
#pragma once
|
|
#include "hipc.h"
|
|
|
|
#define CMIF_IN_HEADER_MAGIC 0x49434653 // "SFCI"
|
|
#define CMIF_OUT_HEADER_MAGIC 0x4F434653 // "SFCO"
|
|
|
|
typedef enum CmifCommandType {
|
|
CmifCommandType_Invalid = 0,
|
|
CmifCommandType_LegacyRequest = 1,
|
|
CmifCommandType_Close = 2,
|
|
CmifCommandType_LegacyControl = 3,
|
|
CmifCommandType_Request = 4,
|
|
CmifCommandType_Control = 5,
|
|
CmifCommandType_RequestWithContext = 6,
|
|
CmifCommandType_ControlWithContext = 7,
|
|
} CmifCommandType;
|
|
|
|
typedef enum CmifDomainRequestType {
|
|
CmifDomainRequestType_Invalid = 0,
|
|
CmifDomainRequestType_SendMessage = 1,
|
|
CmifDomainRequestType_Close = 2,
|
|
} CmifDomainRequestType;
|
|
|
|
typedef struct CmifInHeader {
|
|
u32 magic;
|
|
u32 version;
|
|
u32 command_id;
|
|
u32 token;
|
|
} CmifInHeader;
|
|
|
|
typedef struct CmifOutHeader {
|
|
u32 magic;
|
|
u32 version;
|
|
Result result;
|
|
u32 token;
|
|
} CmifOutHeader;
|
|
|
|
typedef struct CmifDomainInHeader {
|
|
u8 type;
|
|
u8 num_in_objects;
|
|
u16 data_size;
|
|
u32 object_id;
|
|
u32 padding;
|
|
u32 token;
|
|
} CmifDomainInHeader;
|
|
|
|
typedef struct CmifDomainOutHeader {
|
|
u32 num_out_objects;
|
|
u32 padding[3];
|
|
} CmifDomainOutHeader;
|
|
|
|
typedef struct CmifRequestFormat {
|
|
u32 object_id;
|
|
u32 request_id;
|
|
u32 context;
|
|
u32 data_size;
|
|
u32 server_pointer_size;
|
|
u32 num_in_auto_buffers;
|
|
u32 num_out_auto_buffers;
|
|
u32 num_in_buffers;
|
|
u32 num_out_buffers;
|
|
u32 num_inout_buffers;
|
|
u32 num_in_pointers;
|
|
u32 num_out_pointers;
|
|
u32 num_out_fixed_pointers;
|
|
u32 num_objects;
|
|
u32 num_handles;
|
|
u32 send_pid;
|
|
} CmifRequestFormat;
|
|
|
|
typedef struct CmifRequest {
|
|
HipcRequest hipc;
|
|
void* data;
|
|
u16* out_pointer_sizes;
|
|
u32* objects;
|
|
u32 server_pointer_size;
|
|
u32 cur_in_ptr_id;
|
|
} CmifRequest;
|
|
|
|
typedef struct CmifResponse {
|
|
void* data;
|
|
u32* objects;
|
|
Handle* copy_handles;
|
|
Handle* move_handles;
|
|
} CmifResponse;
|
|
|
|
NX_INLINE void* cmifGetAlignedDataStart(u32* data_words, void* base)
|
|
{
|
|
intptr_t data_start = ((u8*)data_words - (u8*)base + 15) &~ 15;
|
|
return (u8*)base + data_start;
|
|
}
|
|
|
|
NX_INLINE CmifRequest cmifMakeRequest(void* base, CmifRequestFormat fmt)
|
|
{
|
|
// First of all, we need to figure out what size we need.
|
|
u32 actual_size = 16;
|
|
if (fmt.object_id)
|
|
actual_size += sizeof(CmifDomainInHeader) + fmt.num_objects*sizeof(u32);
|
|
actual_size += sizeof(CmifInHeader) + fmt.data_size;
|
|
actual_size = (actual_size + 1) &~ 1; // hword-align
|
|
u32 out_pointer_size_table_offset = actual_size;
|
|
u32 out_pointer_size_table_size = fmt.num_out_auto_buffers + fmt.num_out_pointers;
|
|
actual_size += sizeof(u16)*out_pointer_size_table_size;
|
|
u32 num_data_words = (actual_size + 3) / 4;
|
|
|
|
CmifRequest req = {};
|
|
req.hipc = hipcMakeRequestInline(base,
|
|
.type = fmt.context ? CmifCommandType_RequestWithContext : CmifCommandType_Request,
|
|
.num_send_statics = fmt.num_in_auto_buffers + fmt.num_in_pointers,
|
|
.num_send_buffers = fmt.num_in_auto_buffers + fmt.num_in_buffers,
|
|
.num_recv_buffers = fmt.num_out_auto_buffers + fmt.num_out_buffers,
|
|
.num_exch_buffers = fmt.num_inout_buffers,
|
|
.num_data_words = num_data_words,
|
|
.num_recv_statics = out_pointer_size_table_size + fmt.num_out_fixed_pointers,
|
|
.send_pid = fmt.send_pid,
|
|
.num_copy_handles = fmt.num_handles,
|
|
.num_move_handles = 0,
|
|
);
|
|
|
|
CmifInHeader* hdr = NULL;
|
|
void* start = cmifGetAlignedDataStart(req.hipc.data_words, base);
|
|
if (fmt.object_id) {
|
|
CmifDomainInHeader* domain_hdr = (CmifDomainInHeader*)start;
|
|
u32 payload_size = sizeof(CmifInHeader) + fmt.data_size;
|
|
*domain_hdr = (CmifDomainInHeader){
|
|
.type = CmifDomainRequestType_SendMessage,
|
|
.num_in_objects = (u8)fmt.num_objects,
|
|
.data_size = (u16)payload_size,
|
|
.object_id = fmt.object_id,
|
|
.padding = 0,
|
|
.token = fmt.context,
|
|
};
|
|
hdr = (CmifInHeader*)(domain_hdr+1);
|
|
req.objects = (u32*)((u8*)hdr + payload_size);
|
|
} else
|
|
hdr = (CmifInHeader*)start;
|
|
|
|
*hdr = (CmifInHeader){
|
|
.magic = CMIF_IN_HEADER_MAGIC,
|
|
.version = fmt.context ? 1U : 0U,
|
|
.command_id = fmt.request_id,
|
|
.token = fmt.object_id ? 0U : fmt.context,
|
|
};
|
|
|
|
req.data = hdr+1;
|
|
req.out_pointer_sizes = (u16*)(void*)((u8*)(void*)req.hipc.data_words + out_pointer_size_table_offset);
|
|
req.server_pointer_size = fmt.server_pointer_size;
|
|
|
|
return req;
|
|
}
|
|
|
|
NX_INLINE void* cmifMakeControlRequest(void* base, u32 request_id, u32 size)
|
|
{
|
|
u32 actual_size = 16 + sizeof(CmifInHeader) + size;
|
|
HipcRequest hipc = hipcMakeRequestInline(base,
|
|
.type = CmifCommandType_Control,
|
|
.num_data_words = (actual_size + 3) / 4,
|
|
);
|
|
CmifInHeader* hdr = (CmifInHeader*)cmifGetAlignedDataStart(hipc.data_words, base);
|
|
*hdr = (CmifInHeader){
|
|
.magic = CMIF_IN_HEADER_MAGIC,
|
|
.version = 0,
|
|
.command_id = request_id,
|
|
.token = 0,
|
|
};
|
|
return hdr+1;
|
|
}
|
|
|
|
NX_INLINE void cmifMakeCloseRequest(void* base, u32 object_id)
|
|
{
|
|
if (object_id) {
|
|
HipcRequest hipc = hipcMakeRequestInline(base,
|
|
.type = CmifCommandType_Request,
|
|
.num_data_words = (16 + sizeof(CmifDomainInHeader)) / 4,
|
|
);
|
|
CmifDomainInHeader* domain_hdr = (CmifDomainInHeader*)cmifGetAlignedDataStart(hipc.data_words, base);
|
|
*domain_hdr = (CmifDomainInHeader){
|
|
.type = CmifDomainRequestType_Close,
|
|
.object_id = object_id,
|
|
};
|
|
} else {
|
|
hipcMakeRequestInline(base,
|
|
.type = CmifCommandType_Close,
|
|
);
|
|
}
|
|
}
|
|
|
|
NX_CONSTEXPR void cmifRequestInBuffer(CmifRequest* req, const void* buffer, size_t size, HipcBufferMode mode)
|
|
{
|
|
*req->hipc.send_buffers++ = hipcMakeBuffer(buffer, size, mode);
|
|
}
|
|
|
|
NX_CONSTEXPR void cmifRequestOutBuffer(CmifRequest* req, void* buffer, size_t size, HipcBufferMode mode)
|
|
{
|
|
*req->hipc.recv_buffers++ = hipcMakeBuffer(buffer, size, mode);
|
|
}
|
|
|
|
NX_CONSTEXPR void cmifRequestInOutBuffer(CmifRequest* req, void* buffer, size_t size, HipcBufferMode mode)
|
|
{
|
|
*req->hipc.exch_buffers++ = hipcMakeBuffer(buffer, size, mode);
|
|
}
|
|
|
|
NX_CONSTEXPR void cmifRequestInPointer(CmifRequest* req, const void* buffer, size_t size)
|
|
{
|
|
*req->hipc.send_statics++ = hipcMakeSendStatic(buffer, size, req->cur_in_ptr_id++);
|
|
req->server_pointer_size -= size;
|
|
}
|
|
|
|
NX_CONSTEXPR void cmifRequestOutFixedPointer(CmifRequest* req, void* buffer, size_t size)
|
|
{
|
|
*req->hipc.recv_list++ = hipcMakeRecvStatic(buffer, size);
|
|
req->server_pointer_size -= size;
|
|
}
|
|
|
|
NX_CONSTEXPR void cmifRequestOutPointer(CmifRequest* req, void* buffer, size_t size)
|
|
{
|
|
cmifRequestOutFixedPointer(req, buffer, size);
|
|
*req->out_pointer_sizes++ = size;
|
|
}
|
|
|
|
NX_CONSTEXPR void cmifRequestInAutoBuffer(CmifRequest* req, const void* buffer, size_t size, HipcBufferMode mode)
|
|
{
|
|
if (req->server_pointer_size && size <= req->server_pointer_size) {
|
|
cmifRequestInPointer(req, buffer, size);
|
|
cmifRequestInBuffer(req, NULL, 0, mode);
|
|
} else {
|
|
cmifRequestInPointer(req, NULL, 0);
|
|
cmifRequestInBuffer(req, buffer, size, mode);
|
|
}
|
|
}
|
|
|
|
NX_CONSTEXPR void cmifRequestOutAutoBuffer(CmifRequest* req, void* buffer, size_t size, HipcBufferMode mode)
|
|
{
|
|
if (req->server_pointer_size && size <= req->server_pointer_size) {
|
|
cmifRequestOutPointer(req, buffer, size);
|
|
cmifRequestOutBuffer(req, NULL, 0, mode);
|
|
} else {
|
|
cmifRequestOutPointer(req, NULL, 0);
|
|
cmifRequestOutBuffer(req, buffer, size, mode);
|
|
}
|
|
}
|
|
|
|
NX_CONSTEXPR void cmifRequestObject(CmifRequest* req, u32 object_id)
|
|
{
|
|
*req->objects++ = object_id;
|
|
}
|
|
|
|
NX_CONSTEXPR void cmifRequestHandle(CmifRequest* req, Handle handle)
|
|
{
|
|
*req->hipc.copy_handles++ = handle;
|
|
}
|
|
|
|
NX_INLINE Result cmifParseResponse(CmifResponse* res, void* base, bool is_domain, u32 size)
|
|
{
|
|
HipcResponse hipc = hipcParseResponse(base);
|
|
void* start = cmifGetAlignedDataStart(hipc.data_words, base);
|
|
|
|
CmifOutHeader* hdr = NULL;
|
|
u32* objects = NULL;
|
|
if (is_domain) {
|
|
CmifDomainOutHeader* domain_hdr = (CmifDomainOutHeader*)start;
|
|
hdr = (CmifOutHeader*)(domain_hdr+1);
|
|
objects = (u32*)((u8*)hdr + sizeof(CmifOutHeader) + size);
|
|
}
|
|
else
|
|
hdr = (CmifOutHeader*)start;
|
|
|
|
if (hdr->magic != CMIF_OUT_HEADER_MAGIC)
|
|
return MAKERESULT(Module_Libnx, LibnxError_InvalidCmifOutHeader);
|
|
if (R_FAILED(hdr->result))
|
|
return hdr->result;
|
|
|
|
*res = (CmifResponse){
|
|
.data = hdr+1,
|
|
.objects = objects,
|
|
.copy_handles = hipc.copy_handles,
|
|
.move_handles = hipc.move_handles,
|
|
};
|
|
|
|
return 0;
|
|
}
|
|
|
|
NX_CONSTEXPR u32 cmifResponseGetObject(CmifResponse* res)
|
|
{
|
|
return *res->objects++;
|
|
}
|
|
|
|
NX_CONSTEXPR Handle cmifResponseGetCopyHandle(CmifResponse* res)
|
|
{
|
|
return *res->copy_handles++;
|
|
}
|
|
|
|
NX_CONSTEXPR Handle cmifResponseGetMoveHandle(CmifResponse* res)
|
|
{
|
|
return *res->move_handles++;
|
|
}
|
|
|
|
NX_INLINE Result cmifConvertCurrentObjectToDomain(Handle h, u32* out_object_id)
|
|
{
|
|
cmifMakeControlRequest(armGetTls(), 0, 0);
|
|
Result rc = svcSendSyncRequest(h);
|
|
if (R_SUCCEEDED(rc)) {
|
|
CmifResponse resp = {};
|
|
rc = cmifParseResponse(&resp, armGetTls(), false, sizeof(u32));
|
|
if (R_SUCCEEDED(rc) && out_object_id)
|
|
*out_object_id = *(u32*)resp.data;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
NX_INLINE Result cmifCopyFromCurrentDomain(Handle h, u32 object_id, Handle* out_h)
|
|
{
|
|
void* raw = cmifMakeControlRequest(armGetTls(), 1, sizeof(u32));
|
|
*(u32*)raw = object_id;
|
|
Result rc = svcSendSyncRequest(h);
|
|
if (R_SUCCEEDED(rc)) {
|
|
CmifResponse resp = {};
|
|
rc = cmifParseResponse(&resp, armGetTls(), false, 0);
|
|
if (R_SUCCEEDED(rc) && out_h)
|
|
*out_h = resp.move_handles[0];
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
NX_INLINE Result cmifCloneCurrentObject(Handle h, Handle* out_h)
|
|
{
|
|
cmifMakeControlRequest(armGetTls(), 2, 0);
|
|
Result rc = svcSendSyncRequest(h);
|
|
if (R_SUCCEEDED(rc)) {
|
|
CmifResponse resp = {};
|
|
rc = cmifParseResponse(&resp, armGetTls(), false, 0);
|
|
if (R_SUCCEEDED(rc) && out_h)
|
|
*out_h = resp.move_handles[0];
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
NX_INLINE Result cmifQueryPointerBufferSize(Handle h, u16* out_size)
|
|
{
|
|
cmifMakeControlRequest(armGetTls(), 3, 0);
|
|
Result rc = svcSendSyncRequest(h);
|
|
if (R_SUCCEEDED(rc)) {
|
|
CmifResponse resp = {};
|
|
rc = cmifParseResponse(&resp, armGetTls(), false, sizeof(u16));
|
|
if (R_SUCCEEDED(rc) && out_size)
|
|
*out_size = *(u16*)resp.data;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
NX_INLINE Result cmifCloneCurrentObjectEx(Handle h, u32 tag, Handle* out_h)
|
|
{
|
|
void* raw = cmifMakeControlRequest(armGetTls(), 4, sizeof(u32));
|
|
*(u32*)raw = tag;
|
|
Result rc = svcSendSyncRequest(h);
|
|
if (R_SUCCEEDED(rc)) {
|
|
CmifResponse resp = {};
|
|
rc = cmifParseResponse(&resp, armGetTls(), false, 0);
|
|
if (R_SUCCEEDED(rc) && out_h)
|
|
*out_h = resp.move_handles[0];
|
|
}
|
|
return rc;
|
|
}
|