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,
應用程式可以將其用作句柄無效或未設定的指示。
設定cookie()
將呼叫者提供的私有資料與指定的句柄相關聯。
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
伺服器首先建立一個或多個代表其服務端點的命名連接埠。每個連接埠都由一個句柄表示。
伺服器 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]:可以與伺服器交換訊息的通道句柄;如果為負則錯誤
如果未指定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 訊息的結構
如下所示,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
: 指向要釋放的訊息緩衝區的指針
[傳回值]:無