Trusty menyediakan API untuk mengembangkan dua class aplikasi dan layanan:
- Aplikasi dan layanan tepercaya yang berjalan di prosesor TEE
- Aplikasi normal dan tidak tepercaya yang berjalan di prosesor utama dan menggunakan layanan yang disediakan oleh aplikasi tepercaya
Trusty API umumnya menjelaskan sistem komunikasi antarproses (IPC) Trusty, termasuk komunikasi dengan dunia yang tidak aman. Software yang berjalan di prosesor utama dapat menggunakan Trusty API untuk terhubung ke aplikasi dan layanan tepercaya serta bertukar pesan arbitrer dengan aplikasi dan layanan tersebut, seperti layanan jaringan melalui IP. Terserah aplikasi untuk menentukan format data dan semantik pesan ini menggunakan protokol tingkat aplikasi. Pengiriman pesan yang andal dijamin oleh infrastruktur Trusty yang mendasarinya (dalam bentuk driver yang berjalan di prosesor utama), dan komunikasinya sepenuhnya asinkron.
Port dan saluran
Port digunakan oleh aplikasi Tepercaya untuk mengekspos endpoint layanan dalam bentuk
jalur bernama yang dihubungkan oleh klien. Tindakan ini akan memberikan ID layanan sederhana berbasis string
untuk digunakan klien. Konvensi penamaannya adalah penamaan gaya DNS terbalik, misalnya com.google.servicename
.
Saat terhubung ke port, klien akan menerima saluran untuk berinteraksi dengan layanan. Layanan harus menerima koneksi masuk, dan saat melakukannya, layanan juga akan menerima saluran. Pada dasarnya, port digunakan untuk mencari layanan, lalu komunikasi terjadi melalui sepasang saluran yang terhubung (yaitu, instance koneksi di port). Saat klien terhubung ke port, koneksi dua arah simetris akan dibuat. Dengan menggunakan jalur full-duplex ini, klien dan server dapat bertukar pesan arbitrer hingga salah satu pihak memutuskan untuk menghentikan koneksi.
Hanya aplikasi tepercaya sisi aman atau modul kernel Tepercaya yang dapat membuat port. Aplikasi yang berjalan di sisi tidak aman (di dunia normal) hanya dapat terhubung ke layanan yang dipublikasikan oleh sisi aman.
Bergantung pada persyaratan, aplikasi tepercaya dapat menjadi klien dan server secara bersamaan. Aplikasi tepercaya yang memublikasikan layanan (sebagai server) mungkin perlu terhubung ke layanan lain (sebagai klien).
Handle API
Handle adalah bilangan bulat tanpa tanda tangan yang mewakili resource seperti port dan saluran, mirip dengan deskriptor file di UNIX. Setelah dibuat, handle akan ditempatkan ke dalam tabel handle khusus aplikasi dan dapat direferensikan nanti.
Pemanggil dapat mengaitkan data pribadi dengan nama sebutan channel menggunakan
metode set_cookie()
.
Metode di Handle API
Nama sebutan hanya valid dalam konteks aplikasi. Aplikasi tidak boleh
meneruskan nilai nama sebutan ke aplikasi lain kecuali jika
ditentukan secara eksplisit. Nilai nama sebutan hanya boleh ditafsirkan dengan membandingkannya dengan
INVALID_IPC_HANDLE #define,
yang dapat digunakan aplikasi sebagai
indikasi bahwa nama sebutan tidak valid atau tidak ditetapkan.
set_cookie()
Mengaitkan data pribadi yang diberikan pemanggil dengan nama sebutan channel yang ditentukan.
long set_cookie(uint32_t handle, void *cookie)
[in] handle
: Setiap nama sebutan channel yang ditampilkan oleh salah satu panggilan API
[in] cookie
: Pointer ke data ruang pengguna arbitrer di aplikasi Trusty
[retval]: NO_ERROR
jika berhasil, kode error < 0
jika tidak
Panggilan ini berguna untuk menangani peristiwa saat terjadi di lain waktu setelah handle dibuat. Mekanisme penanganan peristiwa menyediakan handle dan cookie-nya kembali ke pengendali peristiwa.
Peristiwa dapat ditunggu dengan menggunakan panggilan wait()
.
wait()
Menunggu peristiwa terjadi di nama sebutan channel tertentu selama jangka waktu yang ditentukan.
long wait(uint32_t handle_id, uevent_t *event, unsigned long timeout_msecs)
[in] handle_id
: Setiap nama sebutan channel yang ditampilkan oleh salah satu panggilan API
[out] event
: Pointer ke struktur yang mewakili
peristiwa yang terjadi pada handle ini
[in] timeout_msecs
: Nilai waktu tunggu dalam milidetik; nilai -1 adalah waktu tunggu tanpa batas
[retval]: NO_ERROR
jika peristiwa yang valid terjadi dalam
interval waktu tunggu yang ditentukan; ERR_TIMED_OUT
jika waktu tunggu yang ditentukan telah berlalu, tetapi tidak ada
peristiwa yang terjadi; < 0
untuk error 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;
Kolom 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 peristiwa yang benar-benar tertunda,
pemanggil harus memulai ulang waktu tunggu
IPC_HANDLE_POLL_ERROR
- terjadi error internal yang tidak ditentukan
IPC_HANDLE_POLL_READY
- bergantung pada jenis nama sebutan channel, sebagai berikut:
- Untuk port, nilai ini menunjukkan bahwa ada koneksi yang tertunda
- Untuk saluran, nilai ini menunjukkan bahwa koneksi asinkron
(lihat
connect()
) telah dibuat
Peristiwa berikut hanya relevan untuk channel:
IPC_HANDLE_POLL_HUP
- menunjukkan bahwa saluran telah ditutup oleh peerIPC_HANDLE_POLL_MSG
- menunjukkan bahwa ada pesan yang menunggu konfirmasi untuk channel iniIPC_HANDLE_POLL_SEND_UNBLOCKED
- menunjukkan bahwa penelepon yang sebelumnya diblokir pengirimannya dapat mencoba mengirim pesan lagi (lihat deskripsisend_msg()
untuk mengetahui detailnya)
Pengendali peristiwa harus siap menangani kombinasi peristiwa yang ditentukan, karena beberapa bit mungkin ditetapkan secara bersamaan. Misalnya, untuk saluran, pesan yang tertunda dan koneksi yang ditutup oleh peer secara bersamaan dapat terjadi.
Sebagian besar peristiwa bersifat melekat. Status ini akan tetap ada selama kondisi yang mendasarinya
tetap 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 peluang untuk
menanganinya.
Handle dapat dihancurkan dengan memanggil metode close()
.
close()
Menghancurkan resource yang terkait dengan handle yang ditentukan dan menghapusnya dari tabel handle.
long close(uint32_t handle_id);
[in] handle_id
: Nama sebutan channel yang akan dihancurkan
[retval]: 0 jika berhasil; error negatif jika tidak
Server API
Server dimulai dengan membuat satu atau beberapa port bernama yang mewakili endpoint layanannya. Setiap port diwakili oleh nama sebutan.
Metode di Server API
port_create()
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 yang dijelaskan di atas). Nama
ini harus unik di seluruh sistem; upaya untuk membuat duplikat akan gagal.
[in] num_recv_bufs
: Jumlah maksimum buffering yang dapat dialokasikan sebelumnya oleh saluran di
port ini untuk memfasilitasi pertukaran data dengan klien. Buffer dihitung
secara terpisah untuk data yang berjalan dalam kedua arah, sehingga menentukan 1 di sini berarti 1
buffer pengiriman dan 1 buffer penerimaan dialokasikan sebelumnya. Secara umum, jumlah buffering
yang diperlukan bergantung pada perjanjian protokol tingkat tinggi antara klien dan
server. Jumlahnya bisa sekecil 1 jika protokol sangat sinkron
(mengirim pesan, menerima balasan sebelum mengirim pesan lain). Namun, jumlahnya dapat
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). Set buffer
yang dialokasikan adalah per saluran, sehingga dua koneksi terpisah (saluran)
akan memiliki set buffer terpisah.
[in] recv_buf_size
: Ukuran maksimum setiap buffer dalam
kumpulan buffer di atas. Nilai ini
bergantung pada protokol dan secara efektif membatasi ukuran pesan maksimum yang dapat Anda tukarkan
dengan peer
[in] flags
: Kombinasi flag yang menentukan perilaku port tambahan
Nilai ini harus berupa kombinasi dari nilai berikut:
IPC_PORT_ALLOW_TA_CONNECT
- mengizinkan koneksi dari aplikasi aman lainnya
IPC_PORT_ALLOW_NS_CONNECT
- mengizinkan koneksi dari dunia yang tidak aman
[retval]: Handle ke port yang dibuat jika bukan negatif atau error tertentu jika negatif
Server kemudian melakukan polling daftar handle port untuk koneksi masuk
menggunakan panggilan wait()
. Setelah menerima permintaan
koneksi yang ditunjukkan oleh bit IPC_HANDLE_POLL_READY
yang ditetapkan di
kolom event
dari struktur uevent_t
, server harus memanggil accept()
untuk menyelesaikan pembuatan koneksi dan membuat
saluran (diwakili oleh
handle lain) yang kemudian dapat di-polling untuk pesan masuk.
accept()
Menerima koneksi masuk dan mendapatkan nama sebutan channel.
long accept(uint32_t handle_id, uuid_t *peer_uuid);
[in] handle_id
: Nama sebutan yang mewakili port tempat klien terhubung
[out] peer_uuid
: Pointer ke struktur uuid_t
yang akan
diisi dengan UUID aplikasi klien yang terhubung. Nilai ini
ditetapkan ke semua nol jika koneksi berasal dari dunia yang tidak aman
[retval]: Nama sebutan untuk saluran (jika bukan negatif) tempat server dapat bertukar pesan dengan klien (atau kode error jika tidak)
Client API
Bagian ini berisi metode di Client API.
Metode di Client API
connect()
Memulai koneksi ke port yang ditentukan berdasarkan nama.
long connect(const char *path, uint flags);
[in] path
: Nama port yang dipublikasikan oleh aplikasi Trusty
[in] flags
: Menentukan perilaku tambahan yang bersifat opsional
[retval]: Nama sebutan untuk saluran tempat pesan dapat ditukar dengan server; error jika negatif
Jika tidak ada flags
yang ditentukan (parameter flags
ditetapkan ke 0), memanggil connect()
akan memulai koneksi sinkron
ke port yang ditentukan yang segera
menampilkan error jika port tidak ada, dan membuat pemblokiran hingga
server menerima koneksi.
Perilaku ini dapat diubah dengan menentukan kombinasi dua nilai, yang 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 langsung ada saat dieksekusi,
bukan langsung gagal.
IPC_CONNECT_ASYNC
- jika ditetapkan, akan memulai koneksi asinkron. Aplikasi
harus melakukan polling untuk
handle yang ditampilkan dengan memanggil wait()
untuk
peristiwa penyelesaian koneksi yang ditunjukkan oleh bit
IPC_HANDLE_POLL_READY
yang ditetapkan di kolom peristiwa struktur uevent_t
sebelum memulai
operasi normal.
Messaging API
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 nama sebutan channel dengan mengeluarkan panggilan connect()
, dan server mendapatkan nama sebutan channel dari panggilan accept()
, seperti yang dijelaskan di atas.
Struktur pesan Trusty
Seperti yang ditunjukkan di bawah ini, pesan yang dipertukarkan oleh Trusty API memiliki struktur minimal, sehingga server dan klien dapat menyepakati 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;
Pesan dapat terdiri dari satu atau beberapa buffering yang tidak berdekatan yang direpresentasikan oleh
array struktur iovec_t
. Trusty melakukan operasi baca dan tulis scatter-gather ke blok ini menggunakan array iov
. Konten buffering yang dapat dijelaskan
oleh array iov
sepenuhnya arbitrer.
Metode di Messaging API
send_msg()
Mengirim pesan melalui saluran yang ditentukan.
long send_msg(uint32_t handle, ipc_msg_t *msg);
[in] handle
: Nama sebutan channel yang akan digunakan untuk mengirim pesan
[in] msg
: Pointer ke ipc_msg_t structure
yang menjelaskan pesan
[retval]: Jumlah total byte yang dikirim jika berhasil; error negatif jika tidak
Jika klien (atau server) mencoba mengirim pesan melalui saluran dan
tidak ada ruang di antrean pesan peer tujuan, saluran mungkin
memasuki status pengiriman yang diblokir (hal ini tidak boleh terjadi untuk protokol permintaan/balasan sinkron
yang sederhana, tetapi mungkin terjadi dalam kasus yang lebih rumit) yang
ditunjukkan dengan menampilkan kode error ERR_NOT_ENOUGH_BUFFER
.
Dalam hal ini, pemanggil harus menunggu hingga peer mengosongkan beberapa
ruang dalam antrean terimanya dengan mengambil pesan penanganan dan penghentian,
yang ditunjukkan oleh bit IPC_HANDLE_POLL_SEND_UNBLOCKED
yang ditetapkan di
kolom event
dari struktur uevent_t
yang ditampilkan oleh panggilan wait()
.
get_msg()
Mendapatkan meta-informasi tentang pesan berikutnya dalam antrean pesan masuk
dari channel yang ditentukan.
long get_msg(uint32_t handle, ipc_msg_info_t *msg_info);
[in] handle
: Nama sebutan channel tempat pesan baru harus diambil
[out] msg_info
: Struktur informasi pesan yang 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 belum terkirim, dan total panjang setiap pesan diisi. Jika dikonfigurasi dan diizinkan oleh protokol, dapat ada beberapa pesan yang belum selesai (terbuka) sekaligus untuk saluran tertentu.
[retval]: NO_ERROR
jika berhasil; error negatif jika tidak
read_msg()
Membaca konten 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
: Nama sebutan channel tempat membaca pesan
[in] msg_id
: ID pesan yang akan dibaca
[in] offset
: Offset ke dalam pesan tempat pembacaan dimulai
[out] msg
: Pointer ke struktur ipc_msg_t
yang menjelaskan
kumpulan buffer tempat menyimpan data pesan
yang masuk
[retval]: Jumlah total byte yang disimpan dalam buffering msg
jika berhasil; error negatif jika tidak
Metode read_msg
dapat dipanggil beberapa kali mulai dari
offset yang berbeda (tidak harus
berurutan) sesuai kebutuhan.
put_msg()
Menghentikan pesan dengan ID yang ditentukan.
long put_msg(uint32_t handle, uint32_t msg_id);
[in] handle
: Nama sebutan channel tempat pesan diterima
[in] msg_id
: ID pesan yang dihentikan
[retval]: NO_ERROR
jika berhasil; error negatif jika tidak
Konten pesan tidak dapat diakses setelah pesan dihentikan dan buffer yang ditempatinya telah dibebaskan.
File Descriptor API
File Descriptor API mencakup panggilan read()
, write()
,
dan ioctl()
. Semua panggilan ini dapat beroperasi pada kumpulan deskripsi file
yang telah ditentukan sebelumnya (statis) yang secara tradisional direpresentasikan oleh angka kecil. Dalam implementasi
saat ini, ruang deskriptor file terpisah dari ruang handle
IPC. File Descriptor API di Trusty
mirip dengan API berbasis deskripsi file tradisional.
Secara default, ada 3 deskriptor file standar dan umum yang telah ditentukan sebelumnya:
- 0 - input standar. Penerapan default input standar
fd
adalah no-op (karena aplikasi tepercaya tidak diharapkan memiliki konsol interaktif) sehingga membaca, menulis, atau memanggilioctl()
padafd
0 akan menampilkan errorERR_NOT_SUPPORTED
. - 1 - output standar. Data yang ditulis ke output standar dapat dirutekan (bergantung
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 non-kritis
harus masuk dalam output standar. Metode
read()
danioctl()
adalah no-ops dan akan menampilkan errorERR_NOT_SUPPORTED
. - 2 - error standar. Data yang ditulis ke error standar harus dirutekan ke UART
atau log memori yang tersedia di sisi yang tidak aman, bergantung pada platform dan
konfigurasi. Sebaiknya hanya tulis pesan penting ke error
standar, karena aliran ini sangat mungkin tidak dibatasi. Metode
read()
danioctl()
bersifat tanpa pengoperasian dan akan menampilkan errorERR_NOT_SUPPORTED
.
Meskipun kumpulan deskripsi file ini dapat diperluas untuk menerapkan lebih banyak
fds
(untuk menerapkan ekstensi khusus platform), perlu
melakukan perluasan deskripsi file dengan hati-hati. Memperluas deskripsi file cenderung menimbulkan
konflik dan umumnya tidak direkomendasikan.
Metode di File Descriptor API
read()
Mencoba membaca data hingga count
byte dari deskriptor file yang ditentukan.
long read(uint32_t fd, void *buf, uint32_t count);
[in] fd
: Deskriptor file tempat membaca
[out] buf
: Pointer ke buffer tempat data disimpan
[in] count
: Jumlah maksimum byte yang akan dibaca
[retval]: Menampilkan jumlah byte yang dibaca; error negatif jika tidak
write()
Menulis data hingga count
byte ke deskriptor file yang ditentukan.
long write(uint32_t fd, void *buf, uint32_t count);
[in] fd
: Deskriptor file yang akan ditulis
[out] buf
: Pointer ke data yang akan ditulis
[in] count
: Jumlah maksimum byte yang akan ditulis
[retval]: Menampilkan jumlah byte yang ditulis; error negatif jika tidak
ioctl()
Memanggil perintah ioctl
yang ditentukan untuk deskriptor file tertentu.
long ioctl(uint32_t fd, uint32_t cmd, void *args);
[in] fd
: Deskripsi file tempat memanggil ioctl()
[in] cmd
: Perintah ioctl
[in/out] args
: Pointer ke argumen ioctl()
API Lainnya
Metode di Miscellaneous API
gettime()
Menampilkan waktu sistem saat ini (dalam nanodetik).
long gettime(uint32_t clock_id, uint32_t flags, int64_t *time);
[in] clock_id
: Bergantung pada platform; teruskan nol untuk default
[in] flags
: Direservasi, harus nol
[out] time
: Pointer ke nilai int64_t
yang akan menyimpan waktu saat ini
[retval]: NO_ERROR
jika berhasil; error negatif jika tidak
nanosleep()
Menangguhkan eksekusi aplikasi panggilan selama jangka waktu tertentu dan melanjutkannya setelah jangka waktu tersebut.
long nanosleep(uint32_t clock_id, uint32_t flags, uint64_t sleep_time)
[in] clock_id
: Direservasi, harus nol
[in] flags
: Direservasi, harus nol
[in] sleep_time
: Waktu tidur dalam nanodetik
[retval]: NO_ERROR
jika berhasil; error negatif jika tidak
Contoh server aplikasi tepercaya
Aplikasi contoh berikut menunjukkan penggunaan API di atas. Contoh ini 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 app 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()
mengirim 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
Kumpulan layanan Trusty, yang dipublikasikan dari sisi aman dan ditandai dengan
atribut IPC_PORT_ALLOW_NS_CONNECT
, dapat diakses oleh kernel
dan program ruang pengguna yang berjalan di
sisi tidak aman.
Lingkungan eksekusi di sisi yang tidak aman (kernel dan ruang pengguna) sangat berbeda dengan lingkungan eksekusi di sisi aman. Oleh karena itu, ada dua kumpulan API yang berbeda, bukan satu library untuk kedua lingkungan. Di kernel, Client API 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.
API Klien Trusty IPC ruang pengguna
Library Trusty IPC Client API ruang pengguna adalah lapisan tipis di atas
node perangkat fd
.
Program ruang pengguna memulai sesi komunikasi
dengan memanggil tipc_connect()
,
melakukan inisialisasi koneksi ke layanan Trusty yang ditentukan. Secara internal,
panggilan tipc_connect()
membuka node perangkat yang ditentukan untuk
mendapatkan deskripsi file dan memanggil panggilan
TIPC_IOC_CONNECT ioctl()
dengan parameter argp
yang mengarah ke string yang berisi
nama layanan yang akan dihubungkan.
#define TIPC_IOC_MAGIC 'r' #define TIPC_IOC_CONNECT _IOW(TIPC_IOC_MAGIC, 0x80, char *)
Deskripsi file yang dihasilkan hanya dapat digunakan untuk berkomunikasi dengan layanan
yang membuatnya. Deskripsi file harus ditutup dengan
memanggil tipc_close()
saat koneksi tidak diperlukan lagi.
Deskripsi file yang diperoleh oleh panggilan tipc_connect()
berperilaku sebagai node perangkat karakter standar; deskripsi file:
- Dapat dialihkan ke mode non-pemblokiran jika diperlukan
- Dapat ditulis menggunakan panggilan
write()
standar untuk mengirim pesan ke sisi lain - Dapat di-polling (menggunakan panggilan
poll()
atau panggilanselect()
) untuk mengetahui ketersediaan pesan masuk sebagai deskriptor file reguler - Dapat dibaca untuk mengambil pesan masuk
Pemanggil mengirim 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 trusty-ipc. Pesan
dikirim ke sisi aman tempat data ditangani oleh subsistem IPC di
kernel Trusty dan dirutekan ke tujuan yang tepat serta dikirim ke loop
peristiwa aplikasi sebagai peristiwa IPC_HANDLE_POLL_MSG
pada nama saluran
tertentu. Bergantung pada protokol khusus
layanan tertentu, layanan Trusty dapat mengirim satu atau beberapa pesan
balasan yang dikirim kembali ke sisi yang tidak aman dan ditempatkan di
antrean pesan deskripsi file saluran yang sesuai untuk diambil oleh panggilan
read()
aplikasi ruang pengguna.
tipc_connect()
Membuka node perangkat tipc
yang ditentukan dan memulai
koneksi ke layanan Trusty yang ditentukan.
int tipc_connect(const char *dev_name, const char *srv_name);
[in] dev_name
: Jalur ke node perangkat Trusty IPC yang akan dibuka
[in] srv_name
: Nama layanan Trusty yang dipublikasikan yang akan dihubungkan
[retval]: Deskripsi file yang 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
: Deskripsi file yang sebelumnya dibuka oleh
panggilan tipc_connect()
Kernel Trusty IPC Client API
Trusty IPC Client API kernel tersedia untuk driver kernel. Trusty IPC API ruang pengguna diterapkan di atas API ini.
Secara umum, penggunaan umum API ini terdiri dari pemanggil yang membuat
objek struct tipc_chan
menggunakan fungsi
tipc_create_channel()
, lalu menggunakan panggilan tipc_chan_connect()
untuk memulai
koneksi ke layanan Trusty IPC yang berjalan di sisi
aman. Koneksi ke sisi jarak jauh dapat dihentikan dengan
memanggil tipc_chan_shutdown()
, diikuti dengan
tipc_chan_destroy()
untuk membersihkan resource.
Setelah menerima notifikasi (melalui callback handle_event()
)
bahwa koneksi telah berhasil dibuat, pemanggil akan melakukan
hal berikut:
- Mendapatkan buffering pesan menggunakan panggilan
tipc_chan_get_txbuf_timeout()
- Menulis pesan, dan
- Mengantrekan pesan menggunakan metode
tipc_chan_queue_msg()
untuk dikirim ke layanan Tepercaya (di sisi aman), yang terhubung ke saluran
Setelah antrean berhasil, pemanggil harus melupakan buffering pesan
karena buffering pesan pada akhirnya akan kembali ke kumpulan buffering bebas setelah
diproses oleh sisi jarak jauh (untuk digunakan kembali nanti, untuk pesan lain). Pengguna
hanya perlu memanggil tipc_chan_put_txbuf()
jika gagal
mengantrekan buffering tersebut atau tidak diperlukan lagi.
Pengguna API menerima pesan dari sisi jarak jauh dengan menangani
callback notifikasi handle_msg()
(yang dipanggil dalam
konteks workqueue rx
trusty-ipc) yang
memberikan pointer ke buffering rx
yang berisi
pesan masuk yang akan ditangani.
Implementasi callback handle_msg()
diharapkan menampilkan pointer ke struct tipc_msg_buf
yang valid.
Buffer ini dapat sama dengan buffering pesan masuk jika ditangani secara lokal
dan tidak diperlukan lagi. Atau, buffer ini dapat berupa buffer baru yang diperoleh oleh
panggilan tipc_chan_get_rxbuf()
jika buffer yang masuk dimasukkan ke dalam antrean
untuk pemrosesan lebih lanjut. Buffer rx
yang terpisah harus dilacak
dan pada akhirnya dirilis menggunakan panggilan tipc_chan_put_rxbuf()
saat
tidak lagi diperlukan.
Metode di Kernel Trusty IPC Client API
tipc_create_channel()
Membuat dan mengonfigurasi instance saluran Trusty IPC untuk perangkat trusty-ipc tertentu.
struct tipc_chan *tipc_create_channel(struct device *dev, const struct tipc_chan_ops *ops, void *cb_arg);
[in] dev
: Pointer ke trusty-ipc yang channel
perangkatnya dibuat
[in] ops
: Pointer ke struct tipc_chan_ops
,
dengan callback khusus
pemanggil diisi
[in] cb_arg
: Pointer ke data yang diteruskan
ke callback tipc_chan_ops
[retval]: Pointer 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 saat aktivitas yang sesuai terjadi.
Peristiwa void (*handle_event)(void *cb_arg, int event)
dipanggil
untuk memberi tahu pemanggil tentang perubahan status saluran.
[in] cb_arg
: Pointer ke data yang diteruskan ke
panggilan tipc_create_channel()
[in] event
: Peristiwa yang dapat berupa salah satu nilai berikut:
TIPC_CHANNEL_CONNECTED
- menunjukkan koneksi yang berhasil ke sisi jarak jauhTIPC_CHANNEL_DISCONNECTED
- menunjukkan bahwa sisi jarak jauh menolak permintaan koneksi baru atau meminta pemutusan koneksi untuk saluran yang terhubung sebelumnyaTIPC_CHANNEL_SHUTDOWN
- menunjukkan bahwa sisi jarak jauh dimatikan, yang menghentikan semua koneksi secara permanen
Callback struct tipc_msg_buf *(*handle_msg)(void *cb_arg, struct tipc_msg_buf *mb)
dipanggil untuk memberikan notifikasi bahwa pesan baru telah
diterima melalui saluran yang ditentukan:
- [in]
cb_arg
: Pointer ke data yang diteruskan ke panggilantipc_create_channel()
- [in]
mb
: Pointer kestruct tipc_msg_buf
yang menjelaskan pesan masuk - [retval]: Implementasi callback diharapkan menampilkan pointer ke
struct tipc_msg_buf
yang dapat berupa pointer yang sama yang diterima sebagai parametermb
jika pesan ditangani secara lokal dan tidak diperlukan lagi (atau dapat berupa buffering baru yang diperoleh oleh panggilantipc_chan_get_rxbuf()
)
tipc_chan_connect()
Memulai koneksi ke layanan Trusty IPC yang ditentukan.
int tipc_chan_connect(struct tipc_chan *chan, const char *port);
[in] chan
: Pointer ke saluran yang ditampilkan oleh
panggilan tipc_create_chan()
[in] port
: Pointer ke string yang berisi
nama layanan yang akan dihubungkan
[retval]: 0 jika berhasil, error negatif jika tidak
Pemanggil akan diberi tahu saat koneksi dibuat dengan menerima
callback handle_event
.
tipc_chan_shutdown()
Mengakhiri koneksi ke layanan Trusty IPC yang sebelumnya dimulai
oleh panggilan tipc_chan_connect()
.
int tipc_chan_shutdown(struct tipc_chan *chan);
[in] chan
: Pointer ke saluran yang ditampilkan oleh
panggilan tipc_create_chan()
tipc_chan_destroy()
Menghancurkan saluran Trusty IPC yang ditentukan.
void tipc_chan_destroy(struct tipc_chan *chan);
[in] chan
: Pointer ke saluran yang ditampilkan oleh
panggilan tipc_create_chan()
tipc_chan_get_txbuf_timeout()
Mendapatkan buffer pesan yang dapat digunakan untuk mengirim data melalui saluran yang ditentukan. Jika buffer tidak segera tersedia, pemanggil dapat diblokir untuk waktu tunggu yang ditentukan (dalam milidetik).
struct tipc_msg_buf * tipc_chan_get_txbuf_timeout(struct tipc_chan *chan, long timeout);
[in] chan
: Pointer ke saluran tempat pesan akan dimasukkan ke antrean
[in] chan
: Waktu tunggu maksimum untuk menunggu hingga
buffer tx
tersedia
[retval]: Buffer pesan yang valid jika berhasil,
ERR_PTR(err)
jika terjadi error
tipc_chan_queue_msg()
Membuat antrean pesan yang akan dikirim melalui saluran Trusty IPC yang ditentukan.
int tipc_chan_queue_msg(struct tipc_chan *chan, struct tipc_msg_buf *mb);
[in] chan
: Pointer ke saluran tempat pesan akan dimasukkan ke dalam antrean
[in] mb:
Pointer ke pesan yang akan dimasukkan ke antrean
(diperoleh dengan panggilan tipc_chan_get_txbuf_timeout()
)
[retval]: 0 jika berhasil, error negatif jika tidak
tipc_chan_put_txbuf()
Merilis buffering pesan Tx
yang ditentukan
yang sebelumnya diperoleh oleh panggilan tipc_chan_get_txbuf_timeout()
.
void tipc_chan_put_txbuf(struct tipc_chan *chan, struct tipc_msg_buf *mb);
[in] chan
: Pointer ke saluran tempat
buffer pesan ini berada
[in] mb
: Pointer ke buffer pesan yang akan dirilis
[retval]: None
tipc_chan_get_rxbuf()
Mendapatkan buffering 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
: Pointer ke channel tempat buffer pesan ini berada
[retval]: Buffer pesan yang valid jika berhasil, ERR_PTR(err)
jika terjadi error
tipc_chan_put_rxbuf()
Melepas buffer pesan yang ditentukan sebelumnya yang diperoleh oleh
panggilan tipc_chan_get_rxbuf()
.
void tipc_chan_put_rxbuf(struct tipc_chan *chan, struct tipc_msg_buf *mb);
[in] chan
: Pointer ke channel tempat buffer pesan ini berada
[in] mb
: Pointer ke buffer pesan yang akan dirilis
[retval]: None