From de4cb3f7831101c91e5d450e6a44388da878a8a1 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 14 Jul 2018 10:24:53 +0900 Subject: Add tests for fileops --- nimgen.nimble | 1 + tests/rununittests.nim | 8 + tests/unittests/common.nim | 5 + .../unittests/data/teststaticexpectedfrontbraces.h | 675 +++++++++++++++++++++ .../data/teststaticexpectednewlinebraces.h | 68 +++ tests/unittests/data/teststaticfrontbraces.h | 675 +++++++++++++++++++++ tests/unittests/data/teststaticnewlinebraces.h | 71 +++ .../data/teststaticnewlinebracesreadded.h | 68 +++ tests/unittests/nim.cfg | 1 + tests/unittests/testfileops.nim | 211 +++++++ 10 files changed, 1783 insertions(+) create mode 100644 tests/rununittests.nim create mode 100644 tests/unittests/common.nim create mode 100644 tests/unittests/data/teststaticexpectedfrontbraces.h create mode 100644 tests/unittests/data/teststaticexpectednewlinebraces.h create mode 100644 tests/unittests/data/teststaticfrontbraces.h create mode 100644 tests/unittests/data/teststaticnewlinebraces.h create mode 100644 tests/unittests/data/teststaticnewlinebracesreadded.h create mode 100644 tests/unittests/nim.cfg create mode 100644 tests/unittests/testfileops.nim diff --git a/nimgen.nimble b/nimgen.nimble index 59de8ed..3e65545 100644 --- a/nimgen.nimble +++ b/nimgen.nimble @@ -14,4 +14,5 @@ skipDirs = @["nimgen", "tests"] requires "nim >= 0.17.0", "c2nim >= 0.9.13", "regex >= 0.7.1" task test, "Test nimgen": + exec "nim c -r tests/rununittests.nim" exec "nim e tests/nimgentest.nims" diff --git a/tests/rununittests.nim b/tests/rununittests.nim new file mode 100644 index 0000000..a92ff0a --- /dev/null +++ b/tests/rununittests.nim @@ -0,0 +1,8 @@ +import os, osproc, strutils + +proc main() = + for file in walkFiles(currentSourcePath().splitPath().head / "unittests/*.nim"): + let (path, fname, ext) = file.splitFile() + if fname.startswith("test"): + discard execCmd "nim c -r " & file +main() diff --git a/tests/unittests/common.nim b/tests/unittests/common.nim new file mode 100644 index 0000000..1769b01 --- /dev/null +++ b/tests/unittests/common.nim @@ -0,0 +1,5 @@ +import unittest + +proc checkFile*(filepath, expected: string) = + let result = readFile(filepath) + check result == expected diff --git a/tests/unittests/data/teststaticexpectedfrontbraces.h b/tests/unittests/data/teststaticexpectedfrontbraces.h new file mode 100644 index 0000000..d8c6a0d --- /dev/null +++ b/tests/unittests/data/teststaticexpectedfrontbraces.h @@ -0,0 +1,675 @@ +/** + * @file ipc.h + * @brief Inter-process communication handling + * @author plutoo + * @copyright libnx Authors + */ +#pragma once +#include "../result.h" +#include "../arm/tls.h" +#include "../kernel/svc.h" + +/// IPC input header magic +#define SFCI_MAGIC 0x49434653 +/// IPC output header magic +#define SFCO_MAGIC 0x4f434653 + +/// IPC invalid object ID +#define IPC_INVALID_OBJECT_ID UINT32_MAX + +///@name IPC request building +///@{ + +/// IPC command (request) structure. +#define IPC_MAX_BUFFERS 8 +#define IPC_MAX_OBJECTS 8 + +typedef enum { + BufferType_Normal=0, ///< Regular buffer. + BufferType_Type1=1, ///< Allows ProcessMemory and shared TransferMemory. + BufferType_Invalid=2, + BufferType_Type3=3 ///< Same as Type1 except remote process is not allowed to use device-mapping. +} BufferType; + +typedef enum { + BufferDirection_Send=0, + BufferDirection_Recv=1, + BufferDirection_Exch=2, +} BufferDirection; + +typedef enum { + IpcCommandType_Invalid = 0, + IpcCommandType_LegacyRequest = 1, + IpcCommandType_Close = 2, + IpcCommandType_LegacyControl = 3, + IpcCommandType_Request = 4, + IpcCommandType_Control = 5, + IpcCommandType_RequestWithContext = 6, + IpcCommandType_ControlWithContext = 7, +} IpcCommandType; + +typedef enum { + DomainMessageType_Invalid = 0, + DomainMessageType_SendMessage = 1, + DomainMessageType_Close = 2, +} DomainMessageType; + +/// IPC domain message header. +typedef struct { + u8 Type; + u8 NumObjectIds; + u16 Length; + u32 ThisObjectId; + u32 Pad[2]; +} DomainMessageHeader; + +typedef struct { + size_t NumSend; // A + size_t NumRecv; // B + size_t NumExch; // W + const void* Buffers[IPC_MAX_BUFFERS]; + size_t BufferSizes[IPC_MAX_BUFFERS]; + BufferType BufferTypes[IPC_MAX_BUFFERS]; + + size_t NumStaticIn; // X + size_t NumStaticOut; // C + const void* Statics[IPC_MAX_BUFFERS]; + size_t StaticSizes[IPC_MAX_BUFFERS]; + u8 StaticIndices[IPC_MAX_BUFFERS]; + + bool SendPid; + size_t NumHandlesCopy; + size_t NumHandlesMove; + Handle Handles[IPC_MAX_OBJECTS]; + + size_t NumObjectIds; + u32 ObjectIds[IPC_MAX_OBJECTS]; +} IpcCommand; + +/** + * @brief Initializes an IPC command structure. + * @param cmd IPC command structure. + */ +static inline void ipcInitialize(IpcCommand* cmd);//{ +// *cmd = (IpcCommand){0}; +//} + +/// IPC buffer descriptor. +typedef struct { + u32 Size; ///< Size of the buffer. + u32 Addr; ///< Lower 32-bits of the address of the buffer + u32 Packed; ///< Packed data (including higher bits of the address) +} IpcBufferDescriptor; + +/// IPC static send-buffer descriptor. +typedef struct { + u32 Packed; ///< Packed data (including higher bits of the address) + u32 Addr; ///< Lower 32-bits of the address +} IpcStaticSendDescriptor; + +/// IPC static receive-buffer descriptor. +typedef struct { + u32 Addr; ///< Lower 32-bits of the address of the buffer + u32 Packed; ///< Packed data (including higher bits of the address) +} IpcStaticRecvDescriptor; + +/** + * @brief Adds a buffer to an IPC command structure. + * @param cmd IPC command structure. + * @param buffer Address of the buffer. + * @param size Size of the buffer. + * @param type Buffer type. + */ +static inline void ipcAddSendBuffer(IpcCommand* cmd, const void* buffer, size_t size, BufferType type);//{ +// size_t off = cmd->NumSend; +// cmd->Buffers[off] = buffer; +// cmd->BufferSizes[off] = size; +// cmd->BufferTypes[off] = type; +// cmd->NumSend++; +//} + +/** + * @brief Adds a receive-buffer to an IPC command structure. + * @param cmd IPC command structure. + * @param buffer Address of the buffer. + * @param size Size of the buffer. + * @param type Buffer type. + */ +static inline void ipcAddRecvBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type);//{ +// size_t off = cmd->NumSend + cmd->NumRecv; +// cmd->Buffers[off] = buffer; +// cmd->BufferSizes[off] = size; +// cmd->BufferTypes[off] = type; +// cmd->NumRecv++; +//} + +/** + * @brief Adds an exchange-buffer to an IPC command structure. + * @param cmd IPC command structure. + * @param buffer Address of the buffer. + * @param size Size of the buffer. + * @param type Buffer type. + */ +static inline void ipcAddExchBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type);//{ +// size_t off = cmd->NumSend + cmd->NumRecv + cmd->NumExch; +// cmd->Buffers[off] = buffer; +// cmd->BufferSizes[off] = size; +// cmd->BufferTypes[off] = type; +// cmd->NumExch++; +//} + +/** + * @brief Adds a static-buffer to an IPC command structure. + * @param cmd IPC command structure. + * @param buffer Address of the buffer. + * @param size Size of the buffer. + * @param index Index of buffer. + */ +static inline void ipcAddSendStatic(IpcCommand* cmd, const void* buffer, size_t size, u8 index);//{ +// size_t off = cmd->NumStaticIn; +// cmd->Statics[off] = buffer; +// cmd->StaticSizes[off] = size; +// cmd->StaticIndices[off] = index; +// cmd->NumStaticIn++; +//} + +/** + * @brief Adds a static-receive-buffer to an IPC command structure. + * @param cmd IPC command structure. + * @param buffer Address of the buffer. + * @param size Size of the buffer. + * @param index Index of buffer. + */ +static inline void ipcAddRecvStatic(IpcCommand* cmd, void* buffer, size_t size, u8 index);//{ +// size_t off = cmd->NumStaticIn + cmd->NumStaticOut; +// cmd->Statics[off] = buffer; +// cmd->StaticSizes[off] = size; +// cmd->StaticIndices[off] = index; +// cmd->NumStaticOut++; +//} + +/** + * @brief Adds a smart-buffer (buffer + static-buffer pair) to an IPC command structure. + * @param cmd IPC command structure. + * @param ipc_buffer_size IPC buffer size. + * @param buffer Address of the buffer. + * @param size Size of the buffer. + * @param index Index of buffer. + */ +static inline void ipcAddSendSmart(IpcCommand* cmd, size_t ipc_buffer_size, const void* buffer, size_t size, u8 index);//{ +// if (ipc_buffer_size != 0 && size <= ipc_buffer_size) { +// ipcAddSendBuffer(cmd, NULL, 0, BufferType_Normal); +// ipcAddSendStatic(cmd, buffer, size, index); +// } else { +// ipcAddSendBuffer(cmd, buffer, size, BufferType_Normal); +// ipcAddSendStatic(cmd, NULL, 0, index); +// } +//} + +/** + * @brief Adds a smart-receive-buffer (buffer + static-receive-buffer pair) to an IPC command structure. + * @param cmd IPC command structure. + * @param ipc_buffer_size IPC buffer size. + * @param buffer Address of the buffer. + * @param size Size of the buffer. + * @param index Index of buffer. + */ +static inline void ipcAddRecvSmart(IpcCommand* cmd, size_t ipc_buffer_size, void* buffer, size_t size, u8 index);//{ +// if (ipc_buffer_size != 0 && size <= ipc_buffer_size) { +// ipcAddRecvBuffer(cmd, NULL, 0, BufferType_Normal); +// ipcAddRecvStatic(cmd, buffer, size, index); +// } else { +// ipcAddRecvBuffer(cmd, buffer, size, BufferType_Normal); +// ipcAddRecvStatic(cmd, NULL, 0, index); +// } +//} + +/** + * @brief Tags an IPC command structure to send the PID. + * @param cmd IPC command structure. + */ +static inline void ipcSendPid(IpcCommand* cmd);//{ +// cmd->SendPid = true; +//} + +/** + * @brief Adds a copy-handle to be sent through an IPC command structure. + * @param cmd IPC command structure. + * @param h Handle to send. + * @remark The receiving process gets a copy of the handle. + */ +static inline void ipcSendHandleCopy(IpcCommand* cmd, Handle h);//{ +// cmd->Handles[cmd->NumHandlesCopy++] = h; +//} + +/** + * @brief Adds a move-handle to be sent through an IPC command structure. + * @param cmd IPC command structure. + * @param h Handle to send. + * @remark The sending process loses ownership of the handle, which is transferred to the receiving process. + */ +static inline void ipcSendHandleMove(IpcCommand* cmd, Handle h);//{ +// cmd->Handles[cmd->NumHandlesCopy + cmd->NumHandlesMove++] = h; +//} + +/** + * @brief Prepares the header of an IPC command structure. + * @param cmd IPC command structure. + * @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request + * @return Pointer to the raw embedded data structure in the request, ready to be filled out. + */ +static inline void* ipcPrepareHeader(IpcCommand* cmd, size_t sizeof_raw);//{ +// u32* buf = (u32*)armGetTls(); +// size_t i; +// *buf++ = IpcCommandType_Request | (cmd->NumStaticIn << 16) | (cmd->NumSend << 20) | (cmd->NumRecv << 24) | (cmd->NumExch << 28); +// +// u32* fill_in_size_later = buf; +// +// if (cmd->NumStaticOut > 0) { +// *buf = (cmd->NumStaticOut + 2) << 10; +// } +// else { +// *buf = 0; +// } +// +// if (cmd->SendPid || cmd->NumHandlesCopy > 0 || cmd->NumHandlesMove > 0) { +// *buf++ |= 0x80000000; +// *buf++ = (!!cmd->SendPid) | (cmd->NumHandlesCopy << 1) | (cmd->NumHandlesMove << 5); +// +// if (cmd->SendPid) +// buf += 2; +// +// for (i=0; i<(cmd->NumHandlesCopy + cmd->NumHandlesMove); i++) +// *buf++ = cmd->Handles[i]; +// } +// else { +// buf++; +// } +// +// for (i=0; iNumStaticIn; i++, buf+=2) { +// IpcStaticSendDescriptor* desc = (IpcStaticSendDescriptor*) buf; +// +// uintptr_t ptr = (uintptr_t) cmd->Statics[i]; +// desc->Addr = ptr; +// desc->Packed = cmd->StaticIndices[i] | (cmd->StaticSizes[i] << 16) | +// (((ptr >> 32) & 15) << 12) | (((ptr >> 36) & 15) << 6); +// } +// +// for (i=0; i<(cmd->NumSend + cmd->NumRecv + cmd->NumExch); i++, buf+=3) { +// IpcBufferDescriptor* desc = (IpcBufferDescriptor*) buf; +// desc->Size = cmd->BufferSizes[i]; +// +// uintptr_t ptr = (uintptr_t) cmd->Buffers[i]; +// desc->Addr = ptr; +// desc->Packed = cmd->BufferTypes[i] | +// (((ptr >> 32) & 15) << 28) | ((ptr >> 36) << 2); +// } +// +// u32 padding = ((16 - (((uintptr_t) buf) & 15)) & 15) / 4; +// u32* raw = (u32*) (buf + padding); +// +// size_t raw_size = (sizeof_raw/4) + 4; +// buf += raw_size; +// +// u16* buf_u16 = (u16*) buf; +// +// for (i=0; iNumStaticOut; i++) { +// size_t off = cmd->NumStaticIn + i; +// size_t sz = (uintptr_t) cmd->StaticSizes[off]; +// +// buf_u16[i] = (sz > 0xFFFF) ? 0 : sz; +// } +// +// size_t u16s_size = ((2*cmd->NumStaticOut) + 3)/4; +// buf += u16s_size; +// raw_size += u16s_size; +// +// *fill_in_size_later |= raw_size; +// +// for (i=0; iNumStaticOut; i++, buf+=2) { +// IpcStaticRecvDescriptor* desc = (IpcStaticRecvDescriptor*) buf; +// size_t off = cmd->NumStaticIn + i; +// +// uintptr_t ptr = (uintptr_t) cmd->Statics[off]; +// desc->Addr = ptr; +// desc->Packed = (ptr >> 32) | (cmd->StaticSizes[off] << 16); +// } +// +// return (void*) raw; +//} + +/** + * @brief Dispatches an IPC request. + * @param session IPC session handle. + * @return Result code. + */ +static inline Result ipcDispatch(Handle session);//{ +// return svcSendSyncRequest(session); +//} + +///@} + +///@name IPC response parsing +///@{ + +/// IPC parsed command (response) structure. +typedef struct { + IpcCommandType CommandType; ///< Type of the command + + bool HasPid; ///< true if the 'Pid' field is filled out. + u64 Pid; ///< PID included in the response (only if HasPid is true) + + size_t NumHandles; ///< Number of handles copied. + Handle Handles[IPC_MAX_OBJECTS]; ///< Handles. + bool WasHandleCopied[IPC_MAX_OBJECTS]; ///< true if the handle was moved, false if it was copied. + + bool IsDomainMessage; ///< true if the the message is a Domain message. + DomainMessageType MessageType; ///< Type of the domain message. + u32 MessageLength; ///< Size of rawdata (for domain messages). + u32 ThisObjectId; ///< Object ID to call the command on (for domain messages). + size_t NumObjectIds; ///< Number of object IDs (for domain messages). + u32 ObjectIds[IPC_MAX_OBJECTS]; ///< Object IDs (for domain messages). + + size_t NumBuffers; ///< Number of buffers in the response. + void* Buffers[IPC_MAX_BUFFERS]; ///< Pointers to the buffers. + size_t BufferSizes[IPC_MAX_BUFFERS]; ///< Sizes of the buffers. + BufferType BufferTypes[IPC_MAX_BUFFERS]; ///< Types of the buffers. + BufferDirection BufferDirections[IPC_MAX_BUFFERS]; ///< Direction of each buffer. + + size_t NumStatics; ///< Number of statics in the response. + void* Statics[IPC_MAX_BUFFERS]; ///< Pointers to the statics. + size_t StaticSizes[IPC_MAX_BUFFERS]; ///< Sizes of the statics. + u8 StaticIndices[IPC_MAX_BUFFERS]; ///< Indices of the statics. + + size_t NumStaticsOut; ///< Number of output statics available in the response. + + void* Raw; ///< Pointer to the raw embedded data structure in the response. + void* RawWithoutPadding; ///< Pointer to the raw embedded data structure, without padding. + size_t RawSize; ///< Size of the raw embedded data. +} IpcParsedCommand; + +/** + * @brief Parse an IPC command response into an IPC parsed command structure. + * @param IPC parsed command structure to fill in. + * @return Result code. + */ +static inline Result ipcParse(IpcParsedCommand* r);//{ +// u32* buf = (u32*)armGetTls(); +// u32 ctrl0 = *buf++; +// u32 ctrl1 = *buf++; +// size_t i; +// +// r->IsDomainMessage = false; +// +// r->CommandType = (IpcCommandType) (ctrl0 & 0xffff); +// r->HasPid = false; +// r->RawSize = (ctrl1 & 0x1ff) * 4; +// r->NumHandles = 0; +// +// r->NumStaticsOut = (ctrl1 >> 10) & 15; +// if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 2 -> Single descriptor +// if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 3+ -> (Value - 2) descriptors +// +// if (ctrl1 & 0x80000000) { +// u32 ctrl2 = *buf++; +// +// if (ctrl2 & 1) { +// r->HasPid = true; +// r->Pid = *buf++; +// r->Pid |= ((u64)(*buf++)) << 32; +// } +// +// size_t num_handles_copy = ((ctrl2 >> 1) & 15); +// size_t num_handles_move = ((ctrl2 >> 5) & 15); +// +// size_t num_handles = num_handles_copy + num_handles_move; +// u32* buf_after_handles = buf + num_handles; +// +// if (num_handles > IPC_MAX_OBJECTS) +// num_handles = IPC_MAX_OBJECTS; +// +// for (i=0; iHandles[i] = *(buf+i); +// r->WasHandleCopied[i] = (i < num_handles_copy); +// } +// +// r->NumHandles = num_handles; +// buf = buf_after_handles; +// } +// +// size_t num_statics = (ctrl0 >> 16) & 15; +// u32* buf_after_statics = buf + num_statics*2; +// +// if (num_statics > IPC_MAX_BUFFERS) +// num_statics = IPC_MAX_BUFFERS; +// +// for (i=0; iPacked; +// +// r->Statics[i] = (void*) (desc->Addr | (((packed >> 12) & 15) << 32) | (((packed >> 6) & 15) << 36)); +// r->StaticSizes[i] = packed >> 16; +// r->StaticIndices[i] = packed & 63; +// } +// +// r->NumStatics = num_statics; +// buf = buf_after_statics; +// +// size_t num_bufs_send = (ctrl0 >> 20) & 15; +// size_t num_bufs_recv = (ctrl0 >> 24) & 15; +// size_t num_bufs_exch = (ctrl0 >> 28) & 15; +// +// size_t num_bufs = num_bufs_send + num_bufs_recv + num_bufs_exch; +// r->Raw = (void*)(((uintptr_t)(buf + num_bufs*3) + 15) &~ 15); +// r->RawWithoutPadding = (void*)((uintptr_t)(buf + num_bufs*3)); +// +// if (num_bufs > IPC_MAX_BUFFERS) +// num_bufs = IPC_MAX_BUFFERS; +// +// for (i=0; iPacked; +// +// r->Buffers[i] = (void*) (desc->Addr | ((packed >> 28) << 32) | (((packed >> 2) & 15) << 36)); +// r->BufferSizes[i] = desc->Size; +// r->BufferTypes[i] = (BufferType) (packed & 3); +// +// if (i < num_bufs_send) +// r->BufferDirections[i] = BufferDirection_Send; +// else if (i < (num_bufs_send + num_bufs_recv)) +// r->BufferDirections[i] = BufferDirection_Recv; +// else +// r->BufferDirections[i] = BufferDirection_Exch; +// } +// +// r->NumBuffers = num_bufs; +// return 0; +//} + +/** + * @brief Queries the size of an IPC pointer buffer. + * @param session IPC session handle. + * @param size Output variable in which to store the size. + * @return Result code. + */ +static inline Result ipcQueryPointerBufferSize(Handle session, size_t *size);//{ +// u32* buf = (u32*)armGetTls(); +// +// buf[0] = IpcCommandType_Control; +// buf[1] = 8; +// buf[2] = 0; +// buf[3] = 0; +// buf[4] = SFCI_MAGIC; +// buf[5] = 0; +// buf[6] = 3; +// buf[7] = 0; +// +// Result rc = ipcDispatch(session); +// +// if (R_SUCCEEDED(rc)) { +// IpcParsedCommand r; +// ipcParse(&r); +// +// struct ipcQueryPointerBufferSizeResponse { +// u64 magic; +// u64 result; +// u32 size; +// } *raw = (struct ipcQueryPointerBufferSizeResponse*)r.Raw; +// +// rc = raw->result; +// +// if (R_SUCCEEDED(rc)) { +// *size = raw->size & 0xffff; +// } +// } +// +// return rc; +//} + +/** + * @brief Closes the IPC session with proper clean up. + * @param session IPC session handle. + * @return Result code. + */ +static inline Result ipcCloseSession(Handle session);//{ +// u32* buf = (u32*)armGetTls(); +// buf[0] = IpcCommandType_Close; +// buf[1] = 0; +// return ipcDispatch(session); +//} +///@} + +///@name IPC domain handling +///@{ + +/** + * @brief Converts an IPC session handle into a domain. + * @param session IPC session handle. + * @param object_id_out Output variable in which to store the object ID. + * @return Result code. + */ +static inline Result ipcConvertSessionToDomain(Handle session, u32* object_id_out);//{ +// u32* buf = (u32*)armGetTls(); +// +// buf[0] = IpcCommandType_Control; +// buf[1] = 8; +// buf[4] = SFCI_MAGIC; +// buf[5] = 0; +// buf[6] = 0; +// buf[7] = 0; +// +// Result rc = ipcDispatch(session); +// +// if (R_SUCCEEDED(rc)) { +// IpcParsedCommand r; +// ipcParse(&r); +// +// struct ipcConvertSessionToDomainResponse { +// u64 magic; +// u64 result; +// u32 object_id; +// } *raw = (struct ipcConvertSessionToDomainResponse*)r.Raw; +// +// rc = raw->result; +// +// if (R_SUCCEEDED(rc)) { +// *object_id_out = raw->object_id; +// } +// } +// +// return rc; +//} + +/** + * @brief Adds an object ID to be sent through an IPC domain command structure. + * @param cmd IPC domain command structure. + * @param object_id Object ID to send. + */ +static inline void ipcSendObjectId(IpcCommand* cmd, u32 object_id);//{ +// cmd->ObjectIds[cmd->NumObjectIds++] = object_id; +//} + +/** + * @brief Prepares the header of an IPC command structure (domain version). + * @param cmd IPC command structure. + * @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request + * @oaram object_id Domain object ID. + * @return Pointer to the raw embedded data structure in the request, ready to be filled out. + */ +static inline void* ipcPrepareHeaderForDomain(IpcCommand* cmd, size_t sizeof_raw, u32 object_id);//{ +// void* raw = ipcPrepareHeader(cmd, sizeof_raw + sizeof(DomainMessageHeader)); +// DomainMessageHeader* hdr = (DomainMessageHeader*) raw; +// u32 *object_ids = (u32*)(((uintptr_t) raw) + sizeof(DomainMessageHeader) + sizeof_raw); +// +// hdr->Type = DomainMessageType_SendMessage; +// hdr->NumObjectIds = (u8)cmd->NumObjectIds; +// hdr->Length = sizeof_raw; +// hdr->ThisObjectId = object_id; +// hdr->Pad[0] = hdr->Pad[1] = 0; +// +// for(size_t i = 0; i < cmd->NumObjectIds; i++) +// object_ids[i] = cmd->ObjectIds[i]; +// return (void*)(((uintptr_t) raw) + sizeof(DomainMessageHeader)); +//} + +/** + * @brief Parse an IPC command response into an IPC parsed command structure (domain version). + * @param IPC parsed command structure to fill in. + * @return Result code. + */ +static inline Result ipcParseForDomain(IpcParsedCommand* r);//{ +// Result rc = ipcParse(r); +// DomainMessageHeader *hdr; +// u32 *object_ids; +// if(R_FAILED(rc)) +// return rc; +// +// hdr = (DomainMessageHeader*) r->Raw; +// object_ids = (u32*)(((uintptr_t) hdr) + sizeof(DomainMessageHeader) + hdr->Length); +// r->Raw = (void*)(((uintptr_t) r->Raw) + sizeof(DomainMessageHeader)); +// +// r->IsDomainMessage = true; +// r->MessageType = (DomainMessageType)(hdr->Type); +// switch (r->MessageType) { +// case DomainMessageType_SendMessage: +// case DomainMessageType_Close: +// break; +// default: +// return MAKERESULT(Module_Libnx, LibnxError_DomainMessageUnknownType); +// } +// r->ThisObjectId = hdr->ThisObjectId; +// r->NumObjectIds = hdr->NumObjectIds > 8 ? 8 : hdr->NumObjectIds; +// if ((uintptr_t)object_ids + sizeof(u32) * r->NumObjectIds - (uintptr_t)armGetTls() >= 0x100) { +// return MAKERESULT(Module_Libnx, LibnxError_DomainMessageTooManyObjectIds); +// } +// for(size_t i = 0; i < r->NumObjectIds; i++) +// r->ObjectIds[i] = object_ids[i]; +// +// return rc; +//} + +/** + * @brief Closes a domain object by ID. + * @param session IPC session handle. + * @param object_id ID of the object to close. + * @return Result code. + */ +static inline Result ipcCloseObjectById(Handle session, u32 object_id);//{ +// IpcCommand c; +// DomainMessageHeader* hdr; +// +// ipcInitialize(&c); +// hdr = (DomainMessageHeader*)ipcPrepareHeader(&c, sizeof(DomainMessageHeader)); +// +// hdr->Type = 2; +// hdr->NumObjectIds = 0; +// hdr->Length = 0; +// hdr->ThisObjectId = object_id; +// hdr->Pad[0] = hdr->Pad[1] = 0; +// +// return ipcDispatch(session); // this command has no associated response +//} + +///@} + diff --git a/tests/unittests/data/teststaticexpectednewlinebraces.h b/tests/unittests/data/teststaticexpectednewlinebraces.h new file mode 100644 index 0000000..3f90ab6 --- /dev/null +++ b/tests/unittests/data/teststaticexpectednewlinebraces.h @@ -0,0 +1,68 @@ +/** + * @file condvar.h + * @brief Condition variable synchronization primitive. + * @author plutoo + * @copyright libnx Authors + */ +#pragma once +#include "../types.h" +#include "../kernel/mutex.h" + +/// Condition variable structure. +typedef struct { + u32 tag; + Mutex* mutex; +} CondVar; + +/** + * @brief Initializes a condition variable. + * @param[in] c Condition variable object. + * @param[in] m Mutex object to use inside the condition variable. + */ +void condvarInit(CondVar* c, Mutex* m); + +/** + * @brief Waits on a condition variable with a timeout. + * @param[in] c Condition variable object. + * @param[in] timeout Timeout in nanoseconds. + * @return Result code (0xEA01 on timeout). + * @remark On function return, the underlying mutex is acquired. + */ +Result condvarWaitTimeout(CondVar* c, u64 timeout); + +/** + * @brief Waits on a condition variable. + * @param[in] c Condition variable object. + * @return Result code. + * @remark On function return, the underlying mutex is acquired. + */ +static inline Result condvarWait(CondVar* c);//{ +// return condvarWaitTimeout(c, -1ull); +//} + +/** + * @brief Wakes up up to the specified number of threads waiting on a condition variable. + * @param[in] c Condition variable object. + * @param[in] num Maximum number of threads to wake up (or -1 to wake them all up). + * @return Result code. + */ +Result condvarWake(CondVar* c, int num); + +/** + * @brief Wakes up a single thread waiting on a condition variable. + * @param[in] c Condition variable object. + * @return Result code. + */ +static inline Result condvarWakeOne(CondVar* c);//{ +// return condvarWake(c, 1); +//} + +/** + * @brief Wakes up all thread waiting on a condition variable. + * @param[in] c Condition variable object. + * @return Result code. + */ +static inline Result condvarWakeAll(CondVar* c);//{ +// return condvarWake(c, -1); +//} + diff --git a/tests/unittests/data/teststaticfrontbraces.h b/tests/unittests/data/teststaticfrontbraces.h new file mode 100644 index 0000000..d62f694 --- /dev/null +++ b/tests/unittests/data/teststaticfrontbraces.h @@ -0,0 +1,675 @@ +/** + * @file ipc.h + * @brief Inter-process communication handling + * @author plutoo + * @copyright libnx Authors + */ +#pragma once +#include "../result.h" +#include "../arm/tls.h" +#include "../kernel/svc.h" + +/// IPC input header magic +#define SFCI_MAGIC 0x49434653 +/// IPC output header magic +#define SFCO_MAGIC 0x4f434653 + +/// IPC invalid object ID +#define IPC_INVALID_OBJECT_ID UINT32_MAX + +///@name IPC request building +///@{ + +/// IPC command (request) structure. +#define IPC_MAX_BUFFERS 8 +#define IPC_MAX_OBJECTS 8 + +typedef enum { + BufferType_Normal=0, ///< Regular buffer. + BufferType_Type1=1, ///< Allows ProcessMemory and shared TransferMemory. + BufferType_Invalid=2, + BufferType_Type3=3 ///< Same as Type1 except remote process is not allowed to use device-mapping. +} BufferType; + +typedef enum { + BufferDirection_Send=0, + BufferDirection_Recv=1, + BufferDirection_Exch=2, +} BufferDirection; + +typedef enum { + IpcCommandType_Invalid = 0, + IpcCommandType_LegacyRequest = 1, + IpcCommandType_Close = 2, + IpcCommandType_LegacyControl = 3, + IpcCommandType_Request = 4, + IpcCommandType_Control = 5, + IpcCommandType_RequestWithContext = 6, + IpcCommandType_ControlWithContext = 7, +} IpcCommandType; + +typedef enum { + DomainMessageType_Invalid = 0, + DomainMessageType_SendMessage = 1, + DomainMessageType_Close = 2, +} DomainMessageType; + +/// IPC domain message header. +typedef struct { + u8 Type; + u8 NumObjectIds; + u16 Length; + u32 ThisObjectId; + u32 Pad[2]; +} DomainMessageHeader; + +typedef struct { + size_t NumSend; // A + size_t NumRecv; // B + size_t NumExch; // W + const void* Buffers[IPC_MAX_BUFFERS]; + size_t BufferSizes[IPC_MAX_BUFFERS]; + BufferType BufferTypes[IPC_MAX_BUFFERS]; + + size_t NumStaticIn; // X + size_t NumStaticOut; // C + const void* Statics[IPC_MAX_BUFFERS]; + size_t StaticSizes[IPC_MAX_BUFFERS]; + u8 StaticIndices[IPC_MAX_BUFFERS]; + + bool SendPid; + size_t NumHandlesCopy; + size_t NumHandlesMove; + Handle Handles[IPC_MAX_OBJECTS]; + + size_t NumObjectIds; + u32 ObjectIds[IPC_MAX_OBJECTS]; +} IpcCommand; + +/** + * @brief Initializes an IPC command structure. + * @param cmd IPC command structure. + */ +static inline void ipcInitialize(IpcCommand* cmd) { + *cmd = (IpcCommand){0}; +} + +/// IPC buffer descriptor. +typedef struct { + u32 Size; ///< Size of the buffer. + u32 Addr; ///< Lower 32-bits of the address of the buffer + u32 Packed; ///< Packed data (including higher bits of the address) +} IpcBufferDescriptor; + +/// IPC static send-buffer descriptor. +typedef struct { + u32 Packed; ///< Packed data (including higher bits of the address) + u32 Addr; ///< Lower 32-bits of the address +} IpcStaticSendDescriptor; + +/// IPC static receive-buffer descriptor. +typedef struct { + u32 Addr; ///< Lower 32-bits of the address of the buffer + u32 Packed; ///< Packed data (including higher bits of the address) +} IpcStaticRecvDescriptor; + +/** + * @brief Adds a buffer to an IPC command structure. + * @param cmd IPC command structure. + * @param buffer Address of the buffer. + * @param size Size of the buffer. + * @param type Buffer type. + */ +static inline void ipcAddSendBuffer(IpcCommand* cmd, const void* buffer, size_t size, BufferType type) { + size_t off = cmd->NumSend; + cmd->Buffers[off] = buffer; + cmd->BufferSizes[off] = size; + cmd->BufferTypes[off] = type; + cmd->NumSend++; +} + +/** + * @brief Adds a receive-buffer to an IPC command structure. + * @param cmd IPC command structure. + * @param buffer Address of the buffer. + * @param size Size of the buffer. + * @param type Buffer type. + */ +static inline void ipcAddRecvBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type) { + size_t off = cmd->NumSend + cmd->NumRecv; + cmd->Buffers[off] = buffer; + cmd->BufferSizes[off] = size; + cmd->BufferTypes[off] = type; + cmd->NumRecv++; +} + +/** + * @brief Adds an exchange-buffer to an IPC command structure. + * @param cmd IPC command structure. + * @param buffer Address of the buffer. + * @param size Size of the buffer. + * @param type Buffer type. + */ +static inline void ipcAddExchBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type) { + size_t off = cmd->NumSend + cmd->NumRecv + cmd->NumExch; + cmd->Buffers[off] = buffer; + cmd->BufferSizes[off] = size; + cmd->BufferTypes[off] = type; + cmd->NumExch++; +} + +/** + * @brief Adds a static-buffer to an IPC command structure. + * @param cmd IPC command structure. + * @param buffer Address of the buffer. + * @param size Size of the buffer. + * @param index Index of buffer. + */ +static inline void ipcAddSendStatic(IpcCommand* cmd, const void* buffer, size_t size, u8 index) { + size_t off = cmd->NumStaticIn; + cmd->Statics[off] = buffer; + cmd->StaticSizes[off] = size; + cmd->StaticIndices[off] = index; + cmd->NumStaticIn++; +} + +/** + * @brief Adds a static-receive-buffer to an IPC command structure. + * @param cmd IPC command structure. + * @param buffer Address of the buffer. + * @param size Size of the buffer. + * @param index Index of buffer. + */ +static inline void ipcAddRecvStatic(IpcCommand* cmd, void* buffer, size_t size, u8 index) { + size_t off = cmd->NumStaticIn + cmd->NumStaticOut; + cmd->Statics[off] = buffer; + cmd->StaticSizes[off] = size; + cmd->StaticIndices[off] = index; + cmd->NumStaticOut++; +} + +/** + * @brief Adds a smart-buffer (buffer + static-buffer pair) to an IPC command structure. + * @param cmd IPC command structure. + * @param ipc_buffer_size IPC buffer size. + * @param buffer Address of the buffer. + * @param size Size of the buffer. + * @param index Index of buffer. + */ +static inline void ipcAddSendSmart(IpcCommand* cmd, size_t ipc_buffer_size, const void* buffer, size_t size, u8 index) { + if (ipc_buffer_size != 0 && size <= ipc_buffer_size) { + ipcAddSendBuffer(cmd, NULL, 0, BufferType_Normal); + ipcAddSendStatic(cmd, buffer, size, index); + } else { + ipcAddSendBuffer(cmd, buffer, size, BufferType_Normal); + ipcAddSendStatic(cmd, NULL, 0, index); + } +} + +/** + * @brief Adds a smart-receive-buffer (buffer + static-receive-buffer pair) to an IPC command structure. + * @param cmd IPC command structure. + * @param ipc_buffer_size IPC buffer size. + * @param buffer Address of the buffer. + * @param size Size of the buffer. + * @param index Index of buffer. + */ +static inline void ipcAddRecvSmart(IpcCommand* cmd, size_t ipc_buffer_size, void* buffer, size_t size, u8 index) { + if (ipc_buffer_size != 0 && size <= ipc_buffer_size) { + ipcAddRecvBuffer(cmd, NULL, 0, BufferType_Normal); + ipcAddRecvStatic(cmd, buffer, size, index); + } else { + ipcAddRecvBuffer(cmd, buffer, size, BufferType_Normal); + ipcAddRecvStatic(cmd, NULL, 0, index); + } +} + +/** + * @brief Tags an IPC command structure to send the PID. + * @param cmd IPC command structure. + */ +static inline void ipcSendPid(IpcCommand* cmd) { + cmd->SendPid = true; +} + +/** + * @brief Adds a copy-handle to be sent through an IPC command structure. + * @param cmd IPC command structure. + * @param h Handle to send. + * @remark The receiving process gets a copy of the handle. + */ +static inline void ipcSendHandleCopy(IpcCommand* cmd, Handle h) { + cmd->Handles[cmd->NumHandlesCopy++] = h; +} + +/** + * @brief Adds a move-handle to be sent through an IPC command structure. + * @param cmd IPC command structure. + * @param h Handle to send. + * @remark The sending process loses ownership of the handle, which is transferred to the receiving process. + */ +static inline void ipcSendHandleMove(IpcCommand* cmd, Handle h) { + cmd->Handles[cmd->NumHandlesCopy + cmd->NumHandlesMove++] = h; +} + +/** + * @brief Prepares the header of an IPC command structure. + * @param cmd IPC command structure. + * @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request + * @return Pointer to the raw embedded data structure in the request, ready to be filled out. + */ +static inline void* ipcPrepareHeader(IpcCommand* cmd, size_t sizeof_raw) { + u32* buf = (u32*)armGetTls(); + size_t i; + *buf++ = IpcCommandType_Request | (cmd->NumStaticIn << 16) | (cmd->NumSend << 20) | (cmd->NumRecv << 24) | (cmd->NumExch << 28); + + u32* fill_in_size_later = buf; + + if (cmd->NumStaticOut > 0) { + *buf = (cmd->NumStaticOut + 2) << 10; + } + else { + *buf = 0; + } + + if (cmd->SendPid || cmd->NumHandlesCopy > 0 || cmd->NumHandlesMove > 0) { + *buf++ |= 0x80000000; + *buf++ = (!!cmd->SendPid) | (cmd->NumHandlesCopy << 1) | (cmd->NumHandlesMove << 5); + + if (cmd->SendPid) + buf += 2; + + for (i=0; i<(cmd->NumHandlesCopy + cmd->NumHandlesMove); i++) + *buf++ = cmd->Handles[i]; + } + else { + buf++; + } + + for (i=0; iNumStaticIn; i++, buf+=2) { + IpcStaticSendDescriptor* desc = (IpcStaticSendDescriptor*) buf; + + uintptr_t ptr = (uintptr_t) cmd->Statics[i]; + desc->Addr = ptr; + desc->Packed = cmd->StaticIndices[i] | (cmd->StaticSizes[i] << 16) | + (((ptr >> 32) & 15) << 12) | (((ptr >> 36) & 15) << 6); + } + + for (i=0; i<(cmd->NumSend + cmd->NumRecv + cmd->NumExch); i++, buf+=3) { + IpcBufferDescriptor* desc = (IpcBufferDescriptor*) buf; + desc->Size = cmd->BufferSizes[i]; + + uintptr_t ptr = (uintptr_t) cmd->Buffers[i]; + desc->Addr = ptr; + desc->Packed = cmd->BufferTypes[i] | + (((ptr >> 32) & 15) << 28) | ((ptr >> 36) << 2); + } + + u32 padding = ((16 - (((uintptr_t) buf) & 15)) & 15) / 4; + u32* raw = (u32*) (buf + padding); + + size_t raw_size = (sizeof_raw/4) + 4; + buf += raw_size; + + u16* buf_u16 = (u16*) buf; + + for (i=0; iNumStaticOut; i++) { + size_t off = cmd->NumStaticIn + i; + size_t sz = (uintptr_t) cmd->StaticSizes[off]; + + buf_u16[i] = (sz > 0xFFFF) ? 0 : sz; + } + + size_t u16s_size = ((2*cmd->NumStaticOut) + 3)/4; + buf += u16s_size; + raw_size += u16s_size; + + *fill_in_size_later |= raw_size; + + for (i=0; iNumStaticOut; i++, buf+=2) { + IpcStaticRecvDescriptor* desc = (IpcStaticRecvDescriptor*) buf; + size_t off = cmd->NumStaticIn + i; + + uintptr_t ptr = (uintptr_t) cmd->Statics[off]; + desc->Addr = ptr; + desc->Packed = (ptr >> 32) | (cmd->StaticSizes[off] << 16); + } + + return (void*) raw; +} + +/** + * @brief Dispatches an IPC request. + * @param session IPC session handle. + * @return Result code. + */ +static inline Result ipcDispatch(Handle session) { + return svcSendSyncRequest(session); +} + +///@} + +///@name IPC response parsing +///@{ + +/// IPC parsed command (response) structure. +typedef struct { + IpcCommandType CommandType; ///< Type of the command + + bool HasPid; ///< true if the 'Pid' field is filled out. + u64 Pid; ///< PID included in the response (only if HasPid is true) + + size_t NumHandles; ///< Number of handles copied. + Handle Handles[IPC_MAX_OBJECTS]; ///< Handles. + bool WasHandleCopied[IPC_MAX_OBJECTS]; ///< true if the handle was moved, false if it was copied. + + bool IsDomainMessage; ///< true if the the message is a Domain message. + DomainMessageType MessageType; ///< Type of the domain message. + u32 MessageLength; ///< Size of rawdata (for domain messages). + u32 ThisObjectId; ///< Object ID to call the command on (for domain messages). + size_t NumObjectIds; ///< Number of object IDs (for domain messages). + u32 ObjectIds[IPC_MAX_OBJECTS]; ///< Object IDs (for domain messages). + + size_t NumBuffers; ///< Number of buffers in the response. + void* Buffers[IPC_MAX_BUFFERS]; ///< Pointers to the buffers. + size_t BufferSizes[IPC_MAX_BUFFERS]; ///< Sizes of the buffers. + BufferType BufferTypes[IPC_MAX_BUFFERS]; ///< Types of the buffers. + BufferDirection BufferDirections[IPC_MAX_BUFFERS]; ///< Direction of each buffer. + + size_t NumStatics; ///< Number of statics in the response. + void* Statics[IPC_MAX_BUFFERS]; ///< Pointers to the statics. + size_t StaticSizes[IPC_MAX_BUFFERS]; ///< Sizes of the statics. + u8 StaticIndices[IPC_MAX_BUFFERS]; ///< Indices of the statics. + + size_t NumStaticsOut; ///< Number of output statics available in the response. + + void* Raw; ///< Pointer to the raw embedded data structure in the response. + void* RawWithoutPadding; ///< Pointer to the raw embedded data structure, without padding. + size_t RawSize; ///< Size of the raw embedded data. +} IpcParsedCommand; + +/** + * @brief Parse an IPC command response into an IPC parsed command structure. + * @param IPC parsed command structure to fill in. + * @return Result code. + */ +static inline Result ipcParse(IpcParsedCommand* r) { + u32* buf = (u32*)armGetTls(); + u32 ctrl0 = *buf++; + u32 ctrl1 = *buf++; + size_t i; + + r->IsDomainMessage = false; + + r->CommandType = (IpcCommandType) (ctrl0 & 0xffff); + r->HasPid = false; + r->RawSize = (ctrl1 & 0x1ff) * 4; + r->NumHandles = 0; + + r->NumStaticsOut = (ctrl1 >> 10) & 15; + if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 2 -> Single descriptor + if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 3+ -> (Value - 2) descriptors + + if (ctrl1 & 0x80000000) { + u32 ctrl2 = *buf++; + + if (ctrl2 & 1) { + r->HasPid = true; + r->Pid = *buf++; + r->Pid |= ((u64)(*buf++)) << 32; + } + + size_t num_handles_copy = ((ctrl2 >> 1) & 15); + size_t num_handles_move = ((ctrl2 >> 5) & 15); + + size_t num_handles = num_handles_copy + num_handles_move; + u32* buf_after_handles = buf + num_handles; + + if (num_handles > IPC_MAX_OBJECTS) + num_handles = IPC_MAX_OBJECTS; + + for (i=0; iHandles[i] = *(buf+i); + r->WasHandleCopied[i] = (i < num_handles_copy); + } + + r->NumHandles = num_handles; + buf = buf_after_handles; + } + + size_t num_statics = (ctrl0 >> 16) & 15; + u32* buf_after_statics = buf + num_statics*2; + + if (num_statics > IPC_MAX_BUFFERS) + num_statics = IPC_MAX_BUFFERS; + + for (i=0; iPacked; + + r->Statics[i] = (void*) (desc->Addr | (((packed >> 12) & 15) << 32) | (((packed >> 6) & 15) << 36)); + r->StaticSizes[i] = packed >> 16; + r->StaticIndices[i] = packed & 63; + } + + r->NumStatics = num_statics; + buf = buf_after_statics; + + size_t num_bufs_send = (ctrl0 >> 20) & 15; + size_t num_bufs_recv = (ctrl0 >> 24) & 15; + size_t num_bufs_exch = (ctrl0 >> 28) & 15; + + size_t num_bufs = num_bufs_send + num_bufs_recv + num_bufs_exch; + r->Raw = (void*)(((uintptr_t)(buf + num_bufs*3) + 15) &~ 15); + r->RawWithoutPadding = (void*)((uintptr_t)(buf + num_bufs*3)); + + if (num_bufs > IPC_MAX_BUFFERS) + num_bufs = IPC_MAX_BUFFERS; + + for (i=0; iPacked; + + r->Buffers[i] = (void*) (desc->Addr | ((packed >> 28) << 32) | (((packed >> 2) & 15) << 36)); + r->BufferSizes[i] = desc->Size; + r->BufferTypes[i] = (BufferType) (packed & 3); + + if (i < num_bufs_send) + r->BufferDirections[i] = BufferDirection_Send; + else if (i < (num_bufs_send + num_bufs_recv)) + r->BufferDirections[i] = BufferDirection_Recv; + else + r->BufferDirections[i] = BufferDirection_Exch; + } + + r->NumBuffers = num_bufs; + return 0; +} + +/** + * @brief Queries the size of an IPC pointer buffer. + * @param session IPC session handle. + * @param size Output variable in which to store the size. + * @return Result code. + */ +static inline Result ipcQueryPointerBufferSize(Handle session, size_t *size) { + u32* buf = (u32*)armGetTls(); + + buf[0] = IpcCommandType_Control; + buf[1] = 8; + buf[2] = 0; + buf[3] = 0; + buf[4] = SFCI_MAGIC; + buf[5] = 0; + buf[6] = 3; + buf[7] = 0; + + Result rc = ipcDispatch(session); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct ipcQueryPointerBufferSizeResponse { + u64 magic; + u64 result; + u32 size; + } *raw = (struct ipcQueryPointerBufferSizeResponse*)r.Raw; + + rc = raw->result; + + if (R_SUCCEEDED(rc)) { + *size = raw->size & 0xffff; + } + } + + return rc; +} + +/** + * @brief Closes the IPC session with proper clean up. + * @param session IPC session handle. + * @return Result code. + */ +static inline Result ipcCloseSession(Handle session) { + u32* buf = (u32*)armGetTls(); + buf[0] = IpcCommandType_Close; + buf[1] = 0; + return ipcDispatch(session); +} +///@} + +///@name IPC domain handling +///@{ + +/** + * @brief Converts an IPC session handle into a domain. + * @param session IPC session handle. + * @param object_id_out Output variable in which to store the object ID. + * @return Result code. + */ +static inline Result ipcConvertSessionToDomain(Handle session, u32* object_id_out) { + u32* buf = (u32*)armGetTls(); + + buf[0] = IpcCommandType_Control; + buf[1] = 8; + buf[4] = SFCI_MAGIC; + buf[5] = 0; + buf[6] = 0; + buf[7] = 0; + + Result rc = ipcDispatch(session); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct ipcConvertSessionToDomainResponse { + u64 magic; + u64 result; + u32 object_id; + } *raw = (struct ipcConvertSessionToDomainResponse*)r.Raw; + + rc = raw->result; + + if (R_SUCCEEDED(rc)) { + *object_id_out = raw->object_id; + } + } + + return rc; +} + +/** + * @brief Adds an object ID to be sent through an IPC domain command structure. + * @param cmd IPC domain command structure. + * @param object_id Object ID to send. + */ +static inline void ipcSendObjectId(IpcCommand* cmd, u32 object_id) { + cmd->ObjectIds[cmd->NumObjectIds++] = object_id; +} + +/** + * @brief Prepares the header of an IPC command structure (domain version). + * @param cmd IPC command structure. + * @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request + * @oaram object_id Domain object ID. + * @return Pointer to the raw embedded data structure in the request, ready to be filled out. + */ +static inline void* ipcPrepareHeaderForDomain(IpcCommand* cmd, size_t sizeof_raw, u32 object_id) { + void* raw = ipcPrepareHeader(cmd, sizeof_raw + sizeof(DomainMessageHeader)); + DomainMessageHeader* hdr = (DomainMessageHeader*) raw; + u32 *object_ids = (u32*)(((uintptr_t) raw) + sizeof(DomainMessageHeader) + sizeof_raw); + + hdr->Type = DomainMessageType_SendMessage; + hdr->NumObjectIds = (u8)cmd->NumObjectIds; + hdr->Length = sizeof_raw; + hdr->ThisObjectId = object_id; + hdr->Pad[0] = hdr->Pad[1] = 0; + + for(size_t i = 0; i < cmd->NumObjectIds; i++) + object_ids[i] = cmd->ObjectIds[i]; + return (void*)(((uintptr_t) raw) + sizeof(DomainMessageHeader)); +} + +/** + * @brief Parse an IPC command response into an IPC parsed command structure (domain version). + * @param IPC parsed command structure to fill in. + * @return Result code. + */ +static inline Result ipcParseForDomain(IpcParsedCommand* r) { + Result rc = ipcParse(r); + DomainMessageHeader *hdr; + u32 *object_ids; + if(R_FAILED(rc)) + return rc; + + hdr = (DomainMessageHeader*) r->Raw; + object_ids = (u32*)(((uintptr_t) hdr) + sizeof(DomainMessageHeader) + hdr->Length); + r->Raw = (void*)(((uintptr_t) r->Raw) + sizeof(DomainMessageHeader)); + + r->IsDomainMessage = true; + r->MessageType = (DomainMessageType)(hdr->Type); + switch (r->MessageType) { + case DomainMessageType_SendMessage: + case DomainMessageType_Close: + break; + default: + return MAKERESULT(Module_Libnx, LibnxError_DomainMessageUnknownType); + } + r->ThisObjectId = hdr->ThisObjectId; + r->NumObjectIds = hdr->NumObjectIds > 8 ? 8 : hdr->NumObjectIds; + if ((uintptr_t)object_ids + sizeof(u32) * r->NumObjectIds - (uintptr_t)armGetTls() >= 0x100) { + return MAKERESULT(Module_Libnx, LibnxError_DomainMessageTooManyObjectIds); + } + for(size_t i = 0; i < r->NumObjectIds; i++) + r->ObjectIds[i] = object_ids[i]; + + return rc; +} + +/** + * @brief Closes a domain object by ID. + * @param session IPC session handle. + * @param object_id ID of the object to close. + * @return Result code. + */ +static inline Result ipcCloseObjectById(Handle session, u32 object_id) { + IpcCommand c; + DomainMessageHeader* hdr; + + ipcInitialize(&c); + hdr = (DomainMessageHeader*)ipcPrepareHeader(&c, sizeof(DomainMessageHeader)); + + hdr->Type = 2; + hdr->NumObjectIds = 0; + hdr->Length = 0; + hdr->ThisObjectId = object_id; + hdr->Pad[0] = hdr->Pad[1] = 0; + + return ipcDispatch(session); // this command has no associated response +} + +///@} + diff --git a/tests/unittests/data/teststaticnewlinebraces.h b/tests/unittests/data/teststaticnewlinebraces.h new file mode 100644 index 0000000..c72c11a --- /dev/null +++ b/tests/unittests/data/teststaticnewlinebraces.h @@ -0,0 +1,71 @@ +/** + * @file condvar.h + * @brief Condition variable synchronization primitive. + * @author plutoo + * @copyright libnx Authors + */ +#pragma once +#include "../types.h" +#include "../kernel/mutex.h" + +/// Condition variable structure. +typedef struct { + u32 tag; + Mutex* mutex; +} CondVar; + +/** + * @brief Initializes a condition variable. + * @param[in] c Condition variable object. + * @param[in] m Mutex object to use inside the condition variable. + */ +void condvarInit(CondVar* c, Mutex* m); + +/** + * @brief Waits on a condition variable with a timeout. + * @param[in] c Condition variable object. + * @param[in] timeout Timeout in nanoseconds. + * @return Result code (0xEA01 on timeout). + * @remark On function return, the underlying mutex is acquired. + */ +Result condvarWaitTimeout(CondVar* c, u64 timeout); + +/** + * @brief Waits on a condition variable. + * @param[in] c Condition variable object. + * @return Result code. + * @remark On function return, the underlying mutex is acquired. + */ +static inline Result condvarWait(CondVar* c) +{ + return condvarWaitTimeout(c, -1ull); +} + +/** + * @brief Wakes up up to the specified number of threads waiting on a condition variable. + * @param[in] c Condition variable object. + * @param[in] num Maximum number of threads to wake up (or -1 to wake them all up). + * @return Result code. + */ +Result condvarWake(CondVar* c, int num); + +/** + * @brief Wakes up a single thread waiting on a condition variable. + * @param[in] c Condition variable object. + * @return Result code. + */ +static inline Result condvarWakeOne(CondVar* c) +{ + return condvarWake(c, 1); +} + +/** + * @brief Wakes up all thread waiting on a condition variable. + * @param[in] c Condition variable object. + * @return Result code. + */ +static inline Result condvarWakeAll(CondVar* c) +{ + return condvarWake(c, -1); +} + diff --git a/tests/unittests/data/teststaticnewlinebracesreadded.h b/tests/unittests/data/teststaticnewlinebracesreadded.h new file mode 100644 index 0000000..176f4fc --- /dev/null +++ b/tests/unittests/data/teststaticnewlinebracesreadded.h @@ -0,0 +1,68 @@ +/** + * @file condvar.h + * @brief Condition variable synchronization primitive. + * @author plutoo + * @copyright libnx Authors + */ +#pragma once +#include "../types.h" +#include "../kernel/mutex.h" + +/// Condition variable structure. +typedef struct { + u32 tag; + Mutex* mutex; +} CondVar; + +/** + * @brief Initializes a condition variable. + * @param[in] c Condition variable object. + * @param[in] m Mutex object to use inside the condition variable. + */ +void condvarInit(CondVar* c, Mutex* m); + +/** + * @brief Waits on a condition variable with a timeout. + * @param[in] c Condition variable object. + * @param[in] timeout Timeout in nanoseconds. + * @return Result code (0xEA01 on timeout). + * @remark On function return, the underlying mutex is acquired. + */ +Result condvarWaitTimeout(CondVar* c, u64 timeout); + +/** + * @brief Waits on a condition variable. + * @param[in] c Condition variable object. + * @return Result code. + * @remark On function return, the underlying mutex is acquired. + */ +static inline Result condvarWait(CondVar* c) { + return condvarWaitTimeout(c, -1ull); +} + +/** + * @brief Wakes up up to the specified number of threads waiting on a condition variable. + * @param[in] c Condition variable object. + * @param[in] num Maximum number of threads to wake up (or -1 to wake them all up). + * @return Result code. + */ +Result condvarWake(CondVar* c, int num); + +/** + * @brief Wakes up a single thread waiting on a condition variable. + * @param[in] c Condition variable object. + * @return Result code. + */ +static inline Result condvarWakeOne(CondVar* c) { + return condvarWake(c, 1); +} + +/** + * @brief Wakes up all thread waiting on a condition variable. + * @param[in] c Condition variable object. + * @return Result code. + */ +static inline Result condvarWakeAll(CondVar* c) { + return condvarWake(c, -1); +} + diff --git a/tests/unittests/nim.cfg b/tests/unittests/nim.cfg new file mode 100644 index 0000000..e9b9a2e --- /dev/null +++ b/tests/unittests/nim.cfg @@ -0,0 +1 @@ +--path="../../src" diff --git a/tests/unittests/testfileops.nim b/tests/unittests/testfileops.nim new file mode 100644 index 0000000..2fff387 --- /dev/null +++ b/tests/unittests/testfileops.nim @@ -0,0 +1,211 @@ +import nimgen/fileops, common, regex, os + +import unittest + +let testFileContent = """ +this is text +this is text +replace me +prepend me +end +""" + +let prependMiddleExpected = """ +this is text +this is text +replace me +prepended data +prepend me +end +""" + +let prependEndExpected = """ +this is text +this is text +replace me +prepend me +data +end +""" + +let appendEndExpected = """ +this is text +this is text +replace me +prepend me +end +data +""" + +let appendMiddleExpected = """ +this is data + text +this is text +replace me +prepend me +end +""" + +let freplaceDefaultExpected = """ + + +replace me +prepend me +end +""" + +let freplaceWithExpected = """ +this is text +this is text +foobar +prepend me +end +""" + +let freplaceRegexExpected = """ +foobar +foobar +replace me +prepend me +end +""" + +let commentExpected = """ +this is text +this is text +//replace me +//prepend me +//end +""" + +let commentMiddleExpected = """ +this //is text +//this is text +replace me +prepend me +end +""" + + +let dataDir = currentSourcePath().splitPath().head / "data" + +let testfilename = dataDir / "testing.txt" + + +suite "test file ops": + if not dataDir.dirExists(): + dataDir.createDir() + + setup: + if testfilename.existsFile(): + removeFile(testfilename) + writeFile(testfilename, testFileContent) + + ################### Prepend ####################### + + test "prepend at beginning of file": + prepend(testfilename, "data\n") + let expected = "data\n" & testFileContent + testfilename.checkFile(expected) + + test "prepend at middle of file": + prepend(testfilename, "prepended data\n", "prepend me") + testfilename.checkFile(prependMiddleExpected) + + test "prepend at end of file": + prepend(testfilename, "data\n", "end\n") + testfilename.checkFile(prependEndExpected) + + ################### Pipe ######################### + + test "pipe command into file": + when defined(windows): + pipe(testfilename, "(ECHO foo)>>$file") + testfilename.checkFile("foo") + else: + pipe(testfilename, "cat $file | grep 'this is text'") + testfilename.checkFile("this is text\nthis is text") + + ################# Append ######################### + + test "append file end": + append(testfilename, "data\n") + testfilename.checkFile(appendEndExpected) + + test "append file middle": + append(testfilename, " data\n", "this is") + testfilename.checkFile(appendMiddleExpected) + + ################# FReplace ######################### + + test "freplace default empty": + freplace(testfilename, "this is text") + testfilename.checkFile(freplaceDefaultExpected) + + test "freplace with content": + freplace(testfilename, "replace me", "foobar") + testfilename.checkFile(freplaceWithExpected) + + test "freplace regex": + freplace(testfilename, re"this .*", "foobar") + testfilename.checkFile(freplaceRegexExpected) + + ####################### Comment ###################### + + test "comment": + comment(testfilename, "replace me", "3") + testfilename.checkFile(commentExpected) + + test "comment over length": + comment(testfilename, "replace me", "10") + testfilename.checkFile(commentExpected) + + test "comment negative": + comment(testfilename, "replace me", "-3") + testfilename.checkFile(testFileContent) + + test "comment zero": + comment(testfilename, "replace me", "0") + testfilename.checkFile(testFileContent) + + test "comment middle": + comment(testfilename, "is text", "2") + testfilename.checkFile(commentMiddleExpected) + + ############### Static inline removal ################ + + test "replace static inline with front braces at end of line": + + let + file = dataDir / "teststaticfrontbraces.h" + resFile = dataDir / "teststaticexpectedfrontbraces.h" + + test = readFile(file) + expected = readFile(resFile) + + writeFile(testfilename, test) + + removeStatic(testfilename) + testfilename.checkFile(expected) + + reAddStatic(testfilename) + testfilename.checkFile(test) + + test "replace static inline with newline before brace": + + let + file = dataDir / "teststaticnewlinebraces.h" + resFile = dataDir / "teststaticexpectednewlinebraces.h" + reAddedFile = dataDir / "teststaticnewlinebracesreadded.h" + + test = readFile(file) + expected = readFile(resFile) + reAdded = readFile(reAddedFile) + + writeFile(testfilename, test) + + removeStatic(testfilename) + testfilename.checkFile(expected) + + reAddStatic(testfilename) + testfilename.checkFile(reAdded) -- cgit v1.2.3