Trusty API 參考資料

Trusty 提供的 API 可用來開發兩種應用程式/服務:

  • 在 TEE 處理器上執行的可信任應用程式或服務
  • 在主要處理器上執行,且使用所提供服務的一般/不受信任的應用程式 由信任的應用程式提供

值得信賴 API 通常描述 Trusty 處理序間通訊 (IPC) 系統, 包括與不安全世界的通訊這類軟體是在 主要處理器可以使用 Trusty API 連結至信任的應用程式/服務 並與使用者交換任意訊息,就像透過 IP 網路服務一樣。 模型的資料格式和語意,是由應用程式決定 訊息。可靠的訊息傳送方式 Google 的底層基礎架構 在主要處理器上執行),且通訊也完全非同步。

連接埠與通道

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_ERROR 如果發生在 指定的逾時間隔;如果經過指定的逾時時間,但沒有回應,則為 ERR_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

伺服器一開始會建立一或多個已命名的通訊埠, 服務端點每個充電座都以控點表示。

Server API 中的方法

通訊埠_create()

建立已命名的服務通訊埠。

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() 呼叫。收到連線後 由 Pod 所設定的 IPC_HANDLE_POLL_READY 位元指定 uevent_t 結構的 event 欄位, 伺服器應呼叫 accept() 來完成建立連線並建立 管道 (以 另一個控制代碼) 來處理收到的訊息。

接受()

接受傳入的連線,並取得頻道的帳號代碼。

long accept(uint32_t handle_id, uuid_t *peer_uuid);

[in] handle_id:這個控制代碼代表用戶端已連線的通訊埠

[out] peer_uuid:指向 uuid_t 結構的指標 填入連線用戶端應用程式的 UUID。這項服務 如果連線是來自不安全世界,則會設為全部零

[retval]:處理伺服器可對該通道執行的操作 (如果不是負數的話) 與用戶端交換訊息 (或其他錯誤代碼)

用戶端 API

本節包含 Client API 的方法。

Client API 中的方法

Connect()

啟動連至由名稱指定的通訊埠的連線。

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()IPC_HANDLE_POLL_READY 指定的連線完成事件 在開始之前,請在 uevent_t 結構的事件欄位中設定位元 執行一般作業

訊息 API

Messaging API 呼叫可讓系統透過 先前建立的連線 (頻道)。Messaging 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 結構的陣列。可信 讀取及寫入這些區塊 方法是使用 iov 陣列。可說明緩衝區的內容 iov 陣列是完全隨機的。

Messaging API 中的方法

send_msg()

透過指定頻道傳送訊息。

long send_msg(uint32_t handle, ipc_msg_t *msg);

[in] handle:用於傳送訊息的管道

[in] msg:指向說明訊息的 ipc_msg_t structure

[retval]:成功傳送的位元組總數;否則就會發生負面錯誤

