libnx/nx/include/switch/sf/hipc.h
fincs 6ad0042147
Introduce new IPC interface wrapper code, see details:
- Service object moved away from sm.h and into its own file (sf/service.h)
- Service object completely redesigned, but a (mostly)
  backwards compatible API was kept
- New low level HIPC request/response code
- New CMIF client-oriented code
- New service IPC dispatch functions
- Pointer buffer size automatically retrieved for all sessions
- Removed previous manual pointer buffer size query code
- SM rewritten with new IPC code
- All other services are still pending IPC rewrite
2019-09-19 03:50:28 +02:00

364 lines
10 KiB
C

/**
* @file hipc.h
* @brief Horizon Inter-Process Communication protocol
* @author fincs
* @author SciresM
* @copyright libnx Authors
*/
#pragma once
#include "../result.h"
#include "../arm/tls.h"
#include "../kernel/svc.h"
#define HIPC_AUTO_RECV_STATIC UINT8_MAX
#define HIPC_RESPONSE_NO_PID UINT32_MAX
typedef struct HipcMetadata {
u32 type;
u32 num_send_statics;
u32 num_send_buffers;
u32 num_recv_buffers;
u32 num_exch_buffers;
u32 num_data_words;
u32 num_recv_statics; // also accepts HIPC_AUTO_RECV_STATIC
u32 send_pid;
u32 num_copy_handles;
u32 num_move_handles;
} HipcMetadata;
typedef struct HipcHeader {
u32 type : 16;
u32 num_send_statics : 4;
u32 num_send_buffers : 4;
u32 num_recv_buffers : 4;
u32 num_exch_buffers : 4;
u32 num_data_words : 10;
u32 recv_static_mode : 4;
u32 padding : 6;
u32 recv_list_offset : 11; // Unused.
u32 has_special_header : 1;
} HipcHeader;
typedef struct HipcSpecialHeader {
u32 send_pid : 1;
u32 num_copy_handles : 4;
u32 num_move_handles : 4;
u32 padding : 23;
} HipcSpecialHeader;
typedef struct HipcStaticDescriptor {
u32 index : 6;
u32 address_high : 6;
u32 address_mid : 4;
u32 size : 16;
u32 address_low;
} HipcStaticDescriptor;
typedef struct HipcBufferDescriptor {
u32 size_low;
u32 address_low;
u32 mode : 2;
u32 address_high : 22;
u32 size_high : 4;
u32 address_mid : 4;
} HipcBufferDescriptor;
typedef struct HipcRecvListEntry {
u32 address_low;
u32 address_high : 16;
u32 size : 16;
} HipcRecvListEntry;
typedef struct HipcRequest {
HipcStaticDescriptor* send_statics;
HipcBufferDescriptor* send_buffers;
HipcBufferDescriptor* recv_buffers;
HipcBufferDescriptor* exch_buffers;
u32* data_words;
HipcRecvListEntry* recv_list;
Handle* copy_handles;
Handle* move_handles;
} HipcRequest;
typedef struct HipcParsedRequest {
HipcMetadata meta;
HipcRequest data;
u64 pid;
} HipcParsedRequest;
typedef struct HipcResponse {
u64 pid;
u32 num_statics;
u32 num_data_words;
u32 num_copy_handles;
u32 num_move_handles;
HipcStaticDescriptor* statics;
u32* data_words;
Handle* copy_handles;
Handle* move_handles;
} HipcResponse;
typedef enum HipcBufferMode {
HipcBufferMode_Normal = 0,
HipcBufferMode_NonSecure = 1,
HipcBufferMode_Invalid = 2,
HipcBufferMode_NonDevice = 3,
} HipcBufferMode;
NX_CONSTEXPR HipcStaticDescriptor hipcMakeSendStatic(const void* buffer, size_t size, u8 index)
{
return (HipcStaticDescriptor){
.index = index,
.address_high = (u32)((uintptr_t)buffer >> 36),
.address_mid = (u32)((uintptr_t)buffer >> 32),
.size = (u32)size,
.address_low = (u32)(uintptr_t)buffer,
};
}
NX_CONSTEXPR HipcBufferDescriptor hipcMakeBuffer(const void* buffer, size_t size, HipcBufferMode mode)
{
return (HipcBufferDescriptor){
.size_low = (u32)size,
.address_low = (u32)(uintptr_t)buffer,
.mode = mode,
.address_high = (u32)((uintptr_t)buffer >> 36),
.size_high = (u32)(size >> 32),
.address_mid = (u32)((uintptr_t)buffer >> 32),
};
}
NX_CONSTEXPR HipcRecvListEntry hipcMakeRecvStatic(void* buffer, size_t size)
{
return (HipcRecvListEntry){
.address_low = (u32)((uintptr_t)buffer),
.address_high = (u32)((uintptr_t)buffer >> 32),
.size = (u32)size,
};
}
NX_CONSTEXPR void* hipcGetStaticAddress(const HipcStaticDescriptor* desc)
{
return (void*)(desc->address_low | ((uintptr_t)desc->address_mid << 32) | ((uintptr_t)desc->address_high << 36));
}
NX_CONSTEXPR size_t hipcGetStaticSize(const HipcStaticDescriptor* desc)
{
return desc->size;
}
NX_CONSTEXPR void* hipcGetBufferAddress(const HipcBufferDescriptor* desc)
{
return (void*)(desc->address_low | ((uintptr_t)desc->address_mid << 32) | ((uintptr_t)desc->address_high << 36));
}
NX_CONSTEXPR size_t hipcGetBufferSize(const HipcBufferDescriptor* desc)
{
return desc->size_low | ((size_t)desc->size_high << 32);
}
NX_CONSTEXPR HipcRequest hipcCalcRequestLayout(HipcMetadata meta, void* base)
{
// Copy handles
Handle* copy_handles = NULL;
if (meta.num_copy_handles) {
copy_handles = (Handle*)base;
base = copy_handles + meta.num_copy_handles;
}
// Move handles
Handle* move_handles = NULL;
if (meta.num_move_handles) {
move_handles = (Handle*)base;
base = move_handles + meta.num_move_handles;
}
// Send statics
HipcStaticDescriptor* send_statics = NULL;
if (meta.num_send_statics) {
send_statics = (HipcStaticDescriptor*)base;
base = send_statics + meta.num_send_statics;
}
// Send buffers
HipcBufferDescriptor* send_buffers = NULL;
if (meta.num_send_buffers) {
send_buffers = (HipcBufferDescriptor*)base;
base = send_buffers + meta.num_send_buffers;
}
// Recv buffers
HipcBufferDescriptor* recv_buffers = NULL;
if (meta.num_recv_buffers) {
recv_buffers = (HipcBufferDescriptor*)base;
base = recv_buffers + meta.num_recv_buffers;
}
// Exch buffers
HipcBufferDescriptor* exch_buffers = NULL;
if (meta.num_exch_buffers) {
exch_buffers = (HipcBufferDescriptor*)base;
base = exch_buffers + meta.num_exch_buffers;
}
// Data words
u32* data_words = NULL;
if (meta.num_data_words) {
data_words = (u32*)base;
base = data_words + meta.num_data_words;
}
// Recv list
HipcRecvListEntry* recv_list = NULL;
if (meta.num_recv_statics)
recv_list = (HipcRecvListEntry*)base;
return (HipcRequest){
.send_statics = send_statics,
.send_buffers = send_buffers,
.recv_buffers = recv_buffers,
.exch_buffers = exch_buffers,
.data_words = data_words,
.recv_list = recv_list,
.copy_handles = copy_handles,
.move_handles = move_handles,
};
}
NX_CONSTEXPR HipcRequest hipcMakeRequest(void* base, HipcMetadata meta)
{
// Write message header
bool has_special_header = meta.send_pid || meta.num_copy_handles || meta.num_move_handles;
HipcHeader* hdr = (HipcHeader*)base;
base = hdr+1;
*hdr = (HipcHeader){
.type = meta.type,
.num_send_statics = meta.num_send_statics,
.num_send_buffers = meta.num_send_buffers,
.num_recv_buffers = meta.num_recv_buffers,
.num_exch_buffers = meta.num_exch_buffers,
.num_data_words = meta.num_data_words,
.recv_static_mode = meta.num_recv_statics ? (meta.num_recv_statics != HIPC_AUTO_RECV_STATIC ? 2u + meta.num_recv_statics : 2u) : 0u,
.padding = 0,
.recv_list_offset = 0,
.has_special_header = has_special_header,
};
// Write special header
if (has_special_header) {
HipcSpecialHeader* sphdr = (HipcSpecialHeader*)base;
base = sphdr+1;
*sphdr = (HipcSpecialHeader){
.send_pid = meta.send_pid,
.num_copy_handles = meta.num_copy_handles,
.num_move_handles = meta.num_move_handles,
};
if (meta.send_pid)
base = (u8*)base + sizeof(u64);
}
// Calculate layout
return hipcCalcRequestLayout(meta, base);
}
#define hipcMakeRequestInline(_base,...) hipcMakeRequest((_base),(HipcMetadata){ __VA_ARGS__ })
NX_CONSTEXPR HipcParsedRequest hipcParseRequest(void* base)
{
// Parse message header
HipcHeader hdr = {};
__builtin_memcpy(&hdr, base, sizeof(hdr));
base = (u8*)base + sizeof(hdr);
u32 num_recv_statics = 0;
u64 pid = 0;
// Parse recv static mode
if (hdr.recv_static_mode) {
if (hdr.recv_static_mode == 2u)
num_recv_statics = HIPC_AUTO_RECV_STATIC;
else if (hdr.recv_static_mode > 2u)
num_recv_statics = hdr.recv_static_mode - 2u;
}
// Parse special header
HipcSpecialHeader sphdr = {};
if (hdr.has_special_header) {
__builtin_memcpy(&sphdr, base, sizeof(sphdr));
base = (u8*)base + sizeof(sphdr);
// Read PID descriptor
if (sphdr.send_pid) {
pid = *(u64*)base;
base = (u8*)base + sizeof(u64);
}
}
const HipcMetadata meta = {
.type = hdr.type,
.num_send_statics = hdr.num_send_statics,
.num_send_buffers = hdr.num_send_buffers,
.num_recv_buffers = hdr.num_recv_buffers,
.num_exch_buffers = hdr.num_exch_buffers,
.num_data_words = hdr.num_data_words,
.num_recv_statics = num_recv_statics,
.send_pid = sphdr.send_pid,
.num_copy_handles = sphdr.num_copy_handles,
.num_move_handles = sphdr.num_move_handles,
};
return (HipcParsedRequest){
.meta = meta,
.data = hipcCalcRequestLayout(meta, base),
.pid = pid,
};
}
NX_CONSTEXPR HipcResponse hipcParseResponse(void* base)
{
// Parse header
HipcHeader hdr = {};
__builtin_memcpy(&hdr, base, sizeof(hdr));
base = (u8*)base + sizeof(hdr);
// Initialize response
HipcResponse response = {};
response.num_statics = hdr.num_send_statics;
response.num_data_words = hdr.num_data_words;
response.pid = HIPC_RESPONSE_NO_PID;
// Parse special header
if (hdr.has_special_header)
{
HipcSpecialHeader sphdr = {};
__builtin_memcpy(&sphdr, base, sizeof(sphdr));
base = (u8*)base + sizeof(sphdr);
// Update response
response.num_copy_handles = sphdr.num_copy_handles;
response.num_move_handles = sphdr.num_move_handles;
// Parse PID descriptor
if (sphdr.send_pid) {
response.pid = *(u64*)base;
base = (u8*)base + sizeof(u64);
}
}
// Copy handles
response.copy_handles = (Handle*)base;
base = response.copy_handles + response.num_copy_handles;
// Move handles
response.move_handles = (Handle*)base;
base = response.move_handles + response.num_move_handles;
// Send statics
response.statics = (HipcStaticDescriptor*)base;
base = response.statics + response.num_statics;
// Data words
response.data_words = (u32*)base;
return response;
}