forked from xuos/xiuos
				
			
		
			
				
	
	
		
			245 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			245 lines
		
	
	
		
			12 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 "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 delayed : 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;
 | 
						|
};
 | 
						|
 | 
						|
/* [header, ipc_arg_buffer_len[], ipc_arg_buffer[]] */
 | 
						|
struct IpcMsg {
 | 
						|
    ipc_msg_header header;
 | 
						|
    uintptr_t buf[];
 | 
						|
};
 | 
						|
enum {
 | 
						|
    IPC_ARG_INFO_BASE_OFFSET = sizeof(ipc_msg_header),
 | 
						|
};
 | 
						|
 | 
						|
typedef int (*IpcInterface)(struct IpcMsg* msg);
 | 
						|
 | 
						|
struct IpcNode {
 | 
						|
    char* name;
 | 
						|
    IpcInterface interfaces[UINT8_MAX];
 | 
						|
};
 | 
						|
 | 
						|
#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)
 | 
						|
{
 | 
						|
    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_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;                                                                                                                            \
 | 
						|
    }
 | 
						|
 | 
						|
bool is_cur_session_delayed(void);
 | 
						|
#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);    \
 | 
						|
        if (!is_cur_session_delayed()) {                \
 | 
						|
            ipc_msg_set_return(msg, &_ret);             \
 | 
						|
            msg->header.done = 1;                       \
 | 
						|
        }                                               \
 | 
						|
        return 0;                                       \
 | 
						|
    }
 | 
						|
 | 
						|
int cur_session_id(void);
 | 
						|
void delay_session(void); |