forked from xuos/xiuos
288 lines
14 KiB
C
288 lines
14 KiB
C
/*
|
|
* Copyright (c) 2020 AIIT XUOS Lab
|
|
* XiUOS is licensed under Mulan PSL v2.
|
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
* You may obtain a copy of Mulan PSL v2 at:
|
|
* http://license.coscl.org.cn/MulanPSL2
|
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
* See the Mulan PSL v2 for more details.
|
|
*/
|
|
/**
|
|
* @file libipc.h
|
|
* @brief libipc header
|
|
* @version 3.0
|
|
* @author AIIT XUOS Lab
|
|
* @date 2023.08.25
|
|
*/
|
|
|
|
/*************************************************
|
|
File name: libipc.h
|
|
Description: libipc header
|
|
Others:
|
|
History:
|
|
1. Date: 2023-08-28
|
|
Author: AIIT XUOS Lab
|
|
Modification:
|
|
1. first version
|
|
*************************************************/
|
|
#pragma once
|
|
|
|
#include <assert.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include "ipcargs.h"
|
|
#include "session.h"
|
|
|
|
#define NR_MAX_SESSION 32
|
|
#define IPC_MSG_MAGIC 0xABCDDCBA
|
|
|
|
typedef struct {
|
|
union {
|
|
uint64_t header;
|
|
struct {
|
|
uint64_t valid : 1; // for server to peek new msg
|
|
uint64_t done : 1; // for client to check request done
|
|
uint64_t init : 1; // for client to check request done
|
|
uint64_t handling : 1;
|
|
uint64_t nr_args : 4;
|
|
uint64_t opcode : 8;
|
|
uint64_t len : 16;
|
|
uint64_t magic : 32;
|
|
};
|
|
};
|
|
int32_t ret_val;
|
|
} __attribute__((packed)) ipc_msg_header;
|
|
|
|
struct IpcArgInfo {
|
|
uint16_t offset;
|
|
uint16_t len;
|
|
union {
|
|
uint16_t attr;
|
|
struct {
|
|
uint16_t null_ptr : 1;
|
|
uint16_t reserved : 15;
|
|
};
|
|
};
|
|
} __attribute__((packed));
|
|
|
|
/* [header, ipc_arg_buffer_len[], ipc_arg_buffer[]] */
|
|
struct IpcMsg {
|
|
ipc_msg_header header;
|
|
uintptr_t buf[];
|
|
} __attribute__((packed));
|
|
enum {
|
|
IPC_ARG_INFO_BASE_OFFSET = sizeof(ipc_msg_header),
|
|
};
|
|
|
|
typedef int (*IpcInterface)(struct IpcMsg* msg);
|
|
|
|
struct IpcNode {
|
|
char* name;
|
|
IpcInterface interfaces[UINT8_MAX];
|
|
} __attribute__((packed));
|
|
|
|
#define IPC_SERVER_LOOP(ipc_node_name) rpc_server_loop_##rpc_node_name
|
|
#define IPC_SERVICES(ipc_node_name, ...) \
|
|
typedef enum { \
|
|
ipc_node_name##_IPC_OP_CODE_DEFAULT = 0, \
|
|
__VA_ARGS__, \
|
|
ipc_node_name##_IPC_OP_CODE_END, \
|
|
} IpcOpCode_##ipc_node_name;
|
|
|
|
#define _IPC_INSERT_SERVE(interface) [interface] = IPC_SERVE(interface)
|
|
#define IPC_SERVER_REGISTER_INTERFACES(ipc_node_name, num_interfaces, ...) \
|
|
static struct IpcNode ipc_node_name = { \
|
|
.interfaces = { \
|
|
_VA_FRONT_WRAP_ARG##num_interfaces(_IPC_INSERT_SERVE, __VA_ARGS__), \
|
|
}, \
|
|
}
|
|
|
|
#define IPCSESSION_MSG(session) ((struct IpcMsg*)((char*)((session)->buf) + (session)->head))
|
|
#define IPCMSG_ARG_INFO_BASE(msg) ((struct IpcArgInfo*)((char*)msg + sizeof(ipc_msg_header)))
|
|
#define IPCMSG_ARG_INFO(msg, nth) ((struct IpcArgInfo*)((char*)IPCMSG_ARG_INFO_BASE(msg) + (nth * sizeof(struct IpcArgInfo))))
|
|
|
|
/// @brief get buffer of nth arg in ipc_msg
|
|
/// @param msg
|
|
/// @param arg_num start with 0 for first arg
|
|
/// @return
|
|
__attribute__((__always_inline__)) static inline void* ipc_msg_get_nth_arg_buf(struct IpcMsg* msg, int arg_num)
|
|
{
|
|
if (IPCMSG_ARG_INFO(msg, arg_num)->null_ptr == 1) {
|
|
return NULL;
|
|
}
|
|
|
|
return (void*)((char*)msg + IPCMSG_ARG_INFO(msg, arg_num)->offset);
|
|
}
|
|
|
|
/// @brief get return val of ipc call after done, used by client
|
|
/// @param msg
|
|
/// @param ret_val
|
|
__attribute__((__always_inline__)) static inline void ipc_msg_get_return(struct IpcMsg* msg, int32_t* ret_val)
|
|
{
|
|
assert(msg->header.done == 1);
|
|
memcpy(ret_val, &msg->header.ret_val, sizeof(msg->header.ret_val));
|
|
return;
|
|
}
|
|
|
|
/// @brief set return val of ipc call after serve, used by server
|
|
/// @param msg
|
|
/// @param ret_val
|
|
__attribute__((__always_inline__)) static inline void ipc_msg_set_return(struct IpcMsg* msg, int32_t* ret_val)
|
|
{
|
|
assert(msg->header.valid == 1);
|
|
memcpy(&msg->header.ret_val, ret_val, sizeof(msg->header.ret_val));
|
|
return;
|
|
}
|
|
|
|
/// @brief set opcode to msg
|
|
/// @param msg
|
|
/// @param opcode
|
|
/// @return
|
|
__attribute__((__always_inline__)) static inline bool ipc_msg_set_opcode(struct IpcMsg* msg, int opcode)
|
|
{
|
|
if (opcode < 0 || opcode > UINT8_MAX) {
|
|
return false;
|
|
}
|
|
msg->header.opcode = opcode;
|
|
return true;
|
|
}
|
|
|
|
/// @brief get opcode from msg
|
|
/// @param msg
|
|
/// @return opcode, -1 if opcode is invalid
|
|
__attribute__((__always_inline__)) static inline int ipc_msg_get_opcode(struct IpcMsg* msg)
|
|
{
|
|
assert(msg->header.valid == 1);
|
|
int opcode = msg->header.opcode;
|
|
if (opcode < 0 || opcode > UINT8_MAX) {
|
|
return -1;
|
|
}
|
|
return opcode;
|
|
}
|
|
|
|
/// @brief delete first msg in session
|
|
/// @param session
|
|
/// @return
|
|
__attribute__((__always_inline__)) static inline bool ipc_session_forward(struct Session* session)
|
|
{
|
|
struct IpcMsg* msg = IPCSESSION_MSG(session);
|
|
if (msg->header.init != 1) {
|
|
return false;
|
|
}
|
|
return session_free_buf(session, msg->header.len);
|
|
}
|
|
|
|
struct IpcMsg* new_ipc_msg(struct Session* session, const int argc, const int* arg_size);
|
|
bool ipc_msg_set_nth_arg(struct IpcMsg* msg, const int arg_num, const void* const data, const int len);
|
|
bool ipc_msg_get_nth_arg(struct IpcMsg* msg, const int arg_num, void* data, const int len);
|
|
void ipc_msg_send_wait(struct IpcMsg* msg);
|
|
void ipc_msg_send_nowait(struct IpcMsg* msg);
|
|
int ipc_session_wait(struct Session* session);
|
|
|
|
void ipc_server_loop(struct IpcNode* ipc_node);
|
|
|
|
#define IPC_CREATE_MSG_FUNC(ipc_name) ipc_msg_##ipc_name
|
|
#define IPC_MSG_ARGS_COPY_SET_FUNC(ipc_name) ipc_msg_args_copy_set_##ipc_name
|
|
#define IPC_MSG_ARGS_COPY_GET_FUNC(ipc_name) ipc_msg_args_copy_get_##ipc_name
|
|
#define IPC_CALL(ipc_name) ipc_call_copy_args_##ipc_name
|
|
|
|
#define IPC_SERVE(ipc_name) ipc_serve_##ipc_name
|
|
#define IPC_THREAD_SERVE(ipc_name) ipc_thread_serve_##ipc_name
|
|
#define IPC_DO_SERVE_FUNC(ipc_name) ipc_do_serve_##ipc_name
|
|
|
|
/// when defining a ipc server:
|
|
/// 1. requires a IPC_SERVICES(server_name, interface_name, ...) to announce the name and interfaces that the server will support
|
|
/// 2. implement IPC_DO_SERVE_FUNC(interface_name) for each interface
|
|
/// 3. use IPC_SERVER_INTERFACE(interface_name, argc) to generate necessary helper functions
|
|
/// 4. use IPC_SERVER_REGISTER_INTERFACES(server_name, nr_interfaces, interface_names, ...) to bind interfaces to server after implementations
|
|
/// 5. use ipc_server_loop in main()
|
|
/// Refer to simple_service.h, simple_service.c and simple_server.c for example
|
|
|
|
#define IPC_INTERFACE(ipc_name, argc, ...) \
|
|
__always_inline static inline struct IpcMsg* IPC_CREATE_MSG_FUNC(ipc_name)(struct Session * session, _VA_FRONT_PTR_ARG##argc(__VA_ARGS__)) \
|
|
{ \
|
|
int argv[argc] = { _VA_LAST_ARG##argc(__VA_ARGS__) }; \
|
|
return new_ipc_msg(session, argc, argv); \
|
|
} \
|
|
\
|
|
__always_inline static inline bool IPC_MSG_ARGS_COPY_SET_FUNC(ipc_name)(struct IpcMsg * msg, _VA_FRONT_PTR_ARG##argc(__VA_ARGS__)) \
|
|
{ \
|
|
IPC_MSG_SET_ARG##argc(msg, __VA_ARGS__); \
|
|
return true; \
|
|
} \
|
|
\
|
|
__always_inline static inline bool IPC_MSG_ARGS_COPY_GET_FUNC(ipc_name)(struct IpcMsg * msg, _VA_FRONT_PTR_ARG##argc(__VA_ARGS__)) \
|
|
{ \
|
|
IPC_MSG_GET_ARG##argc(msg, __VA_ARGS__); \
|
|
return true; \
|
|
} \
|
|
\
|
|
static int IPC_CALL(ipc_name)(struct Session * session, _VA_FRONT_PTR_ARG##argc(__VA_ARGS__)) \
|
|
{ \
|
|
if (session == NULL) { \
|
|
return -1; \
|
|
} \
|
|
struct IpcMsg* msg = IPC_CREATE_MSG_FUNC(ipc_name)(session, _VA_FRONT_ARG##argc(__VA_ARGS__)); \
|
|
int ret = IPC_MSG_ARGS_COPY_SET_FUNC(ipc_name)(msg, _VA_FRONT_ARG##argc(__VA_ARGS__)); \
|
|
ret = ipc_msg_set_opcode(msg, ipc_name); \
|
|
ipc_msg_send_wait(msg); \
|
|
ret = IPC_MSG_ARGS_COPY_GET_FUNC(ipc_name)(msg, _VA_FRONT_ARG##argc(__VA_ARGS__)); \
|
|
int32_t res = 0; \
|
|
ipc_msg_get_return(msg, &res); \
|
|
ipc_session_forward(session); \
|
|
return res; \
|
|
}
|
|
|
|
#define IPC_SERVER_INTERFACE(ipc_name, argc) \
|
|
static int IPC_SERVE(ipc_name)(struct IpcMsg * msg) \
|
|
{ \
|
|
void* argv[argc]; \
|
|
for (int i = 0; i < argc; i++) { \
|
|
argv[i] = ipc_msg_get_nth_arg_buf(msg, i); \
|
|
} \
|
|
int32_t _ret = IPC_DO_SERVE##argc(ipc_name); \
|
|
ipc_msg_set_return(msg, &_ret); \
|
|
msg->header.done = 1; \
|
|
return 0; \
|
|
}
|
|
|
|
void _ipc_addr_to_buf(uintptr_t addr, char buf[17]);
|
|
uintptr_t _ipc_buf_to_addr(char* buf);
|
|
|
|
#define IPC_SERVER_THREAD_INTERFACE(ipc_name, argc) \
|
|
static int IPC_THREAD_SERVE(ipc_name)(int ipc_argc, char** ipc_argv) \
|
|
{ \
|
|
if (ipc_argc != 2) { \
|
|
printf("[%s] Error server thread creation.\n", __func__); \
|
|
exit(1); \
|
|
return -1; \
|
|
} \
|
|
struct IpcMsg* msg = (struct IpcMsg*)_ipc_buf_to_addr(ipc_argv[1]); \
|
|
void* argv[argc]; \
|
|
for (int i = 0; i < argc; i++) { \
|
|
argv[i] = ipc_msg_get_nth_arg_buf(msg, i); \
|
|
} \
|
|
int32_t _ret = IPC_DO_SERVE##argc(ipc_name); \
|
|
ipc_msg_set_return(msg, &_ret); \
|
|
msg->header.done = 1; \
|
|
exit(0); \
|
|
return 0; \
|
|
} \
|
|
static int IPC_SERVE(ipc_name)(struct IpcMsg * msg) \
|
|
{ \
|
|
char addr_buf[17]; \
|
|
_ipc_addr_to_buf((uintptr_t)msg, addr_buf); \
|
|
char* param[] = { #ipc_name, addr_buf, NULL }; \
|
|
int tid = thread(IPC_THREAD_SERVE(ipc_name), #ipc_name, param); \
|
|
if (tid > 0) { \
|
|
msg->header.handling = 1; \
|
|
} \
|
|
return 0; \
|
|
}
|
|
|
|
int cur_session_id(void); |