Trusty API 参考

Trusty 提供用于开发两类应用程序/服务的 API:

  • 在 TEE 处理器上运行的可信应用程序或服务
  • 在主处理器上运行并使用受信任应用程序提供的服务的正常/不受信任的应用程序

Trusty API 一般描述 Trusty 进程间通信 (IPC) 系统,包括与非安全世界的通信。主处理器上运行的软件可以使用 Trusty API 连接到受信任的应用程序/服务,并与它们交换任意消息,就像通过 IP 的网络服务一样。由应用程序使用应用程序级协议来确定这些消息的数据格式和语义。消息的可靠传递由底层 Trusty 基础设施(以在主处理器上运行的驱动程序的形式)保证,并且通信是完全异步的。

端口和通道

Trusty 应用程序使用端口以客户端连接的命名路径的形式公开服务端点。这提供了一个简单的、基于字符串的服务 ID 供客户端使用。命名约定是反向 DNS 风格的命名,例如com.google.servicename

当客户端连接到端口时,客户端会收到用于与服务交互的通道。该服务必须接受传入连接,当它接受时,它也会接收一个通道。本质上,端口用于查找服务,然后通过一对连接的通道(即端口上的连接实例)进行通信。当客户端连接到端口时,就会建立对称的双向连接。使用这种全双工路径,客户端和服务器可以交换任意消息,直到任何一方决定断开连接。

只有安全端可信应用程序或 Trusty 内核模块才能创建端口。在非安全端(正常情况下)运行的应用程序只能连接到安全端发布的服务。

根据要求,可信应用程序可以同时充当客户端和服务器。发布服务(作为服务器)的可信应用程序可能需要连接到其他服务(作为客户端)。

处理API

句柄是表示端口和通道等资源的无符号整数,类似于 UNIX 中的文件描述符。创建句柄后,它们将被放置到特定于应用程序的句柄表中,并可供以后引用。

调用者可以使用set_cookie()方法将私有数据与句柄关联起来。

Handle API 中的方法

句柄仅在应用程序上下文中有效。除非明确指定,否则应用程序不应将句柄的值传递给其他应用程序。句柄值只能通过与INVALID_IPC_HANDLE #define,应用程序可以将其用作句柄无效或未设置的指示。

将调用者提供的私有数据与指定的句柄相关联。

long set_cookie(uint32_t handle, void *cookie)

[in] handle :由 API 调用之一返回的任何句柄

[in] cookie :指向 Trusty 应用程序中任意用户空间数据的指针

[retval]:成功时为NO_ERROR ,否则< 0错误代码

此调用对于处理在创建句柄后稍后发生的事件非常有用。事件处理机制将句柄及其 cookie 提供回事件处理程序。

可以使用wait()调用来等待句柄发生事件。

等待()

在指定的时间段内等待给定句柄上发生事件。

long wait(uint32_t handle_id, uevent_t *event, unsigned long timeout_msecs)

[in] handle_id :API 调用之一返回的任何句柄

[out] event :指向表示此句柄上发生的事件的结构的指针

[in] timeout_msecs :以毫秒为单位的超时值;值 -1 是无限超时

[retval]:如果在指定的超时间隔内发生有效事件,则为NO_ERRORERR_TIMED_OUT如果指定的超时已过但没有发生事件; < 0表示其他错误

成功后( retval == NO_ERROR ), wait()调用将使用有关发生的事件的信息填充指定的uevent_t结构。

typedef struct uevent {
    uint32_t handle; /* handle this event is related to */
    uint32_t event;  /* combination of IPC_HANDLE_POLL_XXX flags */
    void    *cookie; /* cookie associated with this handle */
} uevent_t;

event字段包含以下值的组合:

enum {
  IPC_HANDLE_POLL_NONE    = 0x0,
  IPC_HANDLE_POLL_READY   = 0x1,
  IPC_HANDLE_POLL_ERROR   = 0x2,
  IPC_HANDLE_POLL_HUP     = 0x4,
  IPC_HANDLE_POLL_MSG     = 0x8,
  IPC_HANDLE_POLL_SEND_UNBLOCKED = 0x10,
  … more values[TBD]
};

