Add tests for fileops

This commit is contained in:
Joey Yakimowich-Payne 2018-07-14 10:24:53 +09:00
commit de4cb3f783
10 changed files with 1783 additions and 0 deletions

View file

@ -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"

8
tests/rununittests.nim Normal file
View file

@ -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()

View file

@ -0,0 +1,5 @@
import unittest
proc checkFile*(filepath, expected: string) =
let result = readFile(filepath)
check result == expected

View file

@ -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; i<cmd->NumStaticIn; 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; i<cmd->NumStaticOut; 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; i<cmd->NumStaticOut; 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; i<num_handles; i++)
// {
// r->Handles[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; i<num_statics; i++, buf+=2) {
// IpcStaticSendDescriptor* desc = (IpcStaticSendDescriptor*) buf;
// u64 packed = (u64) desc->Packed;
//
// 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; i<num_bufs; i++, buf+=3) {
// IpcBufferDescriptor* desc = (IpcBufferDescriptor*) buf;
// u64 packed = (u64) desc->Packed;
//
// 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
//}
///@}

View file

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

View file

@ -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; i<cmd->NumStaticIn; 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; i<cmd->NumStaticOut; 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; i<cmd->NumStaticOut; 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; i<num_handles; i++)
{
r->Handles[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; i<num_statics; i++, buf+=2) {
IpcStaticSendDescriptor* desc = (IpcStaticSendDescriptor*) buf;
u64 packed = (u64) desc->Packed;
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; i<num_bufs; i++, buf+=3) {
IpcBufferDescriptor* desc = (IpcBufferDescriptor*) buf;
u64 packed = (u64) desc->Packed;
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
}
///@}

View file

@ -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);
}

View file

@ -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);
}

1
tests/unittests/nim.cfg Normal file
View file

@ -0,0 +1 @@
--path="../../src"

View file

@ -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)