/** * @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; }