IPC_HANDLE_POLL_NONE - 没有事件实际挂起,调用者应该重新启动等待

IPC_HANDLE_POLL_ERROR - 发生了未指定的内部错误

IPC_HANDLE_POLL_READY - 取决于句柄类型,如下所示:

  • 对于端口,该值表示存在挂起的连接
  • 对于通道,该值表示已建立异步连接(请参阅connect()

以下事件仅与通道相关:

  • IPC_HANDLE_POLL_HUP - 表示通道已被对等方关闭
  • IPC_HANDLE_POLL_MSG - 表示该通道有待处理的消息
  • IPC_HANDLE_POLL_SEND_UNBLOCKED - 指示先前发送阻塞的调用者可能会尝试再次发送消息(有关详细信息,请参阅send_msg()的描述)

事件处理程序应准备好处理指定事件的组合,因为可能会同时设置多个位。例如,对于一个通道,可能有待处理的消息,并且连接同时被对等方关闭。

大多数事件都是粘性的。只要底层条件持续存在(例如,接收到所有挂起的消息并处理挂起的连接请求),它们就会持续存在。 IPC_HANDLE_POLL_SEND_UNBLOCKED事件是一个例外,该事件在读取时被清除,并且应用程序只有一次处理该事件的机会。

可以通过调用close()方法来销毁句柄。

关闭()

销毁与指定句柄关联的资源并将其从句柄表中删除。

long close(uint32_t handle_id);

[in] handle_id :要销毁的句柄

[retval]:如果成功则为0;否则为负错误

服务器API

服务器首先创建一个或多个代表其服务端点的命名端口。每个端口都由一个句柄表示。

服务器 API 中的方法

端口创建()

创建命名服务端口。

long port_create (const char *path, uint num_recv_bufs, size_t recv_buf_size,
uint32_t flags)

[in] path :端口的字符串名称(如上所述)。该名称在整个系统中应该是唯一的;尝试创建副本将会失败。

[in] num_recv_bufs :此端口上的通道可以预先分配的最大缓冲区数,以方便与客户端交换数据。对于双向传输的数据,缓冲区是单独计数的,因此此处指定 1 意味着预先分配 1 个发送缓冲区和 1 个接收缓冲区。一般来说,所需的缓冲区数量取决于客户端和服务器之间的高层协议约定。在非常同步的协议(发送消息,在发送另一个消息之前接收回复)的情况下,该数字可以小至 1。但如果客户端希望在出现回复之前发送多条消息(例如,一条消息作为序言,另一条消息作为实际命令),则该数字可能会更多。分配的缓冲区集是按通道分配的,因此两个单独的连接(通道)将具有单独的缓冲区集。

[in] recv_buf_size :上述缓冲区集中每个单独缓冲区的最大大小。该值与协议相关,并有效限制您可以与对等方交换的最大消息大小

[in] flags :指定附加端口行为的标志组合

该值应该是以下值的组合:

IPC_PORT_ALLOW_TA_CONNECT - 允许来自其他安全应用程序的连接

IPC_PORT_ALLOW_NS_CONNECT - 允许来自非安全世界的连接

[retval]:如果为非负数,则创建端口句柄;如果为负数,则为特定错误

然后,服务器使用wait()调用轮询传入连接的端口句柄列表。收到由uevent_t结构的event字段中设置的IPC_HANDLE_POLL_READY位指示的连接请求后,服务器应调用accept()来完成连接建立并创建一个通道(由另一个句柄表示),然后可以轮询该通道以获取传入消息。

接受()

接受传入连接并获取通道句柄。

long accept(uint32_t handle_id, uuid_t *peer_uuid);

[in] handle_id :代表客户端已连接的端口的句柄

[out] peer_uuid :指向uuid_t结构的指针,该结构将填充连接客户端应用程序的 UUID。如果连接源自非安全世界,它将被设置为全零

[retval]:服务器可以与客户端交换消息的通道(如果非负)的句柄(否则为错误代码)

客户端API

本节包含客户端 API 中的方法。

客户端 API 中的方法

连接()

启动与名称指定的端口的连接。

long connect(const char *path, uint flags);

[in] path : Trusty 应用程序发布的端口名称

[in] flags :指定附加的可选行为

[retval]:可以与服务器交换消息的通道句柄;如果为负则错误

如果未指定flagsflags参数设置为 0),则调用connect()会启动到指定端口的同步连接,如果该端口不存在,则立即返回错误,并创建一个块,直到服务器接受连接为止。

