可信賴的 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 :指向一個uuud_t結構的指針,該結構將用連接的客戶端應用程序的 UUID 填充。如果連接來自非安全世界,它將被設置為全零

[retval]:服務器可以與客戶端交換消息的通道(如果非負)的句柄(否則為錯誤代碼)

客戶端 API

本節包含客戶端 API 中的方法。

客戶端 API 中的方法

連接()

啟動到由名稱指定的端口的連接。

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

[in] path : Trusty 應用程序發布的端口名稱

[in] flags : 指定附加的可選行為

[retval]:可以與服務器交換消息的通道的句柄;如果為負,則錯誤

如果沒有指定flags flags設置為 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 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位指示。

get_msg()

獲取有關傳入消息隊列中下一條消息的元信息

指定頻道的。

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 ;否則為負錯誤

read_msg()

從指定的偏移量開始讀取具有指定 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 : 開始閱讀的消息的偏移量

[in] msg :指向ipc_msg_t結構的指針,該結構描述了一組用於存儲傳入消息數據的緩衝區

[retval]:成功時存儲在dst緩衝區中的總字節數;否則為負錯誤

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

為給定的文件描述符調用指定的ioctl命令。

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

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

[在] cmd : ioctl命令

[輸入/輸出] args : 指向ioctl()參數的指針

雜項 API

雜項 API 中的方法

獲取時間()

返回當前系統時間(以納秒為單位)。

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

[in] clock_id :平台相關;默認傳遞零

[in] flags : 保留,應該為零

[in] 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 的用法。該示例創建了一個“echo”服務,該服務處理多個傳入連接並將其從客戶端接收到的來自安全或非安全端的所有消息反射回調用者。

#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()方法將 10,000 條消息異步發送到“echo”服務並處理回复。

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。在內核中,客戶端 API 由 trusty-ipc 內核驅動程序提供,並註冊一個字符設備節點,用戶空間進程可以使用該節點與運行在安全端的服務進行通信。

用戶空間 Trusty IPC Client API

用戶空間 Trusty IPC Client 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。

提示關閉()

關閉與文件描述符指定的 Trusty 服務的連接。

int tipc_close(int fd);

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

內核可信 IPC 客戶端 API

內核 Trusty IPC Client 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()調用最終釋放它。

Kernel Trusty IPC Client 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 :指向要釋放的消息緩衝區的指針

[retval]:無

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 : 指向要釋放的消息緩衝區的指針

[retval]:無