Trusty menyediakan API untuk mengembangkan dua kelas aplikasi/layanan:
- Aplikasi atau layanan tepercaya yang berjalan pada prosesor TEE
- Aplikasi normal/tidak tepercaya yang berjalan pada prosesor utama dan menggunakan layanan yang disediakan oleh aplikasi tepercaya
Trusty API umumnya menggambarkan sistem komunikasi antar-proses (IPC) Trusty, termasuk komunikasi dengan dunia yang tidak aman. Perangkat lunak yang berjalan pada prosesor utama dapat menggunakan Trusty API untuk terhubung ke aplikasi/layanan tepercaya dan bertukar pesan arbitrer dengan aplikasi/layanan tersebut seperti layanan jaringan melalui IP. Terserah pada aplikasi untuk menentukan format data dan semantik pesan-pesan ini menggunakan protokol tingkat aplikasi. Pengiriman pesan yang andal dijamin oleh infrastruktur Trusty yang mendasarinya (dalam bentuk driver yang berjalan pada prosesor utama), dan komunikasi sepenuhnya tidak sinkron.
Port dan saluran
Port digunakan oleh aplikasi Trusty untuk mengekspos titik akhir layanan dalam bentuk jalur bernama yang terhubung dengan klien. Ini memberikan ID layanan sederhana berbasis string untuk digunakan klien. Konvensi penamaannya adalah penamaan bergaya DNS terbalik, misalnya com.google.servicename
.
Ketika klien terhubung ke port, klien menerima saluran untuk berinteraksi dengan layanan. Layanan harus menerima sambungan masuk, dan jika menerima sambungan masuk, layanan juga menerima saluran. Intinya, port digunakan untuk mencari layanan dan kemudian komunikasi terjadi melalui sepasang saluran yang terhubung (yaitu, contoh koneksi pada port). Ketika klien terhubung ke port, koneksi dua arah simetris dibuat. Dengan menggunakan jalur dupleks penuh ini, klien dan server dapat bertukar pesan sewenang-wenang hingga kedua belah pihak memutuskan untuk memutuskan koneksi.
Hanya aplikasi tepercaya sisi aman atau modul kernel Terpercaya yang dapat membuat port. Aplikasi yang berjalan di sisi tidak aman (di dunia normal) hanya dapat terhubung ke layanan yang diterbitkan oleh sisi aman.
Bergantung pada kebutuhan, aplikasi tepercaya dapat menjadi klien dan server secara bersamaan. Aplikasi tepercaya yang menerbitkan layanan (sebagai server) mungkin perlu tersambung ke layanan lain (sebagai klien).
Tangani API
Pegangan adalah bilangan bulat tak bertanda tangan yang mewakili sumber daya seperti port dan saluran, mirip dengan deskriptor file di UNIX. Setelah pegangan dibuat, pegangan tersebut ditempatkan ke dalam tabel pegangan khusus aplikasi dan dapat direferensikan nanti.
Penelepon dapat mengaitkan data pribadi dengan pegangan menggunakan metode set_cookie()
.
Metode di Handle API
Pegangan hanya valid dalam konteks aplikasi. Suatu aplikasi tidak boleh meneruskan nilai pegangan ke aplikasi lain kecuali ditentukan secara eksplisit. Nilai pegangan saja harus diinterpretasikan dengan membandingkannya dengan INVALID_IPC_HANDLE #define,
yang dapat digunakan aplikasi sebagai indikasi bahwa pegangan tidak valid atau tidak disetel.
set_kue()
Mengaitkan data pribadi yang disediakan penelepon dengan pegangan tertentu.
long set_cookie(uint32_t handle, void *cookie)
[in] handle
: Pegangan apa pun yang dikembalikan oleh salah satu panggilan API
[dalam] cookie
: Penunjuk ke data ruang pengguna sewenang-wenang di aplikasi Trusty
[retval]: NO_ERROR
jika berhasil, < 0
kode kesalahan sebaliknya
Panggilan ini berguna untuk menangani peristiwa yang terjadi di lain waktu setelah pegangan dibuat. Mekanisme penanganan event memasok handle dan cookie-nya kembali ke event handler.
Penanganan dapat ditunggu untuk suatu peristiwa dengan menggunakan panggilan wait()
.
Tunggu()
Menunggu suatu peristiwa terjadi pada pegangan tertentu untuk jangka waktu tertentu.
long wait(uint32_t handle_id, uevent_t *event, unsigned long timeout_msecs)
[di] handle_id
: Pegangan apa pun yang dikembalikan oleh salah satu panggilan API
[out] event
: Penunjuk ke struktur yang mewakili peristiwa yang terjadi pada pegangan ini
[dalam] timeout_msecs
: Nilai batas waktu dalam milidetik; nilai -1 adalah batas waktu yang tidak terbatas
[retval]: NO_ERROR
jika peristiwa valid terjadi dalam interval waktu habis yang ditentukan; ERR_TIMED_OUT
jika batas waktu yang ditentukan telah berlalu namun tidak ada peristiwa yang terjadi; < 0
untuk kesalahan lainnya
Setelah berhasil ( retval == NO_ERROR
), panggilan wait()
mengisi struktur uevent_t
yang ditentukan dengan informasi tentang peristiwa yang terjadi.
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;
Bidang event
berisi kombinasi nilai berikut:
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
- tidak ada acara yang tertunda, penelepon harus memulai kembali penantian
IPC_HANDLE_POLL_ERROR
- telah terjadi kesalahan internal yang tidak ditentukan
IPC_HANDLE_POLL_READY
- bergantung pada jenis pegangan, sebagai berikut:
- Untuk port, nilai ini menunjukkan adanya koneksi yang tertunda
- Untuk saluran, nilai ini menunjukkan bahwa koneksi asinkron (lihat
connect()
) telah dibuat
Peristiwa berikut hanya relevan untuk saluran:
-
IPC_HANDLE_POLL_HUP
- menunjukkan bahwa saluran telah ditutup oleh rekannya -
IPC_HANDLE_POLL_MSG
- menunjukkan bahwa ada pesan tertunda untuk saluran ini -
IPC_HANDLE_POLL_SEND_UNBLOCKED
- menunjukkan bahwa penelepon yang sebelumnya diblokir pengiriman mungkin mencoba mengirim pesan lagi (lihat deskripsisend_msg()
untuk detailnya)
Pengendali peristiwa harus siap menangani kombinasi peristiwa tertentu, karena beberapa bit mungkin disetel pada waktu yang sama. Misalnya, untuk suatu saluran, dimungkinkan untuk memiliki pesan yang tertunda, dan koneksi ditutup oleh rekan pada saat yang bersamaan.
Sebagian besar peristiwa bersifat melekat. Mereka bertahan selama kondisi yang mendasarinya masih ada (misalnya semua pesan yang tertunda diterima dan permintaan koneksi yang tertunda ditangani). Pengecualian adalah kasus peristiwa IPC_HANDLE_POLL_SEND_UNBLOCKED
, yang dihapus setelah dibaca dan aplikasi hanya memiliki satu kesempatan untuk menanganinya.
Pegangan dapat dimusnahkan dengan memanggil metode close()
.
menutup()
Menghancurkan sumber daya yang terkait dengan pegangan yang ditentukan dan menghapusnya dari tabel pegangan.
long close(uint32_t handle_id);
[dalam] handle_id
: Pegangan untuk menghancurkan
[retval]: 0 jika berhasil; kesalahan negatif sebaliknya
API server
Server memulai dengan membuat satu atau lebih port bernama yang mewakili titik akhir layanannya. Setiap port diwakili oleh sebuah pegangan.
Metode di Server API
port_buat()
Membuat port layanan bernama.
long port_create (const char *path, uint num_recv_bufs, size_t recv_buf_size, uint32_t flags)
[in] path
: Nama string port (seperti dijelaskan di atas). Nama ini harus unik di seluruh sistem; upaya untuk membuat duplikat akan gagal.
[in] num_recv_bufs
: Jumlah maksimum buffer yang dapat dialokasikan sebelumnya oleh saluran pada port ini untuk memfasilitasi pertukaran data dengan klien. Buffer dihitung secara terpisah untuk data yang dikirim ke dua arah, jadi menentukan 1 di sini berarti 1 buffer pengiriman dan 1 buffer penerimaan telah dialokasikan sebelumnya. Secara umum, jumlah buffer yang dibutuhkan bergantung pada perjanjian protokol tingkat yang lebih tinggi antara klien dan server. Jumlahnya bisa sedikitnya 1 jika protokolnya sangat sinkron (kirim pesan, terima balasan sebelum mengirim pesan lain). Namun jumlahnya bisa lebih banyak jika klien mengharapkan untuk mengirim lebih dari satu pesan sebelum balasan dapat muncul (misalnya, satu pesan sebagai prolog dan pesan lainnya sebagai perintah sebenarnya). Kumpulan buffer yang dialokasikan adalah per saluran, jadi dua koneksi (saluran) terpisah akan memiliki set buffer terpisah.
[dalam] recv_buf_size
: Ukuran maksimum setiap buffer individu dalam set buffer di atas. Nilai ini bergantung pada protokol dan secara efektif membatasi ukuran pesan maksimum yang dapat Anda tukarkan dengan rekan
[in] flags
: Kombinasi flag yang menentukan perilaku port tambahan
Nilai ini harus merupakan kombinasi dari nilai-nilai berikut:
IPC_PORT_ALLOW_TA_CONNECT
- memungkinkan koneksi dari aplikasi aman lainnya
IPC_PORT_ALLOW_NS_CONNECT
- memungkinkan koneksi dari dunia yang tidak aman
[retval]: Menangani port yang dibuat jika non-negatif atau kesalahan tertentu jika negatif
Server kemudian melakukan polling daftar pegangan port untuk koneksi masuk menggunakan panggilan wait()
. Setelah menerima permintaan koneksi yang ditunjukkan oleh set bit IPC_HANDLE_POLL_READY
di bidang event
struktur uevent_t
, server harus memanggil accept()
untuk menyelesaikan pembuatan koneksi dan membuat saluran (diwakili oleh pegangan lain) yang kemudian dapat disurvei untuk pesan masuk .
menerima()
Menerima koneksi masuk dan menangani saluran.
long accept(uint32_t handle_id, uuid_t *peer_uuid);
[di] handle_id
: Pegangan yang mewakili port tempat klien terhubung
[out] peer_uuid
: Penunjuk ke struktur uuid_t
untuk diisi dengan UUID aplikasi klien yang terhubung. Ini akan disetel ke nol semua jika koneksi berasal dari dunia yang tidak aman
[retval]: Menangani saluran (jika non-negatif) di mana server dapat bertukar pesan dengan klien (atau kode kesalahan sebaliknya)
API Klien
Bagian ini berisi metode di API Klien.
Metode di API Klien
Menghubung()
Memulai koneksi ke port yang ditentukan berdasarkan nama.
long connect(const char *path, uint flags);
[di] path
: Nama port yang diterbitkan oleh aplikasi Trusty
[in] flags
: Menentukan perilaku opsional tambahan
[retval]: Menangani saluran di mana pesan dapat dipertukarkan dengan server; kesalahan jika negatif
Jika tidak ada flags
yang ditentukan (parameter flags
disetel ke 0), pemanggilan connect()
akan memulai koneksi sinkron ke port tertentu yang segera mengembalikan kesalahan jika port tidak ada, dan membuat blok hingga server menerima koneksi .
Perilaku ini dapat diubah dengan menentukan kombinasi dua nilai, dijelaskan di bawah:
enum { IPC_CONNECT_WAIT_FOR_PORT = 0x1, IPC_CONNECT_ASYNC = 0x2, };
IPC_CONNECT_WAIT_FOR_PORT
- memaksa panggilan connect()
untuk menunggu jika port yang ditentukan tidak segera ada saat eksekusi, bukannya langsung gagal.
IPC_CONNECT_ASYNC
- jika disetel, memulai koneksi asinkron. Aplikasi harus melakukan polling untuk pegangan yang dikembalikan (dengan memanggil wait()
untuk peristiwa penyelesaian koneksi yang ditunjukkan oleh bit IPC_HANDLE_POLL_READY
yang disetel di bidang peristiwa struktur uevent_t
sebelum memulai operasi normal.
API Pesan
Panggilan Messaging API memungkinkan pengiriman dan pembacaan pesan melalui koneksi (saluran) yang telah dibuat sebelumnya. Panggilan Messaging API sama untuk server dan klien.
Klien menerima pegangan saluran dengan mengeluarkan panggilan connect()
, dan server mendapatkan pegangan saluran dari panggilan accept()
, yang dijelaskan di atas.
Struktur pesan yang dapat dipercaya
Seperti yang ditunjukkan berikut ini, pesan yang dipertukarkan oleh Trusty API memiliki struktur minimal, sehingga server dan klien dapat menyetujui semantik konten sebenarnya:
/* * 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;
Sebuah pesan dapat terdiri dari satu atau lebih buffer yang tidak bersebelahan yang diwakili oleh array struktur iovec_t
. Trusty melakukan pembacaan dan penulisan scatter-gather ke blok-blok ini menggunakan array iov
. Isi buffer yang dapat dijelaskan oleh array iov
sepenuhnya berubah-ubah.
Metode di API Pesan
kirim_pesan()
Mengirim pesan melalui saluran tertentu.
long send_msg(uint32_t handle, ipc_msg_t *msg);
[in] handle
: Menangani saluran untuk mengirim pesan
[dalam] msg
: Penunjuk ke ipc_msg_t structure
yang menjelaskan pesan
[retval]: Jumlah total byte yang dikirim jika berhasil; kesalahan negatif sebaliknya
Jika klien (atau server) mencoba mengirim pesan melalui saluran dan tidak ada ruang di antrian pesan rekan tujuan, saluran mungkin memasuki keadaan kirim-diblokir (hal ini tidak boleh terjadi untuk protokol permintaan/balasan sinkron sederhana tetapi mungkin terjadi dalam kasus yang lebih rumit) yang ditunjukkan dengan mengembalikan kode kesalahan ERR_NOT_ENOUGH_BUFFER
. Dalam kasus seperti ini, pemanggil harus menunggu hingga peer mengosongkan sebagian ruang di antrean penerimaannya dengan mengambil pesan yang ditangani dan dihentikan, yang ditunjukkan oleh bit IPC_HANDLE_POLL_SEND_UNBLOCKED
yang disetel dalam bidang event
struktur uevent_t
yang dikembalikan oleh panggilan wait()
.
dapatkan_pesan()
Mendapatkan informasi meta tentang pesan berikutnya dalam antrean pesan masuk
dari saluran tertentu.
long get_msg(uint32_t handle, ipc_msg_info_t *msg_info);
[in] handle
: Menangani saluran di mana pesan baru harus diambil
[keluar] msg_info
: Struktur informasi pesan dijelaskan sebagai berikut:
typedef struct ipc_msg_info { size_t len; /* total message length */ uint32_t id; /* message id */ } ipc_msg_info_t;
Setiap pesan diberi ID unik di seluruh kumpulan pesan yang beredar, dan total panjang setiap pesan diisi. Jika dikonfigurasi dan diizinkan oleh protokol, mungkin ada beberapa pesan yang beredar (dibuka) sekaligus untuk saluran tertentu.
[retval]: NO_ERROR
jika berhasil; kesalahan negatif sebaliknya
baca_pesan()
Membaca isi pesan dengan ID yang ditentukan mulai dari offset yang ditentukan.
long read_msg(uint32_t handle, uint32_t msg_id, uint32_t offset, ipc_msg_t *msg);
[in] handle
: Menangani saluran untuk membaca pesan
[dalam] msg_id
: ID pesan yang akan dibaca
[in] offset
: Diimbangi ke dalam pesan untuk mulai membaca
[keluar] msg
: Penunjuk ke struktur ipc_msg_t
yang menjelaskan sekumpulan buffer untuk menyimpan data pesan masuk
[retval]: Jumlah total byte yang disimpan dalam buffer msg
jika berhasil; kesalahan negatif sebaliknya
Metode read_msg
dapat dipanggil beberapa kali dimulai pada offset yang berbeda (tidak harus berurutan) sesuai kebutuhan.
put_msg()
Menghentikan pesan dengan ID tertentu.
long put_msg(uint32_t handle, uint32_t msg_id);
[in] handle
: Pegangan saluran tempat pesan tiba
[dalam] msg_id
: ID pesan dihentikan
[retval]: NO_ERROR
jika berhasil; kesalahan negatif sebaliknya
Konten pesan tidak dapat diakses setelah pesan dihentikan dan buffer yang ditempati telah dibebaskan.
API Deskriptor File
File Descriptor API mencakup panggilan read()
, write()
, dan ioctl()
. Semua panggilan ini dapat beroperasi pada kumpulan deskriptor file yang telah ditentukan sebelumnya (statis) yang biasanya diwakili oleh angka kecil. Dalam implementasi saat ini, ruang deskriptor file terpisah dari ruang pegangan IPC. File Descriptor API di Trusty mirip dengan API berbasis deskriptor file tradisional.
Secara default, ada 3 deskriptor file yang telah ditentukan sebelumnya (standar dan terkenal):
- 0 - masukan standar. Implementasi default dari input standar
fd
adalah no-op (karena aplikasi tepercaya tidak diharapkan memiliki konsol interaktif) sehingga membaca, menulis, atau memanggilioctl()
padafd
0 akan menghasilkan kesalahanERR_NOT_SUPPORTED
. - 1 - keluaran standar. Data yang ditulis ke output standar dapat dirutekan (tergantung pada tingkat debug LK) ke UART dan/atau log memori yang tersedia di sisi tidak aman, bergantung pada platform dan konfigurasi. Log dan pesan debug yang tidak penting harus masuk dalam keluaran standar. Metode
read()
danioctl()
tidak boleh dijalankan dan akan mengembalikan kesalahanERR_NOT_SUPPORTED
. - 2 - kesalahan standar. Data yang ditulis ke kesalahan standar harus dirutekan ke UART atau log memori yang tersedia di sisi tidak aman, bergantung pada platform dan konfigurasi. Disarankan untuk hanya menulis pesan penting ke kesalahan standar, karena aliran ini kemungkinan besar tidak akan dibatasi. Metode
read()
danioctl()
tidak boleh dijalankan dan akan mengembalikan kesalahanERR_NOT_SUPPORTED
.
Meskipun kumpulan deskriptor file ini dapat diperluas untuk mengimplementasikan lebih banyak fds
(untuk mengimplementasikan ekstensi khusus platform), perluasan deskriptor file perlu dilakukan dengan hati-hati. Memperluas deskriptor file rentan menimbulkan konflik dan umumnya tidak disarankan.
Metode di File Descriptor API
membaca()
Mencoba membaca hingga count
byte data dari deskriptor file tertentu.
long read(uint32_t fd, void *buf, uint32_t count);
[di] fd
: Deskriptor file untuk dibaca
[keluar] buf
: Penunjuk ke buffer untuk menyimpan data
[dalam] count
: Jumlah byte maksimum untuk dibaca
[retval]: Mengembalikan jumlah byte yang dibaca; kesalahan negatif sebaliknya
menulis()
Menulis hingga count
byte data ke deskriptor file yang ditentukan.
long write(uint32_t fd, void *buf, uint32_t count);
[di] fd
: Deskriptor file untuk menulis
[keluar] buf
: Penunjuk ke data yang akan ditulis
[dalam] count
: Jumlah byte maksimum untuk ditulis
[retval]: Mengembalikan jumlah byte yang ditulis; kesalahan negatif sebaliknya
ioctl()
Memanggil perintah ioctl
tertentu untuk deskriptor file tertentu.
long ioctl(uint32_t fd, uint32_t cmd, void *args);
[dalam] fd
: Deskriptor file untuk memanggil ioctl()
[dalam] cmd
: Perintah ioctl
[masuk/keluar] args
: Penunjuk ke argumen ioctl()
API lain-lain
Metode di API Lain-Lain
dapatkan waktu()
Mengembalikan waktu sistem saat ini (dalam nanodetik).
long gettime(uint32_t clock_id, uint32_t flags, int64_t *time);
[dalam] clock_id
: Bergantung pada platform; lulus nol untuk default
[dalam] flags
: Dicadangkan, harus nol
[out] time
: Menunjuk ke nilai int64_t
untuk menyimpan waktu saat ini
[retval]: NO_ERROR
jika berhasil; kesalahan negatif sebaliknya
tidur nano()
Menunda pelaksanaan aplikasi pemanggil untuk jangka waktu tertentu dan melanjutkannya setelah jangka waktu tersebut.
long nanosleep(uint32_t clock_id, uint32_t flags, uint64_t sleep_time)
[dalam] clock_id
: Dicadangkan, harus nol
[dalam] flags
: Dicadangkan, harus nol
[dalam] sleep_time
: Waktu tidur dalam nanodetik
[retval]: NO_ERROR
jika berhasil; kesalahan negatif sebaliknya
Contoh server aplikasi terpercaya
Contoh aplikasi berikut menunjukkan penggunaan API di atas. Sampel membuat layanan "echo" yang menangani beberapa koneksi masuk dan mencerminkan kembali ke pemanggil semua pesan yang diterima dari klien yang berasal dari sisi aman atau tidak aman.
#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; }
Metode run_end_to_end_msg_test()
mengirimkan 10.000 pesan secara asinkron ke layanan "echo" dan menangani balasan.
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 dan aplikasi dunia yang tidak aman
Serangkaian layanan Trusty, diterbitkan dari sisi aman dan ditandai dengan atribut IPC_PORT_ALLOW_NS_CONNECT
, dapat diakses oleh program kernel dan ruang pengguna yang berjalan di sisi tidak aman.
Lingkungan eksekusi di sisi tidak aman (kernel dan ruang pengguna) sangat berbeda dengan lingkungan eksekusi di sisi aman. Oleh karena itu, dibandingkan satu perpustakaan untuk kedua lingkungan, ada dua set API yang berbeda. Di kernel, API Klien disediakan oleh driver kernel trusty-ipc dan mendaftarkan node perangkat karakter yang dapat digunakan oleh proses ruang pengguna untuk berkomunikasi dengan layanan yang berjalan di sisi aman.
Ruang pengguna API Klien IPC yang Terpercaya
Pustaka API Klien IPC Trusty ruang pengguna adalah lapisan tipis di atas node perangkat fd
.
Program ruang pengguna memulai sesi komunikasi dengan memanggil tipc_connect()
, menginisialisasi koneksi ke layanan Trusty yang ditentukan. Secara internal, panggilan tipc_connect()
membuka node perangkat tertentu untuk mendapatkan deskriptor file dan memanggil panggilan TIPC_IOC_CONNECT ioctl()
dengan parameter argp
yang menunjuk ke string yang berisi nama layanan yang akan dihubungkan.
#define TIPC_IOC_MAGIC 'r' #define TIPC_IOC_CONNECT _IOW(TIPC_IOC_MAGIC, 0x80, char *)
Deskriptor file yang dihasilkan hanya dapat digunakan untuk berkomunikasi dengan layanan yang membuat file tersebut dibuat. Deskriptor file harus ditutup dengan memanggil tipc_close()
ketika koneksi tidak diperlukan lagi.
Deskriptor file yang diperoleh dari panggilan tipc_connect()
berperilaku sebagai node perangkat karakter tipikal; deskriptor file:
- Dapat dialihkan ke mode non-pemblokiran jika diperlukan
- Dapat ditulis menggunakan panggilan
write()
standar untuk mengirim pesan ke pihak lain - Dapat disurvei (menggunakan panggilan
poll()
atau panggilanselect()
) untuk ketersediaan pesan masuk sebagai deskriptor file biasa - Dapat dibaca untuk mengambil pesan masuk
Penelepon mengirimkan pesan ke layanan Trusty dengan menjalankan panggilan tulis untuk fd
yang ditentukan. Semua data yang diteruskan ke panggilan write()
di atas diubah menjadi pesan oleh driver IPC yang terpercaya. Pesan dikirim ke sisi aman di mana data ditangani oleh subsistem IPC di kernel Trusty dan dirutekan ke tujuan yang tepat dan dikirim ke loop peristiwa aplikasi sebagai peristiwa IPC_HANDLE_POLL_MSG
pada pegangan saluran tertentu. Tergantung pada protokol khusus layanan tertentu, layanan Trusty dapat mengirim satu atau lebih pesan balasan yang dikirim kembali ke sisi tidak aman dan ditempatkan di antrian pesan deskriptor file saluran yang sesuai untuk diambil oleh aplikasi ruang pengguna read()
panggilan.
tipc_connect()
Membuka node perangkat tipc
tertentu dan memulai koneksi ke layanan Trusty tertentu.
int tipc_connect(const char *dev_name, const char *srv_name);
[dalam] dev_name
: Jalur ke node perangkat IPC Terpercaya yang akan dibuka
[dalam] srv_name
: Nama layanan Terpercaya yang diterbitkan untuk dihubungkan
[retval]: Deskriptor file valid jika berhasil, -1 jika tidak.
tipc_close()
Menutup koneksi ke layanan Trusty yang ditentukan oleh deskriptor file.
int tipc_close(int fd);
[in] fd
: Deskriptor file yang sebelumnya dibuka oleh panggilan tipc_connect()
API Klien IPC Kernel Terpercaya
Kernel Trusty IPC Client API tersedia untuk driver kernel. Ruang pengguna Trusty IPC API diimplementasikan di atas API ini.
Secara umum, penggunaan umum API ini terdiri dari pemanggil yang membuat objek struct tipc_chan
dengan menggunakan fungsi tipc_create_channel()
dan kemudian menggunakan panggilan tipc_chan_connect()
untuk memulai koneksi ke layanan Trusty IPC yang berjalan di sisi aman. Koneksi ke sisi jarak jauh dapat diakhiri dengan memanggil tipc_chan_shutdown()
diikuti dengan tipc_chan_destroy()
untuk membersihkan sumber daya.
Setelah menerima pemberitahuan (melalui panggilan balik handle_event()
) bahwa koneksi telah berhasil dibuat, pemanggil melakukan hal berikut:
- Mendapatkan buffer pesan menggunakan panggilan
tipc_chan_get_txbuf_timeout()
- Menulis pesan, dan
- Mengantrekan pesan menggunakan metode
tipc_chan_queue_msg()
untuk dikirim ke layanan Trusty (di sisi aman), yang terhubung dengan saluran tersebut
Setelah antrian berhasil, pemanggil harus melupakan buffer pesan karena buffer pesan pada akhirnya kembali ke kumpulan buffer gratis setelah diproses oleh pihak jarak jauh (untuk digunakan kembali nanti, untuk pesan lain). Pengguna hanya perlu memanggil tipc_chan_put_txbuf()
jika gagal mengantri buffer tersebut atau tidak diperlukan lagi.
Pengguna API menerima pesan dari sisi jarak jauh dengan menangani panggilan balik notifikasi handle_msg()
(yang dipanggil dalam konteks antrian kerja rx
terpercaya-ipc) yang menyediakan penunjuk ke buffer rx
yang berisi pesan masuk untuk ditangani.
Implementasi callback handle_msg()
diharapkan akan mengembalikan pointer ke struct tipc_msg_buf
yang valid. Ini bisa sama dengan buffer pesan masuk jika ditangani secara lokal dan tidak diperlukan lagi. Atau, ini bisa berupa buffer baru yang diperoleh melalui panggilan tipc_chan_get_rxbuf()
jika buffer yang masuk dimasukkan dalam antrean untuk diproses lebih lanjut. Buffer rx
yang terlepas harus dilacak dan akhirnya dilepaskan menggunakan panggilan tipc_chan_put_rxbuf()
ketika tidak lagi diperlukan.
Metode dalam Kernel Trusty IPC Client API
tipc_create_channel()
Membuat dan mengonfigurasi instance saluran IPC Terpercaya untuk perangkat IPC Terpercaya tertentu.
struct tipc_chan *tipc_create_channel(struct device *dev, const struct tipc_chan_ops *ops, void *cb_arg);
[in] dev
: Penunjuk ke IPC terpercaya tempat saluran perangkat dibuat
[in] ops
: Penunjuk ke struct tipc_chan_ops
, dengan callback khusus pemanggil terisi
[di] cb_arg
: Penunjuk ke data yang akan diteruskan ke panggilan balik tipc_chan_ops
[retval]: Penunjuk ke instance struct tipc_chan
yang baru dibuat jika berhasil, ERR_PTR(err)
jika tidak
Secara umum, pemanggil harus menyediakan dua callback yang dipanggil secara asinkron ketika aktivitas terkait terjadi.
Acara void (*handle_event)(void *cb_arg, int event)
dipanggil untuk memberi tahu pemanggil tentang perubahan status saluran.
[di] cb_arg
: Penunjuk ke data yang diteruskan ke panggilan tipc_create_channel()
[in] event
: Suatu peristiwa yang dapat berupa salah satu dari nilai berikut:
-
TIPC_CHANNEL_CONNECTED
- menunjukkan koneksi yang berhasil ke sisi jarak jauh -
TIPC_CHANNEL_DISCONNECTED
- menunjukkan sisi jarak jauh menolak permintaan koneksi baru atau meminta pemutusan sambungan untuk saluran yang terhubung sebelumnya -
TIPC_CHANNEL_SHUTDOWN
- menunjukkan sisi jarak jauh dimatikan, mengakhiri semua koneksi secara permanen
Callback struct tipc_msg_buf *(*handle_msg)(void *cb_arg, struct tipc_msg_buf *mb)
dipanggil untuk memberikan pemberitahuan bahwa pesan baru telah diterima melalui saluran tertentu:
- [di]
cb_arg
: Penunjuk ke data yang diteruskan ke panggilantipc_create_channel()
- [in]
mb
: Penunjuk kestruct tipc_msg_buf
yang menjelaskan pesan masuk - [retval]: Implementasi panggilan balik diharapkan mengembalikan pointer ke
struct tipc_msg_buf
yang bisa menjadi pointer yang sama yang diterima sebagai parametermb
jika pesan ditangani secara lokal dan tidak diperlukan lagi (atau bisa berupa buffer baru yang diperoleh olehtipc_chan_get_rxbuf()
panggilan)
tipc_chan_connect()
Memulai koneksi ke layanan IPC Terpercaya yang ditentukan.
int tipc_chan_connect(struct tipc_chan *chan, const char *port);
[in] chan
: Penunjuk ke saluran yang dikembalikan oleh panggilan tipc_create_chan()
[di] port
: Penunjuk ke string yang berisi nama layanan yang akan dihubungkan
[retval]: 0 jika berhasil, sebaliknya kesalahan negatif
Penelepon diberitahu ketika koneksi dibuat dengan menerima panggilan balik handle_event
.
tipc_chan_shutdown()
Mengakhiri koneksi ke layanan IPC Terpercaya yang sebelumnya dimulai oleh panggilan tipc_chan_connect()
.
int tipc_chan_shutdown(struct tipc_chan *chan);
[in] chan
: Penunjuk ke saluran yang dikembalikan oleh panggilan tipc_create_chan()
tipc_chan_destroy()
Menghancurkan saluran IPC Terpercaya tertentu.
void tipc_chan_destroy(struct tipc_chan *chan);
[in] chan
: Penunjuk ke saluran yang dikembalikan oleh panggilan tipc_create_chan()
tipc_chan_get_txbuf_timeout()
Mendapatkan buffer pesan yang dapat digunakan untuk mengirim data melalui saluran tertentu. Jika buffer tidak segera tersedia, pemanggil mungkin diblokir selama batas waktu yang ditentukan (dalam milidetik).
struct tipc_msg_buf * tipc_chan_get_txbuf_timeout(struct tipc_chan *chan, long timeout);
[in] chan
: Penunjuk ke saluran tujuan antrian pesan
[in] chan
: Batas waktu maksimum untuk menunggu hingga buffer tx
tersedia
[retval]: Buffer pesan yang valid jika berhasil, ERR_PTR(err)
jika terjadi kesalahan
tipc_chan_queue_msg()
Mengantri pesan untuk dikirim melalui saluran IPC Terpercaya yang ditentukan.
int tipc_chan_queue_msg(struct tipc_chan *chan, struct tipc_msg_buf *mb);
[in] chan
: Penunjuk ke saluran tujuan antrian pesan
[dalam] mb:
Penunjuk ke pesan yang akan diantri (diperoleh melalui panggilan tipc_chan_get_txbuf_timeout()
)
[retval]: 0 jika berhasil, sebaliknya kesalahan negatif
tipc_chan_put_txbuf()
Melepaskan buffer pesan Tx
tertentu yang sebelumnya diperoleh melalui panggilan tipc_chan_get_txbuf_timeout()
.
void tipc_chan_put_txbuf(struct tipc_chan *chan, struct tipc_msg_buf *mb);
[in] chan
: Penunjuk ke saluran tempat buffer pesan ini berada
[in] mb
: Penunjuk ke buffer pesan yang akan dirilis
[pengembalian]: Tidak ada
tipc_chan_get_rxbuf()
Mendapatkan buffer pesan baru yang dapat digunakan untuk menerima pesan melalui saluran yang ditentukan.
struct tipc_msg_buf *tipc_chan_get_rxbuf(struct tipc_chan *chan);
[in] chan
: Penunjuk ke saluran tempat buffer pesan ini berada
[retval]: Buffer pesan yang valid jika berhasil, ERR_PTR(err)
jika terjadi kesalahan
tipc_chan_put_rxbuf()
Melepaskan buffer pesan tertentu yang sebelumnya diperoleh melalui panggilan tipc_chan_get_rxbuf()
.
void tipc_chan_put_rxbuf(struct tipc_chan *chan, struct tipc_msg_buf *mb);
[in] chan
: Penunjuk ke saluran tempat buffer pesan ini berada
[in] mb
: Penunjuk ke buffer pesan yang akan dirilis
[pengembalian]: Tidak ada