可以通过指定两个值的组合来更改此行为,如下所述:

enum {
IPC_CONNECT_WAIT_FOR_PORT = 0x1,
IPC_CONNECT_ASYNC = 0x2,
};

IPC_CONNECT_WAIT_FOR_PORT - 如果指定的端口在执行时不立即存在,则强制connect()调用等待,而不是立即失败。

IPC_CONNECT_ASYNC - 如果设置,则启动异步连接。在开始正常操作之前,应用程序必须轮询返回的句柄(通过调用wait()来获取由uevent_t结构的事件字段中设置的IPC_HANDLE_POLL_READY位指示的连接完成事件。

消息传递API

消息 API 调用允许通过先前建立的连接(通道)发送和读取消息。服务器和客户端的消息传递 API 调用是相同的。

如上所述,客户端通过发出connect()调用来接收通道句柄,而服务器则通过accept()调用获取通道句柄。

Trusty 消息的结构

如下所示,Trusty API 交换的消息具有最小的结构,让服务器和客户端就实际内容的语义达成一致:

/*
 *  IPC message
 */
typedef struct iovec {
        void   *base;
        size_t  len;
} iovec_t;

typedef struct ipc_msg {
        uint     num_iov; /* number of iovs in this message */
        iovec_t  *iov;    /* pointer to iov array */

        uint     num_handles; /* reserved, currently not supported */
        handle_t *handles;    /* reserved, currently not supported */
} ipc_msg_t;

一条消息可以由一个或多个由iovec_t结构数组表示的非连续缓冲区组成。 Trusty 使用iov数组对这些块执行分散-聚集读取和写入操作。 iov数组可以描述的缓冲区内容是完全任意的。

消息传递 API 中的方法

发送消息()

通过指定通道发送消息。

long send_msg(uint32_t handle, ipc_msg_t *msg);

[in] handle :发送消息的通道的句柄

[in] msg :指向描述消息的ipc_msg_t structure指针

[retval]:成功时发送的总字节数;否则为负错误

如果客户端(或服务器)尝试通过通道发送消息,并且目标对等消息队列中没有空间,则通道可能会进入发送阻塞状态(对于简单的同步请求/回复协议,这种情况永远不会发生)但在更复杂的情况下可能会发生),这通过返回ERR_NOT_ENOUGH_BUFFER错误代码来指示。在这种情况下,调用者必须等待,直到对等方通过检索处理和退出消息来释放其接收队列中的一些空间,这由wait()调用返回的uevent_t结构的event字段中设置的IPC_HANDLE_POLL_SEND_UNBLOCKED位指示。

获取消息()

获取有关传入消息队列中下一条消息的元信息

指定频道的。

long get_msg(uint32_t handle, ipc_msg_info_t *msg_info);

[in] handle :必须检索新消息的通道的句柄

[out] msg_info :消息信息结构描述如下:

typedef struct ipc_msg_info {
        size_t    len;  /* total message length */
        uint32_t  id;   /* message id */
} ipc_msg_info_t;

在未完成的消息集中,每条消息都被分配了一个唯一的 ID,并且填充了每条消息的总长度。如果协议配置并允许,则特定通道可以同时存在多个未完成(打开)的消息。

[retval]:成功时为NO_ERROR ;否则为负错误

读取消息()

从指定偏移量开始读取指定 ID 的消息内容。

long read_msg(uint32_t handle, uint32_t msg_id, uint32_t offset, ipc_msg_t
*msg);

[in] handle : 从中读取消息的通道的句柄

[in] msg_id : 要读取的消息的 ID

[in] offset : 开始读取消息的偏移量

[out] msg :指向ipc_msg_t结构的指针,该结构描述了一组用于存储传入消息数据的缓冲区

[retval]:成功时存储在msg缓冲区中的总字节数;否则为负错误