用戶端 (或伺服器) 嘗試透過頻道傳送訊息,且 目的地對等互連訊息佇列中沒有任何空間,則管道可能 進入封鎖傳送狀態的狀態 (如果是簡易同步,就不應該發生這個情況 可能會發生 系統會傳回 ERR_NOT_ENOUGH_BUFFER 錯誤代碼。 在這樣的情況下,呼叫端必須等待對等點釋出 藉由擷取處理和取消訊息的方式,在接收佇列中增加空間, 由應用程式的 IPC_HANDLE_POLL_SEND_UNBLOCKED 位元設定 uevent_t 結構的 event 欄位 wait() 呼叫傳回的字串。

get_msg()

取得傳入訊息佇列中下一訊息的相關中繼資訊

特定頻道的 ID 值

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,範圍涵蓋所有待處理訊息 並填入每則訊息的總長度如果已在 Pod 中設定及允許 用戶端可能會同時擁有多個未解決 (已開啟) 的訊息, 特定頻道

[retval]:NO_ERROR 表示成功;否則就會發生負面錯誤

read_msg()

從 對於指定偏移值而言

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

File Descriptor API 包含 read()write()、 和 ioctl() 呼叫。這些呼叫都可以在預先定義的 (靜態) 檔案組上運作 也就是以小數字表示的描述元在目前的 檔案描述元空間與處理序間通訊 (IPC) 控制代碼分開 空白鍵。Trusty 中的 File Descriptor API 是 ,類似傳統的檔案描述元式 API

根據預設,有兩種預先定義的 (標準和知名) 檔案描述元:

  • 0 - 標準輸入內容。標準輸入值 fd 的預設實作方式 是免人工管理 (因為受信任的應用程式不應有 遊戲),因此讀取、書寫或叫用ioctl() (fd 0 應傳回 ERR_NOT_SUPPORTED 錯誤。
  • 1 - 標準輸出。可以轉送寫入標準輸出的資料 (取決於 LK 偵錯層級) 到 UART 和/或可在不安全網站上使用的記憶體記錄 視平台和設定而定非關鍵的偵錯記錄檔 訊息應以標準輸出格式顯示《read()》和《ioctl()》 方法為不可操作,且應該傳回 ERR_NOT_SUPPORTED 錯誤。
  • 2 - 標準錯誤。寫入標準錯誤的資料應轉送至 UART 視平台和平台而定 此外還會從 0 自動調整資源配置 您完全不必調整資源調度設定建議您只將重要訊息寫入標準 錯誤,因為這個串流很有可能會發生節流限制。read()ioctl() 方法為非作業,應傳回 ERR_NOT_SUPPORTED 錯誤。

雖然這個檔案描述元集可以擴充 fds (用於實作平台專屬的擴充功能)、擴充檔案描述元需求 請謹慎運動擴充檔案描述元相當容易 因此通常不建議採取此做法。

File Descriptor API 的方法

read()

嘗試從指定檔案描述元讀取最多 count 個位元組的資料。

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

[in] fd:要讀取的檔案描述元

[out] buf:指向要儲存資料的緩衝區

[in] count:可讀取的位元組數上限

[retval]:傳回讀取的位元組數;否則就會發生負面錯誤

Write()

最多會將 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() 的檔案描述元

[in] cmdioctl 指令

[in/out] args:指向 ioctl() 引數的指標

其他 API

Miscellaneous API 中的方法

gettime()

傳回目前的系統時間 (以奈秒為單位)。

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

[in] clock_id:取決於平台;預設的值為 0

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

[out] time:指向用於儲存目前時間的 int64_t

[retval]:NO_ERROR 表示成功;否則就會發生負面錯誤

Nanosleep()

在指定時間範圍內暫停執行呼叫應用程式的執行作業 已恢復訂閱

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 則訊息 並轉換成服務和控制代碼 以及回覆內容

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 和應用程式

一組 Trusty 服務,從安全端發布,並標註 IPC_PORT_ALLOW_NS_CONNECT 屬性,可供核心存取 使用者空間程式 有安全疑慮的情況

非安全端 (核心和使用者空間) 上的執行環境為 與安全端的執行環境有極大差異 因此,不會有兩個環境共用一個程式庫,而是有兩個 不同的 API 組合在核心中,Client API 是由 Trusty-ipc 核心驅動程式,並註冊可用的字元裝置節點 藉由使用者空間程序與

使用者空間 Trusty IPC 用戶端 API

使用者空間 Trusty IPC Client API 程式庫是位於 裝置節點「fd」。

使用者空間程式啟動了通訊工作階段 呼叫 tipc_connect() 初始化連至指定的 Trusty 服務的連線。Google 內部 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() 呼叫進行輪詢 做為一般檔案描述元的可用性
  • 可讀取以擷取收到的訊息

呼叫端會對 Trusty 服務執行寫入呼叫 指定的 fd。傳遞至上述 write() 呼叫的所有資料 會由 Trusty-ipc 驅動程式轉換成訊息訊息是 傳輸至安全端,其中 IPC 子系統處理資料所在的安全端 Trusty 核心並轉送至正確的目的地,並傳送至應用程式 特定管道上做為 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 Client API 適用於核心驅動程式。該使用者 space Trusty IPC API 是由這個 API 之上實作。

一般來說,這個 API 的常見用途包含建立呼叫端 使用 tipc_create_channel() 建立 struct tipc_chan 物件 函式,然後使用 tipc_chan_connect() 呼叫啟動 連線至 可終止與遠端端的連線 呼叫 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 頻道建立和設定執行個體 。

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

終止與先前啟動的 Trusty IPC 服務的連線 tipc_chan_connect() 呼叫。

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

將訊息排入指定的佇列 值得信賴的處理序間通訊 (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()

釋出指定的 Tx 訊息緩衝區 先前經由 tipc_chan_get_txbuf_timeout() 呼叫取得

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]:無