diff --git a/lib/libxpc/CMakeLists.txt b/lib/libxpc/CMakeLists.txt new file mode 100644 index 0000000..e151627 --- /dev/null +++ b/lib/libxpc/CMakeLists.txt @@ -0,0 +1,28 @@ +file(GLOB sources + ${CMAKE_CURRENT_SOURCE_DIR}/*.c) +file(GLOB headers + ${CMAKE_CURRENT_SOURCE_DIR}/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/xpc/*.h) + +set(public_include_dirs + ${CMAKE_CURRENT_SOURCE_DIR}/include) + +rosetta_add_library( + NAME libxpc SHARED STATIC + PUBLIC_INCLUDE_DIRS ${public_include_dirs} + SOURCES ${sources} + HEADERS ${headers}) + +sysroot_add_library( + NAME libxpc + HEADER_DIR /usr/include + LIB_DIR /usr/lib) +sysroot_add_library( + NAME libxpc-static + HEADER_DIR /usr/include + LIB_DIR /usr/lib) + +target_link_libraries(libxpc libmango libc) +target_link_libraries(libxpc-static libmango libc-core) + +#set_target_properties(libxpc-static PROPERTIES POSITION_INDEPENDENT_CODE FALSE) diff --git a/lib/libxpc/buffer.c b/lib/libxpc/buffer.c new file mode 100644 index 0000000..2999213 --- /dev/null +++ b/lib/libxpc/buffer.c @@ -0,0 +1,59 @@ +#include +#include +#include + +kern_status_t xpc_buffer_read( + const xpc_buffer_t *buf, + void *out, + size_t max, + size_t *nr_read) +{ + if ((buf->buf_flags & (XPC_BUFFER_F_IN | XPC_BUFFER_F_REMOTE)) + != (XPC_BUFFER_F_IN | XPC_BUFFER_F_REMOTE)) { + return KERN_BAD_STATE; + } + + size_t to_read = max; + if (to_read > buf->buf_len) { + to_read = buf->buf_len; + } + + kern_status_t status + = xpc_msg_read(buf->buf_origin, buf->buf_offset, out, to_read); + if (status != KERN_OK) { + return status; + } + + /* TODO */ + *nr_read = to_read; + + return KERN_OK; +} + +kern_status_t xpc_buffer_write( + xpc_buffer_t *buf, + const void *in, + size_t len, + size_t *nr_written) +{ + if ((buf->buf_flags & (XPC_BUFFER_F_OUT | XPC_BUFFER_F_REMOTE)) + != (XPC_BUFFER_F_OUT | XPC_BUFFER_F_REMOTE)) { + return KERN_BAD_STATE; + } + + size_t to_write = len; + if (to_write > buf->buf_max) { + to_write = buf->buf_max; + } + + kern_status_t status + = xpc_msg_write(buf->buf_origin, buf->buf_offset, in, to_write); + if (status != KERN_OK) { + return status; + } + + /* TODO */ + *nr_written = to_write; + + return KERN_OK; +} diff --git a/lib/libxpc/include/xpc/buffer.h b/lib/libxpc/include/xpc/buffer.h new file mode 100644 index 0000000..5ace79e --- /dev/null +++ b/lib/libxpc/include/xpc/buffer.h @@ -0,0 +1,86 @@ +#ifndef XPC_BUFFER_H_ +#define XPC_BUFFER_H_ + +#include +#include + +#define XPC_BUFFER_IN(msg, offset, size) \ + { \ + .buf_flags = XPC_BUFFER_F_IN | XPC_BUFFER_F_REMOTE, \ + .buf_origin = (msg), \ + .buf_offset = (offset), \ + .buf_len = (size), \ + } +#define XPC_BUFFER_OUT(msg, offset, size) \ + { \ + .buf_flags = XPC_BUFFER_F_OUT | XPC_BUFFER_F_REMOTE, \ + .buf_origin = (msg), \ + .buf_offset = (offset), \ + .buf_len = (size), \ + } + +struct xpc_msg; + +typedef enum xpc_buffer_flags { + /* the buffer can be read from */ + XPC_BUFFER_F_IN = 0x01u, + /* the buffer can be written to */ + XPC_BUFFER_F_OUT = 0x02u, + /* the buffer is backed by a buffer located in another address space. + * the buffer can only be accessed via xpc_buffer_read and/or + * xpc_buffer_write */ + XPC_BUFFER_F_REMOTE = 0x04u, + /* free the buffer backing this buffer when the buffer is discarded. + * this is only used for out-buffers. the buffer must have been + * allocated using xpc_context_alloc, as it will be freed via a call + * to xpc_context_free */ + XPC_BUFFER_F_FREE_ON_DISCARD = 0x08u, +} xpc_buffer_flags_t; + +typedef struct xpc_buffer { + xpc_buffer_flags_t buf_flags; + union { + /* fields that are only valid if F_OUT is set */ + struct { + /* only valid if F_OUT is set. specifies the maximum + * number of chars that can be written to buf_buf, + * including the null terminator. */ + size_t buf_max; + /* only valid if F_OUT is set. + * if F_FREE_ON_DISCARD is set, must be either NULL or + * allocated via xpc_context_alloc */ + const char *buf_ptr; + }; + + /* fields that are only valid if F_IN is set */ + struct { + /* only valid if F_IN is set. offset of the buffer data + * within the associated message. used when reading + * buffer data from a message. */ + size_t buf_offset; + }; + }; + + /* only valid if F_REMOTE is set. + * used to read/write buffer data from/to the sender's address + * space. */ + const struct xpc_msg *buf_origin; + + /* valid for both F_IN and F_OUT buffers. + * F_IN: specifies the length of the incoming buffer data. + * F_OUT: specifies how many bytes from buf_ptr to send. */ + size_t buf_len; +} xpc_buffer_t; + +extern xpc_status_t xpc_buffer_read( + const xpc_buffer_t *s, + void *out, + size_t max, + size_t *nr_read); +extern xpc_status_t xpc_buffer_write( + xpc_buffer_t *s, + const void *in, + size_t len, + size_t *nr_written); + +#endif diff --git a/lib/libxpc/include/xpc/context.h b/lib/libxpc/include/xpc/context.h new file mode 100644 index 0000000..487cefa --- /dev/null +++ b/lib/libxpc/include/xpc/context.h @@ -0,0 +1,8 @@ +#ifndef XPC_CONTEXT_H_ +#define XPC_CONTEXT_H_ + +typedef struct xpc_context { + +} xpc_context_t; + +#endif diff --git a/lib/libxpc/include/xpc/endpoint.h b/lib/libxpc/include/xpc/endpoint.h new file mode 100644 index 0000000..857dfcf --- /dev/null +++ b/lib/libxpc/include/xpc/endpoint.h @@ -0,0 +1,13 @@ +#ifndef XPC_ENDPOINT_H_ +#define XPC_ENDPOINT_H_ + +#include + +typedef struct xpc_endpoint { + kern_handle_t e_channel; + tid_t e_task; + koid_t e_port; + msgid_t e_msg; +} xpc_endpoint_t; + +#endif diff --git a/lib/libxpc/include/xpc/msg.h b/lib/libxpc/include/xpc/msg.h new file mode 100644 index 0000000..6bcae28 --- /dev/null +++ b/lib/libxpc/include/xpc/msg.h @@ -0,0 +1,53 @@ +#ifndef XPC_MSG_H_ +#define XPC_MSG_H_ + +#include +#include +#include +#include + +#define XPC_MSG_MAGIC 0x5850434D + +typedef struct xpc_msg_header { + uint32_t hdr_magic; + uint32_t hdr_interface; + uint16_t hdr_func; + uint16_t hdr_status; +} xpc_msg_header_t; + +typedef struct xpc_msg { + xpc_endpoint_t msg_sender; + xpc_msg_header_t msg_header; + size_t msg_handles_count; + + kern_msg_handle_t msg_handles[KERN_MSG_MAX_HANDLES]; +} xpc_msg_t; + +extern void xpc_msg_header_init( + xpc_msg_header_t *msg, + unsigned long interface, + unsigned short func); +extern bool xpc_msg_header_validate(const xpc_msg_header_t *msg); +extern kern_status_t xpc_msg_recv(kern_handle_t channel, xpc_msg_t *out); +extern kern_status_t xpc_msg_read( + const xpc_msg_t *msg, + size_t offset, + void *p, + size_t count); +extern kern_status_t xpc_msg_write( + const xpc_msg_t *msg, + size_t offset, + const void *p, + size_t count); + +extern kern_status_t xpc_msg_reply( + const xpc_msg_t *msg, + kern_iovec_t *iov, + size_t iov_count, + kern_msg_handle_t *handles, + size_t handle_count); +extern kern_status_t xpc_msg_reply_error( + const xpc_msg_t *msg, + unsigned short code); + +#endif diff --git a/lib/libxpc/include/xpc/status.h b/lib/libxpc/include/xpc/status.h new file mode 100644 index 0000000..3ead2f5 --- /dev/null +++ b/lib/libxpc/include/xpc/status.h @@ -0,0 +1,12 @@ +#ifndef XPC_STATUS_H_ +#define XPC_STATUS_H_ + +typedef enum xpc_status { + XPC_SUCCESS = 0, + XPC_ERR_BAD_STATE, + XPC_ERR_INVALID_ARGUMENT, + XPC_ERR_NO_MEMORY, + XPC_ERR_MEMORY_FAULT, +} xpc_status_t; + +#endif diff --git a/lib/libxpc/include/xpc/string.h b/lib/libxpc/include/xpc/string.h new file mode 100644 index 0000000..b1b746e --- /dev/null +++ b/lib/libxpc/include/xpc/string.h @@ -0,0 +1,85 @@ +#ifndef XPC_STRING_H_ +#define XPC_STRING_H_ + +#include +#include + +#define XPC_STRING_NPOS ((size_t)-1) + +#define XPC_STRING_IN(msg, offset, size) \ + { \ + .s_flags = XPC_STRING_F_IN | XPC_STRING_F_REMOTE, \ + .s_origin = (msg), \ + .s_offset = (offset), \ + .s_len = (size), \ + } +#define XPC_STRING_OUT(msg, offset, size) \ + { \ + .s_flags = XPC_STRING_F_OUT | XPC_STRING_F_REMOTE, \ + .s_origin = (msg), \ + .s_offset = (offset), \ + .s_len = (size), \ + } + +struct xpc_msg; + +typedef enum xpc_string_flags { + /* the string can be read from */ + XPC_STRING_F_IN = 0x01u, + /* the string can be written to */ + XPC_STRING_F_OUT = 0x02u, + /* the string is backed by a buffer located in another address space. + * the string can only be accessed via xpc_string_read and/or + * xpc_string_write */ + XPC_STRING_F_REMOTE = 0x04u, + /* free the buffer backing this string when the string is discarded. + * this is only used for out-strings. the buffer must have been + * allocated using xpc_context_alloc, as it will be freed via a call + * to xpc_context_free */ + XPC_STRING_F_FREE_ON_DISCARD = 0x08u, +} xpc_string_flags_t; + +typedef struct xpc_string { + xpc_string_flags_t s_flags; + union { + struct { + /* only valid if F_OUT is set. specifies the maximum + * number of chars that can be written to s_buf, + * including the null terminator. */ + size_t s_max; + /* only valid if F_OUT is set. + * if F_FREE_ON_DISCARD is set, must be either NULL or + * allocated via xpc_context_alloc */ + const char *s_buf; + }; + + struct { + /* only valid if F_IN is set. offset of the string data + * within the associated message. used when reading + * string data from a message. */ + size_t s_offset; + }; + }; + + /* only valid if F_REMOTE is set. + * used to read/write string data from/to the sender's address space. */ + const struct xpc_msg *s_origin; + + /* valid for both F_IN and F_OUT strings. + * F_IN: specifies the length of the incoming string data. + * F_OUT: specifies how many characters from s_buf to send. */ + size_t s_len; +} xpc_string_t; + +extern xpc_status_t xpc_string_read( + const xpc_string_t *s, + char *out, + size_t max, + size_t *nr_read); +extern xpc_status_t xpc_string_write( + xpc_string_t *s, + const char *in, + size_t len, + size_t *nr_written); + +#endif diff --git a/lib/libxpc/msg.c b/lib/libxpc/msg.c new file mode 100644 index 0000000..6d92428 --- /dev/null +++ b/lib/libxpc/msg.c @@ -0,0 +1,112 @@ +#include +#include +#include + +void xpc_msg_header_init( + xpc_msg_header_t *msg, + unsigned long interface, + unsigned short func) +{ + memset(msg, 0x0, sizeof *msg); + + msg->hdr_magic = XPC_MSG_MAGIC; + msg->hdr_interface = interface; + msg->hdr_func = func; + msg->hdr_status = 0; +} + +bool xpc_msg_header_validate(const xpc_msg_header_t *msg) +{ + return msg->hdr_magic == XPC_MSG_MAGIC; +} + +kern_status_t xpc_msg_recv(kern_handle_t channel, xpc_msg_t *out) +{ + kern_iovec_t iov = IOVEC(&out->msg_header, sizeof out->msg_header); + kern_msg_t msg = { + .msg_data = &iov, + .msg_data_count = 1, + .msg_handles = out->msg_handles, + .msg_handles_count = KERN_MSG_MAX_HANDLES, + }; + kern_status_t status = msg_recv(channel, &msg); + if (status != KERN_OK) { + return status; + } + + if (!xpc_msg_header_validate(&out->msg_header)) { + return KERN_INVALID_ARGUMENT; + } + + out->msg_sender.e_channel = channel; + out->msg_sender.e_task = msg.msg_sender; + out->msg_sender.e_port = msg.msg_endpoint; + out->msg_sender.e_msg = msg.msg_id; + + return KERN_OK; +} + +kern_status_t xpc_msg_read( + const xpc_msg_t *msg, + size_t offset, + void *p, + size_t count) +{ + kern_iovec_t iov = IOVEC(p, count); + size_t r = 0; + return msg_read( + msg->msg_sender.e_channel, + msg->msg_sender.e_msg, + offset, + &iov, + 1, + &r); +} + +kern_status_t xpc_msg_write( + const xpc_msg_t *msg, + size_t offset, + const void *p, + size_t count) +{ + kern_iovec_t iov = IOVEC(p, count); + size_t w = 0; + return msg_write( + msg->msg_sender.e_channel, + msg->msg_sender.e_msg, + offset, + &iov, + 1, + &w); +} + +kern_status_t xpc_msg_reply( + const xpc_msg_t *msg, + kern_iovec_t *iov, + size_t iov_count, + kern_msg_handle_t *handles, + size_t handle_count) +{ + kern_msg_t reply = MSG(iov, iov_count, handles, handle_count); + return msg_reply( + msg->msg_sender.e_channel, + msg->msg_sender.e_msg, + &reply); +} + +kern_status_t xpc_msg_reply_error(const xpc_msg_t *msg, unsigned short code) +{ + xpc_msg_header_t reply_data = { + .hdr_magic = XPC_MSG_MAGIC, + .hdr_interface = msg->msg_header.hdr_interface, + .hdr_func = msg->msg_header.hdr_func, + .hdr_status = code, + }; + + kern_iovec_t iov = IOVEC(&reply_data, sizeof reply_data); + kern_msg_t reply = MSG(&iov, 1, NULL, 0); + return msg_reply( + msg->msg_sender.e_channel, + msg->msg_sender.e_msg, + &reply); +} diff --git a/lib/libxpc/string.c b/lib/libxpc/string.c new file mode 100644 index 0000000..5c8c946 --- /dev/null +++ b/lib/libxpc/string.c @@ -0,0 +1,60 @@ +#include +#include +#include + +xpc_status_t xpc_string_read( + const xpc_string_t *s, + char *out, + size_t max, + size_t *nr_read) +{ + if ((s->s_flags & (XPC_STRING_F_IN | XPC_STRING_F_REMOTE)) + != (XPC_STRING_F_IN | XPC_STRING_F_REMOTE)) { + return KERN_BAD_STATE; + } + + size_t to_read = max - 1; + if (to_read > s->s_len) { + to_read = s->s_len; + } + + kern_status_t status + = xpc_msg_read(s->s_origin, s->s_offset, out, to_read); + if (status != KERN_OK) { + return status; + } + + out[to_read] = '\0'; + /* TODO */ + *nr_read = to_read; + + return KERN_OK; +} + +xpc_status_t xpc_string_write( + xpc_string_t *s, + const char *in, + size_t len, + size_t *nr_written) +{ + if ((s->s_flags & (XPC_STRING_F_OUT | XPC_STRING_F_REMOTE)) + != (XPC_STRING_F_IN | XPC_STRING_F_REMOTE)) { + return KERN_BAD_STATE; + } + + size_t to_write = len; + if (to_write > s->s_max - 1) { + to_write = s->s_max - 1; + } + + kern_status_t status + = xpc_msg_write(s->s_origin, s->s_offset, in, to_write); + if (status != KERN_OK) { + return status; + } + + /* TODO */ + *nr_written = to_write; + + return KERN_OK; +}