read_msg方法可以根据需要从不同的(不一定是顺序的)偏移量开始多次调用。

put_msg()

撤销具有指定 ID 的消息。

long put_msg(uint32_t handle, uint32_t msg_id);

[in] handle : 消息到达的通道的句柄

[in] msg_id :正在停用的消息的 ID

[retval]:成功时为NO_ERROR ;否则为负错误

消息退出并且其占用的缓冲区已被释放后,将无法访问消息内容。

文件描述符API

文件描述符 API 包括read()write()ioctl()调用。所有这些调用都可以对传统上由小数字表示的一组预定义(静态)文件描述符进行操作。在当前的实现中,文件描述符空间与IPC句柄空间是分开的。 Trusty 中的文件描述符 API 类似于传统的基于文件描述符的 API。

默认情况下,有 3 个预定义的(标准且众所周知的)文件描述符:

  • 0 - 标准输入。标准输入fd的默认实现是无操作(因为受信任的应用程序不会有交互式控制台),因此在fd 0 上读取、写入或调用ioctl()应返回ERR_NOT_SUPPORTED错误。
  • 1 - 标准输出。写入标准输出的数据可以路由(取决于 LK 调试级别)到 UART 和/或非安全侧可用的内存日志,具体取决于平台和配置。非关键调试日志和消息应进入标准输出。 read()ioctl()方法是无操作的,应该返回ERR_NOT_SUPPORTED错误。
  • 2 - 标准误差。写入标准错误的数据应路由到非安全端可用的 UART 或内存日志,具体取决于平台和配置。建议仅将关键消息写入标准错误,因为该流很可能不受限制。 read()ioctl()方法是无操作的,应该返回ERR_NOT_SUPPORTED错误。

尽管这组文件描述符可以扩展以实现更多的fds (以实现特定于平台的扩展),但扩展文件描述符需要谨慎进行。扩展文件描述符很容易产生冲突,通常不建议这样做。

文件描述符 API 中的方法

读()

尝试从指定的文件描述符读取最多count个字节的数据。

long read(uint32_t fd, void *buf, uint32_t count);

[in] fd :从中读取的文件描述符

[out] buf :指向存储数据的缓冲区的指针

[in] count :要读取的最大字节数

[retval]:返回读取的字节数;否则为负错误

写()

将最多count个字节的数据写入指定的文件描述符。

long write(uint32_t fd, void *buf, uint32_t count);

[in] fd :要写入的文件描述符

[out] buf : 指向要写入的数据的指针

[in] count :要写入的最大字节数

[retval]:返回写入的字节数;否则为负错误

读写控制()

为给定的文件描述符调用指定的ioctl命令。

long ioctl(uint32_t fd, uint32_t cmd, void *args);

[in] fd :要调用ioctl()的文件描述符

[in] cmd : ioctl命令

[in/out] args :指向ioctl()参数的指针

杂项API

其他 API 中的方法

获取时间()

返回当前系统时间(以纳秒为单位)。

long gettime(uint32_t clock_id, uint32_t flags, int64_t *time);

[in] clock_id :平台相关;传递零作为默认值

[in] flags :保留,应为零

[out] time :指向存储当前时间的int64_t值的指针

[retval]:成功时为NO_ERROR ;否则为负错误

纳米睡眠()

将调用应用程序的执行暂停指定的时间段,并在该时间段后恢复。

long nanosleep(uint32_t clock_id, uint32_t flags, uint64_t sleep_time)

[in] clock_id :保留,应为零

[in] flags :保留,应为零

[in] sleep_time :睡眠时间(纳秒)

[retval]:成功时为NO_ERROR ;否则为负错误

可信应用服务器示例

以下示例应用程序显示了上述 API 的用法。该示例创建一个“回显”服务,该服务处理多个传入连接,并将其从源自安全端或非安全端的客户端收到的所有消息反射回调用方。

#include <uapi/err.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <trusty_ipc.h>
#define LOG_TAG "echo_srv"
#define TLOGE(fmt, ...) \
    fprintf(stderr, "%s: %d: " fmt, LOG_TAG, __LINE__, ##__VA_ARGS__)

# define MAX_ECHO_MSG_SIZE 64

static
const char * srv_name = "com.android.echo.srv.echo";

static uint8_t msg_buf[MAX_ECHO_MSG_SIZE];

/*
 *  Message handler
 */
static int handle_msg(handle_t chan) {
  int rc;
  struct iovec iov;
  ipc_msg_t msg;
  ipc_msg_info_t msg_inf;

  iov.iov_base = msg_buf;
  iov.iov_len = sizeof(msg_buf);

  msg.num_iov = 1;
  msg.iov = &iov;
  msg.num_handles = 0;
  msg.handles = NULL;

  /* get message info */
  rc = get_msg(chan, &msg_inf);
  if (rc == ERR_NO_MSG)
    return NO_ERROR; /* no new messages */

  if (rc != NO_ERROR) {
    TLOGE("failed (%d) to get_msg for chan (%d)\n",
      rc, chan);
    return rc;
  }

  /* read msg content */
  rc = read_msg(chan, msg_inf.id, 0, &msg);
  if (rc < 0) {
    TLOGE("failed (%d) to read_msg for chan (%d)\n",
      rc, chan);
    return rc;
  }

  /* update number of bytes received */
  iov.iov_len = (size_t) rc;

  /* send message back to the caller */
  rc = send_msg(chan, &msg);
  if (rc < 0) {
    TLOGE("failed (%d) to send_msg for chan (%d)\n",
      rc, chan);
    return rc;
  }

  /* retire message */
  rc = put_msg(chan, msg_inf.id);
  if (rc != NO_ERROR) {
    TLOGE("failed (%d) to put_msg for chan (%d)\n",
      rc, chan);
    return rc;
  }

  return NO_ERROR;
}

/*
 *  Channel event handler
 */
static void handle_channel_event(const uevent_t * ev) {
  int rc;

  if (ev->event & IPC_HANDLE_POLL_MSG) {
    rc = handle_msg(ev->handle);
    if (rc != NO_ERROR) {
      /* report an error and close channel */
      TLOGE("failed (%d) to handle event on channel %d\n",
        rc, ev->handle);
      close(ev->handle);
    }
    return;
  }
  if (ev->event & IPC_HANDLE_POLL_HUP) {
    /* closed by peer. */
    close(ev->handle);
    return;
  }
}

/*
 *  Port event handler
 */
static void handle_port_event(const uevent_t * ev) {
  uuid_t peer_uuid;

  if ((ev->event & IPC_HANDLE_POLL_ERROR) ||
    (ev->event & IPC_HANDLE_POLL_HUP) ||
    (ev->event & IPC_HANDLE_POLL_MSG) ||
    (ev->event & IPC_HANDLE_POLL_SEND_UNBLOCKED)) {
    /* should never happen with port handles */
    TLOGE("error event (0x%x) for port (%d)\n",
      ev->event, ev->handle);
    abort();
  }
  if (ev->event & IPC_HANDLE_POLL_READY) {
    /* incoming connection: accept it */
    int rc = accept(ev->handle, &peer_uuid);
    if (rc < 0) {
      TLOGE("failed (%d) to accept on port %d\n",
        rc, ev->handle);
      return;
    }
    handle_t chan = rc;
    while (true){
      struct uevent cev;

      rc = wait(chan, &cev, INFINITE_TIME);
      if (rc < 0) {
        TLOGE("wait returned (%d)\n", rc);
        abort();
      }
      handle_channel_event(&cev);
      if (cev.event & IPC_HANDLE_POLL_HUP) {
        return;
      }
    }
  }
}


/*
 *  Main application entry point
 */
int main(void) {
  int rc;
  handle_t port;

  /* Initialize service */
  rc = port_create(srv_name, 1, MAX_ECHO_MSG_SIZE,
    IPC_PORT_ALLOW_NS_CONNECT |
    IPC_PORT_ALLOW_TA_CONNECT);
  if (rc < 0) {
    TLOGE("Failed (%d) to create port %s\n",
      rc, srv_name);
    abort();
  }
  port = (handle_t) rc;

  /* enter main event loop */
  while (true) {
    uevent_t ev;

    ev.handle = INVALID_IPC_HANDLE;
    ev.event = 0;
    ev.cookie = NULL;

    /* wait forever */
    rc = wait(port, &ev, INFINITE_TIME);
    if (rc == NO_ERROR) {
      /* got an event */
      handle_port_event(&ev);
    } else {
      TLOGE("wait returned (%d)\n", rc);
      abort();
    }
  }
  return 0;
}

run_end_to_end_msg_test()方法向“echo”服务异步发送 10,000 条消息并处理回复。

static int run_echo_test(void)
{
  int rc;
  handle_t chan;
  uevent_t uevt;
  uint8_t tx_buf[64];
  uint8_t rx_buf[64];
  ipc_msg_info_t inf;
  ipc_msg_t   tx_msg;
  iovec_t     tx_iov;
  ipc_msg_t   rx_msg;
  iovec_t     rx_iov;

  /* prepare tx message buffer */
  tx_iov.base = tx_buf;
  tx_iov.len  = sizeof(tx_buf);
  tx_msg.num_iov = 1;
  tx_msg.iov     = &tx_iov;
  tx_msg.num_handles = 0;
  tx_msg.handles = NULL;

  memset (tx_buf, 0x55, sizeof(tx_buf));

  /* prepare rx message buffer */
  rx_iov.base = rx_buf;
  rx_iov.len  = sizeof(rx_buf);
  rx_msg.num_iov = 1;
  rx_msg.iov     = &rx_iov;
  rx_msg.num_handles = 0;
  rx_msg.handles = NULL;

  /* open connection to echo service */
  rc = sync_connect(srv_name, 1000);
  if(rc < 0)
    return rc;

  /* got channel */
  chan = (handle_t)rc;

  /* send/receive 10000 messages asynchronously. */
  uint tx_cnt = 10000;
  uint rx_cnt = 10000;

  while (tx_cnt || rx_cnt) {
    /* send messages until all buffers are full */
while (tx_cnt) {
    rc = send_msg(chan, &tx_msg);
      if (rc == ERR_NOT_ENOUGH_BUFFER)
      break;  /* no more space */
    if (rc != 64) {
      if (rc > 0) {
        /* incomplete send */
        rc = ERR_NOT_VALID;
}
      goto abort_test;
}
    tx_cnt--;
  }

  /* wait for reply msg or room */
  rc = wait(chan, &uevt, 1000);
  if (rc != NO_ERROR)
    goto abort_test;

  /* drain all messages */
  while (rx_cnt) {
    /* get a reply */
      rc = get_msg(chan, &inf);
    if (rc == ERR_NO_MSG)
        break;  /* no more messages  */
  if (rc != NO_ERROR)
goto abort_test;

  /* read reply data */
    rc = read_msg(chan, inf.id, 0, &rx_msg);
  if (rc != 64) {
    /* unexpected reply length */
    rc = ERR_NOT_VALID;
    goto abort_test;
}

  /* discard reply */
  rc = put_msg(chan, inf.id);
  if (rc != NO_ERROR)
    goto abort_test;
  rx_cnt--;
  }
}

abort_test:
  close(chan);
  return rc;
}

非安全世界 API 和应用程序

一组从安全端发布并标有IPC_PORT_ALLOW_NS_CONNECT属性的 Trusty 服务可供运行在非安全端的内核和用户空间程序访问。

非安全端(内核和用户空间)的执行环境与安全端的执行环境截然不同。因此,不是针对两种环境使用单个库,而是有两组不同的 API。在内核中,Client API 由 trusty-ipc 内核驱动程序提供,并注册一个字符设备节点,用户空间进程可以使用该节点与安全端运行的服务进行通信。

用户空间 Trusty IPC 客户端 API

用户空间 Trusty IPC 客户端 API 库是设备节点fd之上的薄层。

用户空间程序通过调用tipc_connect()来启动通信会话,初始化与指定Trusty服务的连接。在内部, tipc_connect()调用打开指定的设备节点以获取文件描述符,并调用TIPC_IOC_CONNECT ioctl()调用,其中argp参数指向包含要连接的服务名称的字符串。

#define TIPC_IOC_MAGIC  'r'
#define TIPC_IOC_CONNECT  _IOW(TIPC_IOC_MAGIC, 0x80, char *)

生成的文件描述符只能用于与创建它的服务进行通信。当不再需要连接时,应通过调用tipc_close()来关闭文件描述符。

tipc_connect()调用获得的文件描述符表现为典型的字符设备节点;文件描述符:

  • 如果需要可以切换到非阻塞模式
  • 可以使用标准write()调用将消息发送到另一端
  • 可以轮询(使用poll()调用或select()调用)传入消息作为常规文件描述符的可用性
  • 可以读取以检索传入消息

调用者通过对指定的fd执行写入调用来向 Trusty 服务发送消息。传递给上述write()调用的所有数据均由 trusty-ipc 驱动程序转换为消息。该消息被传递到安全端,其中数据由 Trusty 内核中的 IPC 子系统处理,并路由到正确的目的地,并作为特定通道句柄上的IPC_HANDLE_POLL_MSG事件传递到应用程序事件循环。根据特定的服务特定协议,Trusty 服务可能会发送一条或多条回复消息,这些消息被传送回非安全端并放置在适当的通道文件描述符消息队列中,以供用户空间应用程序读取read()称呼。

Tipc_connect()

打开指定的tipc设备节点并发起与指定Trusty服务的连接。

int tipc_connect(const char *dev_name, const char *srv_name);

[in] dev_name :要打开的 Trusty IPC 设备节点的路径

[in] srv_name :要连接的已发布 Trusty 服务的名称

[retval]:成功时有效文件描述符,否则为-1。

Tipc_close()

关闭与文件描述符指定的 Trusty 服务的连接。

int tipc_close(int fd);

[in] fd :先前由tipc_connect()调用打开的文件描述符

内核 Trusty IPC 客户端 API

内核 Trusty IPC 客户端 API 可用于内核驱动程序。用户空间 Trusty IPC API 是在此 API 之上实现的。

一般来说,此 API 的典型用法包括调用者使用tipc_create_channel()函数创建struct tipc_chan对象,然后使用tipc_chan_connect()调用启动与安全端运行的 Trusty IPC 服务的连接。可以通过调用tipc_chan_shutdown()tipc_chan_destroy()来终止与远程端的连接以清理资源。

收到连接已成功建立的通知(通过handle_event()回调)后,调用者将执行以下操作:

  • 使用tipc_chan_get_txbuf_timeout()调用获取消息缓冲区
  • 撰写消息,并且
  • 使用tipc_chan_queue_msg()方法对消息进行排队,以传送到通道所连接的Trusty服务(在安全侧)

排队成功后,调用者应该忘记消息缓冲区,因为消息缓冲区在经过远程端处理后最终会返回到空闲缓冲池(以供以后重用,用于其他消息)。如果无法对此类缓冲区进行排队或不再需要,用户只需调用tipc_chan_put_txbuf()

API 用户通过处理handle_msg()通知回调(在 trusty-ipc rx工作队列的上下文中调用)接收来自远程端的消息,该回调提供指向包含要处理的传入消息的rx缓冲区的指针。

预计handle_msg()回调实现将返回一个指向有效struct tipc_msg_buf的指针。如果它是在本地处理并且不再需要的话,它可以与传入消息缓冲区相同。或者,如果传入缓冲区已排队等待进一步处理,则它可以是通过tipc_chan_get_rxbuf()调用获得的新缓冲区。必须跟踪分离的rx缓冲区,并最终在不再需要时使用tipc_chan_put_rxbuf()调用将其释放。

内核 Trusty IPC 客户端 API 中的方法

Tipc_create_channel()

为特定的 trusty-ipc 设备创建并配置 Trusty IPC 通道的实例。

struct tipc_chan *tipc_create_channel(struct device *dev,
                          const struct tipc_chan_ops *ops,
                              void *cb_arg);

[in] dev :指向为其创建设备通道的 trusty-ipc 的指针

[in] ops :指向struct tipc_chan_ops指针,其中填充了特定于调用者的回调

[in] cb_arg :指向将传递给tipc_chan_ops回调的数据的指针

[retval]:成功时指向新创建的struct tipc_chan实例的指针,否则为ERR_PTR(err)

一般来说,调用者必须提供两个回调,当相应的活动发生时,这两个回调将被异步调用。

void (*handle_event)(void *cb_arg, int event)事件来通知调用者有关通道状态更改的信息。

[in] cb_arg :指向传递给tipc_create_channel()调用的数据的指针

[in] event :可以是以下值之一的事件:

  • TIPC_CHANNEL_CONNECTED - 表示成功连接到远程端
  • TIPC_CHANNEL_DISCONNECTED - 表示远程端拒绝了新的连接请求或请求断开先前连接的通道
  • TIPC_CHANNEL_SHUTDOWN - 表示远程端正在关闭,永久终止所有连接

struct tipc_msg_buf *(*handle_msg)(void *cb_arg, struct tipc_msg_buf *mb)回调来提供已通过指定通道接收到新消息的通知:

  • [in] cb_arg :指向传递给tipc_create_channel()调用的数据的指针
  • [in] mb :指向描述传入消息的struct tipc_msg_buf指针
  • [retval]:回调实现预计返回一个指向struct tipc_msg_buf指针,如果消息在本地处理并且不再需要,则该指针可以与作为mb参数接收的指针相同(或者它可以是由tipc_chan_get_rxbuf()调用)

Tipc_chan_connect()

启动与指定 Trusty IPC 服务的连接。

int tipc_chan_connect(struct tipc_chan *chan, const char *port);

[in] chan :指向tipc_create_chan()调用返回的通道的指针

[in] port :指向包含要连接的服务名称的字符串的指针

[retval]:成功时为 0,否则为负错误

当连接建立时,通过接收handle_event回调来通知调用者。

tipc_chan_shutdown()

终止之前由tipc_chan_connect()调用启动的与Trusty IPC 服务的连接。

int tipc_chan_shutdown(struct tipc_chan *chan);

[in] chan :指向tipc_create_chan()调用返回的通道的指针

tipc_chan_destroy()

销毁指定的 Trusty IPC 通道。

void tipc_chan_destroy(struct tipc_chan *chan);

[in] chan :指向tipc_create_chan()调用返回的通道的指针

tipc_chan_get_txbuf_timeout()

获取可用于通过指定通道发送数据的消息缓冲区。如果缓冲区不能立即可用,则调用者可能会被阻止指定的超时(以毫秒为单位)。

struct tipc_msg_buf *
tipc_chan_get_txbuf_timeout(struct tipc_chan *chan, long timeout);

[in] chan :指向将消息排队的通道的指针

[in] chan :等待tx缓冲区变得可用的最大超时

[retval]:成功时有效消息缓冲区,错误时ERR_PTR(err)

tipc_chan_queue_msg()

将要通过指定 Trusty IPC 通道发送的消息排队。

int tipc_chan_queue_msg(struct tipc_chan *chan, struct tipc_msg_buf *mb);

[in] chan :指向将消息排队的通道的指针

[in] mb:指向队列消息的指针(通过tipc_chan_get_txbuf_timeout()调用获得)

[retval]:成功时为 0,否则为负错误

tipc_chan_put_txbuf()

释放先前通过tipc_chan_get_txbuf_timeout()调用获得的指定Tx消息缓冲区。

void tipc_chan_put_txbuf(struct tipc_chan *chan,
                         struct tipc_msg_buf *mb);

[in] chan : 指向该消息缓冲区所属通道的指针

[in] mb : 指向要释放的消息缓冲区的指针

[返回值]:无

tipc_chan_get_rxbuf()

获取可用于通过指定通道接收消息的新消息缓冲区。

struct tipc_msg_buf *tipc_chan_get_rxbuf(struct tipc_chan *chan);

[in] chan : 指向该消息缓冲区所属通道的指针

[retval]:成功时有效消息缓冲区,错误时ERR_PTR(err)

tipc_chan_put_rxbuf()

释放先前通过tipc_chan_get_rxbuf()调用获得的指定消息缓冲区。

void tipc_chan_put_rxbuf(struct tipc_chan *chan,
                         struct tipc_msg_buf *mb);

[in] chan : 指向该消息缓冲区所属通道的指针

[in] mb : 指向要释放的消息缓冲区的指针

[返回值]:无